From 31c2d45fe64145ed47672754f956fad970e23677 Mon Sep 17 00:00:00 2001 From: Paul Tarter Date: Thu, 16 Nov 2023 14:44:09 -0500 Subject: [PATCH 1/9] removed ManualMap, replaced with ZwLoadDriver --- C/STrace/ManualMap.cpp | 306 ----------------------------------------- C/STrace/ManualMap.h | 26 ---- 2 files changed, 332 deletions(-) delete mode 100644 C/STrace/ManualMap.cpp delete mode 100644 C/STrace/ManualMap.h diff --git a/C/STrace/ManualMap.cpp b/C/STrace/ManualMap.cpp deleted file mode 100644 index 94e9f31..0000000 --- a/C/STrace/ManualMap.cpp +++ /dev/null @@ -1,306 +0,0 @@ -#include -#include - -#include "ManualMap.h" -#include "Logger.h" -#include "Constants.h" - -/* -Modified from: https://github.com/ItsJustMeChris/Manual-Mapper/blob/master/Heroin/needle.cpp -and -https://github.com/DarthTon/Blackbone/blob/master/src/BlackBone/ManualMap/MMap.cpp -*/ -#define DLL_PROCESS_ATTACH 1 -typedef VOID(NTAPI* PIMAGE_TLS_CALLBACK) (PVOID DllHandle, ULONG Reason, PVOID Reserved); - -#define IMAGE_ORDINAL_FLAG64 0x8000000000000000 -#define IMAGE_ORDINAL64(Ordinal) (Ordinal & 0xffff) -#define IMAGE_SNAP_BY_ORDINAL64(Ordinal) ((Ordinal & IMAGE_ORDINAL_FLAG64) != 0) - -#define LOWORD(l) ((uint16_t)(((uintptr_t)(l)) & 0xffff)) -#define HIWORD(l) ((uint16_t)((((uintptr_t)(l)) >> 16) & 0xffff)) - -typedef struct _IMAGE_RELOC -{ - union - { - struct - { - uint16_t wOffset : 12; - uint16_t wType : 4; - }; - uint16_t wData; - }; -} IMAGE_RELOC, * PIMAGE_RELOC; - -bool ManualMapper::loadImage(char* pBase) { - if (!validateImage(pBase)) - return false; - - auto dosHeader = (IMAGE_DOS_HEADER*)pBase; - auto ntHeader = (IMAGE_NT_HEADERS64*)(pBase + dosHeader->e_lfanew); - auto pOptionalHeader = &ntHeader->OptionalHeader; - auto _DllMain = pOptionalHeader->AddressOfEntryPoint ? (tDllMain)(pBase + pOptionalHeader->AddressOfEntryPoint) : 0; - - // I'm assuming this is signed, not sure? - int64_t dwDelta = (int64_t)(pBase - pOptionalHeader->ImageBase); - if (dwDelta) { - if (!pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size) { - LOG_ERROR("[!] image not allocated at preffered base, but has no relocations, loading will attempt to continue\r\n"); - } - - auto dwLimit = (uint64_t)pBase + pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress + pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size; - auto pRelocData = (PIMAGE_BASE_RELOCATION)(pBase + pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); - while ((uint64_t)pRelocData < dwLimit) { - uint64_t dwRelocLimit = (uint64_t)pRelocData + pRelocData->SizeOfBlock; - uint64_t lpBase = (uint64_t)pBase + pRelocData->VirtualAddress; - PIMAGE_RELOC pReloc = (PIMAGE_RELOC)((uint64_t)pRelocData + sizeof(IMAGE_BASE_RELOCATION)); - - while ((uint64_t)pReloc < dwRelocLimit) - { - uint16_t type = pReloc->wType; - int32_t offset = pReloc->wOffset; - - switch (type) - { - case IMAGE_REL_BASED_ABSOLUTE: - break; - case IMAGE_REL_BASED_HIGHLOW: - *((int32_t*)(lpBase + offset)) += (int32_t)dwDelta; - break; - case IMAGE_REL_BASED_DIR64: - *((int64_t*)(lpBase + offset)) += dwDelta; - break; - default: - break; - } - ++pReloc; - } - pRelocData = (PIMAGE_BASE_RELOCATION)pReloc; - } - } - - // Initialize security cookies (needed on drivers Win8+). They do a cmp against the constant in the header - if (pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress) { - uint64_t pCookie = 0; - - switch (ntHeader->FileHeader.Machine) - case IMAGE_FILE_MACHINE_AMD64: { - pCookie = (uint64_t)(((IMAGE_LOAD_CONFIG_DIRECTORY64*)(pBase + pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress))->SecurityCookie); - - //*(uint64_t*)pCookie = rand(); - *(uint64_t*)pCookie = 0x1337; - - // if we somehow hit default ++ it - if (*(uint64_t*)pCookie == 0x2B992DDFA232) - (*(uint64_t*)pCookie)++; - - break; - } - } - - // Walk imports - if (pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size) { - auto* pImportDescr = (IMAGE_IMPORT_DESCRIPTOR*)(pBase + pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); - bool importsAtLeastOneBad = false; - while (pImportDescr->Name) { - char* szMod = pBase + pImportDescr->Name; - - uint64_t* pThunkRef = (uint64_t*)(pBase + pImportDescr->OriginalFirstThunk); - uint64_t* pFuncRef = (uint64_t*)(pBase + pImportDescr->FirstThunk); - - if (!pThunkRef) - pThunkRef = pFuncRef; - - for (; *pThunkRef; ++pThunkRef, ++pFuncRef) { - if (IMAGE_SNAP_BY_ORDINAL64(*pThunkRef)) { - USHORT ordinal = *pThunkRef & 0xFFFF; - LOG_ERROR("[!] DLL Imports ordinal %d from %s. Imports are not supported...fatal\r\n", ordinal, szMod); - importsAtLeastOneBad = true; - } else { - auto pImport = (IMAGE_IMPORT_BY_NAME*)(pBase + (*pThunkRef)); - char* name = pImport->Name; - if (strcmp(szMod, "ntoskrnl.exe") == 0) { - ANSI_STRING name_ansi = {0}; - UNICODE_STRING name_unicode = {0}; - - RtlInitAnsiString(&name_ansi, name); - RtlAnsiStringToUnicodeString(&name_unicode, &name_ansi, TRUE); - uint64_t pFn = (uint64_t)MmGetSystemRoutineAddress(&name_unicode); - if (!pFn) { - LOG_ERROR("[!] DLL Import %s from %s couldn't be found!...fatal\r\n", name, szMod); - *pFuncRef = 0; - importsAtLeastOneBad = true; - } else { - *pFuncRef = pFn; - } - RtlFreeUnicodeString(&name_unicode); - } else { - LOG_ERROR("[!] DLL Imports %s from %s. Imports are not supported...fatal\r\n", name, szMod); - importsAtLeastOneBad = true; - } - } - } - ++pImportDescr; - } - - if (importsAtLeastOneBad) { - return false; - } - } - - // Execute TLS - if (pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].Size) { - LOG_INFO("[+] Executing TLS entires\r\n"); - auto pTLS = (IMAGE_TLS_DIRECTORY64*)(pBase + pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress); - auto pCallback = (PIMAGE_TLS_CALLBACK*)(pTLS->AddressOfCallBacks); - for (; pCallback && *pCallback; ++pCallback) { - (*pCallback)(pBase, DLL_PROCESS_ATTACH, nullptr); - } - } - - // Execute main - if (_DllMain) { - LOG_INFO("[+] Executing DLLMain\r\n"); - _DllMain(pBase, DLL_PROCESS_ATTACH, nullptr); - } - else { - LOG_INFO("[+] No DLLMain\r\n"); - } - LOG_INFO("[+] DLL Load Done\r\n"); - return true; -} - -bool ManualMapper::validateImage(char* pBase) { - if (!pBase) - return false; - - //Optional data - auto dosHeader = (IMAGE_DOS_HEADER*)pBase; - if (dosHeader->e_magic != 0x5A4D) { - LOG_ERROR("DOS Magic Incorrect\r\n"); - return false; - } - - auto ntHeader = (IMAGE_NT_HEADERS64*)(pBase + dosHeader->e_lfanew); - if (ntHeader->Signature != 0x4550) { - LOG_ERROR("NT Magic Incorrect\r\n"); - return false; - } - - return true; -} - -uint64_t ManualMapper::mapImage(char* imageData, uint64_t imageSize) { - IMAGE_NT_HEADERS64* pOldNtHeader = nullptr; - IMAGE_OPTIONAL_HEADER64* pOldOptionalHeader = nullptr; - IMAGE_FILE_HEADER* pOldFileHeader = nullptr; - uint8_t* pTargetBase = nullptr; - - // layout is DOS_HEADER -> DOS_STUB -> NTHDR - // 1. Ensure up to DOS_HEADER size - // 2. Ensure up to start of NTHDR + Size of nthdr - // 3. Validate headers - if (imageSize < sizeof(IMAGE_DOS_HEADER) || - imageSize < ((((IMAGE_DOS_HEADER*)imageData)->e_lfanew) + sizeof(IMAGE_NT_HEADERS64)) || - !validateImage(imageData)) { - return NULL; - } - - // Save the old NT Header - pOldNtHeader = (IMAGE_NT_HEADERS64*)(imageData + ((IMAGE_DOS_HEADER*)imageData)->e_lfanew); - - // Save the old optional header - pOldOptionalHeader = &pOldNtHeader->OptionalHeader; - - // Save the old file header - pOldFileHeader = &pOldNtHeader->FileHeader; - - // Ensure up to IMAGE_SECTION_HEADER (start of first section data) - if (imageSize < pOldOptionalHeader->SizeOfHeaders) { - LOG_ERROR("[!] DLL appears truncated\r\n"); - return NULL; - } - - // If the machine type is not the current file type we fail - if (pOldFileHeader->Machine != IMAGE_FILE_MACHINE_AMD64) { - LOG_ERROR("[!] Only 64Bit DLLs are supported\r\n"); - return NULL; - } - - pTargetBase = (uint8_t*)ExAllocatePoolWithTag(NonPagedPoolExecute, pOldOptionalHeader->SizeOfImage, DRIVER_POOL_TAG); - if (!pTargetBase) { - LOG_ERROR("[!] Failed to allocate final mapped image memory at any address\r\n"); - return NULL; - } - - if (pOldFileHeader->NumberOfSections <= 0) { - LOG_ERROR("[!] DLL has no sections, fatal\r\n"); - return NULL; - } - - auto* pSectionHeader = IMAGE_FIRST_SECTION(pOldNtHeader); - - // copy mem up until first section [0, section1) - uint64_t rollingImageSize = pOldOptionalHeader->SizeOfHeaders; - memcpy(pTargetBase, imageData, pOldOptionalHeader->SizeOfHeaders); - - // copy all the sections [sec1, secN) - // validate each IMAGE_SECTION_DATA is present - for (USHORT i = 0; i != pOldFileHeader->NumberOfSections; ++i, ++pSectionHeader) { - - // Each section is a buffer of raw data - rollingImageSize += pSectionHeader->SizeOfRawData; - if (imageSize < rollingImageSize) { - LOG_ERROR("[!] DLL appears truncated\r\n"); - return NULL; - } - - memcpy(pTargetBase + pSectionHeader->VirtualAddress, imageData + pSectionHeader->PointerToRawData, pSectionHeader->SizeOfRawData); - } - - if (!loadImage((char*)pTargetBase)) { - LOG_ERROR("[!] DLL Load Failed\r\n"); - return NULL; - } - - return (uint64_t)pTargetBase; -} - -ManualMapper::ExportDirectoryPtrs ManualMapper::getExportDir(uint64_t hModule) { - ExportDirectoryPtrs exportPtrs; - exportPtrs.addressOfFunctions = nullptr; - exportPtrs.addressOfNameOrdinals = nullptr; - exportPtrs.addressOfNames = nullptr; - exportPtrs.exports = nullptr; - - IMAGE_DOS_HEADER* pDos = (IMAGE_DOS_HEADER*)hModule; - IMAGE_NT_HEADERS64* pNT = RVA2VA(IMAGE_NT_HEADERS64*, hModule, pDos->e_lfanew); - IMAGE_DATA_DIRECTORY* pDataDir = (IMAGE_DATA_DIRECTORY*)pNT->OptionalHeader.DataDirectory; - - if (pDataDir[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == NULL) { - return exportPtrs; - } - - IMAGE_EXPORT_DIRECTORY* pExports = RVA2VA(IMAGE_EXPORT_DIRECTORY*, hModule, pDataDir[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); - - exportPtrs.addressOfFunctions = RVA2VA(uint32_t*, hModule, pExports->AddressOfFunctions); - exportPtrs.addressOfNames = RVA2VA(uint32_t*, hModule, pExports->AddressOfNames); - exportPtrs.addressOfNameOrdinals = RVA2VA(uint16_t*, hModule, pExports->AddressOfNameOrdinals); - exportPtrs.exports = pExports; - return exportPtrs; -} - -uint64_t ManualMapper::getExport(uint64_t hModule, const char* procName) { - ExportDirectoryPtrs exportPtrs = getExportDir(hModule); - if (!exportPtrs.exports) { - return 0; - } - - for (uint32_t i = 0; i < exportPtrs.exports->NumberOfNames; i++) { - char* exportName = RVA2VA(char*, hModule, exportPtrs.addressOfNames[i]); - if (_stricmp(exportName, procName) == 0) - return RVA2VA(uint64_t, hModule, exportPtrs.addressOfFunctions[i]); - } - return 0; -} \ No newline at end of file diff --git a/C/STrace/ManualMap.h b/C/STrace/ManualMap.h deleted file mode 100644 index 9637ff6..0000000 --- a/C/STrace/ManualMap.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#define WIN32_LEAN_AND_MEAN -#include -#include "MyStdint.h" - -#define RVA2VA(type, base, rva) (type)((ULONG_PTR) base + rva) - -class ManualMapper { -public: - uint64_t mapImage(char* imageData, uint64_t imageSize); - uint64_t getExport(uint64_t hModule,const char* procName); -private: - struct ExportDirectoryPtrs { - uint32_t* addressOfFunctions; - uint32_t* addressOfNames; - uint16_t* addressOfNameOrdinals; - IMAGE_EXPORT_DIRECTORY* exports; - }; - - ExportDirectoryPtrs getExportDir(uint64_t moduleBase); - bool loadImage(char* imageBase); - bool validateImage(char* imageBase); -}; - -typedef bool(__stdcall* tDllMain)(char* hDll, uint32_t dwReason, char* pReserved); From d6674d7beb666e639649f344f314552769871538 Mon Sep 17 00:00:00 2001 From: Paul Tarter Date: Thu, 16 Nov 2023 14:46:59 -0500 Subject: [PATCH 2/9] removed MmGetSystemRoutine from interface, plugins are drivers now --- C/STrace/Interface.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/C/STrace/Interface.h b/C/STrace/Interface.h index 5b8628b..878b0f6 100644 --- a/C/STrace/Interface.h +++ b/C/STrace/Interface.h @@ -43,13 +43,12 @@ typedef NTSTATUS(*tSetCallbackApi)(const char* syscallName, ULONG64 probeId); typedef NTSTATUS(*tUnSetCallbackApi)(const char* syscallName); typedef NTSTATUS(*tSetEtwCallbackApi)(GUID providerGuid); typedef NTSTATUS(*tUnSetEtwCallbackApi)(); -typedef PVOID(NTAPI*tMmGetSystemRoutineAddress)(PUNICODE_STRING SystemRoutineName); typedef BOOLEAN(*tTraceAccessMemory)(PVOID SafeAddress, ULONG_PTR UnsafeAddress, SIZE_T NumberOfBytes, SIZE_T ChunkSize, BOOLEAN DoRead); class PluginApis { public: PluginApis() = default; - PluginApis(tMmGetSystemRoutineAddress getAddress, tLogPrintApi print, tEtwTraceApi etwTrace, tSetCallbackApi setCallback, + PluginApis(tLogPrintApi print, tEtwTraceApi etwTrace, tSetCallbackApi setCallback, tUnSetCallbackApi unsetCallback, tSetEtwCallbackApi etwSetCallback, tUnSetEtwCallbackApi etwUnSetCallback, tTraceAccessMemory accessMemory, tSetTlsData setTlsData, tGetTlsData getTlsData) { @@ -61,7 +60,6 @@ class PluginApis { pUnsetCallback = unsetCallback; pEtwSetCallback = etwSetCallback; pEtwUnSetCallback = etwUnSetCallback; - pGetSystemRoutineAddress = getAddress; pTraceAccessMemory = accessMemory; } @@ -73,7 +71,6 @@ class PluginApis { tUnSetCallbackApi pUnsetCallback; tSetEtwCallbackApi pEtwSetCallback; tUnSetEtwCallbackApi pEtwUnSetCallback; - tMmGetSystemRoutineAddress pGetSystemRoutineAddress; tTraceAccessMemory pTraceAccessMemory; }; From 0dc84ead02691388953d1b2c278e555e82256489 Mon Sep 17 00:00:00 2001 From: Paul Tarter Date: Thu, 16 Nov 2023 14:48:48 -0500 Subject: [PATCH 3/9] Plugin Data now implements load/unload and modified locking --- C/STrace/PluginData.h | 214 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 C/STrace/PluginData.h diff --git a/C/STrace/PluginData.h b/C/STrace/PluginData.h new file mode 100644 index 0000000..fa567c7 --- /dev/null +++ b/C/STrace/PluginData.h @@ -0,0 +1,214 @@ +#pragma once +#define WIN32_LEAN_AND_MEAN +#include +#include +#include "Logger.h" +#include "Interface.h" + +#define RVA2VA(type, base, rva) (type)((ULONG_PTR) base + rva) + +#define _countof(array) (sizeof(array) / sizeof(array[0])) +UNICODE_STRING pluginServiceName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\StracePlugin"); +const char* pluginFullPathNames[] = { + "\\systemroot\\system32\\drivers\\plugin.sys", + "\\??\\C:\\Windows\\system32\\drivers\\plugin.sys" +}; + +struct ExportDirectoryPtrs { + uint32_t* addressOfFunctions; + uint32_t* addressOfNames; + uint16_t* addressOfNameOrdinals; + IMAGE_EXPORT_DIRECTORY* exports; +}; + +class PluginData { +public: + PluginData() { + ExInitializeFastMutex(&loaderLock); + zero(); + } + + bool isLoaded() { + return InterlockedAdd(&loaded, 0) != 0; + } + + NTSTATUS load() { + + NTSTATUS status; + + // LOCK + ExAcquireFastMutex(&loaderLock); + if (!isLoaded()) { + return STATUS_ALREADY_INITIALIZED; + } + + status = ZwLoadDriver(&pluginServiceName); + if (!NT_SUCCESS(status)) { + LOG_ERROR("ZwLoadDriver Failed with: 0x%08X", status); + goto exit; + } + + status = setPluginBaseAddress(); + if (!NT_SUCCESS(status)) + { + LOG_ERROR("setPluginBaseAddress failed to find plugin"); + goto exit; + } + + pCallbackEntry = (tStpCallbackEntryPlugin)pluginData.getExport("StpCallbackEntry"); + pCallbackReturn = (tStpCallbackReturnPlugin)pluginData.getExport("StpCallbackReturn"); + pInitialize = (tStpInitialize)pluginData.getExport("StpInitialize"); + pDeInitialize = (tStpDeInitialize)pluginData.getExport("StpDeInitialize"); + pIsTarget = (tStpIsTarget)pluginData.getExport("StpIsTarget"); + pDtEtwpEventCallback = (tDtEtwpEventCallback)pluginData.getExport("DtEtwpEventCallback"); + + if((pCallbackEntry && pCallbackReturn && pIsTarget && pDtEtwpEventCallback) == 0){ + LOG_ERROR("Failed to acquire plugin exports"); + status = STATUS_PROCEDURE_NOT_FOUND; + goto exit; + } + + InterlockedIncrement(& loaded); + LOG_INFO("[+] Plugin Loaded at: %I64X\r\n", pImageBase); + + exit: + ExReleaseFastMutex(&loaderLock); + return status; + } + + // Must free old plugin data before setting new one + NTSTATUS unload() { + NTSTATUS status; + // set pImageBase last since it's used atomically for isLoaded + ExAcquireFastMutex(&loaderLock); + if (!pluginData.isLoaded()) + { + status = STATUS_ALREADY_COMPLETE; + } + InterlockedDecrement(&loaded); + + status = ZwUnloadDriver(&pluginServiceName); + if (!NT_SUCCESS(status)) + { + // If driver doesn't unload, then it is still loaded and we should + // return the plugin to a loaded state and return an error + LOG_INFO("[!] Failed to unload plugin driver"); + InterlockedIncrement(&loaded); + goto exit; + } + zero(); + + status = STATUS_SUCCESS; + exit: + ExReleaseFastMutex(&loaderLock); + return status; + + } + + uint64_t getExport(const char* procName) { + ExportDirectoryPtrs exportPtrs = getExportDir(pImageBase); + if (!exportPtrs.exports) { + return 0; + } + + for (uint32_t i = 0; i < exportPtrs.exports->NumberOfNames; i++) { + char* exportName = RVA2VA(char*, pImageBase, exportPtrs.addressOfNames[i]); + if (_stricmp(exportName, procName) == 0) + return RVA2VA(uint64_t, pImageBase, exportPtrs.addressOfFunctions[i]); + } + return 0; + } + + tStpIsTarget pIsTarget; + tStpCallbackEntryPlugin pCallbackEntry; + tStpCallbackReturnPlugin pCallbackReturn; + tDtEtwpEventCallback pDtEtwpEventCallback; + + // zeroed immediately after use, these are optional + tStpInitialize pInitialize; + tStpDeInitialize pDeInitialize; + +private: + + ExportDirectoryPtrs getExportDir(uint64_t hModule) + { + ExportDirectoryPtrs exportPtrs; + exportPtrs.addressOfFunctions = nullptr; + exportPtrs.addressOfNameOrdinals = nullptr; + exportPtrs.addressOfNames = nullptr; + exportPtrs.exports = nullptr; + + IMAGE_DOS_HEADER* pDos = (IMAGE_DOS_HEADER*)hModule; + IMAGE_NT_HEADERS64* pNT = RVA2VA(IMAGE_NT_HEADERS64*, hModule, pDos->e_lfanew); + IMAGE_DATA_DIRECTORY* pDataDir = (IMAGE_DATA_DIRECTORY*)pNT->OptionalHeader.DataDirectory; + + if (pDataDir[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == NULL) { + return exportPtrs; + } + + IMAGE_EXPORT_DIRECTORY* pExports = RVA2VA(IMAGE_EXPORT_DIRECTORY*, hModule, pDataDir[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); + + exportPtrs.addressOfFunctions = RVA2VA(uint32_t*, hModule, pExports->AddressOfFunctions); + exportPtrs.addressOfNames = RVA2VA(uint32_t*, hModule, pExports->AddressOfNames); + exportPtrs.addressOfNameOrdinals = RVA2VA(uint16_t*, hModule, pExports->AddressOfNameOrdinals); + exportPtrs.exports = pExports; + return exportPtrs; + } + + NTSTATUS setPluginBaseAddress() + { + NTSTATUS status; + PRTL_PROCESS_MODULES modules = NULL; + PRTL_PROCESS_MODULE_INFORMATION pmi; + ULONG moduleInformationSize = 0; + + // Get Size + status = ZwQuerySystemInformation(SystemModuleInformation, 0, moduleInformationSize, &moduleInformationSize); + + // multiply moduleInformationSize by two just in case somehow the size increases between previous call and now. + modules = (PRTL_PROCESS_MODULES)ExAllocatePoolWithTag(NonPagedPool, moduleInformationSize * 2, DRIVER_POOL_TAG); + if (modules) + { + // get modules + status = ZwQuerySystemInformation(SystemModuleInformation, modules, moduleInformationSize, &moduleInformationSize); + if (!NT_SUCCESS(status)) { + goto exit; + } + + pmi = modules->Modules; + for (ULONG i = 0; i < modules->NumberOfModules; i++) + { + DBGPRINT("Module: %s", pmi[i].FullPathName); + for (int j = 0; j < _countof(pluginFullPathNames); j++) + { + // RtlStringCbLength FullPathName - 1 because the ANSI_STRING maxLenght will need an extra byte + if(_stricmp(pmi[i].FullPathName, pluginFullPathNames[j]) == 0) + { + DBGPRINT("Found Module: %s 0x%I64X", pmi[i].FullPathName, pmi[i].ImageBase); + pImageBase = (uint64_t)pmi[i].ImageBase; + status = STATUS_SUCCESS; + goto exit; + } + } + } + status = STATUS_NOT_FOUND; + } + exit: + if (modules) ExFreePoolWithTag(modules, DRIVER_POOL_TAG); + return status; + } + + void zero() { + pImageBase = 0; + pInitialize = 0; + pCallbackEntry = 0; + pCallbackReturn = 0; + pDeInitialize = 0; + pIsTarget = 0; + pDtEtwpEventCallback = 0; + } + + uint64_t pImageBase; + FAST_MUTEX loaderLock; + volatile LONG loaded; +}; \ No newline at end of file From b8645a5d5bfd5982b95dbfda957f916d42237468 Mon Sep 17 00:00:00 2001 From: Paul Tarter Date: Thu, 16 Nov 2023 14:50:23 -0500 Subject: [PATCH 4/9] new files added --- C/STrace/STrace.vcxproj | 3 +-- C/STrace/STrace.vcxproj.filters | 9 +++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/C/STrace/STrace.vcxproj b/C/STrace/STrace.vcxproj index 42aa63c..8b42d56 100644 --- a/C/STrace/STrace.vcxproj +++ b/C/STrace/STrace.vcxproj @@ -132,7 +132,6 @@ - @@ -142,10 +141,10 @@ - + diff --git a/C/STrace/STrace.vcxproj.filters b/C/STrace/STrace.vcxproj.filters index 1783bae..e7e110b 100644 --- a/C/STrace/STrace.vcxproj.filters +++ b/C/STrace/STrace.vcxproj.filters @@ -25,9 +25,6 @@ Source Files - - Source Files - Source Files @@ -48,9 +45,6 @@ Header Files - - Header Files - Header Files @@ -75,5 +69,8 @@ Header Files + + Header Files + \ No newline at end of file From 5592cea26d5aee90646b8fbd83c3470b616c9eb5 Mon Sep 17 00:00:00 2001 From: Paul Tarter Date: Thu, 16 Nov 2023 14:50:56 -0500 Subject: [PATCH 5/9] modify plugin loading --- C/STrace/driver.cpp | 127 +++++++------------------------------------- 1 file changed, 19 insertions(+), 108 deletions(-) diff --git a/C/STrace/driver.cpp b/C/STrace/driver.cpp index 4fd62c8..e275095 100644 --- a/C/STrace/driver.cpp +++ b/C/STrace/driver.cpp @@ -5,73 +5,10 @@ #include "Etw.h" #include "EtwLogger.h" #include "DynamicTrace.h" +#include "PluginData.h" #include "Logger.h" -#include "ManualMap.h" #include "Interface.h" -class PluginData { -public: - PluginData() { - zero(); - } - - bool isLoaded() { - return InterlockedAdd64((volatile LONG64*)&pImageBase, 0) != 0; - } - - // Must free old plugin data before setting new one - bool setPluginData(uint64_t base, tStpIsTarget istarget, tStpCallbackEntryPlugin entry, tStpCallbackReturnPlugin ret, tStpInitialize init, tStpDeInitialize deinit, tDtEtwpEventCallback etw) { - pCallbackEntry = entry; - pCallbackReturn = ret; - pInitialize = init; - pDeInitialize = deinit; - pIsTarget = istarget; - pDtEtwpEventCallback = etw; - - // set pImageBase last since it's used atomically for isLoaded - auto expected = pImageBase; - auto atomicGot = (uint64_t)_InterlockedCompareExchange64((volatile LONG64*)&pImageBase, base, expected); - - // if the swap worked we get what we expected - return atomicGot == expected; - } - - // Must free old plugin data before setting new one - bool freePluginData() { - // set pImageBase last since it's used atomically for isLoaded - auto expected = pImageBase; - auto atomicGot = (uint64_t)_InterlockedCompareExchange64((volatile LONG64*)&pImageBase, 0, expected); - - if (atomicGot == expected && expected != 0) { - ExFreePoolWithTag((char*)expected, DRIVER_POOL_TAG); - zero(); - return true; - } - return false; - } - - tStpIsTarget pIsTarget; - tStpCallbackEntryPlugin pCallbackEntry; - tStpCallbackReturnPlugin pCallbackReturn; - tDtEtwpEventCallback pDtEtwpEventCallback; - - // zeroed immediately after use, these are optional - tStpInitialize pInitialize; - tStpDeInitialize pDeInitialize; -private: - void zero() { - pImageBase = 0; - pInitialize = 0; - pCallbackEntry = 0; - pCallbackReturn = 0; - pDeInitialize = 0; - pIsTarget = 0; - pDtEtwpEventCallback = 0; - } - - volatile uint64_t pImageBase; -}; - // forward declare extern "C" __declspec(dllexport) void StpCallbackEntry(ULONG64 pService, ULONG32 probeId, ULONG32 paramCount, ULONG64* pArgs, ULONG32 pArgSize, void* pStackArgs); extern "C" __declspec(dllexport) void StpCallbackReturn(ULONG64 pService, ULONG32 probeId, ULONG32 paramCount, ULONG64* pArgs, ULONG32 pArgSize, void* pStackArgs); @@ -135,8 +72,6 @@ NTSTATUS NotImplementedRoutine() return STATUS_NOT_IMPLEMENTED; } -ManualMapper g_DllMapper; - /** pService: Pointer to system service from SSDT probeId: Identifier given in KeSetSystemServiceCallback for this syscall callback @@ -368,42 +303,25 @@ Return Value: return STATUS_SUCCESS; } -NTSTATUS HandleDllLoad(PIRP Irp, PIO_STACK_LOCATION IrpStack) { +NTSTATUS HandlePluginLoad(PIRP Irp, PIO_STACK_LOCATION IrpStack) { char* input = (char*)Irp->AssociatedIrp.SystemBuffer; uint64_t inputLen = IrpStack->Parameters.DeviceIoControl.InputBufferLength; // only 1 plugin is allowed to load at a time NTSTATUS status = STATUS_SUCCESS; if (!pluginData.isLoaded()) { - // Executes DLLMain if it exists - uint64_t dllBase = g_DllMapper.mapImage(input, inputLen); - if (!dllBase) { + + status = pluginData.load(); + if(!NT_SUCCESS(status)) + { LOG_ERROR("[!] Plugin Loading Failed\r\n"); status = STATUS_UNSUCCESSFUL; goto exit; } - - LOG_INFO("[+] Dll Mapped at %I64X\r\n", dllBase); - - auto entry = (tStpCallbackEntryPlugin)g_DllMapper.getExport(dllBase, "StpCallbackEntry"); - auto ret = (tStpCallbackReturnPlugin)g_DllMapper.getExport(dllBase, "StpCallbackReturn"); - auto init = (tStpInitialize)g_DllMapper.getExport(dllBase, "StpInitialize"); - auto deinit = (tStpDeInitialize)g_DllMapper.getExport(dllBase, "StpDeInitialize"); - auto istarget = (tStpIsTarget)g_DllMapper.getExport(dllBase, "StpIsTarget"); - auto etw = (tDtEtwpEventCallback)g_DllMapper.getExport(dllBase, "DtEtwpEventCallback"); - - uint32_t tries = 0; - while (!pluginData.setPluginData(dllBase, istarget, entry, ret, init, deinit, etw)) { - if (tries++ >= 10) { - LOG_ERROR("[!] Atomic plugin load failed\r\n"); - status = STATUS_UNSUCCESSFUL; - goto exit; - } - } if (pluginData.pInitialize) { // The plugin must immediately copy this structure. It must be a local to avoid C++ static initializers, which are created if its a global - PluginApis pluginApis(&MmGetSystemRoutineAddress, &LogPrint, &EtwTrace, &SetCallbackApi, &UnSetCallbackApi, &SetEtwCallback, &UnSetEtwCallback, &TraceAccessMemory, &SetTLSData, &GetTLSData); + PluginApis pluginApis(&LogPrint, &EtwTrace, &SetCallbackApi, &UnSetCallbackApi, &SetEtwCallback, &UnSetEtwCallback, &TraceAccessMemory, &SetTLSData, &GetTLSData); pluginData.pInitialize(pluginApis); // prevent double initialize regardless of rest @@ -424,21 +342,14 @@ NTSTATUS HandleDllLoad(PIRP Irp, PIO_STACK_LOCATION IrpStack) { return status; } -NTSTATUS HandleDllUnLoad() { +NTSTATUS HandlePluginUnLoad() { + NTSTATUS status; if (pluginData.isLoaded()) { - if (pluginData.pDeInitialize) { - pluginData.pDeInitialize(); - - // prevent double deinitialize regardless of rest - pluginData.pDeInitialize = 0; - } - - uint32_t tries = 0; - while (!pluginData.freePluginData()) { - if (tries++ >= 10) { - LOG_ERROR("[!] Atomic plugin unload failed\r\n"); - return STATUS_UNSUCCESSFUL; - } + status = pluginData.unload(); + if (!NT_SUCCESS(status)) + { + LOG_ERROR("[!] Plugin unload failed: 0x%08X\r\n", status); + return status; } if (LogInitialized) { @@ -447,7 +358,7 @@ NTSTATUS HandleDllUnLoad() { } } else { LOG_ERROR("[!] No plugin is loaded, unload failed\r\n"); - return STATUS_UNSUCCESSFUL; + return STATUS_ALREADY_COMPLETE; } return STATUS_SUCCESS; @@ -499,12 +410,12 @@ Return Value: switch (Ioctl) { case IOCTL_LOADDLL: - LOG_INFO("Starting DLL load\r\n"); - Status = HandleDllLoad(Irp, IrpStack); + LOG_INFO("Starting Plugin load\r\n"); + Status = HandlePluginLoad(Irp, IrpStack); break; case IOCTL_UNLOADDLL: - LOG_INFO("Starting DLL unload\r\n"); - Status = HandleDllUnLoad(); + LOG_INFO("Starting Plugin unload\r\n"); + Status = HandlePluginUnLoad(); break; default: LOG_WARN("Unrecognized ioctl 0x%x\r\n", Ioctl); From 74f3546e59bbce330287ac634a69e087dc70fb4a Mon Sep 17 00:00:00 2001 From: Paul Tarter Date: Fri, 17 Nov 2023 07:27:56 -0500 Subject: [PATCH 6/9] changed to custom lock to avoid CRT initialization of FAST_MUTEX --- C/STrace/PluginData.h | 69 ++++++++++++++++++++++++------------------- C/STrace/driver.cpp | 2 ++ 2 files changed, 41 insertions(+), 30 deletions(-) diff --git a/C/STrace/PluginData.h b/C/STrace/PluginData.h index fa567c7..1d744e0 100644 --- a/C/STrace/PluginData.h +++ b/C/STrace/PluginData.h @@ -8,7 +8,9 @@ #define RVA2VA(type, base, rva) (type)((ULONG_PTR) base + rva) #define _countof(array) (sizeof(array) / sizeof(array[0])) + UNICODE_STRING pluginServiceName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\StracePlugin"); + const char* pluginFullPathNames[] = { "\\systemroot\\system32\\drivers\\plugin.sys", "\\??\\C:\\Windows\\system32\\drivers\\plugin.sys" @@ -24,7 +26,7 @@ struct ExportDirectoryPtrs { class PluginData { public: PluginData() { - ExInitializeFastMutex(&loaderLock); + loaderLock = 0; zero(); } @@ -35,9 +37,8 @@ class PluginData { NTSTATUS load() { NTSTATUS status; - - // LOCK - ExAcquireFastMutex(&loaderLock); + + lock(); if (!isLoaded()) { return STATUS_ALREADY_INITIALIZED; } @@ -55,12 +56,12 @@ class PluginData { goto exit; } - pCallbackEntry = (tStpCallbackEntryPlugin)pluginData.getExport("StpCallbackEntry"); - pCallbackReturn = (tStpCallbackReturnPlugin)pluginData.getExport("StpCallbackReturn"); - pInitialize = (tStpInitialize)pluginData.getExport("StpInitialize"); - pDeInitialize = (tStpDeInitialize)pluginData.getExport("StpDeInitialize"); - pIsTarget = (tStpIsTarget)pluginData.getExport("StpIsTarget"); - pDtEtwpEventCallback = (tDtEtwpEventCallback)pluginData.getExport("DtEtwpEventCallback"); + pCallbackEntry = (tStpCallbackEntryPlugin)getExport("StpCallbackEntry"); + pCallbackReturn = (tStpCallbackReturnPlugin)getExport("StpCallbackReturn"); + pInitialize = (tStpInitialize)getExport("StpInitialize"); + pDeInitialize = (tStpDeInitialize)getExport("StpDeInitialize"); + pIsTarget = (tStpIsTarget)getExport("StpIsTarget"); + pDtEtwpEventCallback = (tDtEtwpEventCallback)getExport("DtEtwpEventCallback"); if((pCallbackEntry && pCallbackReturn && pIsTarget && pDtEtwpEventCallback) == 0){ LOG_ERROR("Failed to acquire plugin exports"); @@ -68,11 +69,11 @@ class PluginData { goto exit; } - InterlockedIncrement(& loaded); + InterlockedIncrement(&loaded); LOG_INFO("[+] Plugin Loaded at: %I64X\r\n", pImageBase); exit: - ExReleaseFastMutex(&loaderLock); + unlock(); return status; } @@ -80,8 +81,8 @@ class PluginData { NTSTATUS unload() { NTSTATUS status; // set pImageBase last since it's used atomically for isLoaded - ExAcquireFastMutex(&loaderLock); - if (!pluginData.isLoaded()) + lock(); + if (!isLoaded()) { status = STATUS_ALREADY_COMPLETE; } @@ -100,25 +101,11 @@ class PluginData { status = STATUS_SUCCESS; exit: - ExReleaseFastMutex(&loaderLock); + unlock(); return status; } - uint64_t getExport(const char* procName) { - ExportDirectoryPtrs exportPtrs = getExportDir(pImageBase); - if (!exportPtrs.exports) { - return 0; - } - - for (uint32_t i = 0; i < exportPtrs.exports->NumberOfNames; i++) { - char* exportName = RVA2VA(char*, pImageBase, exportPtrs.addressOfNames[i]); - if (_stricmp(exportName, procName) == 0) - return RVA2VA(uint64_t, pImageBase, exportPtrs.addressOfFunctions[i]); - } - return 0; - } - tStpIsTarget pIsTarget; tStpCallbackEntryPlugin pCallbackEntry; tStpCallbackReturnPlugin pCallbackReturn; @@ -130,6 +117,14 @@ class PluginData { private: + void lock() { + while (_interlockedbittestandset(&loaderLock, 0)) {}; + } + + void unlock() { + _interlockedbittestandreset(&loaderLock, 0); + } + ExportDirectoryPtrs getExportDir(uint64_t hModule) { ExportDirectoryPtrs exportPtrs; @@ -155,6 +150,20 @@ class PluginData { return exportPtrs; } + uint64_t getExport(const char* procName) { + ExportDirectoryPtrs exportPtrs = getExportDir(pImageBase); + if (!exportPtrs.exports) { + return 0; + } + + for (uint32_t i = 0; i < exportPtrs.exports->NumberOfNames; i++) { + char* exportName = RVA2VA(char*, pImageBase, exportPtrs.addressOfNames[i]); + if (_stricmp(exportName, procName) == 0) + return RVA2VA(uint64_t, pImageBase, exportPtrs.addressOfFunctions[i]); + } + return 0; + } + NTSTATUS setPluginBaseAddress() { NTSTATUS status; @@ -209,6 +218,6 @@ class PluginData { } uint64_t pImageBase; - FAST_MUTEX loaderLock; + volatile LONG loaderLock; volatile LONG loaded; }; \ No newline at end of file diff --git a/C/STrace/driver.cpp b/C/STrace/driver.cpp index e275095..87bcb26 100644 --- a/C/STrace/driver.cpp +++ b/C/STrace/driver.cpp @@ -14,6 +14,8 @@ extern "C" __declspec(dllexport) void StpCallbackEntry(ULONG64 pService, ULONG32 extern "C" __declspec(dllexport) void StpCallbackReturn(ULONG64 pService, ULONG32 probeId, ULONG32 paramCount, ULONG64* pArgs, ULONG32 pArgSize, void* pStackArgs); extern "C" __declspec(dllexport) void DtEtwpEventCallback(PEVENT_HEADER pEventHeader, ULONG32 a, PGUID pProviderGuid, ULONG32 b); + + NTSTATUS SetCallbackApi(const char* syscallName, ULONG64 probeId) { if (!TraceSystemApi || !TraceSystemApi->KeSetSystemServiceCallback) { return STATUS_UNSUCCESSFUL; From e0a329e4d0144662805e7f7f55d34a0bd94dc89f Mon Sep 17 00:00:00 2001 From: Paul Tarter Date: Fri, 17 Nov 2023 12:04:09 -0500 Subject: [PATCH 7/9] FileDeleteRecordPluginDriver works without utils.h --- C/FileDeleteRecordPluginDriver/Constants.h | 10 + .../FileDeleteRecordPluginDriver.cpp | 195 +++ .../FileDeleteRecordPluginDriver.inf | 34 + .../FileDeleteRecordPluginDriver.vcxproj | 133 ++ ...leDeleteRecordPluginDriver.vcxproj.filters | 49 + C/FileDeleteRecordPluginDriver/Interface.cpp | 25 + C/FileDeleteRecordPluginDriver/Interface.h | 239 +++ C/FileDeleteRecordPluginDriver/Logger.cpp | 1342 +++++++++++++++++ C/FileDeleteRecordPluginDriver/MyStdint.h | 151 ++ C/FileDeleteRecordPluginDriver/NtBuild.h | 65 + C/FileDeleteRecordPluginDriver/NtStructs.h | 550 +++++++ C/FileDeleteRecordPluginDriver/utils.h | 290 ++++ C/STrace.sln | 48 + C/STrace/PluginData.h | 49 +- 14 files changed, 3160 insertions(+), 20 deletions(-) create mode 100644 C/FileDeleteRecordPluginDriver/Constants.h create mode 100644 C/FileDeleteRecordPluginDriver/FileDeleteRecordPluginDriver.cpp create mode 100644 C/FileDeleteRecordPluginDriver/FileDeleteRecordPluginDriver.inf create mode 100644 C/FileDeleteRecordPluginDriver/FileDeleteRecordPluginDriver.vcxproj create mode 100644 C/FileDeleteRecordPluginDriver/FileDeleteRecordPluginDriver.vcxproj.filters create mode 100644 C/FileDeleteRecordPluginDriver/Interface.cpp create mode 100644 C/FileDeleteRecordPluginDriver/Interface.h create mode 100644 C/FileDeleteRecordPluginDriver/Logger.cpp create mode 100644 C/FileDeleteRecordPluginDriver/MyStdint.h create mode 100644 C/FileDeleteRecordPluginDriver/NtBuild.h create mode 100644 C/FileDeleteRecordPluginDriver/NtStructs.h create mode 100644 C/FileDeleteRecordPluginDriver/utils.h diff --git a/C/FileDeleteRecordPluginDriver/Constants.h b/C/FileDeleteRecordPluginDriver/Constants.h new file mode 100644 index 0000000..9d740a9 --- /dev/null +++ b/C/FileDeleteRecordPluginDriver/Constants.h @@ -0,0 +1,10 @@ +#pragma once + +#define DRIVER_POOL_TAG ' xtS' +#define DRIVER_NAME_WITH_EXT L"strace.sys" +#define NT_DEVICE_NAME L"\\Device\\STrace" +#define DOS_DEVICES_LINK_NAME L"\\DosDevices\\STrace" +#define DEVICE_SDDL L"D:P(A;;GA;;;SY)(A;;GA;;;BA)" + +#define IOCTL_LOADDLL CTL_CODE (FILE_DEVICE_UNKNOWN, (0x800 + 0), METHOD_BUFFERED, FILE_SPECIAL_ACCESS) +#define IOCTL_UNLOADDLL CTL_CODE (FILE_DEVICE_UNKNOWN, (0x800 + 1), METHOD_BUFFERED, FILE_SPECIAL_ACCESS) \ No newline at end of file diff --git a/C/FileDeleteRecordPluginDriver/FileDeleteRecordPluginDriver.cpp b/C/FileDeleteRecordPluginDriver/FileDeleteRecordPluginDriver.cpp new file mode 100644 index 0000000..7d6a789 --- /dev/null +++ b/C/FileDeleteRecordPluginDriver/FileDeleteRecordPluginDriver.cpp @@ -0,0 +1,195 @@ +#pragma warning(disable: 4996) //exallocatepoolwithtag +#include + +#include "interface.h" + +#include "utils.h" + +const unsigned long PLUGIN_POOL_TAG = 'LEDS'; + +#pragma warning(disable: 6011) +PluginApis g_Apis; + +#if defined(ENABLE_LOG) +#if defined(__GNUC__) || defined(__clang__) + +// On GCC and Clang __VA_ARGS__ must be used differently. +#define DBGPRINT(format, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[STRACE] " format "\n", ##__VA_ARGS__) +#define LOG_DEBUG(fmt,...) g_Apis.pLogPrint(LogLevelDebug, __FUNCTION__, fmt, ##__VA_ARGS__) +#define LOG_INFO(fmt,...) g_Apis.pLogPrint(LogLevelInfo, __FUNCTION__, fmt, ##__VA_ARGS__) +#define LOG_WARN(fmt,...) g_Apis.pLogPrint(LogLevelWarn, __FUNCTION__, fmt, ##__VA_ARGS__) +#define LOG_ERROR(fmt,...) g_Apis.pLogPrint(LogLevelError, __FUNCTION__, fmt, ##__VA_ARGS__) +#else + +#define DBGPRINT(format, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[STRACE] " format "\n", __VA_ARGS__) +#define LOG_DEBUG(fmt,...) g_Apis.pLogPrint(LogLevelDebug, __FUNCTION__, fmt, __VA_ARGS__) +#define LOG_INFO(fmt,...) g_Apis.pLogPrint(LogLevelInfo, __FUNCTION__, fmt, __VA_ARGS__) +#define LOG_WARN(fmt,...) g_Apis.pLogPrint(LogLevelWarn, __FUNCTION__, fmt, __VA_ARGS__) +#define LOG_ERROR(fmt,...) g_Apis.pLogPrint(LogLevelError, __FUNCTION__, fmt, __VA_ARGS__) +#endif // __GNUC__ || __clang__ + +#else + +#define DBGPRINT(format, ...) ((void)format) + +#endif // _DEBUG + +enum PROBE_IDS : ULONG64 { + IdSetInformationFile = 0, +}; + +extern "C" __declspec(dllexport) void StpInitialize(PluginApis & pApis) { + g_Apis = pApis; + LOG_INFO("Plugin Initializing...\r\n"); + + g_Apis.pSetCallback("SetInformationFile", PROBE_IDS::IdSetInformationFile); + LOG_INFO("Plugin Initialized\r\n"); +} +ASSERT_INTERFACE_IMPLEMENTED(StpInitialize, tStpInitialize, "StpInitialize does not match the interface type"); + +extern "C" __declspec(dllexport) void StpDeInitialize() { + LOG_INFO("Plugin DeInitializing...\r\n"); + + g_Apis.pUnsetCallback("SetInformationFile"); + + LOG_INFO("Plugin DeInitialized\r\n"); +} +ASSERT_INTERFACE_IMPLEMENTED(StpDeInitialize, tStpDeInitialize, "StpDeInitialize does not match the interface type"); + +extern "C" __declspec(dllexport) bool StpIsTarget(CallerInfo & callerinfo) { + UNREFERENCED_PARAMETER(callerinfo); + return true; +} +ASSERT_INTERFACE_IMPLEMENTED(StpIsTarget, tStpIsTarget, "StpIsTarget does not match the interface type"); + +void PrintStackTrace(CallerInfo& callerinfo) { + for (int i = 0; i < callerinfo.frameDepth; i++) { + if ((callerinfo.frames)[i].frameaddress) { + const auto modulePathLen = (callerinfo.frames)[i].modulePath ? strlen((callerinfo.frames)[i].modulePath) : 0; + + // add brackets around module dynamically + if (modulePathLen) { + char moduleName[sizeof(CallerInfo::StackFrame::modulePath) + 2] = { 0 }; + moduleName[0] = '['; + strcpy(&moduleName[1], (callerinfo.frames)[i].modulePath); + moduleName[modulePathLen + 1] = ']'; + + LOG_INFO(" %-18s +0x%08llx\r\n", moduleName, (callerinfo.frames)[i].frameaddress - (callerinfo.frames)[i].modulebase); + } + else { + LOG_INFO(" %-18s 0x%016llx\r\n", "[UNKNOWN MODULE]", (callerinfo.frames)[i].frameaddress); + } + } + else { + LOG_INFO(" Frame Missing\r\n"); + } + } +} + + + +OBJECT_NAME_INFORMATION* getFilePathFromHandle(HANDLE hFile) { + ULONG dwSize = 0; + OBJECT_NAME_INFORMATION* pObjectName = nullptr; + NTSTATUS status = ZwQueryObject(hFile, (OBJECT_INFORMATION_CLASS)1 /*ObjectNameInformation*/, pObjectName, 0, &dwSize); + if (dwSize) + { + pObjectName = (OBJECT_NAME_INFORMATION*)ExAllocatePoolWithTag(NonPagedPoolNx, dwSize, PLUGIN_POOL_TAG); + if (pObjectName) { + status = ZwQueryObject(hFile, (OBJECT_INFORMATION_CLASS)1 /*ObjectNameInformation*/, pObjectName, dwSize, &dwSize); + } + } + + if (status == STATUS_SUCCESS && pObjectName) { + return pObjectName; + } + + if (pObjectName) { + ExFreePoolWithTag(pObjectName, PLUGIN_POOL_TAG); + pObjectName = nullptr; + } + return nullptr; +} + +extern "C" __declspec(dllexport) void StpCallbackEntry(ULONG64 pService, ULONG32 probeId, MachineState & ctx, CallerInfo & callerinfo) +{ + //LOG_INFO("[ENTRY] %s[0x%x](%d) Id: %d Parameters: [%d]\r\n", callerinfo.processName, callerinfo.processId, callerinfo.isWow64 ? 32 : 64, pService, probeId, ctx.paramCount); + UNREFERENCED_PARAMETER(pService); + UNREFERENCED_PARAMETER(probeId); + UNREFERENCED_PARAMETER(ctx); + UNREFERENCED_PARAMETER(callerinfo); + switch (probeId) { + case PROBE_IDS::IdSetInformationFile: { + auto hFile = (HANDLE)ctx.read_argument(0); + auto InformationClass = ctx.read_argument(4); + if (InformationClass == 13) { // FileDispositionInformation + auto pInformation = (char*)ctx.read_argument(2); // 1 == DeleteFile + if (*pInformation == 1) { + auto pFilePath = getFilePathFromHandle(hFile); + + if (pFilePath) { + LOG_INFO("File %wZ deleted\r\n", pFilePath->Name); + //backupFile((wchar_t*)backup_directory, pFilePath->Name, hFile); + //ExFreePoolWithTag(pFilePath, PLUGIN_POOL_TAG); + //pFilePath = nullptr; + LOG_INFO("File Backup Complete\r\n"); + } + else { + LOG_INFO("File [unknown] deleted\r\n"); + } + + PrintStackTrace(callerinfo); + } + } + break; + } + } +} +ASSERT_INTERFACE_IMPLEMENTED(StpCallbackEntry, tStpCallbackEntryPlugin, "StpCallbackEntry does not match the interface type"); + +extern "C" __declspec(dllexport) void StpCallbackReturn(ULONG64 pService, ULONG32 probeId, MachineState & ctx, CallerInfo & callerinfo) { + UNREFERENCED_PARAMETER(pService); + UNREFERENCED_PARAMETER(probeId); + UNREFERENCED_PARAMETER(ctx); + UNREFERENCED_PARAMETER(callerinfo); + //LOG_INFO("[RETURN] %s[%x](%d) %016llx Id: %d\r\n", callerinfo.processName, callerinfo.processId, callerinfo.isWow64 ? 32 : 64, pService, probeId); +} +ASSERT_INTERFACE_IMPLEMENTED(StpCallbackReturn, tStpCallbackReturnPlugin, "StpCallbackEntry does not match the interface type"); + + +NTSTATUS DeviceCreateClose(_In_ PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp) +{ + UNREFERENCED_PARAMETER(DeviceObject); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; +} + +VOID DeviceUnload(_In_ PDRIVER_OBJECT DriverObject) +{ + UNREFERENCED_PARAMETER(DriverObject); + DBGPRINT("FileDeleteRecord::DeviceUnload"); +} + + +/* +* /GS- must be set to disable stack cookies and have DriverEntry +* be the entrypoint. GsDriverEntry sets up stack cookie and calls +* Driver Entry normally. +*/ +NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) +{ + UNREFERENCED_PARAMETER(RegistryPath); + + DBGPRINT("FileDeleteRecord::DriverEntry()"); + + + DriverObject->MajorFunction[IRP_MJ_CREATE] = DeviceCreateClose; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = DeviceCreateClose; + DriverObject->DriverUnload = DeviceUnload; + + return STATUS_SUCCESS; +} diff --git a/C/FileDeleteRecordPluginDriver/FileDeleteRecordPluginDriver.inf b/C/FileDeleteRecordPluginDriver/FileDeleteRecordPluginDriver.inf new file mode 100644 index 0000000..3c52ef1 --- /dev/null +++ b/C/FileDeleteRecordPluginDriver/FileDeleteRecordPluginDriver.inf @@ -0,0 +1,34 @@ +; +; FileDeleteRecordPluginDriver.inf +; + +[Version] +Signature="$WINDOWS NT$" +Class=System +ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318} +Provider=%ManufacturerName% +DriverVer= +CatalogFile=FileDeleteRecordPluginDriver.cat +PnpLockdown=1 + +;This template is supported for OS version 17763 (Windows 10 version 1809) and after. +;For Windows OS prior to Windows 10 1809 set DefaultDestDir = 12 +[DestinationDirs] +DefaultDestDir = 13 + + +[SourceDisksNames] +1 = %DiskName%,,,"" + +[SourceDisksFiles] + + +[Manufacturer] +%ManufacturerName%=Standard,NT$ARCH$ + +[Standard.NT$ARCH$] + + +[Strings] +ManufacturerName="" ;TODO: Replace with your manufacturer name +DiskName="FileDeleteRecordPluginDriver Source Disk" diff --git a/C/FileDeleteRecordPluginDriver/FileDeleteRecordPluginDriver.vcxproj b/C/FileDeleteRecordPluginDriver/FileDeleteRecordPluginDriver.vcxproj new file mode 100644 index 0000000..db436d8 --- /dev/null +++ b/C/FileDeleteRecordPluginDriver/FileDeleteRecordPluginDriver.vcxproj @@ -0,0 +1,133 @@ + + + + + Debug + x64 + + + Release + x64 + + + Debug + ARM64 + + + Release + ARM64 + + + + {CD47158C-73E3-4197-AE90-92DC38D8BC0E} + {dd38f7fc-d7bd-488b-9242-7d8754cde80d} + v4.5 + 12.0 + Debug + x64 + FileDeleteRecordPluginDriver + $(LatestTargetPlatformVersion) + + + + Windows10 + true + WindowsKernelModeDriver10.0 + Driver + WDM + false + + + Windows10 + false + WindowsKernelModeDriver10.0 + Driver + WDM + false + + + Windows10 + true + WindowsKernelModeDriver10.0 + Driver + WDM + + + Windows10 + false + WindowsKernelModeDriver10.0 + Driver + WDM + + + + + + + + + + + DbgengKernelDebugger + false + + + DbgengKernelDebugger + false + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + + sha256 + + + Default + + + false + false + stdcpp20 + _WIN64;_AMD64_;AMD64;ENABLE_LOG;%(PreprocessorDefinitions) + + + DriverEntry + + + + + sha256 + + + false + false + Default + stdcpp20 + + + DriverEntry + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/C/FileDeleteRecordPluginDriver/FileDeleteRecordPluginDriver.vcxproj.filters b/C/FileDeleteRecordPluginDriver/FileDeleteRecordPluginDriver.vcxproj.filters new file mode 100644 index 0000000..7e95a1d --- /dev/null +++ b/C/FileDeleteRecordPluginDriver/FileDeleteRecordPluginDriver.vcxproj.filters @@ -0,0 +1,49 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {8E41214B-6785-4CFE-B992-037D68949A14} + inf;inv;inx;mof;mc; + + + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Source Files + + + Header Files + + + \ No newline at end of file diff --git a/C/FileDeleteRecordPluginDriver/Interface.cpp b/C/FileDeleteRecordPluginDriver/Interface.cpp new file mode 100644 index 0000000..d95b3b4 --- /dev/null +++ b/C/FileDeleteRecordPluginDriver/Interface.cpp @@ -0,0 +1,25 @@ +#include "Interface.h" + +UNICODE_STRING WideToUnicodeString(PCWSTR SourceString) +{ + UNICODE_STRING DestinationString; + + SIZE_T Size; + CONST SIZE_T MaxSize = (MAXUSHORT & ~1) - sizeof(UNICODE_NULL); // an even number + + if (SourceString) + { + Size = wcslen(SourceString) * sizeof(WCHAR); + if (Size > MaxSize) + Size = MaxSize; + DestinationString.Length = (USHORT)Size; + DestinationString.MaximumLength = (USHORT)Size + sizeof(UNICODE_NULL); + } + else { + DestinationString.Length = 0; + DestinationString.MaximumLength = 0; + } + + DestinationString.Buffer = (PWCHAR)SourceString; + return DestinationString; +} \ No newline at end of file diff --git a/C/FileDeleteRecordPluginDriver/Interface.h b/C/FileDeleteRecordPluginDriver/Interface.h new file mode 100644 index 0000000..db0a9e7 --- /dev/null +++ b/C/FileDeleteRecordPluginDriver/Interface.h @@ -0,0 +1,239 @@ +#pragma once +#include +#include +#define NTSTRSAFE_NO_CB_FUNCTIONS +#include + +#include "MyStdint.h" +#include "Constants.h" +#include "NtStructs.h" + +class MachineState +{ +public: + uint32_t paramCount; + uint32_t regArgsSize; + uint64_t* pStackArgs; + uint64_t* pRegArgs; + + uint64_t read_argument(const uint32_t idx) + { + if (idx > paramCount) + return 0; + + if (idx >= regArgsSize) { + // stack array trims off the register array data, and indexes from that offset value + return pStackArgs[idx - regArgsSize]; + } + else { + return pRegArgs[idx]; + } + } + + template + void write_argument(const uint32_t idx, T value) { + if (idx > paramCount) + return; + + if (idx >= regArgsSize) { + // stack array trims off the register array data, and indexes from that offset value + pStackArgs[idx - regArgsSize] = (uint64_t)value; + } + else { + pRegArgs[idx] = (uint64_t)value; + } + } + + void redirect_syscall(uint64_t pFn) { + // The syscall pointer is stored just after the args + pRegArgs[4] = pFn; + } + + uint64_t read_return_value() { + return pRegArgs[0]; + } + + template + void write_return_value(T value) { + pRegArgs[0] = (uint64_t)value; + } +}; + +typedef enum _LOG_LEVEL_OPTIONS +{ + LogLevelDebug = 0x10ul, + LogLevelInfo = 0x20ul, + LogLevelWarn = 0x40ul, + LogLevelError = 0x80ul, +} LOG_LEVEL_OPTIONS; + +typedef LONG NTSTATUS; +typedef bool(*tSetTlsData)(uint64_t value, uint8_t slot); +typedef bool(*tGetTlsData)(uint64_t& value, uint8_t slot); +typedef NTSTATUS(*tLogPrintApi)(uint32_t Level, const char* FunctionName, const char* Format, ...); +typedef NTSTATUS(*tEtwTraceApi)(const char* providerName, const GUID* providerGuid, const char* eventName, uint8_t eventLevel, uint8_t eventChannel, uint64_t flag, int numberOfFields, ...); +typedef NTSTATUS(*tSetCallbackApi)(const char* syscallName, ULONG64 probeId); +typedef NTSTATUS(*tUnSetCallbackApi)(const char* syscallName); +typedef NTSTATUS(*tSetEtwCallbackApi)(GUID providerGuid); +typedef NTSTATUS(*tUnSetEtwCallbackApi)(); +typedef BOOLEAN(*tTraceAccessMemory)(PVOID SafeAddress, ULONG_PTR UnsafeAddress, SIZE_T NumberOfBytes, SIZE_T ChunkSize, BOOLEAN DoRead); + +class PluginApis { +public: + PluginApis() = default; + + tSetTlsData pSetTlsData; + tGetTlsData pGetTlsData; + tLogPrintApi pLogPrint; + tEtwTraceApi pEtwTrace; + tSetCallbackApi pSetCallback; + tUnSetCallbackApi pUnsetCallback; + tSetEtwCallbackApi pEtwSetCallback; + tUnSetEtwCallbackApi pEtwUnSetCallback; + tTraceAccessMemory pTraceAccessMemory; +}; + +#define MAX_PATH 260 +#define MINCHAR 0x80 // winnt +#define MAXCHAR 0x7f // winnt +#define MINSHORT 0x8000 // winnt +#define MAXSHORT 0x7fff // winnt +#define MINLONG 0x80000000 // winnt +#define MAXLONG 0x7fffffff // winnt +#define MAXUCHAR 0xff // winnt +#define MAXUSHORT 0xffff // winnt +#define MAXULONG 0xffffffff // winnt + +UNICODE_STRING WideToUnicodeString(PCWSTR SourceString); +NTSTATUS MbToUnicodeString(PSTR SourceString, PUNICODE_STRING DestinationString); + +template +T ResolveApi(const wchar_t* name, PluginApis& apis) { + auto ustr = WideToUnicodeString(name); + return (T)MmGetSystemRoutineAddress(&ustr); +} + +class CallerInfo +{ +public: + struct StackFrame { + uint64_t frameaddress; + uint64_t modulebase;; + char modulePath[MAX_PATH]; + }; + + char processName[100]; + uint64_t processId; + StackFrame* frames; + uint8_t frameDepth; + bool isWow64; +}; + +typedef bool(*tStpIsTarget)(CallerInfo& callerinfo); +typedef void(*tStpCallbackEntryPlugin)(ULONG64 pService, ULONG32 probeId, MachineState& ctx, CallerInfo& callerinfo); +typedef void(*tStpCallbackReturnPlugin)(ULONG64 pService, ULONG32 probeId, MachineState& ctx, CallerInfo& callerinfo); +typedef void(*tStpInitialize)(PluginApis& pApis); +typedef void(*tStpDeInitialize)(); + + +// ETW field type definitions, see TlgIn_t and TlgOut_t in TraceLoggingProvider.h +#define ETW_FIELD(in, out) (in | 0x80 | out << 8) + +typedef enum _ETW_IN_FIELD_TYPE +{ + EtwInNull, + EtwInUnicodeString, + EtwInAnsiString, + EtwInInt8, + EtwInUInt8, + EtwInInt16, + EtwInUInt16, + EtwInInt32, + EtwInUInt32, + EtwInInt64, + EtwInUInt64, + EtwInFloat, + EtwInDouble, + EtwInBool32, + EtwInBinary, + EtwInGuid, + EtwInPointer, + EtwInFiletime, + EtwInSystemTime, + EtwInSid, + EtwInHexInt32, + EtwInHexInt64, + EtwInCountedString, + EtwInCountedAnsiString, +} ETW_IN_FIELD_TYPE; + +typedef enum _ETW_OUT_FIELD_TYPE +{ + EtwOutNull, + EtwOutNoPrint, + EtwOutString, + EtwOutBoolean, + EtwOutHex, + EtwOutPid, + EtwOutTid, + EtwOutPort, + EtwOutIpV4, + EtwOutIpV6, + EtwOutSocketAddress, + EtwOutXml, + EtwOutJson, + EtwOutWin32Error, + EtwOutNtstatus, + EtwOutHresult, + EtwOutFiletime, + EtwOutSigned, + EtwOutUnsigned, +} ETW_OUT_FIELD_TYPE; + +typedef enum _ETW_FIELD_TYPE +{ + EtwFieldInt8 = EtwInInt8, + EtwFieldUInt8 = EtwInUInt8, + EtwFieldInt16 = EtwInInt16, + EtwFieldUInt16 = EtwInUInt16, + EtwFieldInt32 = EtwInInt32, + EtwFieldUInt32 = EtwInUInt32, + EtwFieldInt64 = EtwInInt64, + EtwFieldUInt64 = EtwInUInt64, + EtwFieldFloat32 = EtwInFloat, + EtwFieldFloat64 = EtwInDouble, + EtwFieldBool = EtwInBool32, + EtwFieldGuid = EtwInGuid, + EtwFieldPointer = EtwInPointer, + EtwFieldFiletime = EtwInFiletime, + EtwFieldSystemTime = EtwInSystemTime, + EtwFieldHexInt8 = ETW_FIELD(EtwInUInt8, EtwOutHex), + EtwFieldHexUInt8 = ETW_FIELD(EtwInUInt8, EtwOutHex), + EtwFieldHexInt32 = EtwInHexInt32, + EtwFieldHexUInt32 = EtwInHexInt32, + EtwFieldHexInt64 = EtwInHexInt64, + EtwFieldHexUInt64 = EtwInHexInt64, + EtwFieldWChar = ETW_FIELD(EtwInUInt16, EtwOutString), + EtwFieldChar = ETW_FIELD(EtwInUInt8, EtwOutString), + EtwFieldBoolean = ETW_FIELD(EtwInUInt8, EtwOutBoolean), + EtwFieldHexInt16 = ETW_FIELD(EtwInUInt16, EtwOutHex), + EtwFieldHexUInt16 = ETW_FIELD(EtwInUInt16, EtwOutHex), + EtwFieldPid = ETW_FIELD(EtwInUInt32, EtwOutPid), + EtwFieldTid = ETW_FIELD(EtwInUInt32, EtwOutTid), + EtwFieldPort = ETW_FIELD(EtwInUInt16, EtwOutPort), + EtwFieldWinError = ETW_FIELD(EtwInUInt32, EtwOutWin32Error), + EtwFieldNtstatus = ETW_FIELD(EtwInUInt32, EtwOutNtstatus), + EtwFieldHresult = ETW_FIELD(EtwInInt32, EtwOutHresult), + EtwFieldString = EtwInAnsiString, + EtwFieldWideString = EtwInUnicodeString, + EtwFieldCountedString = EtwInCountedAnsiString, + EtwFieldCountedWideString = EtwFieldCountedString, + EtwFieldAnsiString = EtwInCountedAnsiString, + EtwFieldUnicodeString = EtwInCountedString, + EtwFieldBinary = EtwInBinary, + EtwFieldSocketAddress = ETW_FIELD(EtwInBinary, EtwOutSocketAddress), + EtwFieldSid = EtwInSid, +} ETW_FIELD_TYPE; + +// Assert a function is the same type as a function pointer typedef, or throw msg as a compiler error +#define ASSERT_INTERFACE_IMPLEMENTED(Implementer, tFnTypeDef, msg) static_assert(is_same_v, msg); \ No newline at end of file diff --git a/C/FileDeleteRecordPluginDriver/Logger.cpp b/C/FileDeleteRecordPluginDriver/Logger.cpp new file mode 100644 index 0000000..bae3715 --- /dev/null +++ b/C/FileDeleteRecordPluginDriver/Logger.cpp @@ -0,0 +1,1342 @@ +/** + * Copyright (c) 2015-2016, tandasat. All rights reserved. + * Use of this source code is governed by a MIT-style license: + * + * The MIT License ( MIT ) + * + * Copyright (c) 2016 Satoshi Tanda + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files ( the "Software" ), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and / or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions : + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @file log.c + * @authors Satoshi Tanda (tandasat) + * @date 8/30/2018 + */ + +#if defined(ENABLE_LOG) + +#include "Logger.h" +#include +#include + /// + /// < Macros > + /// + + // A size for log buffer in NonPagedPool. Two buffers are allocated with this + // size. Exceeded logs are ignored silently. Make it bigger if a buffered log + // size often reach this size. +#define LOG_BUFFER_SIZE_IN_PAGES (64UL) +// An actual log buffer size in bytes. +#define LOG_BUFFER_SIZE (LOG_BUFFER_SIZE_IN_PAGES << PAGE_SHIFT) +// A size that is usable for logging. Minus one because the last byte is kept for \0. +#define LOG_BUFFER_USABLE_SIZE (LOG_BUFFER_SIZE - 1) +// An interval in milliseconds to flush buffered log entries into a log file. +#define LOG_FLUSH_INTERVAL (50) + +/// +/// < Log Types > +/// + +typedef struct _LOG_BUFFER_INFO +{ + // A pointer to buffer currently used. + // It is either LogBuffer1 or LogBuffer2. + volatile CHAR* LogBufferHead; + // A pointer to where the next log should be written. + volatile CHAR* LogBufferTail; + CHAR* LogBuffer1; + CHAR* LogBuffer2; + // Holds the biggest buffer usage to determine a necessary buffer size. + SIZE_T LogMaxUsage; + HANDLE LogFileHandle; + KSPIN_LOCK SpinLock; + ERESOURCE Resource; + BOOLEAN ResourceInitialized; + volatile BOOLEAN BufferFlushThreadShouldBeAlive; + volatile BOOLEAN BufferFlushThreadStarted; + HANDLE BufferFlushThreadHandle; + WCHAR LogFilePath[200]; +} LOG_BUFFER_INFO, * PLOG_BUFFER_INFO; + +/// +/// < Log Prototypes > +/// + +static +NTSTATUS +LogpInitializeBufferInfo( + IN CONST WCHAR* LogFilePath, + IN OUT PLOG_BUFFER_INFO Info +); + +static +NTSTATUS +LogpInitializeLogFile( + IN OUT PLOG_BUFFER_INFO Info +); + +static +DRIVER_REINITIALIZE +LogpReinitializationRoutine; + +static +VOID +LogpFinalizeBufferInfo( + IN PLOG_BUFFER_INFO Info +); + +static +NTSTATUS +LogpMakePrefix( + IN ULONG Level, + IN CONST CHAR* FunctionName, + IN CONST CHAR* LogMessage, + OUT CHAR* LogBuffer, + IN SIZE_T LogBufferLength +); + +static +CONST CHAR* +LogpFindBaseFunctionName( + IN CONST CHAR* FunctionName +); + +static +NTSTATUS +LogpPut( + IN CHAR* Message, + IN ULONG Attribute +); + +static +NTSTATUS +LogpFlushLogBuffer( + IN OUT PLOG_BUFFER_INFO Info +); + +static +NTSTATUS +LogpWriteMessageToFile( + IN CONST CHAR* Message, + IN CONST LOG_BUFFER_INFO* Info +); + +static +NTSTATUS +LogpBufferMessage( + IN CONST CHAR* Message, + IN OUT PLOG_BUFFER_INFO Info +); + +static +VOID +LogpDoDbgPrint( + IN CHAR* Message +); + +FORCEINLINE +BOOLEAN +LogpIsLogFileEnabled( + IN CONST LOG_BUFFER_INFO* Info +); + +static +BOOLEAN +LogpIsLogFileActivated( + IN CONST LOG_BUFFER_INFO* Info +); + +static +BOOLEAN +LogpIsLogNeeded( + IN ULONG Level +); + +static +KSTART_ROUTINE +LogpBufferFlushThreadRoutine; + +static +BOOLEAN +LogpFileExists( + IN PUNICODE_STRING FilePath +); + +static +VOID +LogpDbgBreak( + VOID +); + +typedef +NTSTATUS +(NTAPI* PNT_FLUSH_BUFFERS_FILE)( + IN HANDLE FileHandle, + OUT PIO_STATUS_BLOCK IoStatusBlock + ); +static PNT_FLUSH_BUFFERS_FILE NtFlushBuffersFile = NULL; + +static ULONG LogFlags = LogPutLevelDisable; +static LOG_BUFFER_INFO LogBufferInfo = { 0 }; + +/** + * Log Implementation + */ + +NTSTATUS +LogInitialize( + IN ULONG Flag, + IN CONST WCHAR* LogFilePath OPTIONAL +) +{ + NTSTATUS Status; + BOOLEAN ReinitializeNeeded = FALSE; + LogFlags = Flag; + + RtlZeroMemory(&LogBufferInfo, sizeof(LOG_BUFFER_INFO)); + + // + // Initialize a log file if a log file path is specified. + // + if (LogFilePath != NULL) + { + Status = LogpInitializeBufferInfo(LogFilePath, &LogBufferInfo); + if (Status == STATUS_REINITIALIZATION_NEEDED) + { + ReinitializeNeeded = TRUE; + } + else if (!NT_SUCCESS(Status)) + { + return Status; + } + } + + // Test the log. + Status = LOG_INFO("Log has been %sinitialized.\r\n", (ReinitializeNeeded ? "partially " : "")); + if (!NT_SUCCESS(Status)) + { + goto Fail; + } + +#ifdef DBG + LOG_DEBUG("Info=%016Ix, Buffer=%016Ix %016Ix, File=\"%S\"", + &LogBufferInfo, LogBufferInfo.LogBuffer1, LogBufferInfo.LogBuffer2, LogFilePath); +#endif + + if (ReinitializeNeeded) + { + return STATUS_REINITIALIZATION_NEEDED; + } + + return STATUS_SUCCESS; + +Fail: + if (LogFilePath) + { + LogpFinalizeBufferInfo(&LogBufferInfo); + } + + return Status; +} + +// Initialize a log file related code such as a flushing thread. +static +NTSTATUS +LogpInitializeBufferInfo( + IN CONST WCHAR* LogFilePath, + IN OUT PLOG_BUFFER_INFO Info +) +{ + NTSTATUS Status; + PHYSICAL_ADDRESS HighestAcceptableAddress; + + if (!LogFilePath || !Info) + { + return STATUS_INVALID_PARAMETER; + } + + KeInitializeSpinLock(&Info->SpinLock); + + Status = RtlStringCchCopyW(Info->LogFilePath, + RTL_NUMBER_OF_FIELD(LOG_BUFFER_INFO, LogFilePath), + LogFilePath); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + Status = ExInitializeResourceLite(&Info->Resource); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + Info->ResourceInitialized = TRUE; + + // + // Allocate two log buffers as NonPagedPools. + // + HighestAcceptableAddress.QuadPart = MAXUINT64; + Info->LogBuffer1 = (CHAR*)MmAllocateContiguousMemory(LOG_BUFFER_SIZE * 2, + HighestAcceptableAddress); + if (!Info->LogBuffer1) + { + LogpFinalizeBufferInfo(Info); + return STATUS_INSUFFICIENT_RESOURCES; + } + Info->LogBuffer2 = Info->LogBuffer1 + LOG_BUFFER_SIZE; + + // + // Initialize these buffers + // + RtlFillMemory(Info->LogBuffer1, LOG_BUFFER_SIZE, 0xFFFFFFFF); // for debugging + Info->LogBuffer1[0] = '\0'; + Info->LogBuffer1[LOG_BUFFER_SIZE - 1] = '\0'; + + RtlFillMemory(Info->LogBuffer2, LOG_BUFFER_SIZE, 0xFFFFFFFF); // for debugging + Info->LogBuffer2[0] = '\0'; + Info->LogBuffer2[LOG_BUFFER_SIZE - 1] = '\0'; + + // + // Buffer should be used is LogBuffer1, and location should be written + // logs is the head of the buffer. + // + Info->LogBufferHead = Info->LogBuffer1; + Info->LogBufferTail = Info->LogBuffer1; + + // + // Initialize the log file. + // + Status = LogpInitializeLogFile(Info); + if (Status == STATUS_OBJECT_PATH_NOT_FOUND) + { + + LOG_INFO("The log file needs to be activated later."); + Status = STATUS_REINITIALIZATION_NEEDED; + + } + else if (!NT_SUCCESS(Status)) + { + + LogpFinalizeBufferInfo(Info); + } + + return Status; +} + +// Initializes a log file and startes a log buffer thread. +static +NTSTATUS +LogpInitializeLogFile( + IN OUT PLOG_BUFFER_INFO Info +) +{ + UNICODE_STRING LogFilePath; + OBJECT_ATTRIBUTES Attributes; + IO_STATUS_BLOCK IoStatus; + LARGE_INTEGER Interval; + ULONG DesiredAccess; + NTSTATUS Status; + + // + // Check if the log file has already been initialized. + // + if (Info->LogFileHandle != NULL) + { + return STATUS_SUCCESS; + } + + // + // Initialize a log file path. + // + LogFilePath.Length = (UINT16)(wcslen(Info->LogFilePath) * sizeof(WCHAR)); + LogFilePath.MaximumLength = RTL_NUMBER_OF_FIELD(LOG_BUFFER_INFO, LogFilePath); + LogFilePath.Buffer = (WCHAR*)Info->LogFilePath; + InitializeObjectAttributes(&Attributes, + &LogFilePath, + OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + // + // Check if the file already exists. + // + if ((LogFlags & LogOptDisableAppend) && LogpFileExists(&LogFilePath)) + { + + Status = ZwDeleteFile(&Attributes); + if (!NT_SUCCESS(Status)) + { + LogpDbgBreak(); + return Status; + } + } + + // + // Create the file handle. + // + DesiredAccess = (ULONG)((LogFlags & LogOptDisableAppend) ? FILE_WRITE_DATA : FILE_APPEND_DATA); + Status = ZwCreateFile(&Info->LogFileHandle, + DesiredAccess | SYNCHRONIZE, + &Attributes, + &IoStatus, + NULL, + FILE_ATTRIBUTE_SYSTEM, + FILE_SHARE_READ, + FILE_OPEN_IF, + FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, + NULL, + 0 + ); + + if (!NT_SUCCESS(Status)) + { + return Status; + } + + // + // Initialize a log buffer flush thread. + // + Info->BufferFlushThreadShouldBeAlive = TRUE; + Status = PsCreateSystemThread(&Info->BufferFlushThreadHandle, + GENERIC_ALL, + NULL, + NULL, + NULL, + LogpBufferFlushThreadRoutine, + Info + ); + if (!NT_SUCCESS(Status)) + { + ZwClose(Info->LogFileHandle); + Info->LogFileHandle = NULL; + Info->BufferFlushThreadShouldBeAlive = FALSE; + return Status; + } + + // + // Wait until the thead has started + // + while (!Info->BufferFlushThreadStarted) + { + Interval = RtlConvertLongToLargeInteger((INT32)(-10000 * LOG_FLUSH_INTERVAL)); + KeDelayExecutionThread(KernelMode, FALSE, &Interval); + } + + return Status; +} + +VOID +LogRegisterReinitialization( + IN PDRIVER_OBJECT DriverObject +) +{ + IoRegisterBootDriverReinitialization(DriverObject, LogpReinitializationRoutine, &LogBufferInfo); +#if defined(DBG) + LOG_DEBUG("The log file will be activated later."); +#endif +} + +// Initializes a log file at the re-initialization phase. +static +VOID +LogpReinitializationRoutine( + IN PDRIVER_OBJECT DriverObject, + IN PVOID Context, + IN ULONG Count +) +{ + NTSTATUS Status; + + UNREFERENCED_PARAMETER(DriverObject); + UNREFERENCED_PARAMETER(Count); + + NT_ASSERT(Context != NULL); + + Status = LogpInitializeLogFile((PLOG_BUFFER_INFO)Context); + + NT_ASSERT(NT_SUCCESS(Status)); + + if (NT_SUCCESS(Status)) + { + LOG_INFO("The log file has been activated."); + } +} + +// Terminates the log functions without releasing resources. +VOID +LogIrpShutdownHandler( + VOID +) +{ + LARGE_INTEGER Interval; + + PAGED_CODE(); + +#if defined(DBG) + LOG_DEBUG("Flushing... (Max log usage = %08x bytes)", LogBufferInfo.LogMaxUsage); +#endif + + // + // Indicate that the log is disabled. + // + LogFlags = LogPutLevelDisable; + + // + // Wait until the log buffer is emptied. + // + while (LogBufferInfo.LogBufferHead[0]) + { + Interval = RtlConvertLongToLargeInteger((INT32)(-10000 * LOG_FLUSH_INTERVAL)); + KeDelayExecutionThread(KernelMode, FALSE, &Interval); + } +} + +// Destroys the log functions. +VOID +LogDestroy( + VOID +) +{ +#if defined(DBG) + LOG_DEBUG("Finalizing... (Max log usage = %08x bytes)", LogBufferInfo.LogMaxUsage); +#endif + + LogFlags = LogPutLevelDisable; + LogpFinalizeBufferInfo(&LogBufferInfo); +} + +// Terminates a log file related code. +static +VOID +LogpFinalizeBufferInfo( + IN PLOG_BUFFER_INFO Info +) +{ + NTSTATUS Status; + + NT_ASSERT(Info != NULL); + + // Closing the log buffer flush thread. + if (Info->BufferFlushThreadHandle) + { + + Info->BufferFlushThreadShouldBeAlive = FALSE; + + Status = ZwWaitForSingleObject(Info->BufferFlushThreadHandle, FALSE, NULL); + if (!NT_SUCCESS(Status)) + { + LogpDbgBreak(); + } + + ZwClose(Info->BufferFlushThreadHandle); + Info->BufferFlushThreadHandle = NULL; + } + + // Clean up other things. + if (Info->LogFileHandle) + { + ZwClose(Info->LogFileHandle); + Info->LogFileHandle = NULL; + } + + if (Info->LogBuffer1) + { + MmFreeContiguousMemory(Info->LogBuffer1); + Info->LogBuffer1 = NULL; + } + + if (Info->ResourceInitialized) + { + ExDeleteResourceLite(&Info->Resource); + Info->ResourceInitialized = FALSE; + } +} + +// Actual implementation of logging API. +NTSTATUS +LogPrint( + IN unsigned int Level, + IN CONST CHAR* FunctionName, + IN CONST CHAR* Format, + ... +) +{ + NTSTATUS Status; + va_list Args; + + SIZE_T cchRemaining; + SIZE_T cchLength; + SIZE_T cchPrinted; + SIZE_T cchLengthToCopy; + + CHAR* LogMessage, * LogMessageEnd; + CHAR LogMessageBuffer[411 + 1]; + CHAR Message[512]; + + // A single entry of a log should not exceed 512 bytes. See + // Reading and Filtering Debugging Messages in MSDN for details. + C_ASSERT(RTL_NUMBER_OF(Message) <= 512); + + if (!LogpIsLogNeeded(Level)) + { + return STATUS_SUCCESS; + } + + LogMessage = LogMessageBuffer; + + va_start(Args, Format); + Status = RtlStringCchVPrintfExA(LogMessage, + RTL_NUMBER_OF(LogMessageBuffer), + &LogMessageEnd, + &cchRemaining, + STRSAFE_NO_TRUNCATION, + Format, + Args + ); + va_end(Args); + + // + // Treat STATUS_BUFFER_OVERFLOW as just a warning. + // + if (Status != STATUS_BUFFER_OVERFLOW && Status != STATUS_SUCCESS) + { + LogpDbgBreak(); + return Status; + } + + // + // Handle buffer overflow. + // + if (Status == STATUS_BUFFER_OVERFLOW) + { + cchLength = cchRemaining; + + LogMessage = (CHAR*)ExAllocatePool(NonPagedPoolNx, PAGE_SIZE); + if (!LogMessage) + { + LogpDbgBreak(); + return STATUS_INSUFFICIENT_RESOURCES; + } + + va_start(Args, Format); + Status = RtlStringCchVPrintfExA(LogMessage, + PAGE_SIZE, + &LogMessageEnd, + &cchRemaining, + STRSAFE_NO_TRUNCATION, + Format, + Args + ); + va_end(Args); + + if (Status != STATUS_SUCCESS) + { + LogpDbgBreak(); + return Status; + } + + cchLength = (LogMessageEnd - LogMessage); + + } + else + { + + if (LogMessage[0] == '\0') + { + LogpDbgBreak(); + return STATUS_INVALID_PARAMETER; + } + cchLength = 0; + } + + if (cchLength) + { + + cchPrinted = 0; + do + { + cchLengthToCopy = min(cchLength - cchPrinted, sizeof(LogMessageBuffer) - sizeof(LogMessageBuffer[0])); + RtlZeroMemory(&LogMessageBuffer, sizeof(LogMessageBuffer) - sizeof(LogMessageBuffer[0])); + RtlCopyMemory(LogMessageBuffer, LogMessage + cchPrinted, cchLengthToCopy); + + if (cchPrinted == 0) + { + + Status = LogpMakePrefix(Level & 0xF0, + FunctionName, + LogMessageBuffer, + Message, + RTL_NUMBER_OF(Message) + ); + + } + else + { + + Status = RtlStringCchPrintfExA(Message, + RTL_NUMBER_OF(Message), + NULL, + NULL, + STRSAFE_NO_TRUNCATION, + "> %s\r\n", + LogMessageBuffer + ); + } + + if (!NT_SUCCESS(Status)) + { + LogpDbgBreak(); + break; + } + + cchPrinted += cchLengthToCopy; + + Status = LogpPut(Message, Level & 0x0F); + if (!NT_SUCCESS(Status)) + { + DBGPRINT("LogpPut failed - Status = 0x%08X Log is: %s\n", Status, Message); + //LogpDbgBreak(); + break; + } + + } while (cchPrinted < cchLength); + + // + // Free the previously allocated log message pool. + // + ExFreePool(LogMessage); + + } + else + { + + // + // No overflow occurred, we should be safe to print. + // + Status = LogpMakePrefix(Level & 0xF0, FunctionName, LogMessage, Message, RTL_NUMBER_OF(Message)); + if (!NT_SUCCESS(Status)) + { + LogpDbgBreak(); + return Status; + } + + Status = LogpPut(Message, Level & 0x0F); + if (!NT_SUCCESS(Status)) + { + DBGPRINT("LogpPut failed - Status = 0x%08X Log is: %s\n", Status, Message); + //LogpDbgBreak(); + } + } + + return Status; +} + +// Concatenates meta information such as the current time and a process ID to +// user given log message. +static +NTSTATUS +LogpMakePrefix( + IN ULONG Level, + IN CONST CHAR* FunctionName, + IN CONST CHAR* LogMessage, + OUT CHAR* LogBuffer, + IN SIZE_T LogBufferLength +) +{ + NTSTATUS Status; + ULONG LogLevelIndex; + CHAR TimeBuffer[20]; + CHAR FunctionNameBuffer[50]; + CHAR ProcessorNumber[10]; + ULONG CurrentProcessorNumber; + TIME_FIELDS TimeFields; + LARGE_INTEGER SystemTime; + LARGE_INTEGER LocalTime; + CONST CHAR* BaseFunctionName; + + static CHAR CONST* LogLevelStrings[4] = { "DBG ", "INF ", "WRN ", "ERR " }; + + // + // Get the log level prefix index. + // + if (!_BitScanForward((unsigned long*)&LogLevelIndex, (Level >> 4))) + { + return STATUS_INVALID_PARAMETER; + } + + // + // Want the current time. + // + if (!(LogFlags & LogOptDisableTime)) + { + + KeQuerySystemTime(&SystemTime); + ExSystemTimeToLocalTime(&SystemTime, &LocalTime); + RtlTimeToTimeFields(&LocalTime, &TimeFields); + Status = RtlStringCchPrintfA(TimeBuffer, + RTL_NUMBER_OF(TimeBuffer), + "%02u:%02u:%02u.%03u ", + TimeFields.Hour, + TimeFields.Minute, + TimeFields.Second, + TimeFields.Milliseconds + ); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + } + else + { + TimeBuffer[0] = '\0'; + } + + // + // Want the function name. + // + if (!(LogFlags & LogOptDisableFunctionName)) + { + + BaseFunctionName = LogpFindBaseFunctionName(FunctionName); + if (BaseFunctionName) + { + + Status = RtlStringCchPrintfA(FunctionNameBuffer, + RTL_NUMBER_OF(FunctionNameBuffer), + "%-40s ", + BaseFunctionName + ); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + } + else + { + FunctionNameBuffer[0] = '\0'; + } + } + else + { + FunctionNameBuffer[0] = '\0'; + } + + // + // Want the processor number. + // + if (!(LogFlags & LogOptDisableProcessorNumber)) + { + + CurrentProcessorNumber = KeGetCurrentProcessorNumberEx(NULL); + Status = RtlStringCchPrintfA(ProcessorNumber, + RTL_NUMBER_OF(ProcessorNumber), + CurrentProcessorNumber >= 10 ? "#%lu" : "#%lu ", + CurrentProcessorNumber + ); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + } + else + { + *((UINT16*)ProcessorNumber) = 0x2020; // " " + ProcessorNumber[2] = '\0'; + } + + // + // It uses PsGetProcessId(PsGetCurrentProcess()) instead of + // PsGetCurrentThreadProcessId() because the latter sometimes returns + // unwanted value, for example: + // PID == 4 but its image name != ntoskrnl.exe + // The author is guessing that it is related to attaching processes but + // not quite sure. The former way works as expected. + // + Status = RtlStringCchPrintfA(LogBuffer, + LogBufferLength, + "%s%s%s%6Iu %s%s", + TimeBuffer, + LogLevelStrings[LogLevelIndex], + ProcessorNumber, + (ULONG_PTR)PsGetCurrentThreadId(), + FunctionNameBuffer, + LogMessage + ); + return Status; +} + +// Returns the function's base name, for example, +// NamespaceName::ClassName::MethodName will be returned as MethodName. +static +CONST CHAR* +LogpFindBaseFunctionName( + IN CONST CHAR* FunctionName +) +{ + const char* p; + const char* BaseFunctionName; + + if (!FunctionName) + { + return NULL; + } + + p = FunctionName; + BaseFunctionName = FunctionName; + + while (*(p++)) + { + if (*p == ':') + BaseFunctionName = p + 1; + } + + return BaseFunctionName; +} + +// Logs the entry according to attribute and the thread condition. +static +NTSTATUS +LogpPut( + IN CHAR* Message, + IN ULONG Attribute +) +{ + //TODO: remove this arg + UNREFERENCED_PARAMETER(Attribute); + + NTSTATUS Status = STATUS_SUCCESS; + + // + // Log the entry to a file or buffer. + // + if (LogpIsLogFileEnabled(&LogBufferInfo)) + { + + // Can it log it to a file now? + if (KeGetCurrentIrql() == PASSIVE_LEVEL && LogpIsLogFileActivated(&LogBufferInfo)) + { +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:28123) // The function is not permitted to be called at a high IRQ level. +#endif + if (!KeAreAllApcsDisabled()) + { + + // Yes, it can! Lets see if we can buffer it though for performance + auto UsedBufferSize = (SIZE_T)(LogBufferInfo.LogBufferTail - LogBufferInfo.LogBufferHead); + auto UsableSpaceLeft = UsedBufferSize > LOG_BUFFER_USABLE_SIZE ? 0 : LOG_BUFFER_USABLE_SIZE - UsedBufferSize; + if (strlen(Message) + 1 <= UsableSpaceLeft) { + Status = LogpBufferMessage(Message, &LogBufferInfo); + } + else { + LogpFlushLogBuffer(&LogBufferInfo); + Status = LogpWriteMessageToFile(Message, &LogBufferInfo); + } + } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + } + else + { + Status = LogpBufferMessage(Message, &LogBufferInfo); + } + } + + // Print to kernel debugger? + // if( KeGetCurrentIrql() < CLOCK_LEVEL) { + LogpDoDbgPrint(Message); + // } + + return Status; +} + +// Switches the current log buffer, saves the contents of old buffer to the log +// file, and prints them out as necessary. This function does not flush the log +// file, so code should call LogpWriteMessageToFile() or ZwFlushBuffersFile() later. +static +NTSTATUS +LogpFlushLogBuffer( + IN OUT PLOG_BUFFER_INFO Info +) +{ + NTSTATUS Status; + KLOCK_QUEUE_HANDLE LockHandle; + IO_STATUS_BLOCK IoStatus; + CHAR* OldLogBuffer; + CHAR* CurrentLogEntry; + ULONG CurrentLogEntryLength; + + NT_ASSERT(Info != NULL); + NT_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); + + Status = STATUS_SUCCESS; + + // + // Enter a critical section and acquire a reader lock for info in order to + // write a log file safely. + // + ExEnterCriticalRegionAndAcquireResourceExclusive(&Info->Resource); + + // + // Acquire a spin lock for Info.LogBuffer(s) in order + // to switch its head safely. + // + KeAcquireInStackQueuedSpinLock(&Info->SpinLock, &LockHandle); + + OldLogBuffer = (CHAR*)(Info->LogBufferHead); + Info->LogBufferHead = (OldLogBuffer == Info->LogBuffer1) + ? Info->LogBuffer2 + : Info->LogBuffer1; + Info->LogBufferHead[0] = '\0'; + Info->LogBufferTail = Info->LogBufferHead; + + KeReleaseInStackQueuedSpinLock(&LockHandle); + + // + // Write all log entries in old log buffer. + // + for (CurrentLogEntry = OldLogBuffer; *CurrentLogEntry; /**/) + { + CurrentLogEntryLength = (ULONG)strlen(CurrentLogEntry); + + Status = ZwWriteFile(Info->LogFileHandle, + NULL, + NULL, + NULL, + &IoStatus, + CurrentLogEntry, + CurrentLogEntryLength, + NULL, + NULL + ); + + if (!NT_SUCCESS(Status)) + { + + // + // It could happen when you did not register IRP_SHUTDOWN and call + // LogIrpShutdownHandler and the system tried to log to a file after + // a file system was unmounted. + // + LogpDbgBreak(); + } + + CurrentLogEntry += ((ULONGLONG)CurrentLogEntryLength + 1); + } + + OldLogBuffer[0] = '\0'; + + ExReleaseResourceAndLeaveCriticalRegion(&Info->Resource); + + return Status; +} + +// Logs the current log entry to and flush the log file. +static +NTSTATUS +LogpWriteMessageToFile( + IN CONST CHAR* Message, + IN CONST LOG_BUFFER_INFO* Info +) +{ + NTSTATUS Status; + IO_STATUS_BLOCK IoStatusBlock; + ULONG MessageLength; + + NT_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); + + MessageLength = (ULONG)strlen(Message); + + Status = ZwWriteFile(Info->LogFileHandle, + NULL, + NULL, + NULL, + &IoStatusBlock, + (PVOID)Message, + MessageLength, + NULL, + NULL + ); + + if (!NT_SUCCESS(Status)) + { + + // + // It could happen when you did not register IRP_SHUTDOWN and call + // LogIrpShutdownHandler and the system tried to log to a file + // after a file system was unmounted. + // + LogpDbgBreak(); + } + + Status = ZwFlushBuffersFile(Info->LogFileHandle, &IoStatusBlock); + + return Status; +} + +// Buffer the log entry to the log buffer. +static +NTSTATUS +LogpBufferMessage( + IN CONST CHAR* Message, + IN OUT PLOG_BUFFER_INFO Info +) +{ + NTSTATUS Status; + KIRQL OldIrql; + KLOCK_QUEUE_HANDLE LockHandle; + SIZE_T UsedBufferSize; + SIZE_T MessageLength; + + //NT_ASSERT( Info != NULL ); + + // + // Acquire a spin lock to add the log safely. + // + OldIrql = KeGetCurrentIrql(); + if (OldIrql < DISPATCH_LEVEL) + { + KeAcquireInStackQueuedSpinLock(&Info->SpinLock, &LockHandle); + } + else + { + KeAcquireInStackQueuedSpinLockAtDpcLevel(&Info->SpinLock, &LockHandle); + } + + NT_ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL); + + // + // Copy the current log to the buffer. + // + UsedBufferSize = (SIZE_T)(Info->LogBufferTail - Info->LogBufferHead); + Status = RtlStringCchCopyA((NTSTRSAFE_PSTR)Info->LogBufferTail, + LOG_BUFFER_USABLE_SIZE - UsedBufferSize, + Message); + + // + // Update Info->LogMaxUsage if necessary. + // + if (NT_SUCCESS(Status)) + { + + MessageLength = (SIZE_T)(strlen(Message) + 1); + + Info->LogBufferTail += MessageLength; + UsedBufferSize += MessageLength; + + if (UsedBufferSize > Info->LogMaxUsage) + { + Info->LogMaxUsage = UsedBufferSize; // Update + } + + } + else + { + + Info->LogMaxUsage = LOG_BUFFER_SIZE; // Indicates overflow + } + + *Info->LogBufferTail = '\0'; + + // + // Release the spin lock. + // + if (OldIrql < DISPATCH_LEVEL) + { + KeReleaseInStackQueuedSpinLock(&LockHandle); + } + else + { + KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle); + } + + return Status; +} + +// Calls DbgPrintEx while converting \r\n to \n\0 +static +VOID +LogpDoDbgPrint( + IN CHAR* Message +) +{ + UNREFERENCED_PARAMETER(Message); + /*size_t LocationOfCr; + LocationOfCr = strlen(Message) - 2; + Message[LocationOfCr] = '\n'; + Message[LocationOfCr + 1] = '\0'; + DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "%s", Message);*/ +} + +// Returns true when a log file is enabled. +FORCEINLINE +BOOLEAN +LogpIsLogFileEnabled( + IN CONST LOG_BUFFER_INFO* Info +) +{ + if (Info->LogBuffer1 != NULL) + { + NT_ASSERT(Info->LogBuffer2 != NULL); + NT_ASSERT(Info->LogBufferHead != NULL); + NT_ASSERT(Info->LogBufferTail != NULL); + return TRUE; + } + + NT_ASSERT(!Info->LogBuffer2); + NT_ASSERT(!Info->LogBufferHead); + NT_ASSERT(!Info->LogBufferTail); + return FALSE; +} + +// Returns true when a log file is opened. +static +BOOLEAN +LogpIsLogFileActivated( + IN CONST LOG_BUFFER_INFO* Info +) +{ + if (Info->BufferFlushThreadShouldBeAlive) + { + NT_ASSERT(Info->BufferFlushThreadHandle != NULL); + NT_ASSERT(Info->LogFileHandle != NULL); + return TRUE; + } + + NT_ASSERT(!Info->BufferFlushThreadHandle); + NT_ASSERT(!Info->LogFileHandle); + return FALSE; +} + +// Returns true when logging is necessary according to the log's severity and +// a set log level. +static +BOOLEAN +LogpIsLogNeeded( + IN ULONG Level +) +{ + return (BOOLEAN)((LogFlags & Level) != 0); +} + +// A thread runs as long as Info.BufferFlushThreadShouldBeAlive is true and +// flushes a log buffer to a log file every kLogpLogFlushIntervalMsec msec. +static +VOID +LogpBufferFlushThreadRoutine( + IN PVOID StartContext +) +{ + NTSTATUS Status; + LARGE_INTEGER Interval; + PLOG_BUFFER_INFO Info; + + PAGED_CODE(); + + Status = STATUS_SUCCESS; + Info = (PLOG_BUFFER_INFO)StartContext; + Info->BufferFlushThreadStarted = TRUE; + +#if defined(DBG) + LOG_DEBUG("Log thread started (TID=%x)", (ULONG)(ULONG_PTR)PsGetCurrentThreadId()); +#endif + + while (Info->BufferFlushThreadShouldBeAlive) + { + NT_ASSERT(LogpIsLogFileActivated(Info)); + + if (Info->LogBufferHead[0]) + { + NT_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); + NT_ASSERT(!KeAreAllApcsDisabled()); + + Status = LogpFlushLogBuffer(Info); + + // + // Do not flush the file for overall performance. Even a case of bug check, + // we should be able to recover logs by looking at both log buffers! + // + } + + // + // Sleep the current thread's execution for LOG_FLUSH_INTERVAL milliseconds. + // + Interval.QuadPart = -(LOG_FLUSH_INTERVAL * 1000 * 10); + KeDelayExecutionThread(KernelMode, FALSE, &Interval); + } + + PsTerminateSystemThread(Status); +} + +// Determines if a specified file path exists. +static +BOOLEAN +LogpFileExists( + IN PUNICODE_STRING FilePath +) +{ + NTSTATUS Status; + HANDLE FileHandle; + IO_STATUS_BLOCK IoStatus; + OBJECT_ATTRIBUTES ObjectAttributes; + + InitializeObjectAttributes(&ObjectAttributes, FilePath, OBJ_KERNEL_HANDLE, NULL, NULL); + + Status = ZwCreateFile(&FileHandle, + FILE_READ_DATA | SYNCHRONIZE, + &ObjectAttributes, + &IoStatus, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0 + ); + + if (NT_SUCCESS(Status)) + { + ZwClose(FileHandle); + return TRUE; + } + + return FALSE; +} + +// Sets a break point that works only when windbg is present +static +VOID +LogpDbgBreak( + VOID +) +{ + if (!KD_DEBUGGER_NOT_PRESENT) + { + __debugbreak(); + } +} + +#endif // ENABLE_LOG \ No newline at end of file diff --git a/C/FileDeleteRecordPluginDriver/MyStdint.h b/C/FileDeleteRecordPluginDriver/MyStdint.h new file mode 100644 index 0000000..473c1a5 --- /dev/null +++ b/C/FileDeleteRecordPluginDriver/MyStdint.h @@ -0,0 +1,151 @@ +#pragma once + +typedef signed char int8_t; +typedef short int16_t; +typedef int int32_t; +typedef long long int64_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +typedef signed char int_least8_t; +typedef short int_least16_t; +typedef int int_least32_t; +typedef long long int_least64_t; +typedef unsigned char uint_least8_t; +typedef unsigned short uint_least16_t; +typedef unsigned int uint_least32_t; +typedef unsigned long long uint_least64_t; + +typedef signed char int_fast8_t; +typedef int int_fast16_t; +typedef int int_fast32_t; +typedef long long int_fast64_t; +typedef unsigned char uint_fast8_t; +typedef unsigned int uint_fast16_t; +typedef unsigned int uint_fast32_t; +typedef unsigned long long uint_fast64_t; + +typedef long long intmax_t; +typedef unsigned long long uintmax_t; + +// These macros must exactly match those in the Windows SDK's intsafe.h. +#define INT8_MIN (-127i8 - 1) +#define INT16_MIN (-32767i16 - 1) +#define INT32_MIN (-2147483647i32 - 1) +#define INT64_MIN (-9223372036854775807i64 - 1) +#define INT8_MAX 127i8 +#define INT16_MAX 32767i16 +#define INT32_MAX 2147483647i32 +#define INT64_MAX 9223372036854775807i64 +#define UINT8_MAX 0xffui8 +#define UINT16_MAX 0xffffui16 +#define UINT32_MAX 0xffffffffui32 +#define UINT64_MAX 0xffffffffffffffffui64 + +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST16_MIN INT32_MIN +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MAX INT32_MAX +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT32_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +#ifdef _WIN64 +#define INTPTR_MIN INT64_MIN +#define INTPTR_MAX INT64_MAX +#define UINTPTR_MAX UINT64_MAX +#else +#define INTPTR_MIN INT32_MIN +#define INTPTR_MAX INT32_MAX +#define UINTPTR_MAX UINT32_MAX +#endif + +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +#define PTRDIFF_MIN INTPTR_MIN +#define PTRDIFF_MAX INTPTR_MAX + +#ifndef SIZE_MAX + // SIZE_MAX definition must match exactly with limits.h for modules support. +#ifdef _WIN64 +#define SIZE_MAX 0xffffffffffffffffui64 +#else +#define SIZE_MAX 0xffffffffui32 +#endif +#endif + +#define SIG_ATOMIC_MIN INT32_MIN +#define SIG_ATOMIC_MAX INT32_MAX + +#define WCHAR_MIN 0x0000 +#define WCHAR_MAX 0xffff + +#define WINT_MIN 0x0000 +#define WINT_MAX 0xffff + +#define INT8_C(x) (x) +#define INT16_C(x) (x) +#define INT32_C(x) (x) +#define INT64_C(x) (x ## LL) + +#define UINT8_C(x) (x) +#define UINT16_C(x) (x) +#define UINT32_C(x) (x ## U) +#define UINT64_C(x) (x ## ULL) + +#define INTMAX_C(x) INT64_C(x) +#define UINTMAX_C(x) UINT64_C(x) + +// copied std::is_same_v from xtr1common in VC_INCLUDE +template +struct integral_constant { + static constexpr _Ty value = _Val; + + using value_type = _Ty; + using type = integral_constant; + + constexpr operator value_type() const noexcept { + return value; + } + + constexpr value_type operator()() const noexcept { + return value; + } +}; + +template +using bool_constant = integral_constant; + +using true_type = bool_constant; +using false_type = bool_constant; + +template +constexpr bool is_same_v = false; + +template +constexpr bool is_same_v<_Ty, _Ty> = true; + +template +struct is_same : bool_constant> {}; \ No newline at end of file diff --git a/C/FileDeleteRecordPluginDriver/NtBuild.h b/C/FileDeleteRecordPluginDriver/NtBuild.h new file mode 100644 index 0000000..3d32bb5 --- /dev/null +++ b/C/FileDeleteRecordPluginDriver/NtBuild.h @@ -0,0 +1,65 @@ +#pragma once + +#define NT_BUILD_XP 2600 // winxp +#define NT_BUILD_2600 2600 // winxp +#define NT_BUILD_2K3 3790 // ws2k3 +#define NT_BUILD_3790 3790 // ws2k3 +#define NT_BUILD_VISTA 6000 // vista +#define NT_BUILD_6000 6000 // vista +#define NT_BUILD_7 7600 // win7 +#define NT_BUILD_7600 7600 // win7 +#define NT_BUILD_7_SP1 7601 // win7sp1 +#define NT_BUILD_7601 7601 // win7sp1 +#define NT_BUILD_8 9200 // win8 +#define NT_BUILD_9200 9200 // win8 +#define NT_BUILD_8_1 9600 // blue +#define NT_BUILD_9600 9600 // blue +#define NT_BUILD_10_1507_9841 9841 // 10 Pre RTM Insider +#define NT_BUILD_10_1507_10041 10041 // 10 Pre RTM +#define NT_BUILD_10_1507 10240 // th1 +#define NT_BUILD_10_1511 10586 // th2 +#define NT_BUILD_10_1607_14316 14316 // rs1 beta +#define NT_BUILD_10_1607 14393 // rs1 +#define NT_BUILD_10_1703 15063 // rs2 +#define NT_BUILD_10_1709 16299 // rs3 +#define NT_BUILD_10_1803 17134 // rs4 +#define NT_BUILD_10_1809 17763 // rs5 +#define NT_BUILD_10_1903 18362 // 19H1 +#define NT_BUILD_10_1909 18363 // 19H2 +#define NT_BUILD_10_20H1 19041 // 20H1 +#define NT_BUILD_10_20H2 19042 // 20H2 +#define NT_BUILD_10_20H2_20226 20226 // 20H2 Insider 2021 January + +#define NT_BUILD_BLUE NT_BUILD_8_1 +#define NT_BUILD_10_TH1 NT_BUILD_10_1507 +#define NT_BUILD_10_TH2 NT_BUILD_10_1511 +#define NT_BUILD_10_RS1_BETA NT_BUILD_10_1607_14316 +#define NT_BUILD_10_RS1 NT_BUILD_10_1607 +#define NT_BUILD_10_RS2 NT_BUILD_10_1703 +#define NT_BUILD_10_RS3 NT_BUILD_10_1709 +#define NT_BUILD_10_RS4 NT_BUILD_10_1803 +#define NT_BUILD_10_RS5 NT_BUILD_10_1809 + +#define $7600 Build7600 +#define $7601 Build7601 +#define $9200 Build9200 +#define $9600 Build9600 +#define $1507 Build1507 +#define $1511 Build1511 +#define $1607 Build1607 +#define $1703 Build1703 +#define $1709 Build1709 +#define $1803 Build1803 +#define $1809 Build1809 + +#define WIN7 Build7600 +#define WIN71 Build7601 +#define WIN8 Build9200 +#define WIN81 Build9600 +#define TH1 Build1507 +#define TH2 Build1511 +#define RS1 Build1607 +#define RS2 Build1703 +#define RS3 Build1709 +#define RS4 Build1803 +#define RS5 Build1809 \ No newline at end of file diff --git a/C/FileDeleteRecordPluginDriver/NtStructs.h b/C/FileDeleteRecordPluginDriver/NtStructs.h new file mode 100644 index 0000000..a6f8d14 --- /dev/null +++ b/C/FileDeleteRecordPluginDriver/NtStructs.h @@ -0,0 +1,550 @@ +#pragma once +#pragma once +#include + +typedef struct _PEB_LDR_DATA +{ + ULONG Length; + UCHAR Initialized; + PVOID SsHandle; + LIST_ENTRY InLoadOrderModuleList; + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; +} PEB_LDR_DATA, * PPEB_LDR_DATA; + +typedef struct _LDR_DATA_TABLE_ENTRY +{ + LIST_ENTRY InLoadOrderLinks; + LIST_ENTRY InMemoryOrderLinks; + LIST_ENTRY InInitializationOrderLinks; + PVOID DllBase; + PVOID EntryPoint; + ULONG SizeOfImage; + UNICODE_STRING FullDllName; + UNICODE_STRING BaseDllName; + ULONG Flags; + USHORT LoadCount; + USHORT TlsIndex; + LIST_ENTRY HashLinks; + ULONG TimeDateStamp; +} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY; + + +typedef struct _PEB +{ + UCHAR InheritedAddressSpace; + UCHAR ReadImageFileExecOptions; + UCHAR BeingDebugged; + UCHAR BitField; + PVOID Mutant; + PVOID ImageBaseAddress; + PPEB_LDR_DATA Ldr; + PVOID ProcessParameters; + PVOID SubSystemData; + PVOID ProcessHeap; + PVOID FastPebLock; + PVOID AtlThunkSListPtr; + PVOID IFEOKey; + PVOID CrossProcessFlags; + PVOID KernelCallbackTable; + ULONG SystemReserved; + ULONG AtlThunkSListPtr32; + PVOID ApiSetMap; +} PEB, * PPEB; + +typedef struct _PEB_LDR_DATA32 +{ + ULONG Length; + UCHAR Initialized; + ULONG SsHandle; + LIST_ENTRY32 InLoadOrderModuleList; + LIST_ENTRY32 InMemoryOrderModuleList; + LIST_ENTRY32 InInitializationOrderModuleList; +} PEB_LDR_DATA32, * PPEB_LDR_DATA32; + +typedef struct _LDR_DATA_TABLE_ENTRY32 +{ + LIST_ENTRY32 InLoadOrderLinks; + LIST_ENTRY32 InMemoryOrderLinks; + LIST_ENTRY32 InInitializationOrderLinks; + ULONG DllBase; + ULONG EntryPoint; + ULONG SizeOfImage; + UNICODE_STRING32 FullDllName; + UNICODE_STRING32 BaseDllName; + ULONG Flags; + USHORT LoadCount; + USHORT TlsIndex; + LIST_ENTRY32 HashLinks; + ULONG TimeDateStamp; +} LDR_DATA_TABLE_ENTRY32, * PLDR_DATA_TABLE_ENTRY32; +typedef struct _PEB32 +{ + UCHAR InheritedAddressSpace; + UCHAR ReadImageFileExecOptions; + UCHAR BeingDebugged; + UCHAR BitField; + ULONG Mutant; + ULONG ImageBaseAddress; + ULONG Ldr; + ULONG ProcessParameters; + ULONG SubSystemData; + ULONG ProcessHeap; + ULONG FastPebLock; + ULONG AtlThunkSListPtr; + ULONG IFEOKey; + ULONG CrossProcessFlags; + ULONG UserSharedInfoPtr; + ULONG SystemReserved; + ULONG AtlThunkSListPtr32; + ULONG ApiSetMap; +} PEB32, * PPEB32; + +#pragma warning( disable : 4201 ) +typedef struct _WNODE_HEADER { + ULONG BufferSize; + ULONG ProviderId; + union { + ULONG64 HistoricalContext; + struct { + ULONG Version; + ULONG Linkage; + }; + }; + union { + HANDLE KernelHandle; + LARGE_INTEGER TimeStamp; + }; + GUID Guid; + ULONG ClientContext; + ULONG Flags; +} WNODE_HEADER, * PWNODE_HEADER; + +typedef struct _EVENT_TRACE_PROPERTIES_V2 { + WNODE_HEADER Wnode; + ULONG BufferSize; + ULONG MinimumBuffers; + ULONG MaximumBuffers; + ULONG MaximumFileSize; + ULONG LogFileMode; + ULONG FlushTimer; + ULONG EnableFlags; + union { + LONG AgeLimit; + LONG FlushThreshold; + } DUMMYUNIONNAME; + ULONG NumberOfBuffers; + ULONG FreeBuffers; + ULONG EventsLost; + ULONG BuffersWritten; + ULONG LogBuffersLost; + ULONG RealTimeBuffersLost; + HANDLE LoggerThreadId; + ULONG LogFileNameOffset; + ULONG LoggerNameOffset; + union { + struct { + ULONG VersionNumber : 8; + } DUMMYSTRUCTNAME; + ULONG V2Control; + } DUMMYUNIONNAME2; + ULONG FilterDescCount; + PEVENT_FILTER_DESCRIPTOR FilterDesc; + union { + struct { + ULONG Wow : 1; + ULONG QpcDeltaTracking : 1; + ULONG LargeMdlPages : 1; + ULONG ExcludeKernelStack : 1; + } DUMMYSTRUCTNAME; + ULONG64 V2Options; + } DUMMYUNIONNAME3; +} EVENT_TRACE_PROPERTIES_V2, * PEVENT_TRACE_PROPERTIES_V2; + +typedef struct _EVENT_HEADER +{ + USHORT Size; + USHORT HeaderType; + USHORT Flags; + USHORT EventProperty; + ULONG ThreadId; + ULONG ProcessId; + LARGE_INTEGER TimeStamp; + GUID ProviderId; + EVENT_DESCRIPTOR EventDescriptor; + union { + struct { + ULONG KernelTime; + ULONG UserTime; + } DUMMYSTRUCTNAME; + ULONG64 ProcessorTime; + } DUMMYUNIONNAME; + GUID ActivityId; +} EVENT_HEADER, * PEVENT_HEADER; +#pragma warning( default : 4201 ) + +// +typedef struct _ETW_NOTIFICATION_HEADER +{ + ULONG NotificationType; + ULONG NotificationSize; + ULONG Offset; + ULONG ReplyRequested; + ULONG Timeout; + union { + ULONG ReplyCount; + ULONG NotifyeeCount; + }; + ULONGLONG Reserved2; + ULONG TargetPID; + ULONG SourcePID; + GUID DestinationGuid; + GUID SourceGuid; +} ETW_NOTIFICATION_HEADER, * PETW_NOTIFICATION_HEADER; + +#define EVENT_CONTROL_CODE_ENABLE_PROVIDER 1 + +#define TRACE_LEVEL_VERBOSE 5 + +#define EtwpStartTrace 1 +#define EtwpStopTrace 2 +#define EtwpSendNotification 17 + +#define WNODE_FLAG_TRACED_GUID 0x00020000 // denotes a trace + +#define EVENT_TRACE_BUFFERING_MODE 0x00000400 // Buffering mode only +#define EVENT_TRACE_INDEPENDENT_SESSION_MODE 0x08000000 // Independent logger session + +// +#define EtwNotificationTypeEnable 3 + +ULONG KphCaptureStackBackTrace( + _In_ ULONG FramesToSkip, + _In_ ULONG FramesToCapture, + _Out_writes_(FramesToCapture) PVOID* BackTrace +); + +typedef struct _RTL_PROCESS_MODULE_INFORMATION +{ + HANDLE Section; + PVOID MappedBase; + PVOID ImageBase; + ULONG ImageSize; + ULONG Flags; + USHORT LoadOrderIndex; + USHORT InitOrderIndex; + USHORT LoadCount; + USHORT OffsetToFileName; + CHAR FullPathName[256]; +} RTL_PROCESS_MODULE_INFORMATION, * PRTL_PROCESS_MODULE_INFORMATION; + +typedef struct _RTL_PROCESS_MODULES +{ + ULONG NumberOfModules; + RTL_PROCESS_MODULE_INFORMATION Modules[1]; +} RTL_PROCESS_MODULES, * PRTL_PROCESS_MODULES; + +// https://github.com/processhacker/processhacker/blob/61e764b021109fe01c0520dd470b5f8d8bf37746/KProcessHacker/util.c#L111 +typedef enum _SYSTEM_INFORMATION_CLASS +{ + SystemBasicInformation, // q: SYSTEM_BASIC_INFORMATION + SystemProcessorInformation, // q: SYSTEM_PROCESSOR_INFORMATION + SystemPerformanceInformation, // q: SYSTEM_PERFORMANCE_INFORMATION + SystemTimeOfDayInformation, // q: SYSTEM_TIMEOFDAY_INFORMATION + SystemPathInformation, // not implemented + SystemProcessInformation, // q: SYSTEM_PROCESS_INFORMATION + SystemCallCountInformation, // q: SYSTEM_CALL_COUNT_INFORMATION + SystemDeviceInformation, // q: SYSTEM_DEVICE_INFORMATION + SystemProcessorPerformanceInformation, // q: SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION (EX in: USHORT ProcessorGroup) + SystemFlagsInformation, // q: SYSTEM_FLAGS_INFORMATION + SystemCallTimeInformation, // not implemented // SYSTEM_CALL_TIME_INFORMATION // 10 + SystemModuleInformation, // q: RTL_PROCESS_MODULES + SystemLocksInformation, // q: RTL_PROCESS_LOCKS + SystemStackTraceInformation, // q: RTL_PROCESS_BACKTRACES + SystemPagedPoolInformation, // not implemented + SystemNonPagedPoolInformation, // not implemented + SystemHandleInformation, // q: SYSTEM_HANDLE_INFORMATION + SystemObjectInformation, // q: SYSTEM_OBJECTTYPE_INFORMATION mixed with SYSTEM_OBJECT_INFORMATION + SystemPageFileInformation, // q: SYSTEM_PAGEFILE_INFORMATION + SystemVdmInstemulInformation, // q: SYSTEM_VDM_INSTEMUL_INFO + SystemVdmBopInformation, // not implemented // 20 + SystemFileCacheInformation, // q: SYSTEM_FILECACHE_INFORMATION; s (requires SeIncreaseQuotaPrivilege) (info for WorkingSetTypeSystemCache) + SystemPoolTagInformation, // q: SYSTEM_POOLTAG_INFORMATION + SystemInterruptInformation, // q: SYSTEM_INTERRUPT_INFORMATION (EX in: USHORT ProcessorGroup) + SystemDpcBehaviorInformation, // q: SYSTEM_DPC_BEHAVIOR_INFORMATION; s: SYSTEM_DPC_BEHAVIOR_INFORMATION (requires SeLoadDriverPrivilege) + SystemFullMemoryInformation, // not implemented // SYSTEM_MEMORY_USAGE_INFORMATION + SystemLoadGdiDriverInformation, // s (kernel-mode only) + SystemUnloadGdiDriverInformation, // s (kernel-mode only) + SystemTimeAdjustmentInformation, // q: SYSTEM_QUERY_TIME_ADJUST_INFORMATION; s: SYSTEM_SET_TIME_ADJUST_INFORMATION (requires SeSystemtimePrivilege) + SystemSummaryMemoryInformation, // not implemented // SYSTEM_MEMORY_USAGE_INFORMATION + SystemMirrorMemoryInformation, // s (requires license value "Kernel-MemoryMirroringSupported") (requires SeShutdownPrivilege) // 30 + SystemPerformanceTraceInformation, // q; s: (type depends on EVENT_TRACE_INFORMATION_CLASS) + SystemObsolete0, // not implemented + SystemExceptionInformation, // q: SYSTEM_EXCEPTION_INFORMATION + SystemCrashDumpStateInformation, // s: SYSTEM_CRASH_DUMP_STATE_INFORMATION (requires SeDebugPrivilege) + SystemKernelDebuggerInformation, // q: SYSTEM_KERNEL_DEBUGGER_INFORMATION + SystemContextSwitchInformation, // q: SYSTEM_CONTEXT_SWITCH_INFORMATION + SystemRegistryQuotaInformation, // q: SYSTEM_REGISTRY_QUOTA_INFORMATION; s (requires SeIncreaseQuotaPrivilege) + SystemExtendServiceTableInformation, // s (requires SeLoadDriverPrivilege) // loads win32k only + SystemPrioritySeperation, // s (requires SeTcbPrivilege) + SystemVerifierAddDriverInformation, // s (requires SeDebugPrivilege) // 40 + SystemVerifierRemoveDriverInformation, // s (requires SeDebugPrivilege) + SystemProcessorIdleInformation, // q: SYSTEM_PROCESSOR_IDLE_INFORMATION (EX in: USHORT ProcessorGroup) + SystemLegacyDriverInformation, // q: SYSTEM_LEGACY_DRIVER_INFORMATION + SystemCurrentTimeZoneInformation, // q; s: RTL_TIME_ZONE_INFORMATION + SystemLookasideInformation, // q: SYSTEM_LOOKASIDE_INFORMATION + SystemTimeSlipNotification, // s: HANDLE (NtCreateEvent) (requires SeSystemtimePrivilege) + SystemSessionCreate, // not implemented + SystemSessionDetach, // not implemented + SystemSessionInformation, // not implemented (SYSTEM_SESSION_INFORMATION) + SystemRangeStartInformation, // q: SYSTEM_RANGE_START_INFORMATION // 50 + SystemVerifierInformation, // q: SYSTEM_VERIFIER_INFORMATION; s (requires SeDebugPrivilege) + SystemVerifierThunkExtend, // s (kernel-mode only) + SystemSessionProcessInformation, // q: SYSTEM_SESSION_PROCESS_INFORMATION + SystemLoadGdiDriverInSystemSpace, // s: SYSTEM_GDI_DRIVER_INFORMATION (kernel-mode only) (same as SystemLoadGdiDriverInformation) + SystemNumaProcessorMap, // q: SYSTEM_NUMA_INFORMATION + SystemPrefetcherInformation, // q; s: PREFETCHER_INFORMATION // PfSnQueryPrefetcherInformation + SystemExtendedProcessInformation, // q: SYSTEM_PROCESS_INFORMATION + SystemRecommendedSharedDataAlignment, // q: ULONG // KeGetRecommendedSharedDataAlignment + SystemComPlusPackage, // q; s: ULONG + SystemNumaAvailableMemory, // q: SYSTEM_NUMA_INFORMATION // 60 + SystemProcessorPowerInformation, // q: SYSTEM_PROCESSOR_POWER_INFORMATION (EX in: USHORT ProcessorGroup) + SystemEmulationBasicInformation, // q: SYSTEM_BASIC_INFORMATION + SystemEmulationProcessorInformation, // q: SYSTEM_PROCESSOR_INFORMATION + SystemExtendedHandleInformation, // q: SYSTEM_HANDLE_INFORMATION_EX + SystemLostDelayedWriteInformation, // q: ULONG + SystemBigPoolInformation, // q: SYSTEM_BIGPOOL_INFORMATION + SystemSessionPoolTagInformation, // q: SYSTEM_SESSION_POOLTAG_INFORMATION + SystemSessionMappedViewInformation, // q: SYSTEM_SESSION_MAPPED_VIEW_INFORMATION + SystemHotpatchInformation, // q; s: SYSTEM_HOTPATCH_CODE_INFORMATION + SystemObjectSecurityMode, // q: ULONG // 70 + SystemWatchdogTimerHandler, // s: SYSTEM_WATCHDOG_HANDLER_INFORMATION // (kernel-mode only) + SystemWatchdogTimerInformation, // q: SYSTEM_WATCHDOG_TIMER_INFORMATION // (kernel-mode only) + SystemLogicalProcessorInformation, // q: SYSTEM_LOGICAL_PROCESSOR_INFORMATION (EX in: USHORT ProcessorGroup) + SystemWow64SharedInformationObsolete, // not implemented + SystemRegisterFirmwareTableInformationHandler, // s: SYSTEM_FIRMWARE_TABLE_HANDLER // (kernel-mode only) + SystemFirmwareTableInformation, // SYSTEM_FIRMWARE_TABLE_INFORMATION + SystemModuleInformationEx, // q: RTL_PROCESS_MODULE_INFORMATION_EX + SystemVerifierTriageInformation, // not implemented + SystemSuperfetchInformation, // q; s: SUPERFETCH_INFORMATION // PfQuerySuperfetchInformation + SystemMemoryListInformation, // q: SYSTEM_MEMORY_LIST_INFORMATION; s: SYSTEM_MEMORY_LIST_COMMAND (requires SeProfileSingleProcessPrivilege) // 80 + SystemFileCacheInformationEx, // q: SYSTEM_FILECACHE_INFORMATION; s (requires SeIncreaseQuotaPrivilege) (same as SystemFileCacheInformation) + SystemThreadPriorityClientIdInformation, // s: SYSTEM_THREAD_CID_PRIORITY_INFORMATION (requires SeIncreaseBasePriorityPrivilege) + SystemProcessorIdleCycleTimeInformation, // q: SYSTEM_PROCESSOR_IDLE_CYCLE_TIME_INFORMATION[] (EX in: USHORT ProcessorGroup) + SystemVerifierCancellationInformation, // SYSTEM_VERIFIER_CANCELLATION_INFORMATION // name:wow64:whNT32QuerySystemVerifierCancellationInformation + SystemProcessorPowerInformationEx, // not implemented + SystemRefTraceInformation, // q; s: SYSTEM_REF_TRACE_INFORMATION // ObQueryRefTraceInformation + SystemSpecialPoolInformation, // q; s: SYSTEM_SPECIAL_POOL_INFORMATION (requires SeDebugPrivilege) // MmSpecialPoolTag, then MmSpecialPoolCatchOverruns != 0 + SystemProcessIdInformation, // q: SYSTEM_PROCESS_ID_INFORMATION + SystemErrorPortInformation, // s (requires SeTcbPrivilege) + SystemBootEnvironmentInformation, // q: SYSTEM_BOOT_ENVIRONMENT_INFORMATION // 90 + SystemHypervisorInformation, // q: SYSTEM_HYPERVISOR_QUERY_INFORMATION + SystemVerifierInformationEx, // q; s: SYSTEM_VERIFIER_INFORMATION_EX + SystemTimeZoneInformation, // q; s: RTL_TIME_ZONE_INFORMATION (requires SeTimeZonePrivilege) + SystemImageFileExecutionOptionsInformation, // s: SYSTEM_IMAGE_FILE_EXECUTION_OPTIONS_INFORMATION (requires SeTcbPrivilege) + SystemCoverageInformation, // q: COVERAGE_MODULES s: COVERAGE_MODULE_REQUEST // ExpCovQueryInformation (requires SeDebugPrivilege) + SystemPrefetchPatchInformation, // SYSTEM_PREFETCH_PATCH_INFORMATION + SystemVerifierFaultsInformation, // s: SYSTEM_VERIFIER_FAULTS_INFORMATION (requires SeDebugPrivilege) + SystemSystemPartitionInformation, // q: SYSTEM_SYSTEM_PARTITION_INFORMATION + SystemSystemDiskInformation, // q: SYSTEM_SYSTEM_DISK_INFORMATION + SystemProcessorPerformanceDistribution, // q: SYSTEM_PROCESSOR_PERFORMANCE_DISTRIBUTION (EX in: USHORT ProcessorGroup) // 100 + SystemNumaProximityNodeInformation, // q; s: SYSTEM_NUMA_PROXIMITY_MAP + SystemDynamicTimeZoneInformation, // q; s: RTL_DYNAMIC_TIME_ZONE_INFORMATION (requires SeTimeZonePrivilege) + SystemCodeIntegrityInformation, // q: SYSTEM_CODEINTEGRITY_INFORMATION // SeCodeIntegrityQueryInformation + SystemProcessorMicrocodeUpdateInformation, // s: SYSTEM_PROCESSOR_MICROCODE_UPDATE_INFORMATION + SystemProcessorBrandString, // q: CHAR[] // HaliQuerySystemInformation -> HalpGetProcessorBrandString, info class 23 + SystemVirtualAddressInformation, // q: SYSTEM_VA_LIST_INFORMATION[]; s: SYSTEM_VA_LIST_INFORMATION[] (requires SeIncreaseQuotaPrivilege) // MmQuerySystemVaInformation + SystemLogicalProcessorAndGroupInformation, // q: SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX (EX in: LOGICAL_PROCESSOR_RELATIONSHIP RelationshipType) // since WIN7 // KeQueryLogicalProcessorRelationship + SystemProcessorCycleTimeInformation, // q: SYSTEM_PROCESSOR_CYCLE_TIME_INFORMATION[] (EX in: USHORT ProcessorGroup) + SystemStoreInformation, // q; s: SYSTEM_STORE_INFORMATION (requires SeProfileSingleProcessPrivilege) // SmQueryStoreInformation + SystemRegistryAppendString, // s: SYSTEM_REGISTRY_APPEND_STRING_PARAMETERS // 110 + SystemAitSamplingValue, // s: ULONG (requires SeProfileSingleProcessPrivilege) + SystemVhdBootInformation, // q: SYSTEM_VHD_BOOT_INFORMATION + SystemCpuQuotaInformation, // q; s: PS_CPU_QUOTA_QUERY_INFORMATION + SystemNativeBasicInformation, // q: SYSTEM_BASIC_INFORMATION + SystemErrorPortTimeouts, // SYSTEM_ERROR_PORT_TIMEOUTS + SystemLowPriorityIoInformation, // q: SYSTEM_LOW_PRIORITY_IO_INFORMATION + SystemTpmBootEntropyInformation, // q: TPM_BOOT_ENTROPY_NT_RESULT // ExQueryTpmBootEntropyInformation + SystemVerifierCountersInformation, // q: SYSTEM_VERIFIER_COUNTERS_INFORMATION + SystemPagedPoolInformationEx, // q: SYSTEM_FILECACHE_INFORMATION; s (requires SeIncreaseQuotaPrivilege) (info for WorkingSetTypePagedPool) + SystemSystemPtesInformationEx, // q: SYSTEM_FILECACHE_INFORMATION; s (requires SeIncreaseQuotaPrivilege) (info for WorkingSetTypeSystemPtes) // 120 + SystemNodeDistanceInformation, // q: USHORT[4*NumaNodes] // (EX in: USHORT NodeNumber) + SystemAcpiAuditInformation, // q: SYSTEM_ACPI_AUDIT_INFORMATION // HaliQuerySystemInformation -> HalpAuditQueryResults, info class 26 + SystemBasicPerformanceInformation, // q: SYSTEM_BASIC_PERFORMANCE_INFORMATION // name:wow64:whNtQuerySystemInformation_SystemBasicPerformanceInformation + SystemQueryPerformanceCounterInformation, // q: SYSTEM_QUERY_PERFORMANCE_COUNTER_INFORMATION // since WIN7 SP1 + SystemSessionBigPoolInformation, // q: SYSTEM_SESSION_POOLTAG_INFORMATION // since WIN8 + SystemBootGraphicsInformation, // q; s: SYSTEM_BOOT_GRAPHICS_INFORMATION (kernel-mode only) + SystemScrubPhysicalMemoryInformation, // q; s: MEMORY_SCRUB_INFORMATION + SystemBadPageInformation, + SystemProcessorProfileControlArea, // q; s: SYSTEM_PROCESSOR_PROFILE_CONTROL_AREA + SystemCombinePhysicalMemoryInformation, // s: MEMORY_COMBINE_INFORMATION, MEMORY_COMBINE_INFORMATION_EX, MEMORY_COMBINE_INFORMATION_EX2 // 130 + SystemEntropyInterruptTimingInformation, // q; s: SYSTEM_ENTROPY_TIMING_INFORMATION + SystemConsoleInformation, // q: SYSTEM_CONSOLE_INFORMATION + SystemPlatformBinaryInformation, // q: SYSTEM_PLATFORM_BINARY_INFORMATION (requires SeTcbPrivilege) + SystemPolicyInformation, // q: SYSTEM_POLICY_INFORMATION + SystemHypervisorProcessorCountInformation, // q: SYSTEM_HYPERVISOR_PROCESSOR_COUNT_INFORMATION + SystemDeviceDataInformation, // q: SYSTEM_DEVICE_DATA_INFORMATION + SystemDeviceDataEnumerationInformation, // q: SYSTEM_DEVICE_DATA_INFORMATION + SystemMemoryTopologyInformation, // q: SYSTEM_MEMORY_TOPOLOGY_INFORMATION + SystemMemoryChannelInformation, // q: SYSTEM_MEMORY_CHANNEL_INFORMATION + SystemBootLogoInformation, // q: SYSTEM_BOOT_LOGO_INFORMATION // 140 + SystemProcessorPerformanceInformationEx, // q: SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION_EX // (EX in: USHORT ProcessorGroup) // since WINBLUE + SystemCriticalProcessErrorLogInformation, + SystemSecureBootPolicyInformation, // q: SYSTEM_SECUREBOOT_POLICY_INFORMATION + SystemPageFileInformationEx, // q: SYSTEM_PAGEFILE_INFORMATION_EX + SystemSecureBootInformation, // q: SYSTEM_SECUREBOOT_INFORMATION + SystemEntropyInterruptTimingRawInformation, + SystemPortableWorkspaceEfiLauncherInformation, // q: SYSTEM_PORTABLE_WORKSPACE_EFI_LAUNCHER_INFORMATION + SystemFullProcessInformation, // q: SYSTEM_PROCESS_INFORMATION with SYSTEM_PROCESS_INFORMATION_EXTENSION (requires admin) + SystemKernelDebuggerInformationEx, // q: SYSTEM_KERNEL_DEBUGGER_INFORMATION_EX + SystemBootMetadataInformation, // 150 + SystemSoftRebootInformation, // q: ULONG + SystemElamCertificateInformation, // s: SYSTEM_ELAM_CERTIFICATE_INFORMATION + SystemOfflineDumpConfigInformation, // q: OFFLINE_CRASHDUMP_CONFIGURATION_TABLE_V2 + SystemProcessorFeaturesInformation, // q: SYSTEM_PROCESSOR_FEATURES_INFORMATION + SystemRegistryReconciliationInformation, // s: NULL (requires admin) (flushes registry hives) + SystemEdidInformation, // q: SYSTEM_EDID_INFORMATION + SystemManufacturingInformation, // q: SYSTEM_MANUFACTURING_INFORMATION // since THRESHOLD + SystemEnergyEstimationConfigInformation, // q: SYSTEM_ENERGY_ESTIMATION_CONFIG_INFORMATION + SystemHypervisorDetailInformation, // q: SYSTEM_HYPERVISOR_DETAIL_INFORMATION + SystemProcessorCycleStatsInformation, // q: SYSTEM_PROCESSOR_CYCLE_STATS_INFORMATION (EX in: USHORT ProcessorGroup) // 160 + SystemVmGenerationCountInformation, + SystemTrustedPlatformModuleInformation, // q: SYSTEM_TPM_INFORMATION + SystemKernelDebuggerFlags, // SYSTEM_KERNEL_DEBUGGER_FLAGS + SystemCodeIntegrityPolicyInformation, // q: SYSTEM_CODEINTEGRITYPOLICY_INFORMATION + SystemIsolatedUserModeInformation, // q: SYSTEM_ISOLATED_USER_MODE_INFORMATION + SystemHardwareSecurityTestInterfaceResultsInformation, + SystemSingleModuleInformation, // q: SYSTEM_SINGLE_MODULE_INFORMATION + SystemAllowedCpuSetsInformation, + SystemVsmProtectionInformation, // q: SYSTEM_VSM_PROTECTION_INFORMATION (previously SystemDmaProtectionInformation) + SystemInterruptCpuSetsInformation, // q: SYSTEM_INTERRUPT_CPU_SET_INFORMATION // 170 + SystemSecureBootPolicyFullInformation, // q: SYSTEM_SECUREBOOT_POLICY_FULL_INFORMATION + SystemCodeIntegrityPolicyFullInformation, + SystemAffinitizedInterruptProcessorInformation, // (requires SeIncreaseBasePriorityPrivilege) + SystemRootSiloInformation, // q: SYSTEM_ROOT_SILO_INFORMATION + SystemCpuSetInformation, // q: SYSTEM_CPU_SET_INFORMATION // since THRESHOLD2 + SystemCpuSetTagInformation, // q: SYSTEM_CPU_SET_TAG_INFORMATION + SystemWin32WerStartCallout, + SystemSecureKernelProfileInformation, // q: SYSTEM_SECURE_KERNEL_HYPERGUARD_PROFILE_INFORMATION + SystemCodeIntegrityPlatformManifestInformation, // q: SYSTEM_SECUREBOOT_PLATFORM_MANIFEST_INFORMATION // since REDSTONE + SystemInterruptSteeringInformation, // SYSTEM_INTERRUPT_STEERING_INFORMATION_INPUT // 180 + SystemSupportedProcessorArchitectures, // p: in opt: HANDLE, out: SYSTEM_SUPPORTED_PROCESSOR_ARCHITECTURES_INFORMATION[] // NtQuerySystemInformationEx + SystemMemoryUsageInformation, // q: SYSTEM_MEMORY_USAGE_INFORMATION + SystemCodeIntegrityCertificateInformation, // q: SYSTEM_CODEINTEGRITY_CERTIFICATE_INFORMATION + SystemPhysicalMemoryInformation, // q: SYSTEM_PHYSICAL_MEMORY_INFORMATION // since REDSTONE2 + SystemControlFlowTransition, + SystemKernelDebuggingAllowed, // s: ULONG + SystemActivityModerationExeState, // SYSTEM_ACTIVITY_MODERATION_EXE_STATE + SystemActivityModerationUserSettings, // SYSTEM_ACTIVITY_MODERATION_USER_SETTINGS + SystemCodeIntegrityPoliciesFullInformation, + SystemCodeIntegrityUnlockInformation, // SYSTEM_CODEINTEGRITY_UNLOCK_INFORMATION // 190 + SystemIntegrityQuotaInformation, + SystemFlushInformation, // q: SYSTEM_FLUSH_INFORMATION + SystemProcessorIdleMaskInformation, // q: ULONG_PTR[ActiveGroupCount] // since REDSTONE3 + SystemSecureDumpEncryptionInformation, + SystemWriteConstraintInformation, // SYSTEM_WRITE_CONSTRAINT_INFORMATION + SystemKernelVaShadowInformation, // SYSTEM_KERNEL_VA_SHADOW_INFORMATION + SystemHypervisorSharedPageInformation, // SYSTEM_HYPERVISOR_SHARED_PAGE_INFORMATION // since REDSTONE4 + SystemFirmwareBootPerformanceInformation, + SystemCodeIntegrityVerificationInformation, // SYSTEM_CODEINTEGRITYVERIFICATION_INFORMATION + SystemFirmwarePartitionInformation, // SYSTEM_FIRMWARE_PARTITION_INFORMATION // 200 + SystemSpeculationControlInformation, // SYSTEM_SPECULATION_CONTROL_INFORMATION // (CVE-2017-5715) REDSTONE3 and above. + SystemDmaGuardPolicyInformation, // SYSTEM_DMA_GUARD_POLICY_INFORMATION + SystemEnclaveLaunchControlInformation, // SYSTEM_ENCLAVE_LAUNCH_CONTROL_INFORMATION + SystemWorkloadAllowedCpuSetsInformation, // SYSTEM_WORKLOAD_ALLOWED_CPU_SET_INFORMATION // since REDSTONE5 + SystemCodeIntegrityUnlockModeInformation, + SystemLeapSecondInformation, // SYSTEM_LEAP_SECOND_INFORMATION + SystemFlags2Information, // q: SYSTEM_FLAGS_INFORMATION + SystemSecurityModelInformation, // SYSTEM_SECURITY_MODEL_INFORMATION // since 19H1 + SystemCodeIntegritySyntheticCacheInformation, + SystemFeatureConfigurationInformation, // SYSTEM_FEATURE_CONFIGURATION_INFORMATION // since 20H1 // 210 + SystemFeatureConfigurationSectionInformation, // SYSTEM_FEATURE_CONFIGURATION_SECTIONS_INFORMATION + SystemFeatureUsageSubscriptionInformation, // SYSTEM_FEATURE_USAGE_SUBSCRIPTION_DETAILS + SystemSecureSpeculationControlInformation, // SECURE_SPECULATION_CONTROL_INFORMATION + SystemSpacesBootInformation, // since 20H2 + SystemFwRamdiskInformation, // SYSTEM_FIRMWARE_RAMDISK_INFORMATION + SystemWheaIpmiHardwareInformation, + SystemDifSetRuleClassInformation, + SystemDifClearRuleClassInformation, + SystemDifApplyPluginVerificationOnDriver, + SystemDifRemovePluginVerificationOnDriver, // 220 + SystemShadowStackInformation, // SYSTEM_SHADOW_STACK_INFORMATION + SystemBuildVersionInformation, // SYSTEM_BUILD_VERSION_INFORMATION + SystemPoolLimitInformation, // SYSTEM_POOL_LIMIT_INFORMATION + SystemCodeIntegrityAddDynamicStore, + SystemCodeIntegrityClearDynamicStores, + SystemDifPoolTrackingInformation, + SystemPoolZeroingInformation, // SYSTEM_POOL_ZEROING_INFORMATION + SystemDpcWatchdogInformation, + SystemDpcWatchdogInformation2, + SystemSupportedProcessorArchitectures2, // q: in opt: HANDLE, out: SYSTEM_SUPPORTED_PROCESSOR_ARCHITECTURES_INFORMATION[] // NtQuerySystemInformationEx // 230 + SystemSingleProcessorRelationshipInformation, // q: SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX // (EX in: PROCESSOR_NUMBER Processor) + SystemXfgCheckFailureInformation, + SystemIommuStateInformation, // SYSTEM_IOMMU_STATE_INFORMATION // since 22H1 + SystemHypervisorMinrootInformation, // SYSTEM_HYPERVISOR_MINROOT_INFORMATION + SystemHypervisorBootPagesInformation, // SYSTEM_HYPERVISOR_BOOT_PAGES_INFORMATION + SystemPointerAuthInformation, // SYSTEM_POINTER_AUTH_INFORMATION + SystemSecureKernelDebuggerInformation, + SystemOriginalImageFeatureInformation, + MaxSystemInfoClass +} SYSTEM_INFORMATION_CLASS; + +extern "C" NTSYSCALLAPI NTSTATUS NTAPI ZwQuerySystemInformation( + _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, + _Out_writes_bytes_opt_(SystemInformationLength) PVOID SystemInformation, + _In_ ULONG SystemInformationLength, + _Out_opt_ PULONG ReturnLength +); + +extern "C" NTSYSCALLAPI NTSTATUS NTAPI ZwTraceControl( + ULONG FunctionCode, + PVOID InBuffer, + ULONG InBufferLen, + PVOID OutBuffer, + ULONG OutBufferLen, + ULONG * ReturnSize +); + +template +NTSTATUS KphEnumerateSystemModules(T callback) { + NTSTATUS status; + PVOID buffer; + ULONG bufferSize; + ULONG attempts; + + bufferSize = 2048; + attempts = 8; + + do + { + buffer = ExAllocatePoolZero(NonPagedPoolNx, bufferSize, 'ThpK'); + + if (!buffer) + { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + status = ZwQuerySystemInformation( + SystemModuleInformation, + buffer, + bufferSize, + &bufferSize + ); + + if (NT_SUCCESS(status)) + { + callback((PRTL_PROCESS_MODULES)buffer); + ExFreePoolWithTag(buffer, 'ThpK'); + return STATUS_SUCCESS; + } + + ExFreePoolWithTag(buffer, 'ThpK'); + + if (status != STATUS_INFO_LENGTH_MISMATCH) + { + break; + } + } while (--attempts); + return status; +} \ No newline at end of file diff --git a/C/FileDeleteRecordPluginDriver/utils.h b/C/FileDeleteRecordPluginDriver/utils.h new file mode 100644 index 0000000..efc4048 --- /dev/null +++ b/C/FileDeleteRecordPluginDriver/utils.h @@ -0,0 +1,290 @@ +#pragma once + +// Include order matters here sadly. The C++ headers below may include C headers that re-define kernel apis. We must define our things first. +#include +#include "Interface.h" +//#include "string.h" + +#define _ITERATOR_DEBUG_LEVEL 0 +//#include +//#include +//#include +//#include + +#define ObjectNameInformation (OBJECT_INFORMATION_CLASS)1 + + + +const unsigned long POOL_TAG = '0RTS'; +const wchar_t* backup_directory = L"\\??\\C:\\deleted"; + +//template +//int string_printf(String& str, T printer, Args&&... args) { +// char tmp[512] = { 0 }; +// +// int size = printer(tmp, sizeof(tmp), std::forward(args)...); +// if (size < 0) { +// return -1; +// } +// +// str += (char*)tmp; +// return size; +//} + +using hash_t = uint64_t; + +consteval uint64_t fnv1a_imp(uint64_t h, const char* s) +{ + return (*s == 0) ? h : + fnv1a_imp((h ^ static_cast(*s)) * 1099511628211ull, s + 1); +} + +consteval uint64_t fnv1a(const char* s) +{ + return fnv1a_imp(14695981039346656037ull, s); +} + +#ifdef KCPP +// Abuse template instantion rules to generate a unique name for a given type. Each template is a unique function in C++. +// Then, convert that string to a numeric hash. Stable for the lifetime of the application, may change between compilations. +template +consteval uint64_t get_type_id() { + return fnv1a(__FUNCSIG__); +} + +// given a typedef, match the arg list and convert each arg to a typeid. Store results in an array. +template +struct arg_types {}; + +template +struct arg_types { + static constexpr std::array value = { get_type_id()... }; +}; + +// msvc doesn't implement a constructor for std::span from iterators. This does that... +template +constexpr auto make_span(It begin, It end) { + return std::span>>(&(*begin), std::distance(begin, end)); +} + +template +class FinalAction { +public: + FinalAction(Func f) :FinalActionFunc(std::move(f)) {} + ~FinalAction() + { + FinalActionFunc(); + } +private: + Func FinalActionFunc; + + /*Uses RAII to call a final function on destruction + C++ 11 version of java's finally (kindof)*/ +}; + +template +FinalAction finally(F f) { + return FinalAction(f); +} + +template +T FnCast(uint64_t fnToCast, T pFnCastTo) { + PH_UNUSED(pFnCastTo); + return (T)fnToCast; +} + +template +T FnCast(void* fnToCast, T pFnCastTo) { + PH_UNUSED(pFnCastTo); + return (T)fnToCast; +} + +// analog of dtrace_copyin. Given a pointer to a usermode structure, safely read that structure in. +// Dtrace returns a pointer to that result. We can be slightly nicer and give a copy of the value exactly. +//template +//std::remove_pointer_t readUserArg(T2 pUserAddress, PluginApis pApis) { +// std::remove_pointer_t tmp = { 0 }; +// pApis.pTraceAccessMemory(&tmp, (uint64_t)pUserAddress, sizeof(tmp), 1, TRUE); +// return tmp; +//} + +bool createFile(PUNICODE_STRING filePath, PHANDLE hFileOut) { + *hFileOut = NULL; + + OBJECT_ATTRIBUTES attrs = { 0 }; + InitializeObjectAttributes(&attrs, filePath, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL); + + IO_STATUS_BLOCK IoStatus; + NTSTATUS Status = ZwCreateFile(hFileOut, + FILE_WRITE_DATA | SYNCHRONIZE, + &attrs, + &IoStatus, + NULL, + FILE_ATTRIBUTE_SYSTEM, + FILE_SHARE_READ, + FILE_OPEN_IF, + FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, + NULL, + 0); + return Status == STATUS_SUCCESS; +} + +bool openFile(PUNICODE_STRING filePath, PHANDLE hFileOut) { + *hFileOut = NULL; + + OBJECT_ATTRIBUTES attrs = { 0 }; + InitializeObjectAttributes(&attrs, filePath, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL); + + IO_STATUS_BLOCK IoStatus; + NTSTATUS Status = ZwOpenFile(hFileOut, + FILE_READ_DATA | SYNCHRONIZE, + &attrs, + &IoStatus, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE + ); + return Status == STATUS_SUCCESS; +} + +// backupFileName must begin with a slash, or be a filepath with slashes +bool backupFile(PWSTR backupDir, UNICODE_STRING backupFileName, HANDLE hFileSource) { + if (!backupFileName.Buffer || backupFileName.Length <= sizeof(wchar_t)) + return false; + + // scan backwards until first slash + PWCHAR fileName = backupFileName.Buffer + (backupFileName.Length - sizeof(wchar_t)); + size_t fileNameLen = 0; + while (*fileName != L'\\') { + fileName--; + fileNameLen++; + } + + UNICODE_STRING backupPath = { 0 }; + backupPath.Length = (USHORT)(wcslen(backupDir) * sizeof(wchar_t)); + backupPath.MaximumLength = (USHORT)(backupPath.Length + (fileNameLen * sizeof(wchar_t))); + backupPath.Buffer = (PWSTR)ExAllocatePoolWithTag(NonPagedPoolNx, backupPath.MaximumLength, POOL_TAG); + memcpy(backupPath.Buffer, backupDir, backupPath.Length); + + NTSTATUS status = RtlAppendUnicodeToString(&backupPath, fileName); + if (status != STATUS_SUCCESS) + return false; + + HANDLE hFileCopy = NULL; + auto close_handles = finally([&] { + if (hFileCopy != NULL) { + ZwClose(hFileCopy); + } + }); + + if (!createFile(&backupPath, &hFileCopy)) + return false; + + LARGE_INTEGER pos = { 0 }; + IO_STATUS_BLOCK iosb = { 0 }; + + LARGE_INTEGER pos_write = { 0 }; + IO_STATUS_BLOCK iosb_write = { 0 }; + + while (true) { + char pBuf[512] = { 0 }; + status = ZwReadFile( + hFileSource, + NULL, NULL, NULL, + &iosb, + pBuf, (ULONG)sizeof(pBuf), + &pos, + NULL + ); + + if (status == STATUS_END_OF_FILE) { + if (iosb.Information == 0) { + break; + } + } + + if (status != STATUS_SUCCESS) { + break; + } + + status = ZwWriteFile(hFileCopy, + NULL, NULL, NULL, + &iosb_write, + pBuf, (ULONG)sizeof(pBuf), + &pos_write, + NULL + ); + + if (iosb_write.Status != STATUS_SUCCESS) { + break; + } + + if (status != STATUS_SUCCESS) { + break; + } + + pos.QuadPart += iosb.Information; + pos_write.QuadPart += iosb_write.Information; + } + return true; +} + +VOID NTAPI FreeUnicodeString(PUNICODE_STRING UnicodeString, ULONG Tag) +{ + if (UnicodeString->Buffer) + { + ExFreePoolWithTag(UnicodeString->Buffer, Tag); + } +} + +NTSTATUS DuplicateUnicodeString(PCUNICODE_STRING SourceString, PUNICODE_STRING DestinationString, ULONG Tag) +{ + if (SourceString == NULL || DestinationString == NULL || + SourceString->Length > SourceString->MaximumLength || + (SourceString->Length == 0 && SourceString->MaximumLength > 0 && SourceString->Buffer == NULL)) + { + return STATUS_INVALID_PARAMETER; + } + + if (SourceString->Length == 0) + { + DestinationString->Length = 0; + DestinationString->MaximumLength = 0; + DestinationString->Buffer = NULL; + } + else { + SIZE_T DestMaxLength = SourceString->Length; + + DestinationString->Buffer = (PWSTR)ExAllocatePoolWithTag(NonPagedPoolNx, DestMaxLength, Tag); + if (DestinationString->Buffer == NULL) + return STATUS_NO_MEMORY; + + memcpy(DestinationString->Buffer, SourceString->Buffer, SourceString->Length); + DestinationString->Length = SourceString->Length; + DestinationString->MaximumLength = DestMaxLength; + } + return STATUS_SUCCESS; +} + +OBJECT_NAME_INFORMATION* getFilePathFromHandle(HANDLE hFile) { + ULONG dwSize = 0; + OBJECT_NAME_INFORMATION* pObjectName = nullptr; + NTSTATUS status = ZwQueryObject(hFile, ObjectNameInformation, pObjectName, 0, &dwSize); + if (dwSize) + { + pObjectName = (OBJECT_NAME_INFORMATION*)ExAllocatePoolWithTag(NonPagedPoolNx, dwSize, POOL_TAG); + if (pObjectName) { + status = ZwQueryObject(hFile, ObjectNameInformation, pObjectName, dwSize, &dwSize); + } + } + + if (status == STATUS_SUCCESS && pObjectName) { + return pObjectName; + } + + if (pObjectName) { + ExFreePoolWithTag(pObjectName, POOL_TAG); + pObjectName = nullptr; + } + return nullptr; +} +#endif \ No newline at end of file diff --git a/C/STrace.sln b/C/STrace.sln index 24db2e9..4372585 100644 --- a/C/STrace.sln +++ b/C/STrace.sln @@ -19,66 +19,114 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EtwCallbackPlugin", "EtwCal EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AddNewEtwEventPlugin", "AddNewEtwEventPlugin\AddNewEtwEventPlugin.vcxproj", "{BB90E9EE-4505-4EB2-91DB-5C63BD9A79FE}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FileDeleteRecordPluginDriver", "FileDeleteRecordPluginDriver\FileDeleteRecordPluginDriver.vcxproj", "{CD47158C-73E3-4197-AE90-92DC38D8BC0E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 + Release|ARM64 = Release|ARM64 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EFE6E1B0-82E5-4AF2-8423-FA89F34AF1F2}.Debug|ARM64.ActiveCfg = Debug|x64 + {EFE6E1B0-82E5-4AF2-8423-FA89F34AF1F2}.Debug|ARM64.Build.0 = Debug|x64 + {EFE6E1B0-82E5-4AF2-8423-FA89F34AF1F2}.Debug|ARM64.Deploy.0 = Debug|x64 {EFE6E1B0-82E5-4AF2-8423-FA89F34AF1F2}.Debug|x64.ActiveCfg = Debug|x64 {EFE6E1B0-82E5-4AF2-8423-FA89F34AF1F2}.Debug|x64.Build.0 = Debug|x64 {EFE6E1B0-82E5-4AF2-8423-FA89F34AF1F2}.Debug|x64.Deploy.0 = Debug|x64 {EFE6E1B0-82E5-4AF2-8423-FA89F34AF1F2}.Debug|x86.ActiveCfg = Debug|x64 {EFE6E1B0-82E5-4AF2-8423-FA89F34AF1F2}.Debug|x86.Build.0 = Debug|x64 {EFE6E1B0-82E5-4AF2-8423-FA89F34AF1F2}.Debug|x86.Deploy.0 = Debug|x64 + {EFE6E1B0-82E5-4AF2-8423-FA89F34AF1F2}.Release|ARM64.ActiveCfg = Release|x64 + {EFE6E1B0-82E5-4AF2-8423-FA89F34AF1F2}.Release|ARM64.Build.0 = Release|x64 + {EFE6E1B0-82E5-4AF2-8423-FA89F34AF1F2}.Release|ARM64.Deploy.0 = Release|x64 {EFE6E1B0-82E5-4AF2-8423-FA89F34AF1F2}.Release|x64.ActiveCfg = Release|x64 {EFE6E1B0-82E5-4AF2-8423-FA89F34AF1F2}.Release|x64.Build.0 = Release|x64 {EFE6E1B0-82E5-4AF2-8423-FA89F34AF1F2}.Release|x64.Deploy.0 = Release|x64 {EFE6E1B0-82E5-4AF2-8423-FA89F34AF1F2}.Release|x86.ActiveCfg = Release|x64 {EFE6E1B0-82E5-4AF2-8423-FA89F34AF1F2}.Release|x86.Build.0 = Release|x64 {EFE6E1B0-82E5-4AF2-8423-FA89F34AF1F2}.Release|x86.Deploy.0 = Release|x64 + {4349310C-30F9-48A9-9AE7-13D181F958B5}.Debug|ARM64.ActiveCfg = Debug|x64 + {4349310C-30F9-48A9-9AE7-13D181F958B5}.Debug|ARM64.Build.0 = Debug|x64 {4349310C-30F9-48A9-9AE7-13D181F958B5}.Debug|x64.ActiveCfg = Debug|x64 {4349310C-30F9-48A9-9AE7-13D181F958B5}.Debug|x64.Build.0 = Debug|x64 {4349310C-30F9-48A9-9AE7-13D181F958B5}.Debug|x86.ActiveCfg = Debug|x64 {4349310C-30F9-48A9-9AE7-13D181F958B5}.Debug|x86.Build.0 = Debug|x64 + {4349310C-30F9-48A9-9AE7-13D181F958B5}.Release|ARM64.ActiveCfg = Release|x64 + {4349310C-30F9-48A9-9AE7-13D181F958B5}.Release|ARM64.Build.0 = Release|x64 {4349310C-30F9-48A9-9AE7-13D181F958B5}.Release|x64.ActiveCfg = Release|x64 {4349310C-30F9-48A9-9AE7-13D181F958B5}.Release|x64.Build.0 = Release|x64 {4349310C-30F9-48A9-9AE7-13D181F958B5}.Release|x86.ActiveCfg = Release|x64 {4349310C-30F9-48A9-9AE7-13D181F958B5}.Release|x86.Build.0 = Release|x64 + {C09F1082-CDCA-4320-AB91-CC3EAB12560C}.Debug|ARM64.ActiveCfg = Debug|x64 + {C09F1082-CDCA-4320-AB91-CC3EAB12560C}.Debug|ARM64.Build.0 = Debug|x64 {C09F1082-CDCA-4320-AB91-CC3EAB12560C}.Debug|x64.ActiveCfg = Debug|x64 {C09F1082-CDCA-4320-AB91-CC3EAB12560C}.Debug|x64.Build.0 = Debug|x64 {C09F1082-CDCA-4320-AB91-CC3EAB12560C}.Debug|x86.ActiveCfg = Debug|Win32 {C09F1082-CDCA-4320-AB91-CC3EAB12560C}.Debug|x86.Build.0 = Debug|Win32 + {C09F1082-CDCA-4320-AB91-CC3EAB12560C}.Release|ARM64.ActiveCfg = Release|x64 + {C09F1082-CDCA-4320-AB91-CC3EAB12560C}.Release|ARM64.Build.0 = Release|x64 {C09F1082-CDCA-4320-AB91-CC3EAB12560C}.Release|x64.ActiveCfg = Release|x64 {C09F1082-CDCA-4320-AB91-CC3EAB12560C}.Release|x64.Build.0 = Release|x64 {C09F1082-CDCA-4320-AB91-CC3EAB12560C}.Release|x86.ActiveCfg = Release|Win32 {C09F1082-CDCA-4320-AB91-CC3EAB12560C}.Release|x86.Build.0 = Release|Win32 + {50C1055F-A9D3-43A3-9FDB-35FD9EBB5645}.Debug|ARM64.ActiveCfg = Debug|x64 + {50C1055F-A9D3-43A3-9FDB-35FD9EBB5645}.Debug|ARM64.Build.0 = Debug|x64 {50C1055F-A9D3-43A3-9FDB-35FD9EBB5645}.Debug|x64.ActiveCfg = Debug|x64 {50C1055F-A9D3-43A3-9FDB-35FD9EBB5645}.Debug|x64.Build.0 = Debug|x64 {50C1055F-A9D3-43A3-9FDB-35FD9EBB5645}.Debug|x86.ActiveCfg = Debug|Win32 {50C1055F-A9D3-43A3-9FDB-35FD9EBB5645}.Debug|x86.Build.0 = Debug|Win32 + {50C1055F-A9D3-43A3-9FDB-35FD9EBB5645}.Release|ARM64.ActiveCfg = Release|x64 + {50C1055F-A9D3-43A3-9FDB-35FD9EBB5645}.Release|ARM64.Build.0 = Release|x64 {50C1055F-A9D3-43A3-9FDB-35FD9EBB5645}.Release|x64.ActiveCfg = Release|x64 {50C1055F-A9D3-43A3-9FDB-35FD9EBB5645}.Release|x64.Build.0 = Release|x64 {50C1055F-A9D3-43A3-9FDB-35FD9EBB5645}.Release|x86.ActiveCfg = Release|Win32 {50C1055F-A9D3-43A3-9FDB-35FD9EBB5645}.Release|x86.Build.0 = Release|Win32 + {3AC65894-4A8A-487A-BC59-5917B91E2169}.Debug|ARM64.ActiveCfg = Debug|x64 + {3AC65894-4A8A-487A-BC59-5917B91E2169}.Debug|ARM64.Build.0 = Debug|x64 {3AC65894-4A8A-487A-BC59-5917B91E2169}.Debug|x64.ActiveCfg = Debug|x64 {3AC65894-4A8A-487A-BC59-5917B91E2169}.Debug|x64.Build.0 = Debug|x64 {3AC65894-4A8A-487A-BC59-5917B91E2169}.Debug|x86.ActiveCfg = Debug|Win32 {3AC65894-4A8A-487A-BC59-5917B91E2169}.Debug|x86.Build.0 = Debug|Win32 + {3AC65894-4A8A-487A-BC59-5917B91E2169}.Release|ARM64.ActiveCfg = Release|x64 + {3AC65894-4A8A-487A-BC59-5917B91E2169}.Release|ARM64.Build.0 = Release|x64 {3AC65894-4A8A-487A-BC59-5917B91E2169}.Release|x64.ActiveCfg = Release|x64 {3AC65894-4A8A-487A-BC59-5917B91E2169}.Release|x64.Build.0 = Release|x64 {3AC65894-4A8A-487A-BC59-5917B91E2169}.Release|x86.ActiveCfg = Release|Win32 {3AC65894-4A8A-487A-BC59-5917B91E2169}.Release|x86.Build.0 = Release|Win32 + {BB90E9EE-4505-4EB2-91DB-5C63BD9A79FE}.Debug|ARM64.ActiveCfg = Debug|x64 + {BB90E9EE-4505-4EB2-91DB-5C63BD9A79FE}.Debug|ARM64.Build.0 = Debug|x64 {BB90E9EE-4505-4EB2-91DB-5C63BD9A79FE}.Debug|x64.ActiveCfg = Debug|x64 {BB90E9EE-4505-4EB2-91DB-5C63BD9A79FE}.Debug|x64.Build.0 = Debug|x64 {BB90E9EE-4505-4EB2-91DB-5C63BD9A79FE}.Debug|x86.ActiveCfg = Debug|Win32 {BB90E9EE-4505-4EB2-91DB-5C63BD9A79FE}.Debug|x86.Build.0 = Debug|Win32 + {BB90E9EE-4505-4EB2-91DB-5C63BD9A79FE}.Release|ARM64.ActiveCfg = Release|x64 + {BB90E9EE-4505-4EB2-91DB-5C63BD9A79FE}.Release|ARM64.Build.0 = Release|x64 {BB90E9EE-4505-4EB2-91DB-5C63BD9A79FE}.Release|x64.ActiveCfg = Release|x64 {BB90E9EE-4505-4EB2-91DB-5C63BD9A79FE}.Release|x64.Build.0 = Release|x64 {BB90E9EE-4505-4EB2-91DB-5C63BD9A79FE}.Release|x86.ActiveCfg = Release|Win32 {BB90E9EE-4505-4EB2-91DB-5C63BD9A79FE}.Release|x86.Build.0 = Release|Win32 + {CD47158C-73E3-4197-AE90-92DC38D8BC0E}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {CD47158C-73E3-4197-AE90-92DC38D8BC0E}.Debug|ARM64.Build.0 = Debug|ARM64 + {CD47158C-73E3-4197-AE90-92DC38D8BC0E}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {CD47158C-73E3-4197-AE90-92DC38D8BC0E}.Debug|x64.ActiveCfg = Debug|x64 + {CD47158C-73E3-4197-AE90-92DC38D8BC0E}.Debug|x64.Build.0 = Debug|x64 + {CD47158C-73E3-4197-AE90-92DC38D8BC0E}.Debug|x64.Deploy.0 = Debug|x64 + {CD47158C-73E3-4197-AE90-92DC38D8BC0E}.Debug|x86.ActiveCfg = Debug|x64 + {CD47158C-73E3-4197-AE90-92DC38D8BC0E}.Debug|x86.Build.0 = Debug|x64 + {CD47158C-73E3-4197-AE90-92DC38D8BC0E}.Debug|x86.Deploy.0 = Debug|x64 + {CD47158C-73E3-4197-AE90-92DC38D8BC0E}.Release|ARM64.ActiveCfg = Release|ARM64 + {CD47158C-73E3-4197-AE90-92DC38D8BC0E}.Release|ARM64.Build.0 = Release|ARM64 + {CD47158C-73E3-4197-AE90-92DC38D8BC0E}.Release|ARM64.Deploy.0 = Release|ARM64 + {CD47158C-73E3-4197-AE90-92DC38D8BC0E}.Release|x64.ActiveCfg = Release|x64 + {CD47158C-73E3-4197-AE90-92DC38D8BC0E}.Release|x64.Build.0 = Release|x64 + {CD47158C-73E3-4197-AE90-92DC38D8BC0E}.Release|x64.Deploy.0 = Release|x64 + {CD47158C-73E3-4197-AE90-92DC38D8BC0E}.Release|x86.ActiveCfg = Release|x64 + {CD47158C-73E3-4197-AE90-92DC38D8BC0E}.Release|x86.Build.0 = Release|x64 + {CD47158C-73E3-4197-AE90-92DC38D8BC0E}.Release|x86.Deploy.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/C/STrace/PluginData.h b/C/STrace/PluginData.h index 1d744e0..a26067e 100644 --- a/C/STrace/PluginData.h +++ b/C/STrace/PluginData.h @@ -9,11 +9,12 @@ #define _countof(array) (sizeof(array) / sizeof(array[0])) +// sc create StracePlugin type = kernel start = demand binPath = System32\drivers\StracePlugin.sys UNICODE_STRING pluginServiceName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\StracePlugin"); const char* pluginFullPathNames[] = { - "\\systemroot\\system32\\drivers\\plugin.sys", - "\\??\\C:\\Windows\\system32\\drivers\\plugin.sys" + "\\systemroot\\system32\\drivers\\StracePlugin.sys", + "\\??\\C:\\Windows\\system32\\drivers\\StracePlugin.sys" }; struct ExportDirectoryPtrs { @@ -30,8 +31,13 @@ class PluginData { zero(); } - bool isLoaded() { - return InterlockedAdd(&loaded, 0) != 0; + bool isLoaded() + { + bool _loaded; + lock(); + _loaded = loaded; + unlock(); + return _loaded; } NTSTATUS load() { @@ -39,7 +45,9 @@ class PluginData { NTSTATUS status; lock(); - if (!isLoaded()) { + + // don't use isLoaded() here because of locking + if (loaded) { return STATUS_ALREADY_INITIALIZED; } @@ -48,10 +56,10 @@ class PluginData { LOG_ERROR("ZwLoadDriver Failed with: 0x%08X", status); goto exit; } + InterlockedIncrement(&loaded); status = setPluginBaseAddress(); - if (!NT_SUCCESS(status)) - { + if (!NT_SUCCESS(status)){ LOG_ERROR("setPluginBaseAddress failed to find plugin"); goto exit; } @@ -63,45 +71,45 @@ class PluginData { pIsTarget = (tStpIsTarget)getExport("StpIsTarget"); pDtEtwpEventCallback = (tDtEtwpEventCallback)getExport("DtEtwpEventCallback"); - if((pCallbackEntry && pCallbackReturn && pIsTarget && pDtEtwpEventCallback) == 0){ + if((pCallbackEntry && pCallbackReturn && pIsTarget) == 0){ LOG_ERROR("Failed to acquire plugin exports"); status = STATUS_PROCEDURE_NOT_FOUND; goto exit; } - InterlockedIncrement(&loaded); LOG_INFO("[+] Plugin Loaded at: %I64X\r\n", pImageBase); exit: + if (!NT_SUCCESS(status)){ + unload(TRUE); + } unlock(); return status; } // Must free old plugin data before setting new one - NTSTATUS unload() { + // can call from load for cleanup, if so pass true for locked + NTSTATUS unload(BOOLEAN locked = FALSE) { NTSTATUS status; // set pImageBase last since it's used atomically for isLoaded - lock(); - if (!isLoaded()) - { + if(!locked) lock(); + if (!loaded){ status = STATUS_ALREADY_COMPLETE; } - InterlockedDecrement(&loaded); status = ZwUnloadDriver(&pluginServiceName); - if (!NT_SUCCESS(status)) - { + if (!NT_SUCCESS(status)){ // If driver doesn't unload, then it is still loaded and we should // return the plugin to a loaded state and return an error LOG_INFO("[!] Failed to unload plugin driver"); - InterlockedIncrement(&loaded); goto exit; } + InterlockedDecrement(&loaded); zero(); status = STATUS_SUCCESS; exit: - unlock(); + if(locked)unlock(); return status; } @@ -109,6 +117,8 @@ class PluginData { tStpIsTarget pIsTarget; tStpCallbackEntryPlugin pCallbackEntry; tStpCallbackReturnPlugin pCallbackReturn; + + // Optional tDtEtwpEventCallback pDtEtwpEventCallback; // zeroed immediately after use, these are optional @@ -187,10 +197,9 @@ class PluginData { pmi = modules->Modules; for (ULONG i = 0; i < modules->NumberOfModules; i++) { - DBGPRINT("Module: %s", pmi[i].FullPathName); + //DBGPRINT("Module: %s", pmi[i].FullPathName); for (int j = 0; j < _countof(pluginFullPathNames); j++) { - // RtlStringCbLength FullPathName - 1 because the ANSI_STRING maxLenght will need an extra byte if(_stricmp(pmi[i].FullPathName, pluginFullPathNames[j]) == 0) { DBGPRINT("Found Module: %s 0x%I64X", pmi[i].FullPathName, pmi[i].ImageBase); From bdc3de4d431943f79e4af5bf8fa3e19c1e71116b Mon Sep 17 00:00:00 2001 From: Paul Tarter Date: Fri, 17 Nov 2023 12:22:37 -0500 Subject: [PATCH 8/9] deadlock fixes for loaderlock --- C/STrace/PluginData.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/C/STrace/PluginData.h b/C/STrace/PluginData.h index a26067e..850ba4f 100644 --- a/C/STrace/PluginData.h +++ b/C/STrace/PluginData.h @@ -48,19 +48,20 @@ class PluginData { // don't use isLoaded() here because of locking if (loaded) { - return STATUS_ALREADY_INITIALIZED; + status = STATUS_ALREADY_INITIALIZED; + goto exit; } status = ZwLoadDriver(&pluginServiceName); if (!NT_SUCCESS(status)) { - LOG_ERROR("ZwLoadDriver Failed with: 0x%08X", status); + LOG_ERROR("ZwLoadDriver Failed with: 0x%08X\r\n", status); goto exit; } InterlockedIncrement(&loaded); status = setPluginBaseAddress(); if (!NT_SUCCESS(status)){ - LOG_ERROR("setPluginBaseAddress failed to find plugin"); + LOG_ERROR("setPluginBaseAddress failed to find plugin\n"); goto exit; } @@ -72,7 +73,7 @@ class PluginData { pDtEtwpEventCallback = (tDtEtwpEventCallback)getExport("DtEtwpEventCallback"); if((pCallbackEntry && pCallbackReturn && pIsTarget) == 0){ - LOG_ERROR("Failed to acquire plugin exports"); + LOG_ERROR("Failed to acquire plugin exports\r\n"); status = STATUS_PROCEDURE_NOT_FOUND; goto exit; } @@ -95,13 +96,14 @@ class PluginData { if(!locked) lock(); if (!loaded){ status = STATUS_ALREADY_COMPLETE; + goto exit; } status = ZwUnloadDriver(&pluginServiceName); if (!NT_SUCCESS(status)){ // If driver doesn't unload, then it is still loaded and we should // return the plugin to a loaded state and return an error - LOG_INFO("[!] Failed to unload plugin driver"); + LOG_INFO("[!] Failed to unload plugin driver\r\n"); goto exit; } InterlockedDecrement(&loaded); @@ -109,7 +111,7 @@ class PluginData { status = STATUS_SUCCESS; exit: - if(locked)unlock(); + if(!locked)unlock(); return status; } From 4a1993db0c9c6fd096b9584d8be30bf60ccbf0d0 Mon Sep 17 00:00:00 2001 From: Paul Tarter Date: Fri, 17 Nov 2023 12:33:47 -0500 Subject: [PATCH 9/9] incomplete utils.h, builds --- C/FileDeleteRecordPluginDriver/utils.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/C/FileDeleteRecordPluginDriver/utils.h b/C/FileDeleteRecordPluginDriver/utils.h index efc4048..ce2711c 100644 --- a/C/FileDeleteRecordPluginDriver/utils.h +++ b/C/FileDeleteRecordPluginDriver/utils.h @@ -13,8 +13,6 @@ #define ObjectNameInformation (OBJECT_INFORMATION_CLASS)1 - - const unsigned long POOL_TAG = '0RTS'; const wchar_t* backup_directory = L"\\??\\C:\\deleted"; @@ -44,7 +42,6 @@ consteval uint64_t fnv1a(const char* s) return fnv1a_imp(14695981039346656037ull, s); } -#ifdef KCPP // Abuse template instantion rules to generate a unique name for a given type. Each template is a unique function in C++. // Then, convert that string to a numeric hash. Stable for the lifetime of the application, may change between compilations. template @@ -52,6 +49,9 @@ consteval uint64_t get_type_id() { return fnv1a(__FUNCSIG__); } +#ifdef KCPP + + // given a typedef, match the arg list and convert each arg to a typeid. Store results in an array. template struct arg_types {};