diff --git a/MemProcFS/version.h b/MemProcFS/version.h index a159e3e0..3a12d8f5 100644 --- a/MemProcFS/version.h +++ b/MemProcFS/version.h @@ -1,12 +1,12 @@ #define STRINGIZE2(s) #s #define STRINGIZE(s) STRINGIZE2(s) -#define VERSION_MAJOR 2 -#define VERSION_MINOR 10 -#define VERSION_REVISION 2 -#define VERSION_BUILD 2 +#define VERSION_MAJOR 3 +#define VERSION_MINOR 0 +#define VERSION_REVISION 0 +#define VERSION_BUILD 3 -#define VER_FILE_DESCRIPTION_STR "The Memory Process File System" +#define VER_FILE_DESCRIPTION_STR "MemProcFS" #define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD #define VER_FILE_VERSION_STR STRINGIZE(VERSION_MAJOR) \ "." STRINGIZE(VERSION_MINOR) \ diff --git a/MemProcFS/vmmdll.h b/MemProcFS/vmmdll.h index 8a6b6a38..87f3f191 100644 --- a/MemProcFS/vmmdll.h +++ b/MemProcFS/vmmdll.h @@ -4,7 +4,7 @@ // (c) Ulf Frisk, 2018-2019 // Author: Ulf Frisk, pcileech@frizk.net // -// Header Version: 2.10.2 +// Header Version: 3.0 // #include @@ -23,11 +23,11 @@ extern "C" { /* * Initialize VMM.DLL with command line parameters. For a more detailed info -* about the parameters please see github wiki for Memory Process File System -* and LeechCore. THIS IS THE PREFERED WAY OF INITIALIZING VMM.DLL +* about the parameters please see github wiki for MemProcFS and LeechCore. +* NB! LeechCore initialization parameters are _also_ valid to this function. * Important parameters are: -* -printf = show printf style outputs) -* -v -vv -vvv = extra verbosity levels) +* -printf = show printf style outputs. +* -v -vv -vvv = extra verbosity levels. * -device = device as on format for LeechCore - please see leechcore.h or * Github documentation for additional information. Some values * are: , fpga, usb3380, hvsavedstate, totalmeltdown, pmem @@ -35,8 +35,19 @@ extern "C" { * documentation for additional information. * -norefresh = disable background refreshes (even if backing memory is * volatile memory). -* -symbolserverdisable = disable symbol server until user change. This -* parameter will take precedence over registry settings. +* -symbolserverdisable = disable symbol server until user change. +* This parameter will take precedence over registry settings. +* -pagefile[0-9] = page file(s) to use in addition to physical memory. +* Normally pagefile.sys have index 0 and swapfile.sys index 1. +* Page files are in constant flux - do not use if time diff +* between memory dump and page files are more than few minutes. +* Example: 'pagefile0 swapfile.sys' +* -waitinitialize = Wait for initialization to complete before returning. +* Normal use is that some initialization is done asynchronously +* and may not be completed when initialization call is completed. +* This includes virtual memory compression, registry and more. +* Example: '-waitinitialize' +* * -- argc * -- argv * -- return = success/fail @@ -78,9 +89,9 @@ VOID VMMDLL_MemFree(_Frees_ptr_opt_ PVOID pvMem); //----------------------------------------------------------------------------- /* -* Options used together with the functions: VMMDLL_GetOption & VMMDLL_SetOption +* Options used together with the functions: VMMDLL_ConfigGet & VMMDLL_ConfigSet * Options are defined with either: VMMDLL_OPT_* in this header file or as -* MEMDEVICE_OPT_* in memdevice.h +* LEECHCORE_OPT_* in leechcore.h * For more detailed information check the sources for individual device types. */ #define VMMDLL_OPT_CORE_PRINTF_ENABLE 0x80000001 // RW @@ -349,17 +360,17 @@ typedef struct tdVMMDLL_PLUGIN_REGINFO { #define VMMDLL_FLAG_ZEROPAD_ON_FAIL 0x0002 // zero pad failed physical memory reads and report success if read within range of physical memory. #define VMMDLL_FLAG_FORCECACHE_READ 0x0008 // force use of cache - fail non-cached pages - only valid for reads, invalid with VMM_FLAG_NOCACHE/VMM_FLAG_ZEROPAD_ON_FAIL. #define VMMDLL_FLAG_NOPAGING 0x0010 // do not try to retrieve memory from paged out memory from pagefile/compressed (even if possible) +#define VMMDLL_FLAG_NOPAGING_IO 0x0020 // do not try to retrieve memory from paged out memory if read would incur additional I/O (even if possible). /* * Read memory in various non-contigious locations specified by the pointers to -* the items in the ppDMAs array. Result for each unit of work will be given +* the items in the ppMEMs array. Result for each unit of work will be given * individually. No upper limit of number of items to read, but no performance * boost will be given if above hardware limit. Max size of each unit of work is * one 4k page (4096 bytes). * -- dwPID - PID of target process, (DWORD)-1 to read physical memory. * -- ppMEMs = array of scatter read headers. -* -- cpMEMs = count of ppDMAs. -* -- pcpDMAsRead = optional count of number of successfully read ppDMAs. +* -- cpMEMs = count of ppMEMs. * -- flags = optional flags as given by VMMDLL_FLAG_* * -- return = the number of successfully read items. */ @@ -384,7 +395,7 @@ BOOL VMMDLL_MemReadPage(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Inout_bytecount_(40 * -- return = success/fail (depending if all requested bytes are read or not). */ _Success_(return) -BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWORD cb); +BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb); /* * Read a contigious amount of memory and report the number of bytes read in pcbRead. @@ -398,7 +409,7 @@ BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWO * read - it's recommended to verify pcbReadOpt parameter. */ _Success_(return) -BOOL VMMDLL_MemReadEx(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWORD cb, _Out_opt_ PDWORD pcbReadOpt, _In_ ULONG64 flags); +BOOL VMMDLL_MemReadEx(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb, _Out_opt_ PDWORD pcbReadOpt, _In_ ULONG64 flags); /* * Prefetch a number of addresses (specified in the pA array) into the memory @@ -426,7 +437,7 @@ BOOL VMMDLL_MemPrefetchPages(_In_ DWORD dwPID, _In_reads_(cPrefetchAddresses) PU * -- return = TRUE on success, FALSE on partial or zero write. */ _Success_(return) -BOOL VMMDLL_MemWrite(_In_ DWORD dwPID, _In_ ULONG64 qwA, _In_ PBYTE pb, _In_ DWORD cb); +BOOL VMMDLL_MemWrite(_In_ DWORD dwPID, _In_ ULONG64 qwA, _In_reads_(cb) PBYTE pb, _In_ DWORD cb); /* * Translate a virtual address to a physical address by walking the page tables @@ -442,106 +453,286 @@ BOOL VMMDLL_MemVirt2Phys(_In_ DWORD dwPID, _In_ ULONG64 qwVA, _Out_ PULONG64 pqw //----------------------------------------------------------------------------- -// VMM PROCESS FUNCTIONALITY BELOW: -// Functionality below is mostly relating to Windows processes. +// VMM PROCESS MAP FUNCTIONALITY BELOW: +// Functionality for retrieving process related collections of items such as +// page table map (PTE), virtual address descriptor map (VAD), loaded modules, +// heaps and threads. //----------------------------------------------------------------------------- -/* -* Retrieve an active process given it's name. Please note that if multiple -* processes with the same name exists only one will be returned. If required to -* parse all processes with the same name please iterate over the PID list by -* calling VMMDLL_PidList together with VMMDLL_ProcessGetInformation. -* -- szProcName = process name case insensitive. -* -- pdwPID = pointer that will receive PID on success. -* -- return -*/ -_Success_(return) -BOOL VMMDLL_PidGetFromName(_In_ LPSTR szProcName, _Out_ PDWORD pdwPID); - -/* -* List the PIDs in the system. -* -- pPIDs = DWORD array of at least number of PIDs in system, or NULL. -* -- pcPIDs = size of (in number of DWORDs) pPIDs array on entry, number of PIDs in system on exit. -* -- return = success/fail. -*/ -_Success_(return) -BOOL VMMDLL_PidList(_Out_opt_ PDWORD pPIDs, _Inout_ PULONG64 pcPIDs); +#define VMMDLL_MAP_PTE_VERSION 1 +#define VMMDLL_MAP_VAD_VERSION 1 +#define VMMDLL_MAP_MODULE_VERSION 1 +#define VMMDLL_MAP_HEAP_VERSION 1 +#define VMMDLL_MAP_THREAD_VERSION 1 +#define VMMDLL_MAP_HANDLE_VERSION 1 -// flags to check for existence in the fPage field of PCILEECH_VMM_MEMMAP_ENTRY +// flags to check for existence in the fPage field of VMMDLL_MAP_PTEENTRY #define VMMDLL_MEMMAP_FLAG_PAGE_W 0x0000000000000002 #define VMMDLL_MEMMAP_FLAG_PAGE_NS 0x0000000000000004 #define VMMDLL_MEMMAP_FLAG_PAGE_NX 0x8000000000000000 #define VMMDLL_MEMMAP_FLAG_PAGE_MASK 0x8000000000000006 -typedef struct tdVMMDLL_MEMMAP_ENTRY { - ULONG64 AddrBase; - ULONG64 cPages; - ULONG64 fPage; +typedef struct tdVMMDLL_MAP_PTEENTRY { + QWORD vaBase; + QWORD cPages; + QWORD fPage; BOOL fWoW64; - CHAR szTag[32]; -} VMMDLL_MEMMAP_ENTRY, *PVMMDLL_MEMMAP_ENTRY; + DWORD cwszText; + LPWSTR wszText; + DWORD _Reserved1[2]; +} VMMDLL_MAP_PTEENTRY, *PVMMDLL_MAP_PTEENTRY; + +typedef struct tdVMMDLL_MAP_VADENTRY { + QWORD vaStart; + QWORD vaEnd; + QWORD vaVad; + // DWORD 0 + DWORD VadType : 3; // Pos 0 + DWORD Protection : 5; // Pos 3 + DWORD fImage : 1; // Pos 8 + DWORD fFile : 1; // Pos 9 + DWORD fPageFile : 1; // Pos 10 + DWORD fPrivateMemory : 1; // Pos 11 + DWORD fTeb : 1; // Pos 12 + DWORD fStack : 1; // Pos 13 + DWORD fSpare : 2; // Pos 14 + DWORD HeapNum : 7; // Pos 16 + DWORD fHeap : 1; // Pos 23 + DWORD cwszDescription : 8; // Pos 24 + // DWORD 1 + DWORD CommitCharge : 31; // Pos 0 + DWORD MemCommit : 1; // Pos 31 + DWORD u2; + DWORD cbPrototypePte; + QWORD vaPrototypePte; + QWORD vaSubsection; + LPWSTR wszText; // optional LPWSTR pointed into VMMDLL_MAP_VAD.wszMultiText + DWORD cwszText; // WCHAR count not including terminating null + DWORD _Reserved1; +} VMMDLL_MAP_VADENTRY, *PVMMDLL_MAP_VADENTRY; + +typedef struct tdVMMDLL_MAP_MODULEENTRY { + QWORD vaBase; + QWORD vaEntry; + DWORD cbImageSize; + BOOL fWoW64; + LPWSTR wszText; + DWORD cwszText; // wchar count not including terminating null + DWORD _Reserved1[7]; +} VMMDLL_MAP_MODULEENTRY, *PVMMDLL_MAP_MODULEENTRY; + +typedef struct tdVMMDLL_MAP_HEAPENTRY { + QWORD vaHeapSegment; + DWORD cPages; + DWORD cPagesUnCommitted : 24; + DWORD HeapId : 7; + DWORD fPrimary : 1; +} VMMDLL_MAP_HEAPENTRY, *PVMMDLL_MAP_HEAPENTRY; + +typedef struct tdVMMDLL_MAP_THREADENTRY { + DWORD dwTID; + DWORD dwPID; + DWORD dwExitStatus; + UCHAR bState; + UCHAR bRunning; + UCHAR bPriority; + UCHAR bBasePriority; + QWORD vaETHREAD; + QWORD vaTeb; + QWORD ftCreateTime; + QWORD ftExitTime; + QWORD vaStartAddress; + QWORD vaStackBaseUser; + QWORD vaStackLimitUser; + QWORD vaStackBaseKernel; + QWORD vaStackLimitKernel; + DWORD _FutureUse[10]; +} VMMDLL_MAP_THREADENTRY, *PVMMDLL_MAP_THREADENTRY; + +typedef struct tdVMMDLL_MAP_HANDLEENTRY { + QWORD vaObject; + DWORD dwHandle; + DWORD dwGrantedAccess : 24; + DWORD iType : 8; + QWORD qwHandleCount; + QWORD qwPointerCount; + QWORD vaObjectCreateInfo; + QWORD vaSecurityDescriptor; + LPWSTR wszText; + DWORD cwszText; + DWORD dwPID; + DWORD dwPoolTag; + DWORD _FutureUse[4]; + DWORD cwszType; + LPWSTR wszType; +} VMMDLL_MAP_HANDLEENTRY, *PVMMDLL_MAP_HANDLEENTRY; + +typedef struct tdVMMDLL_MAP_PTE { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // NULL or multi-wstr pointed into by VMMDLL_MAP_VADENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_PTEENTRY pMap[]; // map entries. +} VMMDLL_MAP_PTE, *PVMMDLL_MAP_PTE; + +typedef struct tdVMMDLL_MAP_VAD { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // NULL or multi-wstr pointed into by VMMDLL_MAP_VADENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_VADENTRY pMap[]; // map entries. +} VMMDLL_MAP_VAD, *PVMMDLL_MAP_VAD; + +typedef struct tdVMMDLL_MAP_MODULE { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // multi-wstr pointed into by VMMDLL_MAP_MODULEENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_MODULEENTRY pMap[]; // map entries. +} VMMDLL_MAP_MODULE, *PVMMDLL_MAP_MODULE; + +typedef struct tdVMMDLL_MAP_HEAP { + DWORD dwVersion; + DWORD _Reserved1[8]; + DWORD cMap; // # map entries. + VMMDLL_MAP_HEAPENTRY pMap[]; // map entries. +} VMMDLL_MAP_HEAP, *PVMMDLL_MAP_HEAP; + +typedef struct tdVMMDLL_MAP_THREAD { + DWORD dwVersion; + DWORD _Reserved[8]; + DWORD cMap; // # map entries. + VMMDLL_MAP_THREADENTRY pMap[]; // map entries. +} VMMDLL_MAP_THREAD, *PVMMDLL_MAP_THREAD; + +typedef struct tdVMMDLL_MAP_HANDLE { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // multi-wstr pointed into by VMMDLL_MAP_HANDLEENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_HANDLEENTRY pMap[]; // map entries. +} VMMDLL_MAP_HANDLE, *PVMMDLL_MAP_HANDLE; /* -* Retrieve memory map entries from the specified process. Memory map entries -* are copied into the user supplied buffer that must be at least of size: -* sizeof(VMMDLL_MEMMAP_ENTRY)*pcMemMapEntries bytes. -* If the pMemMapEntries is set to NULL the number of memory map entries will be -* given in the pcMemMapEntries parameter. +* Retrieve the memory map entries based on hardware page tables (PTE) for the +* process. If pPteMap is set to NULL the number of bytes required will be +* returned in parameter pcbPteMap. +* Entries returned are sorted on VMMDLL_MAP_PTEENTRY.vaBase * -- dwPID -* -- pMemMapEntries = buffer of minimum length sizeof(VMMDLL_MEMMAP_ENTRY)*pcMemMapEntries, or NULL. -* -- pcMemMapEntries = pointer to number of memory map entries. +* -- pPteMap = buffer of minimum byte length *pcbPteMap or NULL. +* -- pcbPteMap = pointer to byte count of pPteMap buffer. * -- fIdentifyModules = try identify modules as well (= slower) * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetMemoryMap(_In_ DWORD dwPID, _Out_opt_ PVMMDLL_MEMMAP_ENTRY pMemMapEntries, _Inout_ PULONG64 pcMemMapEntries, _In_ BOOL fIdentifyModules); +BOOL VMMDLL_ProcessMap_GetPte(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbPteMap) PVMMDLL_MAP_PTE pPteMap, _Inout_ PDWORD pcbPteMap, _In_ BOOL fIdentifyModules); /* -* Retrieve a single memory map entry given a virtual address within that entrys -* range. +* Retrieve memory map entries based on virtual address descriptor (VAD) for +* the process. If pVadMap is set to NULL the number of bytes required +* will be returned in parameter pcbVadMap. +* Entries returned are sorted on VMMDLL_MAP_VADENTRY.vaStart * -- dwPID -* -- pMemMapEntry -* -- va = virtual address in the memory map entry to retrieve. +* -- pVadMap = buffer of minimum byte length *pcbVadMap or NULL. +* -- pcbVadMap = pointer to byte count of pVadMap buffer. * -- fIdentifyModules = try identify modules as well (= slower) * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetMemoryMapEntry(_In_ DWORD dwPID, _Out_ PVMMDLL_MEMMAP_ENTRY pMemMapEntry, _In_ ULONG64 va, _In_ BOOL fIdentifyModules); - -typedef struct tdVMMDLL_MODULEMAP_ENTRY { - ULONG64 BaseAddress; - ULONG64 EntryPoint; - DWORD SizeOfImage; - BOOL fWoW64; - CHAR szName[32]; -} VMMDLL_MODULEMAP_ENTRY, *PVMMDLL_MODULEMAP_ENTRY; +BOOL VMMDLL_ProcessMap_GetVad(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbVadMap) PVMMDLL_MAP_VAD pVadMap, _Inout_ PDWORD pcbVadMap, _In_ BOOL fIdentifyModules); /* -* Retrieve the module entries from the specified process. The module entries -* are copied into the user supplied buffer that must be at least of size: -* sizeof(VMMDLL_MODULEMAP_ENTRY)*pcModuleEntries bytes long. If the -* pcModuleEntries is set to NULL the number of module entries will be given -* in the pcModuleEntries parameter. +* Retrieve the modules (.dlls) for the specified process. If pModuleMap is set +* to NULL the number of bytes required will be returned in parameter pcbModuleMap. * -- dwPID -* -- pModuleEntries = buffer of minimum length sizeof(VMMDLL_MODULEMAP_ENTRY)*pcModuleEntries, or NULL. -* -- pcModuleEntries = pointer to number of memory map entries. +* -- pModuleMap = buffer of minimum byte length *pcbModuleMap or NULL. +* -- pcbModuleMap = pointer to byte count of pModuleMap buffer. * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetModuleMap(_In_ DWORD dwPID, _Out_opt_ PVMMDLL_MODULEMAP_ENTRY pModuleEntries, _Inout_ PULONG64 pcModuleEntries); +BOOL VMMDLL_ProcessMap_GetModule(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbModuleMap) PVMMDLL_MAP_MODULE pModuleMap, _Inout_ PDWORD pcbModuleMap); /* -* Retrieve a module (.exe or .dll or similar) given a module name. +* Retrieve a module map entry (.exe / .dll) given a process and module name. +* NB! PVMMDLL_MAP_MODULEENTRY->wszText will not be set by this function. +* If module name is required use VMMDLL_ProcessMap_GetModule(). * -- dwPID * -- szModuleName -* -- pModuleEntry +* -- pModuleMapEntry * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetModuleFromName(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _Out_ PVMMDLL_MODULEMAP_ENTRY pModuleEntry); +BOOL VMMDLL_ProcessMap_GetModuleFromName(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _Out_ PVMMDLL_MAP_MODULEENTRY pModuleMapEntry); + +/* +* Retrieve the heaps for the specified process. If pHeapMap is set to NULL +* the number of bytes required will be returned in parameter pcbHeapMap. +* -- dwPID +* -- pHeapMap = buffer of minimum byte length *pcbHeapMap or NULL. +* -- pcbHeapMap = pointer to byte count of pHeapMap buffer. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_ProcessMap_GetHeap(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbHeapMap) PVMMDLL_MAP_HEAP pHeapMap, _Inout_ PDWORD pcbHeapMap); + +/* +* Retrieve the threads for the specified process. If pThreadMap is set to NULL +* the number of bytes required will be returned in parameter pcbThreadMap. +* Entries returned are sorted on VMMDLL_MAP_THREADENTRY.dwTID +* -- dwPID +* -- pThreadMap = buffer of minimum byte length *pcbThreadMap or NULL. +* -- pcbThreadMap = pointer to byte count of pThreadMap buffer. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_ProcessMap_GetThread(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbThreadMap) PVMMDLL_MAP_THREAD pThreadMap, _Inout_ PDWORD pcbThreadMap); + +/* +* Retrieve the handles for the specified process. If pHandleMap is set to NULL +* the number of bytes required will be returned in parameter pcbHandleMap. +* Entries returned are sorted on VMMDLL_MAP_HANDLEENTRY.dwHandle +* -- dwPID +* -- pHandleMap = buffer of minimum byte length *pcbHandleMap or NULL. +* -- pcbHandleMap = pointer to byte count of pHandleMap buffer. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_ProcessMap_GetHandle(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbHandleMap) PVMMDLL_MAP_HANDLE pHandleMap, _Inout_ PDWORD pcbHandleMap); + + + +//----------------------------------------------------------------------------- +// VMM PROCESS FUNCTIONALITY BELOW: +// Functionality below is mostly relating to Windows processes. +//----------------------------------------------------------------------------- + +/* +* Retrieve an active process given it's name. Please note that if multiple +* processes with the same name exists only one will be returned. If required to +* parse all processes with the same name please iterate over the PID list by +* calling VMMDLL_PidList together with VMMDLL_ProcessGetInformation. +* -- szProcName = process name case insensitive. +* -- pdwPID = pointer that will receive PID on success. +* -- return +*/ +_Success_(return) +BOOL VMMDLL_PidGetFromName(_In_ LPSTR szProcName, _Out_ PDWORD pdwPID); + +/* +* List the PIDs in the system. +* -- pPIDs = DWORD array of at least number of PIDs in system, or NULL. +* -- pcPIDs = size of (in number of DWORDs) pPIDs array on entry, number of PIDs in system on exit. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_PidList(_Out_writes_opt_(*pcPIDs) PDWORD pPIDs, _Inout_ PULONG64 pcPIDs); #define VMMDLL_PROCESS_INFORMATION_MAGIC 0xc0ffee663df9301e -#define VMMDLL_PROCESS_INFORMATION_VERSION 4 +#define VMMDLL_PROCESS_INFORMATION_VERSION 5 typedef struct tdVMMDLL_PROCESS_INFORMATION { ULONG64 magic; @@ -561,7 +752,7 @@ typedef struct tdVMMDLL_PROCESS_INFORMATION { struct { ULONG64 vaEPROCESS; ULONG64 vaPEB; - ULONG64 vaENTRY; + ULONG64 _Reserved1; BOOL fWow64; DWORD vaPEB32; // WoW64 only } win; @@ -613,37 +804,37 @@ typedef struct tdVMMDLL_IAT_ENTRY { * If the pData == NULL upon entry the number of entries of the pData array must * have in order to be able to hold the data is returned. * -- dwPID -* -- szModule +* -- wszModule * -- pData * -- cData * -- pcData * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetDirectories(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_writes_(16) PIMAGE_DATA_DIRECTORY pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetDirectories(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_writes_(16) PIMAGE_DATA_DIRECTORY pData, _In_ DWORD cData, _Out_ PDWORD pcData); _Success_(return) -BOOL VMMDLL_ProcessGetSections(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PIMAGE_SECTION_HEADER pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetSections(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PIMAGE_SECTION_HEADER pData, _In_ DWORD cData, _Out_ PDWORD pcData); _Success_(return) -BOOL VMMDLL_ProcessGetEAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PVMMDLL_EAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetEAT(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PVMMDLL_EAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); _Success_(return) -BOOL VMMDLL_ProcessGetIAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PVMMDLL_IAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetIAT(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PVMMDLL_IAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); /* * Retrieve the virtual address of a given function inside a process/module. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- szFunctionName * -- return = virtual address of function, zero on fail. */ -ULONG64 VMMDLL_ProcessGetProcAddress(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szFunctionName); +ULONG64 VMMDLL_ProcessGetProcAddress(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szFunctionName); /* * Retrieve the base address of a given module. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- return = virtual address of module base, zero on fail. */ -ULONG64 VMMDLL_ProcessGetModuleBase(_In_ DWORD dwPID, _In_ LPSTR szModuleName); +ULONG64 VMMDLL_ProcessGetModuleBase(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName); @@ -729,7 +920,7 @@ BOOL VMMDLL_WinReg_HiveList(_Out_writes_(cHives) PVMMDLL_REGISTRY_HIVE_INFORMATI * -- ra * -- pb * -- cb -* -- pcbRead +* -- pcbReadOpt * -- flags = flags as in VMMDLL_FLAG_* * -- return = success/fail. NB! reads may report as success even if 0 bytes are * read - it's recommended to verify pcbReadOpt parameter. @@ -901,26 +1092,26 @@ typedef struct tdVMMDLL_WIN_THUNKINFO_EAT { * function. This includes the virtual address of the IAT thunk which is useful * for hooking. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- szImportModuleName * -- szImportFunctionName * -- pThunkIAT * -- return */ _Success_(return) -BOOL VMMDLL_WinGetThunkInfoIAT(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szImportModuleName, _In_ LPSTR szImportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_IAT pThunkInfoIAT); +BOOL VMMDLL_WinGetThunkInfoIAT(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szImportModuleName, _In_ LPSTR szImportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_IAT pThunkInfoIAT); /* * Retrieve information about the export address table EAT thunk for an exported * function. This includes the virtual address of the EAT thunk which is useful * for hooking. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- pThunkEAT * -- return */ _Success_(return) -BOOL VMMDLL_WinGetThunkInfoEAT(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT); +BOOL VMMDLL_WinGetThunkInfoEAT(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT); diff --git a/README.md b/README.md index d63277d6..f2d4b637 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ The Memory Process File System is an easy and convenient way of accessing physic Easy trivial point and click memory analysis without the need for complicated commandline arguments! Access memory content and artifacts via files in a mounted virtual file system or via a feature rich application library to include in your own projects! -Analyze memory dump files, live memory via [DumpIt](https://www.comae.com/) or WinPMEM, live memory in read-write mode via linked [PCILeech](https://github.com/ufrisk/pcileech/) and [PCILeech-FPGA](https://github.com/ufrisk/pcileech-fpga/) devices! +Analyze memory dump files, live memory via DumpIt or WinPMEM, live memory in read-write mode via linked [PCILeech](https://github.com/ufrisk/pcileech/) and [PCILeech-FPGA](https://github.com/ufrisk/pcileech-fpga/) devices! It's even possible to connect to a remote LeechAgent memory acquisition agent over a secured connection - allowing for remote live memory incident response - even over higher latency low band-width connections! @@ -13,13 +13,13 @@ Use your favorite tools to analyze memory - use your favorite hex editors, your

-Include the Memory Process File System in your Python or C/C++ programming projects! Almost everything in the Memory Process File System is exposed via an easy-to-use API for use in your own projects! The Plugin friendly architecture allows users to easily extend the Memory Process File System with native C .DLL plugins or Python .py plugins - providing additional analysis capabilities! +Include the Memory Process File System in your Python or C/C++ programming projects! Everything in the Memory Process File System is exposed via an easy-to-use API for use in your own projects! The Plugin friendly architecture allows users to easily extend the Memory Process File System with native C .DLL plugins or Python .py plugins - providing additional analysis capabilities! Please check out the [project wiki](https://github.com/ufrisk/MemProcFS/wiki) for more in-depth detailed information about the file system itself, its API and its plugin modules! Please check out the [LeechCore project](https://github.com/ufrisk/LeechCore) for information about supported memory acquisition methods and remote memory access via the LeechService. -To get going clone the sources in the repository or download the [latest binaries, modules and configuration files](https://github.com/ufrisk/MemProcFS/releases/latest) from the releases section. +To get going clone the sources in the repository or download the [latest binaries, modules and configuration files](https://github.com/ufrisk/MemProcFS/releases/latest) from the releases section and check out the [guide](https://github.com/ufrisk/MemProcFS/wiki). Fast and easy memory analysis via mounted file system: ====================================================== @@ -45,11 +45,11 @@ Anyone is able to extend the Memory Process File System with custom plugins! It Installing: =========== ## Windows -Get the latest [binaries, modules and configuration files](https://github.com/ufrisk/MemProcFS/releases/latest) from the latest release. Alternatively clone the repository and build from source. If the Memory Process File System is used as an API it is only dependant on the Microsoft Visual C++ Redistributables for Visual Studio 2019 (see below). +Get the latest [binaries, modules and configuration files](https://github.com/ufrisk/MemProcFS/releases/latest) from the latest release. Alternatively clone the repository and build from source. -The Memory Process File System is dependant on the [LeechCore project](https://github.com/ufrisk/LeechCore) for memory acquisition. The necessary _leechcore.dll_ file is already pre-built and included together with the pre-built binaries. +The Memory Process File System is dependent on the [LeechCore project](https://github.com/ufrisk/LeechCore) for memory acquisition. The necessary _leechcore.dll_ file is already pre-built and included together with the pre-built binaries. -The Memory Process File System is also dependant in the Microsoft Visual C++ Redistributables for Visual Studio 2019. They can be downloaded from Microsoft [here](https://go.microsoft.com/fwlink/?LinkId=746572). Alternatively, if installing the Dokany file system driver please install the DokanSetup_redist version and it will install the required redistributables. +The Memory Process File System is also dependent in the Microsoft Visual C++ Redistributables for Visual Studio 2019. They can be downloaded from Microsoft [here](https://go.microsoft.com/fwlink/?LinkId=746572). Alternatively, if installing the Dokany file system driver please install the DokanSetup_redist version and it will install the required redistributables. Mounting the file system requires the Dokany file system library to be installed. Please download and install the latest version of Dokany at: https://github.com/dokan-dev/dokany/releases/latest It is recommended to download and install the DokanSetup_redist version. @@ -60,7 +60,7 @@ To capture live memory (without PCILeech FPGA hardware) download [DumpIt](https: PCILeech FPGA will require hardware as well as _FTD3XX.dll_ to be dropped alongside the MemProcFS binaries. Please check out the [LeechCore](https://github.com/ufrisk/LeechCore) project for instructions. ## Linux -The memory process file system is not yet supported on Linux. +The memory process file system is not supported on Linux. Examples: ========= @@ -76,7 +76,7 @@ Or register the memory dump file extension with MemProcFS.exe so that the file s - mount live target memory, in read-only mode, with WinPMEM driver:
`memprocfs.exe -device pmem` - mount live target memory, in read/write mode, with PCILeech FPGA memory acquisition device:
`memprocfs.exe -device fpga` - mount live target memory, in read/write mode, with TotalMeltdown vulnerability acquisition device:
`memprocfs.exe -device totalmeltdown` -- mount an arbitrary x64 memory dump by specifying the process or kernel page table base in the cr3 option:
`memprocfs.exe -device c:\temp\unknown-x64-dump.raw -cr3 0x1aa000` +- mount a memory dump with a corresponding page files:
`memprocfs.exe -device unknown-x64-dump.raw -pagefile0 pagefile.sys -pagefile1 swapfile.sys` Documentation: ============== @@ -93,7 +93,7 @@ Detailed build instructions may be found in the [Wiki](https://github.com/ufrisk Current Limitations & Future Development: ========================================= -The Memory Process File System is currently limited to analyzing Windows (32-bit and 64-bit XP to 10) memory dumps (other x64 dumps in a very limited way). Also, the Memory Process File System currently does not run on Linux. +The Memory Process File System is currently limited to analyzing Windows (32-bit and 64-bit XP to 10) memory dumps. Please find some ideas for possible future expansions of the memory process file system listed below. This is a list of ideas - not a list of features that will be implemented. Even though some items are put as prioritized there is no guarantee that they will be implemented in a timely fashion. @@ -101,9 +101,9 @@ Please find some ideas for possible future expansions of the memory process file - More/new plugins. ### Other items: -- PFN support. -- Support for analyzing x64 Linux, macOS and UEFI memory dumps. - Hash lookup of executable memory pages in DB. +- Additional file recovery. +- PFN support. License: ====== @@ -122,71 +122,16 @@ Changelog: v1.0 * Initial Release. -v1.1-v1.2 -* Various updates. please see individual relases for more information. - -v2.0 -* Major new release with multiple changes. Most noteworty are: -* Multi-Threading support. -* Performance optimizations. -* Memory acqusition via the [LeechCore](https://github.com/ufrisk/LeechCore/) library with additional support for: - * Live memory acquisition with DumpIt in /LIVEKD mode or loaded kernel driver. - * Support for Microsoft Crash Dumps - such as created by default by [Comae DumpIt](https://www.comae.com). - * Hyper-V save files. - * Remote capture via remotely installed LeechService. - -v2.1 -* New APIs: - * IAT/EAT hook functionality. - * Limited Windows 10 MemCompression support. -* Bug fixes. - -v2.2 -* New API: - * Force refresh of process list and caches. - -v2.3 -* Project upgrade to Visual Studio 2019. -* Bug fixes. -* Additional plugins for download available from [MemProcFS-plugins](https://github.com/ufrisk/MemProcFS-plugins). -* Python plugin updater - easy installs and updates from [MemProcFS-plugins](https://github.com/ufrisk/MemProcFS-plugins). -* Pypykatz plugin for 'mimikatz' style functionality available as separate download from [MemProcFS-plugins](https://github.com/ufrisk/MemProcFS-plugins) project. Thanks to [@SkelSec](https://twitter.com/SkelSec) for the contribution. -* Python API support for version >3.6 (i.e Python 3.7 now fully supported). - -v2.4 -* Bug fixes. -* New module: [PEDump](https://github.com/ufrisk/MemProcFS/wiki/FS_Process_PEDump) - best-effort reconstructed PE modules (.exe, .dll and .sys files) in process pedump sub-folder. - -v2.5 -* Performance optimizations. -* Windows transition page support. -* New module: [Registry](https://github.com/ufrisk/MemProcFS/wiki/FS_Registry) - best-effort reconstructed registry hives in the registry/hive_files/ sub-folder. - -v2.6 -* Additional performance optimizations. -* Support for process long names (previously capped to 15 chars), image path and command line. -* New module: [SysInfo](https://github.com/ufrisk/MemProcFS/wiki/FS_SysInfo) - system information including OS version number and process tree with command line. - -v2.7 -* Bug fixes and optimizations. -* Network support: TCP connections added to [SysInfo](https://github.com/ufrisk/MemProcFS/wiki/FS_SysInfo) module. -* New module: [Phys2Virt](https://github.com/ufrisk/MemProcFS/wiki/FS_Phys2Virt) - search individual or all process address spaces for virtual addresses that map to specific physical address. - -v2.8 -* Bug fixes. -* Windows 10 Compressed Memory support. - -v2.9 -* Bug fixes and major internal refactorings. -* Full Registry support - Explore the Windows registry in the file system or via the API. -* NB! The v2.9 C/C++ API vfs (virtual file system) API is incompatible with earlier versions. - -[v2.10](https://github.com/ufrisk/MemProcFS/releases/tag/v2.10) -* Dump file support - create a WinDbg compatible `memory.dmp` file in the root folder. -* Early .pdb debugging subsystem with Microsoft symbol server integration. -* Process create/terminate timestamps on process directories. - -Latest -* Bug fixes. -* Increased performance. -* Move binaries into releases section. +v1.1-v2.10 +* Various updates. Please see individual relases for more information. + +[v3.0](https://github.com/ufrisk/MemProcFS/releases/tag/v3.0) +* Major release with new features, optimizations and refactorings. +* Rewritten, new virtual memory management core for increased memory recovery: + * VAD (virtual address descriptor) support. + * Win10 memory decompression bug-fixes. + * Pagefile support. +* Threads and Handles. +* API changes: + * API for above new features. + * Function signature changes - module names from ansi string to wide string. diff --git a/files/leechcore.lib b/files/leechcore.lib index b75a47fc..afba017b 100644 Binary files a/files/leechcore.lib and b/files/leechcore.lib differ diff --git a/files/vmm.lib b/files/vmm.lib index 2db6e7b9..5c1115f1 100644 Binary files a/files/vmm.lib and b/files/vmm.lib differ diff --git a/files/vmmdll.h b/files/vmmdll.h index 8a6b6a38..87f3f191 100644 --- a/files/vmmdll.h +++ b/files/vmmdll.h @@ -4,7 +4,7 @@ // (c) Ulf Frisk, 2018-2019 // Author: Ulf Frisk, pcileech@frizk.net // -// Header Version: 2.10.2 +// Header Version: 3.0 // #include @@ -23,11 +23,11 @@ extern "C" { /* * Initialize VMM.DLL with command line parameters. For a more detailed info -* about the parameters please see github wiki for Memory Process File System -* and LeechCore. THIS IS THE PREFERED WAY OF INITIALIZING VMM.DLL +* about the parameters please see github wiki for MemProcFS and LeechCore. +* NB! LeechCore initialization parameters are _also_ valid to this function. * Important parameters are: -* -printf = show printf style outputs) -* -v -vv -vvv = extra verbosity levels) +* -printf = show printf style outputs. +* -v -vv -vvv = extra verbosity levels. * -device = device as on format for LeechCore - please see leechcore.h or * Github documentation for additional information. Some values * are: , fpga, usb3380, hvsavedstate, totalmeltdown, pmem @@ -35,8 +35,19 @@ extern "C" { * documentation for additional information. * -norefresh = disable background refreshes (even if backing memory is * volatile memory). -* -symbolserverdisable = disable symbol server until user change. This -* parameter will take precedence over registry settings. +* -symbolserverdisable = disable symbol server until user change. +* This parameter will take precedence over registry settings. +* -pagefile[0-9] = page file(s) to use in addition to physical memory. +* Normally pagefile.sys have index 0 and swapfile.sys index 1. +* Page files are in constant flux - do not use if time diff +* between memory dump and page files are more than few minutes. +* Example: 'pagefile0 swapfile.sys' +* -waitinitialize = Wait for initialization to complete before returning. +* Normal use is that some initialization is done asynchronously +* and may not be completed when initialization call is completed. +* This includes virtual memory compression, registry and more. +* Example: '-waitinitialize' +* * -- argc * -- argv * -- return = success/fail @@ -78,9 +89,9 @@ VOID VMMDLL_MemFree(_Frees_ptr_opt_ PVOID pvMem); //----------------------------------------------------------------------------- /* -* Options used together with the functions: VMMDLL_GetOption & VMMDLL_SetOption +* Options used together with the functions: VMMDLL_ConfigGet & VMMDLL_ConfigSet * Options are defined with either: VMMDLL_OPT_* in this header file or as -* MEMDEVICE_OPT_* in memdevice.h +* LEECHCORE_OPT_* in leechcore.h * For more detailed information check the sources for individual device types. */ #define VMMDLL_OPT_CORE_PRINTF_ENABLE 0x80000001 // RW @@ -349,17 +360,17 @@ typedef struct tdVMMDLL_PLUGIN_REGINFO { #define VMMDLL_FLAG_ZEROPAD_ON_FAIL 0x0002 // zero pad failed physical memory reads and report success if read within range of physical memory. #define VMMDLL_FLAG_FORCECACHE_READ 0x0008 // force use of cache - fail non-cached pages - only valid for reads, invalid with VMM_FLAG_NOCACHE/VMM_FLAG_ZEROPAD_ON_FAIL. #define VMMDLL_FLAG_NOPAGING 0x0010 // do not try to retrieve memory from paged out memory from pagefile/compressed (even if possible) +#define VMMDLL_FLAG_NOPAGING_IO 0x0020 // do not try to retrieve memory from paged out memory if read would incur additional I/O (even if possible). /* * Read memory in various non-contigious locations specified by the pointers to -* the items in the ppDMAs array. Result for each unit of work will be given +* the items in the ppMEMs array. Result for each unit of work will be given * individually. No upper limit of number of items to read, but no performance * boost will be given if above hardware limit. Max size of each unit of work is * one 4k page (4096 bytes). * -- dwPID - PID of target process, (DWORD)-1 to read physical memory. * -- ppMEMs = array of scatter read headers. -* -- cpMEMs = count of ppDMAs. -* -- pcpDMAsRead = optional count of number of successfully read ppDMAs. +* -- cpMEMs = count of ppMEMs. * -- flags = optional flags as given by VMMDLL_FLAG_* * -- return = the number of successfully read items. */ @@ -384,7 +395,7 @@ BOOL VMMDLL_MemReadPage(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Inout_bytecount_(40 * -- return = success/fail (depending if all requested bytes are read or not). */ _Success_(return) -BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWORD cb); +BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb); /* * Read a contigious amount of memory and report the number of bytes read in pcbRead. @@ -398,7 +409,7 @@ BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWO * read - it's recommended to verify pcbReadOpt parameter. */ _Success_(return) -BOOL VMMDLL_MemReadEx(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWORD cb, _Out_opt_ PDWORD pcbReadOpt, _In_ ULONG64 flags); +BOOL VMMDLL_MemReadEx(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb, _Out_opt_ PDWORD pcbReadOpt, _In_ ULONG64 flags); /* * Prefetch a number of addresses (specified in the pA array) into the memory @@ -426,7 +437,7 @@ BOOL VMMDLL_MemPrefetchPages(_In_ DWORD dwPID, _In_reads_(cPrefetchAddresses) PU * -- return = TRUE on success, FALSE on partial or zero write. */ _Success_(return) -BOOL VMMDLL_MemWrite(_In_ DWORD dwPID, _In_ ULONG64 qwA, _In_ PBYTE pb, _In_ DWORD cb); +BOOL VMMDLL_MemWrite(_In_ DWORD dwPID, _In_ ULONG64 qwA, _In_reads_(cb) PBYTE pb, _In_ DWORD cb); /* * Translate a virtual address to a physical address by walking the page tables @@ -442,106 +453,286 @@ BOOL VMMDLL_MemVirt2Phys(_In_ DWORD dwPID, _In_ ULONG64 qwVA, _Out_ PULONG64 pqw //----------------------------------------------------------------------------- -// VMM PROCESS FUNCTIONALITY BELOW: -// Functionality below is mostly relating to Windows processes. +// VMM PROCESS MAP FUNCTIONALITY BELOW: +// Functionality for retrieving process related collections of items such as +// page table map (PTE), virtual address descriptor map (VAD), loaded modules, +// heaps and threads. //----------------------------------------------------------------------------- -/* -* Retrieve an active process given it's name. Please note that if multiple -* processes with the same name exists only one will be returned. If required to -* parse all processes with the same name please iterate over the PID list by -* calling VMMDLL_PidList together with VMMDLL_ProcessGetInformation. -* -- szProcName = process name case insensitive. -* -- pdwPID = pointer that will receive PID on success. -* -- return -*/ -_Success_(return) -BOOL VMMDLL_PidGetFromName(_In_ LPSTR szProcName, _Out_ PDWORD pdwPID); - -/* -* List the PIDs in the system. -* -- pPIDs = DWORD array of at least number of PIDs in system, or NULL. -* -- pcPIDs = size of (in number of DWORDs) pPIDs array on entry, number of PIDs in system on exit. -* -- return = success/fail. -*/ -_Success_(return) -BOOL VMMDLL_PidList(_Out_opt_ PDWORD pPIDs, _Inout_ PULONG64 pcPIDs); +#define VMMDLL_MAP_PTE_VERSION 1 +#define VMMDLL_MAP_VAD_VERSION 1 +#define VMMDLL_MAP_MODULE_VERSION 1 +#define VMMDLL_MAP_HEAP_VERSION 1 +#define VMMDLL_MAP_THREAD_VERSION 1 +#define VMMDLL_MAP_HANDLE_VERSION 1 -// flags to check for existence in the fPage field of PCILEECH_VMM_MEMMAP_ENTRY +// flags to check for existence in the fPage field of VMMDLL_MAP_PTEENTRY #define VMMDLL_MEMMAP_FLAG_PAGE_W 0x0000000000000002 #define VMMDLL_MEMMAP_FLAG_PAGE_NS 0x0000000000000004 #define VMMDLL_MEMMAP_FLAG_PAGE_NX 0x8000000000000000 #define VMMDLL_MEMMAP_FLAG_PAGE_MASK 0x8000000000000006 -typedef struct tdVMMDLL_MEMMAP_ENTRY { - ULONG64 AddrBase; - ULONG64 cPages; - ULONG64 fPage; +typedef struct tdVMMDLL_MAP_PTEENTRY { + QWORD vaBase; + QWORD cPages; + QWORD fPage; BOOL fWoW64; - CHAR szTag[32]; -} VMMDLL_MEMMAP_ENTRY, *PVMMDLL_MEMMAP_ENTRY; + DWORD cwszText; + LPWSTR wszText; + DWORD _Reserved1[2]; +} VMMDLL_MAP_PTEENTRY, *PVMMDLL_MAP_PTEENTRY; + +typedef struct tdVMMDLL_MAP_VADENTRY { + QWORD vaStart; + QWORD vaEnd; + QWORD vaVad; + // DWORD 0 + DWORD VadType : 3; // Pos 0 + DWORD Protection : 5; // Pos 3 + DWORD fImage : 1; // Pos 8 + DWORD fFile : 1; // Pos 9 + DWORD fPageFile : 1; // Pos 10 + DWORD fPrivateMemory : 1; // Pos 11 + DWORD fTeb : 1; // Pos 12 + DWORD fStack : 1; // Pos 13 + DWORD fSpare : 2; // Pos 14 + DWORD HeapNum : 7; // Pos 16 + DWORD fHeap : 1; // Pos 23 + DWORD cwszDescription : 8; // Pos 24 + // DWORD 1 + DWORD CommitCharge : 31; // Pos 0 + DWORD MemCommit : 1; // Pos 31 + DWORD u2; + DWORD cbPrototypePte; + QWORD vaPrototypePte; + QWORD vaSubsection; + LPWSTR wszText; // optional LPWSTR pointed into VMMDLL_MAP_VAD.wszMultiText + DWORD cwszText; // WCHAR count not including terminating null + DWORD _Reserved1; +} VMMDLL_MAP_VADENTRY, *PVMMDLL_MAP_VADENTRY; + +typedef struct tdVMMDLL_MAP_MODULEENTRY { + QWORD vaBase; + QWORD vaEntry; + DWORD cbImageSize; + BOOL fWoW64; + LPWSTR wszText; + DWORD cwszText; // wchar count not including terminating null + DWORD _Reserved1[7]; +} VMMDLL_MAP_MODULEENTRY, *PVMMDLL_MAP_MODULEENTRY; + +typedef struct tdVMMDLL_MAP_HEAPENTRY { + QWORD vaHeapSegment; + DWORD cPages; + DWORD cPagesUnCommitted : 24; + DWORD HeapId : 7; + DWORD fPrimary : 1; +} VMMDLL_MAP_HEAPENTRY, *PVMMDLL_MAP_HEAPENTRY; + +typedef struct tdVMMDLL_MAP_THREADENTRY { + DWORD dwTID; + DWORD dwPID; + DWORD dwExitStatus; + UCHAR bState; + UCHAR bRunning; + UCHAR bPriority; + UCHAR bBasePriority; + QWORD vaETHREAD; + QWORD vaTeb; + QWORD ftCreateTime; + QWORD ftExitTime; + QWORD vaStartAddress; + QWORD vaStackBaseUser; + QWORD vaStackLimitUser; + QWORD vaStackBaseKernel; + QWORD vaStackLimitKernel; + DWORD _FutureUse[10]; +} VMMDLL_MAP_THREADENTRY, *PVMMDLL_MAP_THREADENTRY; + +typedef struct tdVMMDLL_MAP_HANDLEENTRY { + QWORD vaObject; + DWORD dwHandle; + DWORD dwGrantedAccess : 24; + DWORD iType : 8; + QWORD qwHandleCount; + QWORD qwPointerCount; + QWORD vaObjectCreateInfo; + QWORD vaSecurityDescriptor; + LPWSTR wszText; + DWORD cwszText; + DWORD dwPID; + DWORD dwPoolTag; + DWORD _FutureUse[4]; + DWORD cwszType; + LPWSTR wszType; +} VMMDLL_MAP_HANDLEENTRY, *PVMMDLL_MAP_HANDLEENTRY; + +typedef struct tdVMMDLL_MAP_PTE { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // NULL or multi-wstr pointed into by VMMDLL_MAP_VADENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_PTEENTRY pMap[]; // map entries. +} VMMDLL_MAP_PTE, *PVMMDLL_MAP_PTE; + +typedef struct tdVMMDLL_MAP_VAD { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // NULL or multi-wstr pointed into by VMMDLL_MAP_VADENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_VADENTRY pMap[]; // map entries. +} VMMDLL_MAP_VAD, *PVMMDLL_MAP_VAD; + +typedef struct tdVMMDLL_MAP_MODULE { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // multi-wstr pointed into by VMMDLL_MAP_MODULEENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_MODULEENTRY pMap[]; // map entries. +} VMMDLL_MAP_MODULE, *PVMMDLL_MAP_MODULE; + +typedef struct tdVMMDLL_MAP_HEAP { + DWORD dwVersion; + DWORD _Reserved1[8]; + DWORD cMap; // # map entries. + VMMDLL_MAP_HEAPENTRY pMap[]; // map entries. +} VMMDLL_MAP_HEAP, *PVMMDLL_MAP_HEAP; + +typedef struct tdVMMDLL_MAP_THREAD { + DWORD dwVersion; + DWORD _Reserved[8]; + DWORD cMap; // # map entries. + VMMDLL_MAP_THREADENTRY pMap[]; // map entries. +} VMMDLL_MAP_THREAD, *PVMMDLL_MAP_THREAD; + +typedef struct tdVMMDLL_MAP_HANDLE { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // multi-wstr pointed into by VMMDLL_MAP_HANDLEENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_HANDLEENTRY pMap[]; // map entries. +} VMMDLL_MAP_HANDLE, *PVMMDLL_MAP_HANDLE; /* -* Retrieve memory map entries from the specified process. Memory map entries -* are copied into the user supplied buffer that must be at least of size: -* sizeof(VMMDLL_MEMMAP_ENTRY)*pcMemMapEntries bytes. -* If the pMemMapEntries is set to NULL the number of memory map entries will be -* given in the pcMemMapEntries parameter. +* Retrieve the memory map entries based on hardware page tables (PTE) for the +* process. If pPteMap is set to NULL the number of bytes required will be +* returned in parameter pcbPteMap. +* Entries returned are sorted on VMMDLL_MAP_PTEENTRY.vaBase * -- dwPID -* -- pMemMapEntries = buffer of minimum length sizeof(VMMDLL_MEMMAP_ENTRY)*pcMemMapEntries, or NULL. -* -- pcMemMapEntries = pointer to number of memory map entries. +* -- pPteMap = buffer of minimum byte length *pcbPteMap or NULL. +* -- pcbPteMap = pointer to byte count of pPteMap buffer. * -- fIdentifyModules = try identify modules as well (= slower) * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetMemoryMap(_In_ DWORD dwPID, _Out_opt_ PVMMDLL_MEMMAP_ENTRY pMemMapEntries, _Inout_ PULONG64 pcMemMapEntries, _In_ BOOL fIdentifyModules); +BOOL VMMDLL_ProcessMap_GetPte(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbPteMap) PVMMDLL_MAP_PTE pPteMap, _Inout_ PDWORD pcbPteMap, _In_ BOOL fIdentifyModules); /* -* Retrieve a single memory map entry given a virtual address within that entrys -* range. +* Retrieve memory map entries based on virtual address descriptor (VAD) for +* the process. If pVadMap is set to NULL the number of bytes required +* will be returned in parameter pcbVadMap. +* Entries returned are sorted on VMMDLL_MAP_VADENTRY.vaStart * -- dwPID -* -- pMemMapEntry -* -- va = virtual address in the memory map entry to retrieve. +* -- pVadMap = buffer of minimum byte length *pcbVadMap or NULL. +* -- pcbVadMap = pointer to byte count of pVadMap buffer. * -- fIdentifyModules = try identify modules as well (= slower) * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetMemoryMapEntry(_In_ DWORD dwPID, _Out_ PVMMDLL_MEMMAP_ENTRY pMemMapEntry, _In_ ULONG64 va, _In_ BOOL fIdentifyModules); - -typedef struct tdVMMDLL_MODULEMAP_ENTRY { - ULONG64 BaseAddress; - ULONG64 EntryPoint; - DWORD SizeOfImage; - BOOL fWoW64; - CHAR szName[32]; -} VMMDLL_MODULEMAP_ENTRY, *PVMMDLL_MODULEMAP_ENTRY; +BOOL VMMDLL_ProcessMap_GetVad(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbVadMap) PVMMDLL_MAP_VAD pVadMap, _Inout_ PDWORD pcbVadMap, _In_ BOOL fIdentifyModules); /* -* Retrieve the module entries from the specified process. The module entries -* are copied into the user supplied buffer that must be at least of size: -* sizeof(VMMDLL_MODULEMAP_ENTRY)*pcModuleEntries bytes long. If the -* pcModuleEntries is set to NULL the number of module entries will be given -* in the pcModuleEntries parameter. +* Retrieve the modules (.dlls) for the specified process. If pModuleMap is set +* to NULL the number of bytes required will be returned in parameter pcbModuleMap. * -- dwPID -* -- pModuleEntries = buffer of minimum length sizeof(VMMDLL_MODULEMAP_ENTRY)*pcModuleEntries, or NULL. -* -- pcModuleEntries = pointer to number of memory map entries. +* -- pModuleMap = buffer of minimum byte length *pcbModuleMap or NULL. +* -- pcbModuleMap = pointer to byte count of pModuleMap buffer. * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetModuleMap(_In_ DWORD dwPID, _Out_opt_ PVMMDLL_MODULEMAP_ENTRY pModuleEntries, _Inout_ PULONG64 pcModuleEntries); +BOOL VMMDLL_ProcessMap_GetModule(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbModuleMap) PVMMDLL_MAP_MODULE pModuleMap, _Inout_ PDWORD pcbModuleMap); /* -* Retrieve a module (.exe or .dll or similar) given a module name. +* Retrieve a module map entry (.exe / .dll) given a process and module name. +* NB! PVMMDLL_MAP_MODULEENTRY->wszText will not be set by this function. +* If module name is required use VMMDLL_ProcessMap_GetModule(). * -- dwPID * -- szModuleName -* -- pModuleEntry +* -- pModuleMapEntry * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetModuleFromName(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _Out_ PVMMDLL_MODULEMAP_ENTRY pModuleEntry); +BOOL VMMDLL_ProcessMap_GetModuleFromName(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _Out_ PVMMDLL_MAP_MODULEENTRY pModuleMapEntry); + +/* +* Retrieve the heaps for the specified process. If pHeapMap is set to NULL +* the number of bytes required will be returned in parameter pcbHeapMap. +* -- dwPID +* -- pHeapMap = buffer of minimum byte length *pcbHeapMap or NULL. +* -- pcbHeapMap = pointer to byte count of pHeapMap buffer. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_ProcessMap_GetHeap(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbHeapMap) PVMMDLL_MAP_HEAP pHeapMap, _Inout_ PDWORD pcbHeapMap); + +/* +* Retrieve the threads for the specified process. If pThreadMap is set to NULL +* the number of bytes required will be returned in parameter pcbThreadMap. +* Entries returned are sorted on VMMDLL_MAP_THREADENTRY.dwTID +* -- dwPID +* -- pThreadMap = buffer of minimum byte length *pcbThreadMap or NULL. +* -- pcbThreadMap = pointer to byte count of pThreadMap buffer. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_ProcessMap_GetThread(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbThreadMap) PVMMDLL_MAP_THREAD pThreadMap, _Inout_ PDWORD pcbThreadMap); + +/* +* Retrieve the handles for the specified process. If pHandleMap is set to NULL +* the number of bytes required will be returned in parameter pcbHandleMap. +* Entries returned are sorted on VMMDLL_MAP_HANDLEENTRY.dwHandle +* -- dwPID +* -- pHandleMap = buffer of minimum byte length *pcbHandleMap or NULL. +* -- pcbHandleMap = pointer to byte count of pHandleMap buffer. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_ProcessMap_GetHandle(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbHandleMap) PVMMDLL_MAP_HANDLE pHandleMap, _Inout_ PDWORD pcbHandleMap); + + + +//----------------------------------------------------------------------------- +// VMM PROCESS FUNCTIONALITY BELOW: +// Functionality below is mostly relating to Windows processes. +//----------------------------------------------------------------------------- + +/* +* Retrieve an active process given it's name. Please note that if multiple +* processes with the same name exists only one will be returned. If required to +* parse all processes with the same name please iterate over the PID list by +* calling VMMDLL_PidList together with VMMDLL_ProcessGetInformation. +* -- szProcName = process name case insensitive. +* -- pdwPID = pointer that will receive PID on success. +* -- return +*/ +_Success_(return) +BOOL VMMDLL_PidGetFromName(_In_ LPSTR szProcName, _Out_ PDWORD pdwPID); + +/* +* List the PIDs in the system. +* -- pPIDs = DWORD array of at least number of PIDs in system, or NULL. +* -- pcPIDs = size of (in number of DWORDs) pPIDs array on entry, number of PIDs in system on exit. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_PidList(_Out_writes_opt_(*pcPIDs) PDWORD pPIDs, _Inout_ PULONG64 pcPIDs); #define VMMDLL_PROCESS_INFORMATION_MAGIC 0xc0ffee663df9301e -#define VMMDLL_PROCESS_INFORMATION_VERSION 4 +#define VMMDLL_PROCESS_INFORMATION_VERSION 5 typedef struct tdVMMDLL_PROCESS_INFORMATION { ULONG64 magic; @@ -561,7 +752,7 @@ typedef struct tdVMMDLL_PROCESS_INFORMATION { struct { ULONG64 vaEPROCESS; ULONG64 vaPEB; - ULONG64 vaENTRY; + ULONG64 _Reserved1; BOOL fWow64; DWORD vaPEB32; // WoW64 only } win; @@ -613,37 +804,37 @@ typedef struct tdVMMDLL_IAT_ENTRY { * If the pData == NULL upon entry the number of entries of the pData array must * have in order to be able to hold the data is returned. * -- dwPID -* -- szModule +* -- wszModule * -- pData * -- cData * -- pcData * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetDirectories(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_writes_(16) PIMAGE_DATA_DIRECTORY pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetDirectories(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_writes_(16) PIMAGE_DATA_DIRECTORY pData, _In_ DWORD cData, _Out_ PDWORD pcData); _Success_(return) -BOOL VMMDLL_ProcessGetSections(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PIMAGE_SECTION_HEADER pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetSections(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PIMAGE_SECTION_HEADER pData, _In_ DWORD cData, _Out_ PDWORD pcData); _Success_(return) -BOOL VMMDLL_ProcessGetEAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PVMMDLL_EAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetEAT(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PVMMDLL_EAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); _Success_(return) -BOOL VMMDLL_ProcessGetIAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PVMMDLL_IAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetIAT(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PVMMDLL_IAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); /* * Retrieve the virtual address of a given function inside a process/module. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- szFunctionName * -- return = virtual address of function, zero on fail. */ -ULONG64 VMMDLL_ProcessGetProcAddress(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szFunctionName); +ULONG64 VMMDLL_ProcessGetProcAddress(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szFunctionName); /* * Retrieve the base address of a given module. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- return = virtual address of module base, zero on fail. */ -ULONG64 VMMDLL_ProcessGetModuleBase(_In_ DWORD dwPID, _In_ LPSTR szModuleName); +ULONG64 VMMDLL_ProcessGetModuleBase(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName); @@ -729,7 +920,7 @@ BOOL VMMDLL_WinReg_HiveList(_Out_writes_(cHives) PVMMDLL_REGISTRY_HIVE_INFORMATI * -- ra * -- pb * -- cb -* -- pcbRead +* -- pcbReadOpt * -- flags = flags as in VMMDLL_FLAG_* * -- return = success/fail. NB! reads may report as success even if 0 bytes are * read - it's recommended to verify pcbReadOpt parameter. @@ -901,26 +1092,26 @@ typedef struct tdVMMDLL_WIN_THUNKINFO_EAT { * function. This includes the virtual address of the IAT thunk which is useful * for hooking. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- szImportModuleName * -- szImportFunctionName * -- pThunkIAT * -- return */ _Success_(return) -BOOL VMMDLL_WinGetThunkInfoIAT(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szImportModuleName, _In_ LPSTR szImportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_IAT pThunkInfoIAT); +BOOL VMMDLL_WinGetThunkInfoIAT(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szImportModuleName, _In_ LPSTR szImportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_IAT pThunkInfoIAT); /* * Retrieve information about the export address table EAT thunk for an exported * function. This includes the virtual address of the EAT thunk which is useful * for hooking. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- pThunkEAT * -- return */ _Success_(return) -BOOL VMMDLL_WinGetThunkInfoEAT(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT); +BOOL VMMDLL_WinGetThunkInfoEAT(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT); diff --git a/files/vmmpy.py b/files/vmmpy.py index 5a99b6c5..bde5b206 100644 --- a/files/vmmpy.py +++ b/files/vmmpy.py @@ -12,7 +12,7 @@ # (c) Ulf Frisk, 2018-2019 # Author: Ulf Frisk, pcileech@frizk.net # -# Header Version: 2.8 +# Header Version: 3.0 # import atexit @@ -136,7 +136,6 @@ def VmmPy_Initialize(args, is_printf = True, is_verbose = False, is_verbose_extr VMMPY_OPT_CORE_VERBOSE_EXTRA = 0x80000003 # RW VMMPY_OPT_CORE_VERBOSE_EXTRA_TLP = 0x80000004 # RW VMMPY_OPT_CORE_MAX_NATIVE_ADDRESS = 0x80000005 # R -VMMPY_OPT_CORE_MAX_NATIVE_IOSIZE = 0x80000006 # R VMMPY_OPT_CORE_SYSTEM = 0x80000007 # R VMMPY_OPT_CORE_MEMORYMODEL = 0x80000008 # R @@ -304,34 +303,79 @@ def VmmPy_PidGetFromName(process_name): -def VmmPy_ProcessGetMemoryMap(pid, is_identify_modules = False): - """Retrieve the memory map for a specific pid. +def VmmPy_ProcessGetPteMap(pid, is_identify_modules = False): + """Retrieve the pte memory map for a specific pid. Keyword arguments: - pid -- int: the process identifier (pid) when reading process virtual memory. + pid -- int: the process identifier (pid). is_identify_modules -- bool: (optional) identify module names (slow). - return -- list: of dict of memory map entries. + return -- list: of dict of PTE memory map entries. Example: - VmmPy_ProcessGetMemoryMap(4) --> [{'va': 2147352576, 'size': 4096, 'pages': 1, 'wow64': False, 'tag': '', 'flags-pte': 9223372036854775812, 'flags': 'srwx'}, ...] + VmmPy_ProcessGetMemoryMap(4) --> [{'va': 140715078701056, 'size': 8192, 'pages': 2, 'wow64': False, 'tag': 'ntdll.dll', 'flags-pte': 9223372036854775812, 'flags': '-r--'}, ...] """ - return VMMPYC_ProcessGetMemoryMap(pid, is_identify_modules) + return VMMPYC_ProcessGetPteMap(pid, is_identify_modules) + +def VmmPy_ProcessGetMemoryMap(pid, is_identify_modules = False): + """Deprecated - use VmmPy_ProcessGetPteMap instead!""" + return VMMPYC_ProcessGetPteMap(pid, is_identify_modules) -def VmmPy_ProcessGetMemoryMapEntry(pid, va, is_identify_modules = False): - """Retrieve a single memory map entry for a given pid and virtual address (va). +def VmmPy_ProcessGetVadMap(pid, is_identify_modules = False): + """Retrieve the virtual address descriptor (VAD) memory map for a specific pid. Keyword arguments: - pid -- int: the process identifier (pid) when reading process virtual memory. - va -- int: a virtual address inside the entry to retrieve. + pid -- int: the process identifier (pid). is_identify_modules -- bool: (optional) identify module names (slow). - return -- dict: of memory map entries. + return -- list: of dict of VAD memory map entries. + + Example: + VmmPy_ProcessGetMemoryMap(4) --> [{'start': 140715077140480, 'end': 140715079172095, 'subsection': 18446644053817718944, 'prototype': 18446663847518789648, 'prototype-len': 3968, 'mem_commit': False, 'commit_charge': 17, 'protection': '---wxc', 'type': 'Image', 'tag': '\\Windows\\System32\\ntdll.dll'}, ...] + """ + return VMMPYC_ProcessGetVadMap(pid, is_identify_modules) + + + +def VmmPy_ProcessGetHeapMap(pid): + """Retrieve information about heaps for a specific pid. + + Keyword arguments: + pid -- int: the process identifier (pid). + return -- list: of dict of heap entries. Example: - VmmPy_ProcessGetMemoryMapEntry(4, 0x7ffe0000) --> {'va': 2147352576, 'size': 4096, 'pages': 1, 'wow64': False, 'name': '', 'flags-pte': 9223372036854775812, 'flags': 'srwx'} + VmmPy_ProcessGetHeapMap(256) --> [{'va': 296288256, 'size': 16576512, 'size-uncommitted': 13893632, 'id': 64, 'primary': False}, ...] """ - return VMMPYC_ProcessGetMemoryMapEntry(pid, va, is_identify_modules) + return VMMPYC_ProcessGetHeapMap(pid) + + + +def VmmPy_ProcessGetThreadMap(pid): + """Retrieve information about threads for a specific pid. + + Keyword arguments: + pid -- int: the process identifier (pid). + return -- list: of dict of thread entries. + + Example: + VmmPy_ProcessGetThreadMap(4) --> [{'tid': 9920, 'pid': 4280, 'exitstatus': 0, 'state': 5, 'running': 0, 'priority': 9, 'basepriority': 8, 'va-ethread': 18446644053942476992, 'va-teb': 13279232, 'va-start': 140715077586608, 'va-stackbase': 50331648, 'va-stacklimit': 50274304, 'va-stackbase-kernel': 18446613807470415872, 'va-stacklimit-kernel': 18446613807470391296, 'time-create': 132162322866787797, 'time-exit': 0, 'time-create-str': '2019-10-22 15:38:06 UTC', 'time-exit-str': ' ***'}, ...] + """ + return VMMPYC_ProcessGetThreadMap(pid) + + + +def VmmPy_ProcessGetHandleMap(pid): + """Retrieve information about handles for a specific pid. + + Keyword arguments: + pid -- int: the process identifier (pid). + return -- list: of dict of handle entries. + + Example: + VmmPy_ProcessGetHandleMap(4) --> [{'va-object': 18446644053936528592, 'handle': 12268, 'access': 1180063, 'typeindex': 37, 'pid': 4280, 'pooltag': 1701603654, 'chandle': 1, 'cpointer': 1, 'va-object-creatinfo': 18446644053883285568, 'va-securitydescriptor': 0, 'tag': '\\Users\\User\\AppData\\Local\\Microsoft\\Windows\\Explorer\\thumbcache_256.db', 'type': 'File'}, ...] + """ + return VMMPYC_ProcessGetHandleMap(pid) @@ -339,11 +383,11 @@ def VmmPy_ProcessGetModuleMap(pid): """Retrieve the module map for a specific pid. Keyword arguments: - pid -- int: the process identifier (pid) when reading process virtual memory. + pid -- int: the process identifier (pid). return -- list: of dict of module map information entries. Example: - VmmPy_ProcessGetModuleMap(332) --> [{'va': 140718422491136, 'va-entry': 0, 'wow64': False, 'size': 1966080, 'name': 'ntdll.dll'}, ...] + VmmPy_ProcessGetModuleMap(332) --> [{'va': 140700185460736, 'va-entry': 140700186087664, 'wow64': False, 'size': 4599808, 'name': 'explorer.exe'}, ...] """ return VMMPYC_ProcessGetModuleMap(pid) @@ -353,12 +397,12 @@ def VmmPy_ProcessGetModuleFromName(pid, module_name): """Retrieve the module map for a specific pid and module name. Keyword arguments: - pid -- int: the process identifier (pid) when reading process virtual memory. + pid -- int: the process identifier (pid). module_name -- bool: name of the module to retrieve. return -- dict: of module information. Example: - VmmPy_ProcessGetModuleMap(332, "ntdll.dll") --> {'va': 140718422491136, 'va-entry': 0, 'wow64': False, 'size': 1966080, 'name': 'ntdll.dll'} + VmmPy_ProcessGetModuleMap(332, "ntdll.dll") --> {'va': 140700185460736, 'va-entry': 140700186087664, 'wow64': False, 'size': 4599808, 'name': 'explorer.exe'} """ return VMMPYC_ProcessGetModuleFromName(pid, module_name) @@ -372,7 +416,7 @@ def VmmPy_ProcessGetInformation(pid): return -- dict: of process information. Example: - VmmPy_ProcessGetInformation(332) --> {'pid': 8796, 'pa-dtb': 5798625280, 'pa-dtb-user': 6237978624, 'state': 0, 'tp-system': 2, 'usermode': True, 'name': 'cmd.exe', 'name-long': 'cmd.exe', 'wow64': False, 'va-entry': 140700131683072, 'va-eprocess': 18446635809067693440, 'va-peb': 708313505792, 'va-peb32': 0, 'path-kernel': '', 'path-user': '', 'cmdline': ''} + VmmPy_ProcessGetInformation(332) --> {'pid': 8796, 'pa-dtb': 5798625280, 'pa-dtb-user': 6237978624, 'state': 0, 'tp-system': 2, 'usermode': True, 'name': 'cmd.exe', 'name-long': 'cmd.exe', 'wow64': False, 'va-eprocess': 18446635809067693440, 'va-peb': 708313505792, 'va-peb32': 0, 'path-kernel': '', 'path-user': '', 'cmdline': ''} """ return VMMPYC_ProcessGetInformation(pid) diff --git a/files/vmmpy_example.py b/files/vmmpy_example.py index 5f395617..065d1429 100644 --- a/files/vmmpy_example.py +++ b/files/vmmpy_example.py @@ -132,6 +132,24 @@ def VmmPy_Example(dump_file_name): print("SUCCESS: VmmPy_ProcessListInformation()") print(result) + # PTE MEM MAP + print("--------------------------------------------------------------------") + print("Get the PTE memory map of 'explorer.exe' by walking the page table. ") + input("Press Enter to continue...") + print("CALL: VmmPy_ProcessGetPteMap()") + result = VmmPy_ProcessGetPteMap(pid, True) + print("SUCCESS: VmmPy_ProcessGetPteMap()") + print(result) + + # VAD MEM MAP + print("--------------------------------------------------------------------") + print("Get the VAD memory map of 'explorer.exe' by looking at VADs ") + input("Press Enter to continue...") + print("CALL: VmmPy_ProcessGetVadMap()") + result = VmmPy_ProcessGetVadMap(pid, True) + print("SUCCESS: VmmPy_ProcessGetVadMap()") + print(result) + # MODULE INFORMATION print("--------------------------------------------------------------------") print("Get module information about the explorer.exe module in the process.") @@ -142,22 +160,31 @@ def VmmPy_Example(dump_file_name): print(result) va = result['va'] - # MEM MAP + # HEAP MAP + print("--------------------------------------------------------------------") + print("Get the HEAP map of 'explorer.exe' ") + input("Press Enter to continue...") + print("CALL: VmmPy_ProcessGetHeapMap()") + result = VmmPy_ProcessGetHeapMap(pid) + print("SUCCESS: VmmPy_ProcessGetHeapMap()") + print(result) + + # THREAD MAP print("--------------------------------------------------------------------") - print("Get the memory map of 'explorer.exe' by walking the page table. ") + print("Get the THREAD map of 'explorer.exe' by walking ETHREAD list ") input("Press Enter to continue...") - print("CALL: VmmPy_ProcessGetMemoryMap()") - result = VmmPy_ProcessGetMemoryMap(pid, True) - print("SUCCESS: VmmPy_ProcessGetMemoryMap()") + print("CALL: VmmPy_ProcessGetThreadMap()") + result = VmmPy_ProcessGetThreadMap(pid) + print("SUCCESS: VmmPy_ProcessGetThreadMap()") print(result) - # MEM MAP ENTRY + # HANDLE MAP print("--------------------------------------------------------------------") - print("Get the PE base of 'explorer.exe' in the 'explorer.exe' process. ") + print("Get the HANDLE map of 'explorer.exe' ") input("Press Enter to continue...") - print("CALL: VmmPy_ProcessGetMemoryMapEntry()") - result = VmmPy_ProcessGetMemoryMapEntry(pid, va, True) - print("SUCCESS: VmmPy_ProcessGetMemoryMapEntry()") + print("CALL: VmmPy_ProcessGetHandleMap()") + result = VmmPy_ProcessGetHandleMap(pid) + print("SUCCESS: VmmPy_ProcessGetHandleMap()") print(result) # MEM VIRTUAL2PHYSICAL @@ -255,10 +282,10 @@ def VmmPy_Example(dump_file_name): # VFS READ print("--------------------------------------------------------------------") - print("Read from a file in the virtual file system (/pmem at offset 0x1000)") + print("Read from a file in the virtual file system (/memory.pmem at offset 0x1000)") input("Press Enter to continue...") print("CALL: VmmPy_VfsRead()") - result = VmmPy_UtilFillHexAscii(VmmPy_VfsRead('/pmem', 0x100, 0x1000)) + result = VmmPy_UtilFillHexAscii(VmmPy_VfsRead('/memory.pmem', 0x100, 0x1000)) print("SUCCESS: VmmPy_VfsRead()") print(result) diff --git a/m_vmemd/m_vmemd.c b/m_vmemd/m_vmemd.c index 0143a1d9..22b784bb 100644 --- a/m_vmemd/m_vmemd.c +++ b/m_vmemd/m_vmemd.c @@ -1,7 +1,7 @@ // m_vmemd.h : implementation related to the vmemd native plugin module for the // memory process file system. // -// (c) Ulf Frisk, 2018 +// (c) Ulf Frisk, 2018-2019 // Author: Ulf Frisk, pcileech@frizk.net // #include @@ -10,10 +10,182 @@ VMMDLL_MEMORYMODEL_TP g_VMemD_TpMemoryModel = VMMDLL_MEMORYMODEL_NA; -ULONG64 VMemD_GetBaseFromFileName(_In_ LPWSTR wsz) +#define UTIL_ASCIIFILENAME_ALLOW \ + "0000000000000000000000000000000011011111111111101111111111010100" \ + "1111111111111111111111111111011111111111111111111111111111110111" \ + "0000000000000000000000000000000000000000000000000000000000000000" \ + "0000000000000000000000000000000000000000000000000000000000000000" + +/* +* Utility function to retrieve base address and the type of entry from a file name. +* -- wsz +* -- return +*/ +_Success_(return) +BOOL VMemD_GetBaseAndTypeFromFileName(_In_ LPWSTR wsz, _Out_ PQWORD pva, _Out_ PBOOL pfVad) +{ + if((wcslen(wsz) < 15) || (wsz[0] != '0') || (wsz[1] != 'x')) { return FALSE; } + *pva = wcstoull(wsz, NULL, 16); + *pfVad = wcsstr(wsz, L".vvmem") ? TRUE : FALSE; + return TRUE; +} + +VOID VMemD_Util_FileNameW(_Out_writes_(64) LPWSTR wszOut, _In_ LPCWSTR wsz) +{ + WCHAR ch; + DWORD i = 0; + while(wsz[i]) { + if(wsz[i] == '\\') { + wsz += i + 1ULL; + i = 0; + continue; + } + if(i == 62) { + wsz += 1; + continue; + } + i++; + } + i = 0; + while((ch = wsz[i])) { + wszOut[i] = ((ch < 128) && (UTIL_ASCIIFILENAME_ALLOW[ch] == '0')) ? '_' : ch; + i++; + } + wszOut[i] = 0; +} + +/* +* Utility function to efficiently search through an ordered array of data with +* a comparator function. +*/ +PVOID VMemD_Util_qfind(_In_ PVOID pvFind, _In_ DWORD cMap, _In_ PVOID pvMap, _In_ DWORD cbEntry, _In_ int(*pfnCmp)(_In_ PVOID pvFind, _In_ PVOID pvEntry)) { - if((wcslen(wsz) < 15) || (wsz[0] != '0') || (wsz[1] != 'x')) { return (ULONG64)-1; } - return wcstoull(wsz, NULL, 16); + int f; + DWORD i, cbSearch, cbStep, cbMap; + PBYTE pbMap = pvMap; + if(!cMap || !cbEntry) { return NULL; } + for(i = 1; ((cMap - 1) >> i); i++); + cbMap = cMap * cbEntry; + cbSearch = cbEntry * min(1UL << (i - 1), cMap - 1); + cbStep = max(cbEntry, cbSearch >> 1); + while(cbStep >= cbEntry) { + f = pfnCmp(pvFind, pbMap + cbSearch); + if(f < 0) { + cbSearch -= cbStep; + } else if(f > 0) { + if(cbSearch + cbStep < cbMap) { + cbSearch += cbStep; + } + } else { + return pbMap + cbSearch; + } + cbStep = cbStep >> 1; + } + if(cbSearch < cbMap) { + if(!pfnCmp(pvFind, pbMap + cbSearch)) { + return pbMap + cbSearch; + } + if((cbSearch >= cbEntry) && !pfnCmp(pvFind, pbMap + cbSearch - cbEntry)) { + return pbMap + cbSearch - cbEntry; + } + } + return NULL; +} + +/* +* Comparator function for VMemD_Util_qfind to serach entries in PTEMAP. +*/ +int VMemD_ReadPte_CmpFind(_In_ QWORD vaFind, _In_ PVMMDLL_MAP_PTEENTRY pEntry) +{ + if(pEntry->vaBase > vaFind) { return -1; } + if(pEntry->vaBase < vaFind) { return 1; } + return 0; +} + +/* +* Comparator function for VMemD_Util_qfind to serach entries in VADMAP. +*/ +int VMemD_ReadVad_CmpFind(_In_ QWORD vaFind, _In_ PVMMDLL_MAP_VADENTRY pEntry) +{ + if(pEntry->vaStart > vaFind) { return -1; } + if(pEntry->vaStart < vaFind) { return 1; } + return 0; +} + +/* +* Read/Write virtual memory inside a memory map entry of PTE-type. +*/ +NTSTATUS VMemD_ReadWritePte(_In_ DWORD dwPID, _In_ QWORD vaBase, _In_ BOOL fRead, _Out_writes_bytes_(*pcbReadWrite) PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbReadWrite, _In_ ULONG64 cbOffset) +{ + NTSTATUS nt = VMMDLL_STATUS_FILE_INVALID; + BOOL result; + QWORD cbMax; + DWORD cbPteMap = 0; + PVMMDLL_MAP_PTE pPteMap = NULL; + PVMMDLL_MAP_PTEENTRY pe = NULL; + // read memory from "vmemd" directory file - "pte mapped" + *pcbReadWrite = 0; + result = + VMMDLL_ProcessMap_GetPte(dwPID, NULL, &cbPteMap, FALSE) && + (pPteMap = LocalAlloc(0, cbPteMap)) && + VMMDLL_ProcessMap_GetPte(dwPID, pPteMap, &cbPteMap, FALSE) && + (pe = VMemD_Util_qfind((PVOID)vaBase, pPteMap->cMap, pPteMap->pMap, sizeof(VMMDLL_MAP_PTEENTRY), (int(*)(PVOID, PVOID))VMemD_ReadPte_CmpFind)); + if(!result) { goto fail; } + if(pe->vaBase + (pe->cPages << 12) <= vaBase + cbOffset) { + nt = VMMDLL_STATUS_END_OF_FILE; + goto fail; + } + cbMax = min((pe->vaBase + (pe->cPages << 12)), (vaBase + cb + cbOffset)) - (vaBase + cbOffset); // min(entry_top_addr, request_top_addr) - request_start_addr + if(fRead) { + result = VMMDLL_MemReadEx(dwPID, vaBase + cbOffset, pb, (DWORD)min(cb, cbMax), NULL, VMMDLL_FLAG_ZEROPAD_ON_FAIL); + *pcbReadWrite = (DWORD)min(cb, cbMax); + nt = (result && *pcbReadWrite) ? VMMDLL_STATUS_SUCCESS : VMMDLL_STATUS_END_OF_FILE; + } else { + VMMDLL_MemWrite(dwPID, vaBase + cbOffset, pb, (DWORD)min(cb, cbMax)); + *pcbReadWrite = cb; + nt = VMMDLL_STATUS_SUCCESS; + } +fail: + LocalFree(pPteMap); + return nt; +} + +/* +* Read/Write virtual memory inside a memory map entry of VAD-type. +*/ +NTSTATUS VMemD_ReadWriteVad(_In_ DWORD dwPID, _In_ QWORD vaBase, _In_ BOOL fRead, _Out_writes_bytes_(*pcbReadWrite) PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbReadWrite, _In_ ULONG64 cbOffset) +{ + NTSTATUS nt = VMMDLL_STATUS_FILE_INVALID; + BOOL result; + QWORD cbMax; + DWORD cbVadMap = 0; + PVMMDLL_MAP_VAD pVadMap = NULL; + PVMMDLL_MAP_VADENTRY pe = NULL; + // read memory from "vmemd" directory file - "pte mapped" + *pcbReadWrite = 0; + result = + VMMDLL_ProcessMap_GetVad(dwPID, NULL, &cbVadMap, FALSE) && + (pVadMap = LocalAlloc(0, cbVadMap)) && + VMMDLL_ProcessMap_GetVad(dwPID, pVadMap, &cbVadMap, FALSE) && + (pe = VMemD_Util_qfind((PVOID)vaBase, pVadMap->cMap, pVadMap->pMap, sizeof(VMMDLL_MAP_VADENTRY), (int(*)(PVOID, PVOID))VMemD_ReadVad_CmpFind)); + if(!result) { goto fail; } + if(pe->vaEnd <= vaBase + cbOffset) { + nt = VMMDLL_STATUS_END_OF_FILE; + goto fail; + } + cbMax = min(pe->vaEnd + 1, (vaBase + cb + cbOffset)) - (vaBase + cbOffset); // min(entry_top_addr, request_top_addr) - request_start_addr + if(fRead) { + result = VMMDLL_MemReadEx(dwPID, vaBase + cbOffset, pb, (DWORD)min(cb, cbMax), NULL, VMMDLL_FLAG_ZEROPAD_ON_FAIL); + *pcbReadWrite = (DWORD)min(cb, cbMax); + nt = (result && *pcbReadWrite) ? VMMDLL_STATUS_SUCCESS : VMMDLL_STATUS_END_OF_FILE; + } else { + VMMDLL_MemWrite(dwPID, vaBase + cbOffset, pb, (DWORD)min(cb, cbMax)); + *pcbReadWrite = cb; + nt = VMMDLL_STATUS_SUCCESS; + } +fail: + LocalFree(pVadMap); + return nt; } /* @@ -28,19 +200,12 @@ ULONG64 VMemD_GetBaseFromFileName(_In_ LPWSTR wsz) */ NTSTATUS VMemD_Read(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _Out_ LPVOID pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ ULONG64 cbOffset) { - BOOL result; - ULONG64 cbMax, vaBase; - VMMDLL_MEMMAP_ENTRY entry; - // read memory from "vmemd" directory file - vaBase = VMemD_GetBaseFromFileName(ctx->wszPath); - if(vaBase & 0xfff) { return VMMDLL_STATUS_FILE_INVALID; } - result = VMMDLL_ProcessGetMemoryMapEntry(ctx->dwPID, &entry, vaBase, FALSE); - if(!result) { return VMMDLL_STATUS_FILE_INVALID; } - *pcbRead = 0; - if(entry.AddrBase + (entry.cPages << 12) <= vaBase + cbOffset) { return VMMDLL_STATUS_END_OF_FILE; } - cbMax = min((entry.AddrBase + (entry.cPages << 12)), (vaBase + cb + cbOffset)) - (vaBase - cbOffset); // min(entry_top_addr, request_top_addr) - request_start_addr - result = VMMDLL_MemReadEx(ctx->dwPID, vaBase + cbOffset, pb, (DWORD)min(cb, cbMax), pcbRead, 0); - return (result && *pcbRead) ? VMMDLL_STATUS_SUCCESS : VMMDLL_STATUS_END_OF_FILE; + BOOL fVad; + QWORD vaBase; + if(!VMemD_GetBaseAndTypeFromFileName(ctx->wszPath, &vaBase, &fVad)) { return VMMDLL_STATUS_FILE_INVALID; } + return fVad ? + VMemD_ReadWriteVad(ctx->dwPID, vaBase, TRUE, pb, cb, pcbRead, cbOffset) : + VMemD_ReadWritePte(ctx->dwPID, vaBase, TRUE, pb, cb, pcbRead, cbOffset); } /* @@ -53,22 +218,14 @@ NTSTATUS VMemD_Read(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _Out_ LPVOID pb, _In_ DWORD * -- cbOffset * -- return */ -NTSTATUS VMemD_Write(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ LPVOID pb, _In_ DWORD cb, _Out_ PDWORD pcbWrite, _In_ ULONG64 cbOffset) +NTSTATUS VMemD_WritePte(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ LPVOID pb, _In_ DWORD cb, _Out_ PDWORD pcbWrite, _In_ ULONG64 cbOffset) { - BOOL result; - ULONG64 cbMax, vaBase; - VMMDLL_MEMMAP_ENTRY entry; - // write memory from "vmemd" directory file - vaBase = VMemD_GetBaseFromFileName(ctx->wszPath); - if(vaBase & 0xfff) { return VMMDLL_STATUS_FILE_INVALID; } - result = VMMDLL_ProcessGetMemoryMapEntry(ctx->dwPID, &entry, vaBase, FALSE); - if(!result) { return VMMDLL_STATUS_FILE_INVALID; } - *pcbWrite = 0; - if(entry.AddrBase + (entry.cPages << 12) <= vaBase + cbOffset) { return VMMDLL_STATUS_END_OF_FILE; } - cbMax = min((entry.AddrBase + (entry.cPages << 12)), (vaBase + cb + cbOffset)) - (vaBase - cbOffset); // min(entry_top_addr, request_top_addr) - request_start_addr - VMMDLL_MemWrite(ctx->dwPID, vaBase + cbOffset, pb, (DWORD)min(cb, cbMax)); - *pcbWrite = cb; - return VMMDLL_STATUS_SUCCESS; + BOOL fVad; + QWORD vaBase; + if(!VMemD_GetBaseAndTypeFromFileName(ctx->wszPath, &vaBase, &fVad)) { return VMMDLL_STATUS_FILE_INVALID; } + return fVad ? + VMemD_ReadWriteVad(ctx->dwPID, vaBase, FALSE, pb, cb, pcbWrite, cbOffset) : + VMemD_ReadWritePte(ctx->dwPID, vaBase, FALSE, pb, cb, pcbWrite, cbOffset); } /* @@ -81,49 +238,81 @@ NTSTATUS VMemD_Write(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ LPVOID pb, _In_ DWORD */ BOOL VMemD_List(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _Inout_ PHANDLE pFileList) { - BOOL result; - DWORD i; - ULONG64 cEntries = 0; - CHAR szBufferFileName[MAX_PATH]; - PVMMDLL_MEMMAP_ENTRY pMemMap; - if(ctx->wszPath[0]) { - // only list in module root directory. - // not root directory == error for this module. - return FALSE; - } - // populate memory map directory - result = VMMDLL_ProcessGetMemoryMap(ctx->dwPID, NULL, &cEntries, FALSE); - if(!result) { return FALSE; } - pMemMap = (PVMMDLL_MEMMAP_ENTRY)LocalAlloc(0, cEntries * sizeof(VMMDLL_MEMMAP_ENTRY)); - if(!pMemMap) { return FALSE; } - result = VMMDLL_ProcessGetMemoryMap(ctx->dwPID, pMemMap, &cEntries, TRUE); - if(!result) { - LocalFree(pMemMap); - return FALSE; + BOOL f, fResult = FALSE; + DWORD iVad, iPte, cbPteMap = 0, cbVadMap = 0; + PVMMDLL_MAP_PTE pPteMap = NULL; + PVMMDLL_MAP_PTEENTRY pPte = NULL; + PVMMDLL_MAP_VAD pVadMap = NULL; + PVMMDLL_MAP_VADENTRY pVad = NULL; + WCHAR wszBufferFileName[MAX_PATH] = { 0 }, wszInfo[64] = { 0 }; + // Retrieve mandatory memory map based on hardware page tables. + f = VMMDLL_ProcessMap_GetPte(ctx->dwPID, NULL, &cbPteMap, TRUE) && + (pPteMap = LocalAlloc(0, cbPteMap)); + f = f && VMMDLL_ProcessMap_GetPte(ctx->dwPID, pPteMap, &cbPteMap, TRUE); + if(!f) { goto fail; } + // Retrieve optional memory map based on virtual address descriptors (VADs). + f = VMMDLL_ProcessMap_GetVad(ctx->dwPID, NULL, &cbVadMap, TRUE) && + (pVadMap = LocalAlloc(0, cbVadMap)) && + VMMDLL_ProcessMap_GetVad(ctx->dwPID, pVadMap, &cbVadMap, TRUE); + // Display VadMap entries in the file system (if any) + for(iVad = 0; (f && (iVad < pVadMap->cMap)); iVad++) { + pVad = pVadMap->pMap + iVad; + VMemD_Util_FileNameW(wszInfo, pVad->wszText); + if(g_VMemD_TpMemoryModel == VMMDLL_MEMORYMODEL_X64) { + swprintf_s( + wszBufferFileName, + MAX_PATH - 1, + L"0x%016llx%s%s.vvmem", + pVad->vaStart, + pVad->cwszText ? L"-" : L"", + wszInfo + ); + } else if((g_VMemD_TpMemoryModel == VMMDLL_MEMORYMODEL_X86) || (g_VMemD_TpMemoryModel == VMMDLL_MEMORYMODEL_X86PAE)) { + swprintf_s( + wszBufferFileName, + MAX_PATH - 1, + L"0x%08lx%s%s.vvmem", + (DWORD)pVad->vaStart, + pVad->cwszText ? L"-" : L"", + wszInfo + ); + } + VMMDLL_VfsList_AddFileEx(pFileList, NULL, wszBufferFileName, pVad->vaEnd + 1 - pVad->vaStart, NULL); } - for(i = 0; i < cEntries; i++) { + // Display PteMap entries in the file system unless already part of Vad + for(iPte = 0, iVad = 0; iPte < pPteMap->cMap; iPte++) { + pPte = pPteMap->pMap + iPte; + if(pVadMap) { + while((iVad < pVadMap->cMap) && (pVadMap->pMap[iVad].vaEnd < pPte->vaBase) && ++iVad); + if((iVad < pVadMap->cMap) && (pVadMap->pMap[iVad].vaStart <= pPte->vaBase) && (pVadMap->pMap[iVad].vaEnd >= pPte->vaBase)) { continue; } + } + VMemD_Util_FileNameW(wszInfo, pPte->wszText); if(g_VMemD_TpMemoryModel == VMMDLL_MEMORYMODEL_X64) { - sprintf_s( - szBufferFileName, + swprintf_s( + wszBufferFileName, MAX_PATH - 1, - "0x%016llx%s%s.vmem", - pMemMap[i].AddrBase, - pMemMap[i].szTag[0] ? "-" : "", - pMemMap[i].szTag[0] ? pMemMap[i].szTag : ""); + L"0x%016llx%s%s.vmem", + pPte->vaBase, + pPte->cwszText ? L"-" : L"", + wszInfo + ); } else if((g_VMemD_TpMemoryModel == VMMDLL_MEMORYMODEL_X86) || (g_VMemD_TpMemoryModel == VMMDLL_MEMORYMODEL_X86PAE)) { - sprintf_s( - szBufferFileName, + swprintf_s( + wszBufferFileName, MAX_PATH - 1, - "0x%08x%s%s.vmem", - (DWORD)pMemMap[i].AddrBase, - pMemMap[i].szTag[0] ? "-" : "", - pMemMap[i].szTag[0] ? pMemMap[i].szTag : ""); + L"0x%08lx%s%s.vmem", + (DWORD)pPte->vaBase, + pPte->cwszText ? L"-" : L"", + wszInfo + ); } - szBufferFileName[MAX_PATH - 1] = 0; - VMMDLL_VfsList_AddFile(pFileList, szBufferFileName, (pMemMap[i].cPages << 12)); + VMMDLL_VfsList_AddFileEx(pFileList, NULL, wszBufferFileName, pPte->cPages << 12, NULL); } - LocalFree(pMemMap); - return TRUE; + fResult = TRUE; +fail: + LocalFree(pPteMap); + LocalFree(pVadMap); + return fResult; } /* @@ -147,6 +336,6 @@ VOID InitializeVmmPlugin(_In_ PVMMDLL_PLUGIN_REGINFO pRegInfo) pRegInfo->reg_info.fProcessModule = TRUE; // module shows in process directory. pRegInfo->reg_fn.pfnList = VMemD_List; // List function supported. pRegInfo->reg_fn.pfnRead = VMemD_Read; // Read function supported. - pRegInfo->reg_fn.pfnWrite = VMemD_Write; // Write function supported. + pRegInfo->reg_fn.pfnWrite = VMemD_WritePte; // Write function supported. pRegInfo->pfnPluginManager_Register(pRegInfo); // Register with the plugin maanger. } diff --git a/m_vmemd/version.h b/m_vmemd/version.h index 71272b5e..642806de 100644 --- a/m_vmemd/version.h +++ b/m_vmemd/version.h @@ -1,12 +1,12 @@ #define STRINGIZE2(s) #s #define STRINGIZE(s) STRINGIZE2(s) -#define VERSION_MAJOR 2 -#define VERSION_MINOR 10 -#define VERSION_REVISION 2 -#define VERSION_BUILD 2 +#define VERSION_MAJOR 3 +#define VERSION_MINOR 0 +#define VERSION_REVISION 0 +#define VERSION_BUILD 3 -#define VER_FILE_DESCRIPTION_STR "The Memory Process File System : Plugin vmemd" +#define VER_FILE_DESCRIPTION_STR "MemProcFS : Plugin vmemd" #define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD #define VER_FILE_VERSION_STR STRINGIZE(VERSION_MAJOR) \ "." STRINGIZE(VERSION_MINOR) \ diff --git a/m_vmemd/vmmdll.h b/m_vmemd/vmmdll.h index 8a6b6a38..87f3f191 100644 --- a/m_vmemd/vmmdll.h +++ b/m_vmemd/vmmdll.h @@ -4,7 +4,7 @@ // (c) Ulf Frisk, 2018-2019 // Author: Ulf Frisk, pcileech@frizk.net // -// Header Version: 2.10.2 +// Header Version: 3.0 // #include @@ -23,11 +23,11 @@ extern "C" { /* * Initialize VMM.DLL with command line parameters. For a more detailed info -* about the parameters please see github wiki for Memory Process File System -* and LeechCore. THIS IS THE PREFERED WAY OF INITIALIZING VMM.DLL +* about the parameters please see github wiki for MemProcFS and LeechCore. +* NB! LeechCore initialization parameters are _also_ valid to this function. * Important parameters are: -* -printf = show printf style outputs) -* -v -vv -vvv = extra verbosity levels) +* -printf = show printf style outputs. +* -v -vv -vvv = extra verbosity levels. * -device = device as on format for LeechCore - please see leechcore.h or * Github documentation for additional information. Some values * are: , fpga, usb3380, hvsavedstate, totalmeltdown, pmem @@ -35,8 +35,19 @@ extern "C" { * documentation for additional information. * -norefresh = disable background refreshes (even if backing memory is * volatile memory). -* -symbolserverdisable = disable symbol server until user change. This -* parameter will take precedence over registry settings. +* -symbolserverdisable = disable symbol server until user change. +* This parameter will take precedence over registry settings. +* -pagefile[0-9] = page file(s) to use in addition to physical memory. +* Normally pagefile.sys have index 0 and swapfile.sys index 1. +* Page files are in constant flux - do not use if time diff +* between memory dump and page files are more than few minutes. +* Example: 'pagefile0 swapfile.sys' +* -waitinitialize = Wait for initialization to complete before returning. +* Normal use is that some initialization is done asynchronously +* and may not be completed when initialization call is completed. +* This includes virtual memory compression, registry and more. +* Example: '-waitinitialize' +* * -- argc * -- argv * -- return = success/fail @@ -78,9 +89,9 @@ VOID VMMDLL_MemFree(_Frees_ptr_opt_ PVOID pvMem); //----------------------------------------------------------------------------- /* -* Options used together with the functions: VMMDLL_GetOption & VMMDLL_SetOption +* Options used together with the functions: VMMDLL_ConfigGet & VMMDLL_ConfigSet * Options are defined with either: VMMDLL_OPT_* in this header file or as -* MEMDEVICE_OPT_* in memdevice.h +* LEECHCORE_OPT_* in leechcore.h * For more detailed information check the sources for individual device types. */ #define VMMDLL_OPT_CORE_PRINTF_ENABLE 0x80000001 // RW @@ -349,17 +360,17 @@ typedef struct tdVMMDLL_PLUGIN_REGINFO { #define VMMDLL_FLAG_ZEROPAD_ON_FAIL 0x0002 // zero pad failed physical memory reads and report success if read within range of physical memory. #define VMMDLL_FLAG_FORCECACHE_READ 0x0008 // force use of cache - fail non-cached pages - only valid for reads, invalid with VMM_FLAG_NOCACHE/VMM_FLAG_ZEROPAD_ON_FAIL. #define VMMDLL_FLAG_NOPAGING 0x0010 // do not try to retrieve memory from paged out memory from pagefile/compressed (even if possible) +#define VMMDLL_FLAG_NOPAGING_IO 0x0020 // do not try to retrieve memory from paged out memory if read would incur additional I/O (even if possible). /* * Read memory in various non-contigious locations specified by the pointers to -* the items in the ppDMAs array. Result for each unit of work will be given +* the items in the ppMEMs array. Result for each unit of work will be given * individually. No upper limit of number of items to read, but no performance * boost will be given if above hardware limit. Max size of each unit of work is * one 4k page (4096 bytes). * -- dwPID - PID of target process, (DWORD)-1 to read physical memory. * -- ppMEMs = array of scatter read headers. -* -- cpMEMs = count of ppDMAs. -* -- pcpDMAsRead = optional count of number of successfully read ppDMAs. +* -- cpMEMs = count of ppMEMs. * -- flags = optional flags as given by VMMDLL_FLAG_* * -- return = the number of successfully read items. */ @@ -384,7 +395,7 @@ BOOL VMMDLL_MemReadPage(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Inout_bytecount_(40 * -- return = success/fail (depending if all requested bytes are read or not). */ _Success_(return) -BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWORD cb); +BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb); /* * Read a contigious amount of memory and report the number of bytes read in pcbRead. @@ -398,7 +409,7 @@ BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWO * read - it's recommended to verify pcbReadOpt parameter. */ _Success_(return) -BOOL VMMDLL_MemReadEx(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWORD cb, _Out_opt_ PDWORD pcbReadOpt, _In_ ULONG64 flags); +BOOL VMMDLL_MemReadEx(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb, _Out_opt_ PDWORD pcbReadOpt, _In_ ULONG64 flags); /* * Prefetch a number of addresses (specified in the pA array) into the memory @@ -426,7 +437,7 @@ BOOL VMMDLL_MemPrefetchPages(_In_ DWORD dwPID, _In_reads_(cPrefetchAddresses) PU * -- return = TRUE on success, FALSE on partial or zero write. */ _Success_(return) -BOOL VMMDLL_MemWrite(_In_ DWORD dwPID, _In_ ULONG64 qwA, _In_ PBYTE pb, _In_ DWORD cb); +BOOL VMMDLL_MemWrite(_In_ DWORD dwPID, _In_ ULONG64 qwA, _In_reads_(cb) PBYTE pb, _In_ DWORD cb); /* * Translate a virtual address to a physical address by walking the page tables @@ -442,106 +453,286 @@ BOOL VMMDLL_MemVirt2Phys(_In_ DWORD dwPID, _In_ ULONG64 qwVA, _Out_ PULONG64 pqw //----------------------------------------------------------------------------- -// VMM PROCESS FUNCTIONALITY BELOW: -// Functionality below is mostly relating to Windows processes. +// VMM PROCESS MAP FUNCTIONALITY BELOW: +// Functionality for retrieving process related collections of items such as +// page table map (PTE), virtual address descriptor map (VAD), loaded modules, +// heaps and threads. //----------------------------------------------------------------------------- -/* -* Retrieve an active process given it's name. Please note that if multiple -* processes with the same name exists only one will be returned. If required to -* parse all processes with the same name please iterate over the PID list by -* calling VMMDLL_PidList together with VMMDLL_ProcessGetInformation. -* -- szProcName = process name case insensitive. -* -- pdwPID = pointer that will receive PID on success. -* -- return -*/ -_Success_(return) -BOOL VMMDLL_PidGetFromName(_In_ LPSTR szProcName, _Out_ PDWORD pdwPID); - -/* -* List the PIDs in the system. -* -- pPIDs = DWORD array of at least number of PIDs in system, or NULL. -* -- pcPIDs = size of (in number of DWORDs) pPIDs array on entry, number of PIDs in system on exit. -* -- return = success/fail. -*/ -_Success_(return) -BOOL VMMDLL_PidList(_Out_opt_ PDWORD pPIDs, _Inout_ PULONG64 pcPIDs); +#define VMMDLL_MAP_PTE_VERSION 1 +#define VMMDLL_MAP_VAD_VERSION 1 +#define VMMDLL_MAP_MODULE_VERSION 1 +#define VMMDLL_MAP_HEAP_VERSION 1 +#define VMMDLL_MAP_THREAD_VERSION 1 +#define VMMDLL_MAP_HANDLE_VERSION 1 -// flags to check for existence in the fPage field of PCILEECH_VMM_MEMMAP_ENTRY +// flags to check for existence in the fPage field of VMMDLL_MAP_PTEENTRY #define VMMDLL_MEMMAP_FLAG_PAGE_W 0x0000000000000002 #define VMMDLL_MEMMAP_FLAG_PAGE_NS 0x0000000000000004 #define VMMDLL_MEMMAP_FLAG_PAGE_NX 0x8000000000000000 #define VMMDLL_MEMMAP_FLAG_PAGE_MASK 0x8000000000000006 -typedef struct tdVMMDLL_MEMMAP_ENTRY { - ULONG64 AddrBase; - ULONG64 cPages; - ULONG64 fPage; +typedef struct tdVMMDLL_MAP_PTEENTRY { + QWORD vaBase; + QWORD cPages; + QWORD fPage; BOOL fWoW64; - CHAR szTag[32]; -} VMMDLL_MEMMAP_ENTRY, *PVMMDLL_MEMMAP_ENTRY; + DWORD cwszText; + LPWSTR wszText; + DWORD _Reserved1[2]; +} VMMDLL_MAP_PTEENTRY, *PVMMDLL_MAP_PTEENTRY; + +typedef struct tdVMMDLL_MAP_VADENTRY { + QWORD vaStart; + QWORD vaEnd; + QWORD vaVad; + // DWORD 0 + DWORD VadType : 3; // Pos 0 + DWORD Protection : 5; // Pos 3 + DWORD fImage : 1; // Pos 8 + DWORD fFile : 1; // Pos 9 + DWORD fPageFile : 1; // Pos 10 + DWORD fPrivateMemory : 1; // Pos 11 + DWORD fTeb : 1; // Pos 12 + DWORD fStack : 1; // Pos 13 + DWORD fSpare : 2; // Pos 14 + DWORD HeapNum : 7; // Pos 16 + DWORD fHeap : 1; // Pos 23 + DWORD cwszDescription : 8; // Pos 24 + // DWORD 1 + DWORD CommitCharge : 31; // Pos 0 + DWORD MemCommit : 1; // Pos 31 + DWORD u2; + DWORD cbPrototypePte; + QWORD vaPrototypePte; + QWORD vaSubsection; + LPWSTR wszText; // optional LPWSTR pointed into VMMDLL_MAP_VAD.wszMultiText + DWORD cwszText; // WCHAR count not including terminating null + DWORD _Reserved1; +} VMMDLL_MAP_VADENTRY, *PVMMDLL_MAP_VADENTRY; + +typedef struct tdVMMDLL_MAP_MODULEENTRY { + QWORD vaBase; + QWORD vaEntry; + DWORD cbImageSize; + BOOL fWoW64; + LPWSTR wszText; + DWORD cwszText; // wchar count not including terminating null + DWORD _Reserved1[7]; +} VMMDLL_MAP_MODULEENTRY, *PVMMDLL_MAP_MODULEENTRY; + +typedef struct tdVMMDLL_MAP_HEAPENTRY { + QWORD vaHeapSegment; + DWORD cPages; + DWORD cPagesUnCommitted : 24; + DWORD HeapId : 7; + DWORD fPrimary : 1; +} VMMDLL_MAP_HEAPENTRY, *PVMMDLL_MAP_HEAPENTRY; + +typedef struct tdVMMDLL_MAP_THREADENTRY { + DWORD dwTID; + DWORD dwPID; + DWORD dwExitStatus; + UCHAR bState; + UCHAR bRunning; + UCHAR bPriority; + UCHAR bBasePriority; + QWORD vaETHREAD; + QWORD vaTeb; + QWORD ftCreateTime; + QWORD ftExitTime; + QWORD vaStartAddress; + QWORD vaStackBaseUser; + QWORD vaStackLimitUser; + QWORD vaStackBaseKernel; + QWORD vaStackLimitKernel; + DWORD _FutureUse[10]; +} VMMDLL_MAP_THREADENTRY, *PVMMDLL_MAP_THREADENTRY; + +typedef struct tdVMMDLL_MAP_HANDLEENTRY { + QWORD vaObject; + DWORD dwHandle; + DWORD dwGrantedAccess : 24; + DWORD iType : 8; + QWORD qwHandleCount; + QWORD qwPointerCount; + QWORD vaObjectCreateInfo; + QWORD vaSecurityDescriptor; + LPWSTR wszText; + DWORD cwszText; + DWORD dwPID; + DWORD dwPoolTag; + DWORD _FutureUse[4]; + DWORD cwszType; + LPWSTR wszType; +} VMMDLL_MAP_HANDLEENTRY, *PVMMDLL_MAP_HANDLEENTRY; + +typedef struct tdVMMDLL_MAP_PTE { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // NULL or multi-wstr pointed into by VMMDLL_MAP_VADENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_PTEENTRY pMap[]; // map entries. +} VMMDLL_MAP_PTE, *PVMMDLL_MAP_PTE; + +typedef struct tdVMMDLL_MAP_VAD { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // NULL or multi-wstr pointed into by VMMDLL_MAP_VADENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_VADENTRY pMap[]; // map entries. +} VMMDLL_MAP_VAD, *PVMMDLL_MAP_VAD; + +typedef struct tdVMMDLL_MAP_MODULE { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // multi-wstr pointed into by VMMDLL_MAP_MODULEENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_MODULEENTRY pMap[]; // map entries. +} VMMDLL_MAP_MODULE, *PVMMDLL_MAP_MODULE; + +typedef struct tdVMMDLL_MAP_HEAP { + DWORD dwVersion; + DWORD _Reserved1[8]; + DWORD cMap; // # map entries. + VMMDLL_MAP_HEAPENTRY pMap[]; // map entries. +} VMMDLL_MAP_HEAP, *PVMMDLL_MAP_HEAP; + +typedef struct tdVMMDLL_MAP_THREAD { + DWORD dwVersion; + DWORD _Reserved[8]; + DWORD cMap; // # map entries. + VMMDLL_MAP_THREADENTRY pMap[]; // map entries. +} VMMDLL_MAP_THREAD, *PVMMDLL_MAP_THREAD; + +typedef struct tdVMMDLL_MAP_HANDLE { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // multi-wstr pointed into by VMMDLL_MAP_HANDLEENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_HANDLEENTRY pMap[]; // map entries. +} VMMDLL_MAP_HANDLE, *PVMMDLL_MAP_HANDLE; /* -* Retrieve memory map entries from the specified process. Memory map entries -* are copied into the user supplied buffer that must be at least of size: -* sizeof(VMMDLL_MEMMAP_ENTRY)*pcMemMapEntries bytes. -* If the pMemMapEntries is set to NULL the number of memory map entries will be -* given in the pcMemMapEntries parameter. +* Retrieve the memory map entries based on hardware page tables (PTE) for the +* process. If pPteMap is set to NULL the number of bytes required will be +* returned in parameter pcbPteMap. +* Entries returned are sorted on VMMDLL_MAP_PTEENTRY.vaBase * -- dwPID -* -- pMemMapEntries = buffer of minimum length sizeof(VMMDLL_MEMMAP_ENTRY)*pcMemMapEntries, or NULL. -* -- pcMemMapEntries = pointer to number of memory map entries. +* -- pPteMap = buffer of minimum byte length *pcbPteMap or NULL. +* -- pcbPteMap = pointer to byte count of pPteMap buffer. * -- fIdentifyModules = try identify modules as well (= slower) * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetMemoryMap(_In_ DWORD dwPID, _Out_opt_ PVMMDLL_MEMMAP_ENTRY pMemMapEntries, _Inout_ PULONG64 pcMemMapEntries, _In_ BOOL fIdentifyModules); +BOOL VMMDLL_ProcessMap_GetPte(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbPteMap) PVMMDLL_MAP_PTE pPteMap, _Inout_ PDWORD pcbPteMap, _In_ BOOL fIdentifyModules); /* -* Retrieve a single memory map entry given a virtual address within that entrys -* range. +* Retrieve memory map entries based on virtual address descriptor (VAD) for +* the process. If pVadMap is set to NULL the number of bytes required +* will be returned in parameter pcbVadMap. +* Entries returned are sorted on VMMDLL_MAP_VADENTRY.vaStart * -- dwPID -* -- pMemMapEntry -* -- va = virtual address in the memory map entry to retrieve. +* -- pVadMap = buffer of minimum byte length *pcbVadMap or NULL. +* -- pcbVadMap = pointer to byte count of pVadMap buffer. * -- fIdentifyModules = try identify modules as well (= slower) * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetMemoryMapEntry(_In_ DWORD dwPID, _Out_ PVMMDLL_MEMMAP_ENTRY pMemMapEntry, _In_ ULONG64 va, _In_ BOOL fIdentifyModules); - -typedef struct tdVMMDLL_MODULEMAP_ENTRY { - ULONG64 BaseAddress; - ULONG64 EntryPoint; - DWORD SizeOfImage; - BOOL fWoW64; - CHAR szName[32]; -} VMMDLL_MODULEMAP_ENTRY, *PVMMDLL_MODULEMAP_ENTRY; +BOOL VMMDLL_ProcessMap_GetVad(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbVadMap) PVMMDLL_MAP_VAD pVadMap, _Inout_ PDWORD pcbVadMap, _In_ BOOL fIdentifyModules); /* -* Retrieve the module entries from the specified process. The module entries -* are copied into the user supplied buffer that must be at least of size: -* sizeof(VMMDLL_MODULEMAP_ENTRY)*pcModuleEntries bytes long. If the -* pcModuleEntries is set to NULL the number of module entries will be given -* in the pcModuleEntries parameter. +* Retrieve the modules (.dlls) for the specified process. If pModuleMap is set +* to NULL the number of bytes required will be returned in parameter pcbModuleMap. * -- dwPID -* -- pModuleEntries = buffer of minimum length sizeof(VMMDLL_MODULEMAP_ENTRY)*pcModuleEntries, or NULL. -* -- pcModuleEntries = pointer to number of memory map entries. +* -- pModuleMap = buffer of minimum byte length *pcbModuleMap or NULL. +* -- pcbModuleMap = pointer to byte count of pModuleMap buffer. * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetModuleMap(_In_ DWORD dwPID, _Out_opt_ PVMMDLL_MODULEMAP_ENTRY pModuleEntries, _Inout_ PULONG64 pcModuleEntries); +BOOL VMMDLL_ProcessMap_GetModule(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbModuleMap) PVMMDLL_MAP_MODULE pModuleMap, _Inout_ PDWORD pcbModuleMap); /* -* Retrieve a module (.exe or .dll or similar) given a module name. +* Retrieve a module map entry (.exe / .dll) given a process and module name. +* NB! PVMMDLL_MAP_MODULEENTRY->wszText will not be set by this function. +* If module name is required use VMMDLL_ProcessMap_GetModule(). * -- dwPID * -- szModuleName -* -- pModuleEntry +* -- pModuleMapEntry * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetModuleFromName(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _Out_ PVMMDLL_MODULEMAP_ENTRY pModuleEntry); +BOOL VMMDLL_ProcessMap_GetModuleFromName(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _Out_ PVMMDLL_MAP_MODULEENTRY pModuleMapEntry); + +/* +* Retrieve the heaps for the specified process. If pHeapMap is set to NULL +* the number of bytes required will be returned in parameter pcbHeapMap. +* -- dwPID +* -- pHeapMap = buffer of minimum byte length *pcbHeapMap or NULL. +* -- pcbHeapMap = pointer to byte count of pHeapMap buffer. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_ProcessMap_GetHeap(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbHeapMap) PVMMDLL_MAP_HEAP pHeapMap, _Inout_ PDWORD pcbHeapMap); + +/* +* Retrieve the threads for the specified process. If pThreadMap is set to NULL +* the number of bytes required will be returned in parameter pcbThreadMap. +* Entries returned are sorted on VMMDLL_MAP_THREADENTRY.dwTID +* -- dwPID +* -- pThreadMap = buffer of minimum byte length *pcbThreadMap or NULL. +* -- pcbThreadMap = pointer to byte count of pThreadMap buffer. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_ProcessMap_GetThread(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbThreadMap) PVMMDLL_MAP_THREAD pThreadMap, _Inout_ PDWORD pcbThreadMap); + +/* +* Retrieve the handles for the specified process. If pHandleMap is set to NULL +* the number of bytes required will be returned in parameter pcbHandleMap. +* Entries returned are sorted on VMMDLL_MAP_HANDLEENTRY.dwHandle +* -- dwPID +* -- pHandleMap = buffer of minimum byte length *pcbHandleMap or NULL. +* -- pcbHandleMap = pointer to byte count of pHandleMap buffer. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_ProcessMap_GetHandle(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbHandleMap) PVMMDLL_MAP_HANDLE pHandleMap, _Inout_ PDWORD pcbHandleMap); + + + +//----------------------------------------------------------------------------- +// VMM PROCESS FUNCTIONALITY BELOW: +// Functionality below is mostly relating to Windows processes. +//----------------------------------------------------------------------------- + +/* +* Retrieve an active process given it's name. Please note that if multiple +* processes with the same name exists only one will be returned. If required to +* parse all processes with the same name please iterate over the PID list by +* calling VMMDLL_PidList together with VMMDLL_ProcessGetInformation. +* -- szProcName = process name case insensitive. +* -- pdwPID = pointer that will receive PID on success. +* -- return +*/ +_Success_(return) +BOOL VMMDLL_PidGetFromName(_In_ LPSTR szProcName, _Out_ PDWORD pdwPID); + +/* +* List the PIDs in the system. +* -- pPIDs = DWORD array of at least number of PIDs in system, or NULL. +* -- pcPIDs = size of (in number of DWORDs) pPIDs array on entry, number of PIDs in system on exit. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_PidList(_Out_writes_opt_(*pcPIDs) PDWORD pPIDs, _Inout_ PULONG64 pcPIDs); #define VMMDLL_PROCESS_INFORMATION_MAGIC 0xc0ffee663df9301e -#define VMMDLL_PROCESS_INFORMATION_VERSION 4 +#define VMMDLL_PROCESS_INFORMATION_VERSION 5 typedef struct tdVMMDLL_PROCESS_INFORMATION { ULONG64 magic; @@ -561,7 +752,7 @@ typedef struct tdVMMDLL_PROCESS_INFORMATION { struct { ULONG64 vaEPROCESS; ULONG64 vaPEB; - ULONG64 vaENTRY; + ULONG64 _Reserved1; BOOL fWow64; DWORD vaPEB32; // WoW64 only } win; @@ -613,37 +804,37 @@ typedef struct tdVMMDLL_IAT_ENTRY { * If the pData == NULL upon entry the number of entries of the pData array must * have in order to be able to hold the data is returned. * -- dwPID -* -- szModule +* -- wszModule * -- pData * -- cData * -- pcData * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetDirectories(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_writes_(16) PIMAGE_DATA_DIRECTORY pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetDirectories(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_writes_(16) PIMAGE_DATA_DIRECTORY pData, _In_ DWORD cData, _Out_ PDWORD pcData); _Success_(return) -BOOL VMMDLL_ProcessGetSections(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PIMAGE_SECTION_HEADER pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetSections(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PIMAGE_SECTION_HEADER pData, _In_ DWORD cData, _Out_ PDWORD pcData); _Success_(return) -BOOL VMMDLL_ProcessGetEAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PVMMDLL_EAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetEAT(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PVMMDLL_EAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); _Success_(return) -BOOL VMMDLL_ProcessGetIAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PVMMDLL_IAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetIAT(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PVMMDLL_IAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); /* * Retrieve the virtual address of a given function inside a process/module. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- szFunctionName * -- return = virtual address of function, zero on fail. */ -ULONG64 VMMDLL_ProcessGetProcAddress(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szFunctionName); +ULONG64 VMMDLL_ProcessGetProcAddress(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szFunctionName); /* * Retrieve the base address of a given module. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- return = virtual address of module base, zero on fail. */ -ULONG64 VMMDLL_ProcessGetModuleBase(_In_ DWORD dwPID, _In_ LPSTR szModuleName); +ULONG64 VMMDLL_ProcessGetModuleBase(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName); @@ -729,7 +920,7 @@ BOOL VMMDLL_WinReg_HiveList(_Out_writes_(cHives) PVMMDLL_REGISTRY_HIVE_INFORMATI * -- ra * -- pb * -- cb -* -- pcbRead +* -- pcbReadOpt * -- flags = flags as in VMMDLL_FLAG_* * -- return = success/fail. NB! reads may report as success even if 0 bytes are * read - it's recommended to verify pcbReadOpt parameter. @@ -901,26 +1092,26 @@ typedef struct tdVMMDLL_WIN_THUNKINFO_EAT { * function. This includes the virtual address of the IAT thunk which is useful * for hooking. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- szImportModuleName * -- szImportFunctionName * -- pThunkIAT * -- return */ _Success_(return) -BOOL VMMDLL_WinGetThunkInfoIAT(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szImportModuleName, _In_ LPSTR szImportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_IAT pThunkInfoIAT); +BOOL VMMDLL_WinGetThunkInfoIAT(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szImportModuleName, _In_ LPSTR szImportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_IAT pThunkInfoIAT); /* * Retrieve information about the export address table EAT thunk for an exported * function. This includes the virtual address of the EAT thunk which is useful * for hooking. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- pThunkEAT * -- return */ _Success_(return) -BOOL VMMDLL_WinGetThunkInfoEAT(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT); +BOOL VMMDLL_WinGetThunkInfoEAT(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT); diff --git a/vmm/m_handleinfo.c b/vmm/m_handleinfo.c new file mode 100644 index 00000000..1ad462b4 --- /dev/null +++ b/vmm/m_handleinfo.c @@ -0,0 +1,118 @@ +// m_handleinfo.c : implementation of the handle info built-in module. +// +// (c) Ulf Frisk, 2019 +// Author: Ulf Frisk, pcileech@frizk.net +// +#include "pluginmanager.h" +#include "util.h" +#include "vmm.h" +#include "vmmdll.h" +#include "vmmvfs.h" +#include "vmmwin.h" + +#define HANDLEINFO_LINELENGTH 190ULL + +_Success_(return == 0) +NTSTATUS HandleInfo_Read_HandleMap(_In_ PVMMOB_MAP_HANDLE pHandleMap, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset) +{ + NTSTATUS nt; + LPSTR sz; + QWORD i, o = 0, cbMax, cStart, cEnd, cbLINELENGTH; + PVMM_MAP_HANDLEENTRY pH; + PVMMWIN_OBJECT_TYPE pOT; + CHAR szType[MAX_PATH] = { 0 }; + cbLINELENGTH = HANDLEINFO_LINELENGTH; + cStart = (DWORD)(cbOffset / cbLINELENGTH); + cEnd = (DWORD)min(pHandleMap->cMap - 1, (cb + cbOffset + cbLINELENGTH - 1) / cbLINELENGTH); + cbMax = 1 + (1 + cEnd - cStart) * cbLINELENGTH; + if(!pHandleMap->cMap || (cStart > pHandleMap->cMap)) { return VMMDLL_STATUS_END_OF_FILE; } + if(!(sz = LocalAlloc(LMEM_ZEROINIT, cbMax))) { return VMMDLL_STATUS_FILE_INVALID; } + for(i = cStart; i <= cEnd; i++) { + pH = pHandleMap->pMap + i; + if((pOT = VmmWin_ObjectTypeGet((BYTE)pH->iType))) { + snprintf(szType, _MAX_PATH, "%S", pOT->wsz); + szType[16] = 0; + } else { + *(PDWORD)szType = pH->dwPoolTag; + szType[4] = 0; + } + o += Util_snprintf_ln( + sz + o, + cbMax - o, + cbLINELENGTH, + "%04x%7i%8x %16llx %6x %-16s %-128S\n", + (DWORD)i, + pH->dwPID, + pH->dwHandle, + pH->vaObject, + pH->dwGrantedAccess, + szType, + pH->wszText + pH->cwszText - min(128, pH->cwszText) + ); + } + nt = Util_VfsReadFile_FromPBYTE(sz, cbMax - 1, pb, cb, pcbRead, cbOffset - cStart * cbLINELENGTH); + LocalFree(sz); + return nt; +} + +/* +* Read : function as specified by the module manager. The module manager will +* call into this callback function whenever a read shall occur from a "file". +* -- ctx +* -- pb +* -- cb +* -- pcbRead +* -- cbOffset +* -- return +*/ +_Success_(return == 0) +NTSTATUS HandleInfo_Read(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset) +{ + NTSTATUS nt = VMMDLL_STATUS_FILE_INVALID; + PVMMOB_MAP_HANDLE pObHandleMap = NULL; + if(!_wcsicmp(ctx->wszPath, L"handles.txt") && VmmMap_GetHandle(ctx->pProcess, &pObHandleMap, TRUE)) { + nt = HandleInfo_Read_HandleMap(pObHandleMap, pb, cb, pcbRead, cbOffset); + Ob_DECREF(pObHandleMap); + } + return nt; +} + +/* +* List : function as specified by the module manager. The module manager will +* call into this callback function whenever a list directory shall occur from +* the given module. +* -- ctx +* -- pFileList +* -- return +*/ +BOOL HandleInfo_List(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _Inout_ PHANDLE pFileList) +{ + PVMMOB_MAP_HANDLE pObHandleMap = NULL; + // list thread map + if(VmmMap_GetHandle(ctx->pProcess, &pObHandleMap, FALSE)) { + VMMDLL_VfsList_AddFileEx(pFileList, "handles.txt", NULL, pObHandleMap->cMap * HANDLEINFO_LINELENGTH, NULL); + Ob_DECREF_NULL(&pObHandleMap); + } + return TRUE; +} + +/* +* Initialization function. The module manager shall call into this function +* when the module shall be initialized. If the module wish to initialize it +* shall call the supplied pfnPluginManager_Register function. +* NB! the module does not have to register itself - for example if the target +* operating system or architecture is unsupported. +* -- pPluginRegInfo +*/ +VOID M_HandleInfo_Initialize(_Inout_ PVMMDLL_PLUGIN_REGINFO pRI) +{ + PVMMOB_PHYS2VIRT_INFORMATION pObPhys2Virt = NULL; + if((pRI->magic != VMMDLL_PLUGIN_REGINFO_MAGIC) || (pRI->wVersion != VMMDLL_PLUGIN_REGINFO_VERSION)) { return; } + if(!((pRI->tpSystem == VMM_SYSTEM_WINDOWS_X64) || (pRI->tpSystem == VMM_SYSTEM_WINDOWS_X86))) { return; } + wcscpy_s(pRI->reg_info.wszModuleName, 32, L"handles"); // module name + pRI->reg_info.fRootModule = FALSE; // module shows in root directory + pRI->reg_info.fProcessModule = TRUE; // module shows in process directory + pRI->reg_fn.pfnList = HandleInfo_List; // List function supported + pRI->reg_fn.pfnRead = HandleInfo_Read; // Read function supported + pRI->pfnPluginManager_Register(pRI); +} diff --git a/vmm/m_ldrmodules.c b/vmm/m_ldrmodules.c index 79ae6236..ac28df2f 100644 --- a/vmm/m_ldrmodules.c +++ b/vmm/m_ldrmodules.c @@ -3,28 +3,84 @@ // (c) Ulf Frisk, 2018-2019 // Author: Ulf Frisk, pcileech@frizk.net // -#include "m_ldrmodules.h" +#include "m_modules.h" #include "pluginmanager.h" #include "vmm.h" -#include "vmmproc.h" #include "vmmwin.h" -#include "vmmvfs.h" #include "util.h" #include "pe.h" #define LDRMODULES_CACHE_TP_EAT 1 #define LDRMODULES_CACHE_TP_IAT 2 #define LDRMODULES_NUM_CACHE 8 +#define LDRMODULES_LINELENGTH_X86 97ULL +#define LDRMODULES_LINELENGTH_X64 116ULL + +#define LDRMODULES_MAX_IATEAT 0x10000 + typedef struct tdOBLDRMODULES_CACHE_ENTRY { OB ObHdr; - CHAR szDll[32]; + DWORD dwHash; DWORD tp; DWORD cb; BYTE pb[]; } OBLDRMODULES_CACHE_ENTRY, *POBLDRMODULES_CACHE_ENTRY; - -#define LDRMODULES_MAX_IATEAT 0x10000 +/* +* Dynamically generate the file \modules.txt. +* -- pModuleMap +* -- pb +* -- cb +* -- pcbRead +* -- cbOffset +* -- return +*/ +_Success_(return == 0) +NTSTATUS LdrModules_ReadModulesFile(_In_ PVMMOB_MAP_MODULE pModuleMap, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset) +{ + NTSTATUS nt; + LPSTR sz; + QWORD i, o = 0, cbMax, cStart, cEnd, cbLINELENGTH; + PVMM_MAP_MODULEENTRY pModule; + cbLINELENGTH = ctxVmm->f32 ? LDRMODULES_LINELENGTH_X86 : LDRMODULES_LINELENGTH_X64; + cStart = (DWORD)(cbOffset / cbLINELENGTH); + cEnd = (DWORD)min(pModuleMap->cMap - 1, (cb + cbOffset + cbLINELENGTH - 1) / cbLINELENGTH); + cbMax = 1 + (1 + cEnd - cStart) * cbLINELENGTH; + if(!pModuleMap->cMap || (cStart > pModuleMap->cMap)) { return VMMDLL_STATUS_END_OF_FILE; } + if(!(sz = LocalAlloc(LMEM_ZEROINIT, cbMax))) { return VMMDLL_STATUS_FILE_INVALID; } + for(i = cStart; i <= cEnd; i++) { + pModule = pModuleMap->pMap + i; + if(ctxVmm->f32) { + o += Util_snprintf_ln( + sz + o, + cbMax - o, + cbLINELENGTH, + "%04x %8x %08x-%08x %-64S\n", + (DWORD)i, + pModule->cbImageSize >> 12, + (DWORD)pModule->vaBase, + (DWORD)(pModule->vaBase + pModule->cbImageSize - 1), + pModule->wszText + pModule->cwszText - min(64, pModule->cwszText) + ); + } else { + o += Util_snprintf_ln( + sz + o, + cbMax - o, + cbLINELENGTH, + "%04x %8x %016llx-%016llx %s %-64S\n", + (DWORD)i, + pModule->cbImageSize >> 12, + pModule->vaBase, + pModule->vaBase + pModule->cbImageSize - 1, + pModule->fWoW64 ? "32" : " ", + pModule->wszText + pModule->cwszText - min(64, pModule->cwszText) + ); + } + } + nt = Util_VfsReadFile_FromPBYTE(sz, cbMax - 1, pb, cb, pcbRead, cbOffset - cStart * cbLINELENGTH); + LocalFree(sz); + return nt; +} /* * Retrieve a OBLDRMODULES_CACHE_ENTRY object for the Export Address Table (EAT). @@ -33,7 +89,7 @@ typedef struct tdOBLDRMODULES_CACHE_ENTRY { * -- pModule * -- return */ -POBLDRMODULES_CACHE_ENTRY LdrModule_GetEAT(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ PVMM_MODULEMAP_ENTRY pModule) +POBLDRMODULES_CACHE_ENTRY LdrModule_GetEAT(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ PVMM_MAP_MODULEENTRY pModule) { DWORD i, o, cEATs = 0; PVMMPROC_WINDOWS_EAT_ENTRY pEATs = NULL; @@ -41,7 +97,7 @@ POBLDRMODULES_CACHE_ENTRY LdrModule_GetEAT(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ PVMM_PROCESS pProcess = (PVMM_PROCESS)ctx->pProcess; // 1: retrieve cache pObCacheEntry = ObContainer_GetOb(pProcess->Plugin.pObCLdrModulesDisplayCache); - if(pObCacheEntry && (pObCacheEntry->tp == LDRMODULES_CACHE_TP_EAT) && !_strnicmp(pObCacheEntry->szDll, pModule->szName, 32)) { + if(pObCacheEntry && (pObCacheEntry->tp == LDRMODULES_CACHE_TP_EAT) && (pObCacheEntry->dwHash == Util_HashStringUpperW(pModule->wszText))) { return pObCacheEntry; } Ob_DECREF(pObCacheEntry); @@ -52,18 +108,18 @@ POBLDRMODULES_CACHE_ENTRY LdrModule_GetEAT(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ VmmWin_PE_LoadEAT_DisplayBuffer(ctx->pProcess, pModule, pEATs, LDRMODULES_MAX_IATEAT, &cEATs); if(!cEATs) { goto fail; } // 3: fill "display buffer" - pObCacheEntry = Ob_Alloc('EA', LMEM_ZEROINIT, sizeof(OBLDRMODULES_CACHE_ENTRY) + (QWORD)cEATs * 64 + 1, NULL, NULL); + pObCacheEntry = Ob_Alloc('PeEA', LMEM_ZEROINIT, sizeof(OBLDRMODULES_CACHE_ENTRY) + (QWORD)cEATs * 64 + 1, NULL, NULL); if(!pObCacheEntry) { goto fail; } pObCacheEntry->tp = LDRMODULES_CACHE_TP_EAT; pObCacheEntry->cb = cEATs * 64 + 1; - memcpy(pObCacheEntry->szDll, pModule->szName, 32); + pObCacheEntry->dwHash = Util_HashStringUpperW(pModule->wszText); for(i = 0, o = 0; i < cEATs; i++) { o += snprintf( pObCacheEntry->pb + o, pObCacheEntry->cb - o, "%04x %016llx %-40.40s \n", // 64 bytes (chars) / line (function) (WORD)i, - pModule->BaseAddress + pEATs[i].vaFunctionOffset, + pModule->vaBase + pEATs[i].vaFunctionOffset, pEATs[i].szFunction ); } @@ -84,7 +140,7 @@ POBLDRMODULES_CACHE_ENTRY LdrModule_GetEAT(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ * -- pModule * -- return */ -POBLDRMODULES_CACHE_ENTRY LdrModule_GetIAT(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ PVMM_MODULEMAP_ENTRY pModule) +POBLDRMODULES_CACHE_ENTRY LdrModule_GetIAT(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ PVMM_MAP_MODULEENTRY pModule) { DWORD i, o, cIATs = 0; PVMMWIN_IAT_ENTRY pIATs = NULL; @@ -92,7 +148,7 @@ POBLDRMODULES_CACHE_ENTRY LdrModule_GetIAT(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ PVMM_PROCESS pProcess = (PVMM_PROCESS)ctx->pProcess; // 1: retrieve cache pObCacheEntry = ObContainer_GetOb(pProcess->Plugin.pObCLdrModulesDisplayCache); - if(pObCacheEntry && (pObCacheEntry->tp == LDRMODULES_CACHE_TP_IAT) && !_strnicmp(pObCacheEntry->szDll, pModule->szName, 32)) { + if(pObCacheEntry && (pObCacheEntry->tp == LDRMODULES_CACHE_TP_IAT) && (pObCacheEntry->dwHash == Util_HashStringUpperW(pModule->wszText))) { return pObCacheEntry; } Ob_DECREF(pObCacheEntry); @@ -103,11 +159,11 @@ POBLDRMODULES_CACHE_ENTRY LdrModule_GetIAT(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ VmmWin_PE_LoadIAT_DisplayBuffer(ctx->pProcess, pModule, pIATs, LDRMODULES_MAX_IATEAT, &cIATs); if(!cIATs) { goto fail; } // 3: fill "display buffer" - pObCacheEntry = Ob_Alloc('IA', LMEM_ZEROINIT, sizeof(OBLDRMODULES_CACHE_ENTRY) + (QWORD)cIATs * 128 + 1, NULL, NULL); + pObCacheEntry = Ob_Alloc('PeIA', LMEM_ZEROINIT, sizeof(OBLDRMODULES_CACHE_ENTRY) + (QWORD)cIATs * 128 + 1, NULL, NULL); if(!pObCacheEntry) { goto fail; } pObCacheEntry->tp = LDRMODULES_CACHE_TP_IAT; pObCacheEntry->cb = cIATs * 128 + 1; - memcpy(pObCacheEntry->szDll, pModule->szName, 32); + pObCacheEntry->dwHash = Util_HashStringUpperW(pModule->wszText); for(i = 0, o = 0; i < cIATs; i++) { o += snprintf( pObCacheEntry->pb + o, @@ -129,28 +185,18 @@ POBLDRMODULES_CACHE_ENTRY LdrModule_GetIAT(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ return NULL; } -/* -* Helper write function - Write to a virtual memory backed "file". -*/ -VOID LdrModules_Write_MemFile(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaMem, _In_ QWORD cbMem, _In_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbWrite, _In_ ULONG64 cbOffset) -{ - if(cbMem <= cbOffset) { *pcbWrite = 0; return; } - *pcbWrite = (DWORD)min(cb, cbMem - cbOffset); - VmmWrite(pProcess, vaMem + cbOffset, pb, *pcbWrite); -} - /* * Helper write function - Write to the requested data directory file. */ -VOID LdrModules_Write_DirectoriesD(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MODULEMAP_ENTRY pModule, _In_ LPWSTR wszDirectory, _In_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbWrite, _In_ QWORD cbOffset) +VOID LdrModules_Write_DirectoriesD(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MAP_MODULEENTRY pModule, _In_ LPWSTR wszDirectory, _In_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbWrite, _In_ QWORD cbOffset) { DWORD i; - IMAGE_DATA_DIRECTORY pDataDirectories[16]; + IMAGE_DATA_DIRECTORY pDataDirectories[16] = { 0 }; *pcbWrite = 0; for(i = 0; i < 16; i++) { if(!Util_wcsstrncmp((LPSTR)PE_DATA_DIRECTORIES[i], wszDirectory, 0)) { VmmWin_PE_DIRECTORY_DisplayBuffer(pProcess, pModule, NULL, 0, NULL, pDataDirectories); - LdrModules_Write_MemFile(pProcess, pModule->BaseAddress + pDataDirectories[i].VirtualAddress, pDataDirectories[i].Size, pb, cb, pcbWrite, cbOffset); + VmmWriteAsFile(pProcess, pModule->vaBase + pDataDirectories[i].VirtualAddress, pDataDirectories[i].Size, pb, cb, pcbWrite, cbOffset); } } } @@ -158,14 +204,14 @@ VOID LdrModules_Write_DirectoriesD(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MODULEM /* * Helper write function - Write to the requested section header file. */ -VOID LdrModules_Write_SectionsD(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MODULEMAP_ENTRY pModule, _In_ LPWSTR wszSection, _In_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbWrite, _In_ QWORD cbOffset) +VOID LdrModules_Write_SectionsD(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MAP_MODULEENTRY pModule, _In_ LPWSTR wszSection, _In_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbWrite, _In_ QWORD cbOffset) { IMAGE_SECTION_HEADER SectionHeader; CHAR szSection[32]; CHAR chDefault = '_'; WideCharToMultiByte(CP_ACP, 0, wszSection, -1, szSection, sizeof(szSection), &chDefault, NULL); - if(!PE_SectionGetFromName(pProcess, pModule->BaseAddress, szSection, &SectionHeader)) { *pcbWrite = 0; return; } - LdrModules_Write_MemFile(pProcess, pModule->BaseAddress + SectionHeader.VirtualAddress, SectionHeader.Misc.VirtualSize, pb, cb, pcbWrite, cbOffset); + if(!PE_SectionGetFromName(pProcess, pModule->vaBase, szSection, &SectionHeader)) { *pcbWrite = 0; return; } + VmmWriteAsFile(pProcess, pModule->vaBase + SectionHeader.VirtualAddress, SectionHeader.Misc.VirtualSize, pb, cb, pcbWrite, cbOffset); } /* @@ -180,17 +226,16 @@ VOID LdrModules_Write_SectionsD(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MODULEMAP_ */ NTSTATUS LdrModules_Write(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbWrite, _In_ QWORD cbOffset) { - CHAR _szBuf[MAX_PATH] = { 0 }; - PVMM_MODULEMAP_ENTRY pModule = NULL; - PVMMOB_MODULEMAP pObModuleMap = NULL; - WCHAR wszModuleName[32+1]; + PVMM_MAP_MODULEENTRY pModule = NULL; + PVMMOB_MAP_MODULE pObModuleMap = NULL; + WCHAR wszModuleName[MAX_PATH]; LPWSTR wszModuleSubPath; PVMM_PROCESS pProcess = (PVMM_PROCESS)ctx->pProcess; *pcbWrite = 0; wszModuleSubPath = Util_PathSplit2_ExWCHAR(ctx->wszPath, wszModuleName, _countof(wszModuleName)); - if(wszModuleName[0] && wszModuleSubPath[0] && VmmProc_ModuleMapGetSingleEntry(pProcess, wszModuleName, &pObModuleMap, &pModule)) { + if(wszModuleName[0] && wszModuleSubPath[0] && VmmMap_GetModule(pProcess, &pObModuleMap) && (pModule = VmmMap_GetModuleEntry(pObModuleMap, wszModuleName))) { if(!_wcsicmp(wszModuleSubPath, L"pefile.dll")) { - PE_FileRaw_Write(pProcess, pModule->BaseAddress, pb, cb, pcbWrite, (DWORD)cbOffset); + PE_FileRaw_Write(pProcess, pModule->vaBase, pb, cb, pcbWrite, (DWORD)cbOffset); } if(!_wcsnicmp(wszModuleSubPath, L"sectionsd\\", 10)) { LdrModules_Write_SectionsD(pProcess, pModule, wszModuleSubPath + 10, pb, cb, pcbWrite, cbOffset); @@ -198,27 +243,15 @@ NTSTATUS LdrModules_Write(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ PBYTE pb, _In_ D if(!_wcsnicmp(wszModuleSubPath, L"directoriesd\\", 13)) { LdrModules_Write_DirectoriesD(pProcess, pModule, wszModuleSubPath + 13, pb, cb, pcbWrite, cbOffset); } - Ob_DECREF(pObModuleMap); } + Ob_DECREF(pObModuleMap); return VMM_STATUS_SUCCESS; } -/* -* Helper read function - Read a virtual memory backed "file". -*/ -NTSTATUS LdrModules_Read_MemFile(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaMem, _In_ QWORD cbMem, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ ULONG64 cbOffset) -{ - if(cbMem <= cbOffset) { return VMM_STATUS_END_OF_FILE; } - cb = (DWORD)min(cb, cbMem - cbOffset); - VmmReadEx(pProcess, vaMem + cbOffset, pb, cb, NULL, VMM_FLAG_ZEROPAD_ON_FAIL); - *pcbRead = cb; - return VMMDLL_STATUS_SUCCESS; -} - /* * Helper read function - Read the requested data directory file. */ -NTSTATUS LdrModules_Read_DirectoriesD(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MODULEMAP_ENTRY pModule, _In_ LPWSTR wszDirectory, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset) +NTSTATUS LdrModules_Read_DirectoriesD(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MAP_MODULEENTRY pModule, _In_ LPWSTR wszDirectory, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset) { DWORD i; CHAR chDefault = '_'; @@ -228,7 +261,7 @@ NTSTATUS LdrModules_Read_DirectoriesD(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MODU for(i = 0; i < 16; i++) { if(!strcmp(szDirectory, PE_DATA_DIRECTORIES[i])) { VmmWin_PE_DIRECTORY_DisplayBuffer(pProcess, pModule, NULL, 0, NULL, pDataDirectories); - return LdrModules_Read_MemFile(pProcess, pModule->BaseAddress + pDataDirectories[i].VirtualAddress, pDataDirectories[i].Size, pb, cb, pcbRead, cbOffset); + return VmmReadAsFile(pProcess, pModule->vaBase + pDataDirectories[i].VirtualAddress, pDataDirectories[i].Size, pb, cb, pcbRead, cbOffset); } } return VMMDLL_STATUS_FILE_INVALID; @@ -237,17 +270,17 @@ NTSTATUS LdrModules_Read_DirectoriesD(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MODU /* * Helper read function - Read the requested section header file. */ -NTSTATUS LdrModules_Read_SectionsD(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MODULEMAP_ENTRY pModule, _In_ LPWSTR wszSection, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset) +NTSTATUS LdrModules_Read_SectionsD(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MAP_MODULEENTRY pModule, _In_ LPWSTR wszSection, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset) { CHAR szSection[32]; IMAGE_SECTION_HEADER SectionHeader; CHAR chDefault = '_'; WideCharToMultiByte(CP_ACP, 0, wszSection, -1, szSection, sizeof(szSection), &chDefault, NULL); - if(!PE_SectionGetFromName(pProcess, pModule->BaseAddress, szSection, &SectionHeader)) { return VMMDLL_STATUS_FILE_INVALID; } - return LdrModules_Read_MemFile(pProcess, pModule->BaseAddress + SectionHeader.VirtualAddress, SectionHeader.Misc.VirtualSize, pb, cb, pcbRead, cbOffset); + if(!PE_SectionGetFromName(pProcess, pModule->vaBase, szSection, &SectionHeader)) { return VMMDLL_STATUS_FILE_INVALID; } + return VmmReadAsFile(pProcess, pModule->vaBase + SectionHeader.VirtualAddress, SectionHeader.Misc.VirtualSize, pb, cb, pcbRead, cbOffset); } -NTSTATUS LdrModules_Read_ModuleSubFile(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ PVMM_MODULEMAP_ENTRY pModule, _In_ LPWSTR wszPath, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset) +NTSTATUS LdrModules_Read_ModuleSubFile(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ PVMM_MAP_MODULEENTRY pModule, _In_ LPWSTR wszPath, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset) { NTSTATUS nt; DWORD cbBuffer; @@ -255,13 +288,13 @@ NTSTATUS LdrModules_Read_ModuleSubFile(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ PVM POBLDRMODULES_CACHE_ENTRY pObCacheEntry = NULL; PVMM_PROCESS pProcess = (PVMM_PROCESS)ctx->pProcess; if(!_wcsicmp(wszPath, L"base")) { - return Util_VfsReadFile_FromQWORD(pModule->BaseAddress, pb, cb, pcbRead, cbOffset, FALSE); + return Util_VfsReadFile_FromQWORD(pModule->vaBase, pb, cb, pcbRead, cbOffset, FALSE); } if(!_wcsicmp(wszPath, L"entry")) { - return Util_VfsReadFile_FromQWORD(pModule->EntryPoint, pb, cb, pcbRead, cbOffset, FALSE); + return Util_VfsReadFile_FromQWORD(pModule->vaEntry, pb, cb, pcbRead, cbOffset, FALSE); } if(!_wcsicmp(wszPath, L"size")) { - return Util_VfsReadFile_FromDWORD(pModule->SizeOfImage, pb, cb, pcbRead, cbOffset, FALSE); + return Util_VfsReadFile_FromDWORD(pModule->cbImageSize, pb, cb, pcbRead, cbOffset, FALSE); } if(!_wcsicmp(wszPath, L"directories")) { VmmWin_PE_DIRECTORY_DisplayBuffer(ctx->pProcess, pModule, pbBuffer, 0x800, &cbBuffer, NULL); @@ -282,7 +315,7 @@ NTSTATUS LdrModules_Read_ModuleSubFile(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ PVM return nt; } if(!_wcsicmp(wszPath, L"pefile.dll")) { - return PE_FileRaw_Read(pProcess, pModule->BaseAddress, pb, cb, pcbRead, (DWORD)cbOffset) ? VMMDLL_STATUS_SUCCESS : VMMDLL_STATUS_FILE_INVALID; + return PE_FileRaw_Read(pProcess, pModule->vaBase, pb, cb, pcbRead, (DWORD)cbOffset) ? VMMDLL_STATUS_SUCCESS : VMMDLL_STATUS_FILE_INVALID; } if(!_wcsicmp(wszPath, L"sections")) { VmmWin_PE_SECTION_DisplayBuffer(ctx->pProcess, pModule, pbBuffer, 0x800, &cbBuffer, NULL, NULL); @@ -309,18 +342,26 @@ NTSTATUS LdrModules_Read_ModuleSubFile(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ PVM */ NTSTATUS LdrModules_Read(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset) { - NTSTATUS nt; + NTSTATUS nt = VMMDLL_STATUS_FILE_INVALID; WCHAR wszModuleName[32 + 1]; LPWSTR wszModuleSubPath; - PVMM_MODULEMAP_ENTRY pModule = NULL; - PVMMOB_MODULEMAP pObModuleMap = NULL; + PVMM_MAP_MODULEENTRY pModule = NULL; + PVMMOB_MAP_MODULE pObModuleMap = NULL; + if(!_wcsicmp(ctx->wszPath, L"modules.txt")) { + if(VmmMap_GetModule((PVMM_PROCESS)ctx->pProcess, &pObModuleMap)) { + nt = LdrModules_ReadModulesFile(pObModuleMap, pb, cb, pcbRead, cbOffset); + Ob_DECREF(pObModuleMap); + } + return nt; + } wszModuleSubPath = Util_PathSplit2_ExWCHAR(ctx->wszPath, wszModuleName, _countof(wszModuleName)); *pcbRead = 0; - if(wszModuleName[0] && wszModuleSubPath[0] && VmmProc_ModuleMapGetSingleEntry((PVMM_PROCESS)ctx->pProcess, wszModuleName, &pObModuleMap, &pModule)) { + if(wszModuleName[0] && wszModuleSubPath[0] && VmmMap_GetModule((PVMM_PROCESS)ctx->pProcess, &pObModuleMap) && (pModule = VmmMap_GetModuleEntry(pObModuleMap, wszModuleName))) { nt = LdrModules_Read_ModuleSubFile(ctx, pModule, wszModuleSubPath, pb, cb, pcbRead, cbOffset); Ob_DECREF(pObModuleMap); return nt; } + Ob_DECREF(pObModuleMap); return VMMDLL_STATUS_FILE_INVALID; } @@ -336,48 +377,43 @@ BOOL LdrModules_List(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _Inout_ PHANDLE pFileList) { DWORD c, i; CHAR szSectionName[9] = { 0 }; - WCHAR wszPath1[32+1]; + WCHAR wszPath1[MAX_PATH]; LPWSTR wszPath2; - PVMMOB_MODULEMAP pObModuleMap = NULL; - PVMM_MODULEMAP_ENTRY pModule = NULL; + PVMMOB_MAP_MODULE pObModuleMap = NULL; + PVMM_MAP_MODULEENTRY pModule = NULL; PIMAGE_SECTION_HEADER pSections = NULL; IMAGE_DATA_DIRECTORY pDataDirectories[16]; PVMM_PROCESS pProcess = (PVMM_PROCESS)ctx->pProcess; - if(!VmmProc_ModuleMapGet(pProcess, &pObModuleMap)) { goto fail; } + if(!VmmMap_GetModule(pProcess, &pObModuleMap)) { goto fail; } // modules root directory -> add directory per DLL if(!ctx->wszPath[0]) { for(i = 0; i < pObModuleMap->cMap; i++) { - VMMDLL_VfsList_AddDirectory(pFileList, pObModuleMap->pMap[i].szName); + VMMDLL_VfsList_AddDirectoryEx(pFileList, NULL, pObModuleMap->pMap[i].wszText, NULL); } + VMMDLL_VfsList_AddFileEx(pFileList, NULL, L"modules.txt", pObModuleMap->cMap * (ctxVmm->f32 ? LDRMODULES_LINELENGTH_X86 : LDRMODULES_LINELENGTH_X64), NULL); goto success; } // individual module directory -> list files wszPath2 = Util_PathSplit2_ExWCHAR(ctx->wszPath, wszPath1, _countof(wszPath1)); - for(i = 0; i < pObModuleMap->cMap; i++) { - if(0 == Util_wcsstrncmp(pObModuleMap->pMap[i].szName, wszPath1, 32)) { - pModule = pObModuleMap->pMap + i; - break; - } - } - if(!pModule) { goto fail; } + if(!(pModule = VmmMap_GetModuleEntry(pObModuleMap, wszPath1))) { goto fail; } // module-specific 'root' directory if(!wszPath2[0]) { - VmmWin_PE_SetSizeSectionIATEAT_DisplayBuffer(ctx->pProcess, pObModuleMap->pMap + i); - VMMDLL_VfsList_AddFile(pFileList, "base", 16); - VMMDLL_VfsList_AddFile(pFileList, "entry", 16); - VMMDLL_VfsList_AddFile(pFileList, "size", 8); - VMMDLL_VfsList_AddFile(pFileList, "directories", 864); - VMMDLL_VfsList_AddFile(pFileList, "export", pObModuleMap->pMap[i].cbDisplayBufferEAT); - VMMDLL_VfsList_AddFile(pFileList, "import", pObModuleMap->pMap[i].cbDisplayBufferIAT); - VMMDLL_VfsList_AddFile(pFileList, "sections", pObModuleMap->pMap[i].cbDisplayBufferSections); - VMMDLL_VfsList_AddFile(pFileList, "pefile.dll", pObModuleMap->pMap[i].cbFileSizeRaw); - VMMDLL_VfsList_AddDirectory(pFileList, "sectionsd"); - VMMDLL_VfsList_AddDirectory(pFileList, "directoriesd"); + VmmWin_PE_SetSizeSectionIATEAT_DisplayBuffer(ctx->pProcess, pModule); + VMMDLL_VfsList_AddFileEx(pFileList, NULL, L"base", 16, NULL); + VMMDLL_VfsList_AddFileEx(pFileList, NULL, L"entry", 16, NULL); + VMMDLL_VfsList_AddFileEx(pFileList, NULL, L"size", 8, NULL); + VMMDLL_VfsList_AddFileEx(pFileList, NULL, L"directories", 864, NULL); + VMMDLL_VfsList_AddFileEx(pFileList, NULL, L"export", pModule->cbDisplayBufferEAT, NULL); + VMMDLL_VfsList_AddFileEx(pFileList, NULL, L"import", pModule->cbDisplayBufferIAT, NULL); + VMMDLL_VfsList_AddFileEx(pFileList, NULL, L"sections", pModule->cbDisplayBufferSections, NULL); + VMMDLL_VfsList_AddFileEx(pFileList, NULL, L"pefile.dll", pModule->cbFileSizeRaw, NULL); + VMMDLL_VfsList_AddDirectoryEx(pFileList, NULL, L"sectionsd", NULL); + VMMDLL_VfsList_AddDirectoryEx(pFileList, NULL, L"directoriesd", NULL); goto success; } // module-specific 'sectiond' directory if(wszPath2[0] && !wcscmp(wszPath2, L"sectionsd")) { - c = PE_SectionGetNumberOf(pProcess, pModule->BaseAddress); + c = PE_SectionGetNumberOf(pProcess, pModule->vaBase); if(!(pSections = LocalAlloc(0, c * sizeof(IMAGE_SECTION_HEADER)))) { goto fail; } VmmWin_PE_SECTION_DisplayBuffer(pProcess, pModule, NULL, 0, NULL, &c, pSections); for(i = 0; i < c; i++) { diff --git a/vmm/m_ldrmodules.h b/vmm/m_ldrmodules.h deleted file mode 100644 index 08d40440..00000000 --- a/vmm/m_ldrmodules.h +++ /dev/null @@ -1,17 +0,0 @@ -// m_ldrmodules.h : definitions related to the ldrmodules built-in module. -// -// (c) Ulf Frisk, 2018-2019 -// Author: Ulf Frisk, pcileech@frizk.net -// -#ifndef __M_LDRMODULES_H__ -#define __M_LDRMODULES_H__ -#include -#include "vmmdll.h" - -/* -* Initialization function for the built-in ldrmodules module. -* -- pPluginRegInfo -*/ -VOID M_LdrModules_Initialize(_Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); - -#endif /* __M_LDRMODULES_H__ */ diff --git a/vmm/m_memmap.c b/vmm/m_memmap.c new file mode 100644 index 00000000..1dabf4d7 --- /dev/null +++ b/vmm/m_memmap.c @@ -0,0 +1,237 @@ +// m_memmap.c : implementation of the memmap built-in module. +// +// (c) Ulf Frisk, 2019 +// Author: Ulf Frisk, pcileech@frizk.net +// +#include "m_modules.h" +#include "pluginmanager.h" +#include "util.h" +#include "vmm.h" +#include "vmmdll.h" +#include "vmmvfs.h" + +#define MEMMAP_PTE_LINELENGTH_X86 102ULL +#define MEMMAP_PTE_LINELENGTH_X64 121ULL +#define MEMMAP_VAD_LINELENGTH_X86 121ULL +#define MEMMAP_VAD_LINELENGTH_X64 137ULL + +VOID MemMap_Read_VadMap_Protection(_In_ PVMM_MAP_VADENTRY pVad, _Out_writes_(6) LPSTR sz) +{ + BYTE vh = (BYTE)pVad->Protection >> 3; + BYTE vl = (BYTE)pVad->Protection & 7; + sz[0] = pVad->fPrivateMemory ? 'p' : '-'; // PRIVATE MEMORY + sz[1] = (vh & 2) ? ((vh & 1) ? 'm' : 'g') : ((vh & 1) ? 'n' : '-'); // -/NO_CACHE/GUARD/WRITECOMBINE + sz[2] = ((vl == 1) || (vl == 3) || (vl == 4) || (vl == 6)) ? 'r' : '-'; // COPY ON WRITE + sz[3] = (vl & 4) ? 'w' : '-'; // WRITE + sz[4] = (vl & 2) ? 'x' : '-'; // EXECUTE + sz[5] = ((vl == 5) || (vl == 7)) ? 'c' : '-'; // COPY ON WRITE + if(sz[1] != '-' && sz[2] == '-' && sz[3] == '-' && sz[4] == '-' && sz[5] == '-') { sz[1] = '-'; } +} + +LPSTR MemMap_Read_VadMap_Type(_In_ PVMM_MAP_VADENTRY pVad) +{ + if(pVad->fImage) { + return "Image"; + } else if(pVad->fFile) { + return "File "; + } else if(pVad->fHeap) { + return "Heap "; + } else if(pVad->fStack) { + return "Stack"; + } else if(pVad->fTeb) { + return "Teb "; + } else if(pVad->fPageFile) { + return "Pf "; + } else { + return " "; + } +} + +_Success_(return == 0) +NTSTATUS MemMap_Read_VadMap(_In_ PVMMOB_MAP_VAD pVadMap, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset) +{ + NTSTATUS nt; + LPSTR sz; + QWORD i, o = 0, cbMax, cStart, cVad, cbLINELENGTH; + PVMM_MAP_VADENTRY pVad; + CHAR szProtection[7] = { 0 }; + cbLINELENGTH = ctxVmm->f32 ? MEMMAP_VAD_LINELENGTH_X86 : MEMMAP_VAD_LINELENGTH_X64; + cStart = (DWORD)(cbOffset / cbLINELENGTH); + cVad = (DWORD)min(pVadMap->cMap - 1, (cb + cbOffset + cbLINELENGTH - 1) / cbLINELENGTH); + cbMax = 1 + (1 + cVad - cStart) * cbLINELENGTH; + if(!pVadMap->cMap || (cStart > pVadMap->cMap)) { return VMMDLL_STATUS_END_OF_FILE; } + if(!(sz = LocalAlloc(LMEM_ZEROINIT, cbMax))) { return VMMDLL_STATUS_FILE_INVALID; } + for(i = cStart; i <= cVad; i++) { + pVad = pVadMap->pMap + i; + MemMap_Read_VadMap_Protection(pVad, szProtection); + if(ctxVmm->f32) { + o += Util_snprintf_ln( + sz + o, + cbMax - o, + cbLINELENGTH, + "%04x %8x %8x %i %08x-%08x %s %s %-64S\n", + (DWORD)i, + (DWORD)((pVad->vaEnd - pVad->vaStart + 1) >> 12), + pVad->CommitCharge, + pVad->MemCommit ? 1 : 0, + (DWORD)pVad->vaStart, + (DWORD)pVad->vaEnd, + MemMap_Read_VadMap_Type(pVad), + szProtection, + pVad->wszText + pVad->cwszText - min(64, pVad->cwszText) + ); + } else { + o += Util_snprintf_ln( + sz + o, + cbMax - o, + cbLINELENGTH, + "%04x %8x %8x %i %016llx-%016llx %s %s %-64S\n", + (DWORD)i, + (DWORD)((pVad->vaEnd - pVad->vaStart + 1) >> 12), + pVad->CommitCharge, + pVad->MemCommit ? 1 : 0, + pVad->vaStart, + pVad->vaEnd, + MemMap_Read_VadMap_Type(pVad), + szProtection, + pVad->wszText + pVad->cwszText - min(64, pVad->cwszText) + ); + } + } + nt = Util_VfsReadFile_FromPBYTE(sz, cbMax - 1, pb, cb, pcbRead, cbOffset - cStart * cbLINELENGTH); + LocalFree(sz); + return nt; +} + +_Success_(return == 0) +NTSTATUS MemMap_Read_PteMap(_In_ PVMMOB_MAP_PTE pPteMap, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset) +{ + NTSTATUS nt; + LPSTR sz; + QWORD i, o = 0, cbMax, cStart, cEnd, cbLINELENGTH; + PVMM_MAP_PTEENTRY pPte; + cbLINELENGTH = ctxVmm->f32 ? MEMMAP_PTE_LINELENGTH_X86 : MEMMAP_PTE_LINELENGTH_X64; + cStart = (DWORD)(cbOffset / cbLINELENGTH); + cEnd = (DWORD)min(pPteMap->cMap - 1, (cb + cbOffset + cbLINELENGTH - 1) / cbLINELENGTH); + cbMax = 1 + (1 + cEnd - cStart) * cbLINELENGTH; + if(!pPteMap->cMap || (cStart > pPteMap->cMap)) { return VMMDLL_STATUS_END_OF_FILE; } + if(!(sz = LocalAlloc(LMEM_ZEROINIT, cbMax))) { return VMMDLL_STATUS_FILE_INVALID; } + for(i = cStart; i <= cEnd; i++) { + pPte = pPteMap->pMap + i; + if(ctxVmm->f32) { + o += Util_snprintf_ln( + sz + o, + cbMax - o, + cbLINELENGTH, + "%04x %8x %08x-%08x %sr%s%s %-64S\n", + (DWORD)i, + (DWORD)pPte->cPages, + (DWORD)pPte->vaBase, + (DWORD)(pPte->vaBase + (pPte->cPages << 12) - 1), + pPte->fPage & VMM_MEMMAP_PAGE_NS ? "-" : "s", + pPte->fPage & VMM_MEMMAP_PAGE_W ? "w" : "-", + pPte->fPage & VMM_MEMMAP_PAGE_NX ? "-" : "x", + pPte->wszText + pPte->cwszText - min(64, pPte->cwszText) + ); + } else { + o += Util_snprintf_ln( + sz + o, + cbMax - o, + cbLINELENGTH, + "%04x %8x %016llx-%016llx %sr%s%s%s%-64S\n", + (DWORD)i, + (DWORD)pPte->cPages, + pPte->vaBase, + pPte->vaBase + (pPte->cPages << 12) - 1, + pPte->fPage & VMM_MEMMAP_PAGE_NS ? "-" : "s", + pPte->fPage & VMM_MEMMAP_PAGE_W ? "w" : "-", + pPte->fPage & VMM_MEMMAP_PAGE_NX ? "-" : "x", + pPte->cwszText ? (pPte->fWoW64 ? " 32 " : " ") : " ", + pPte->wszText + pPte->cwszText - min(64, pPte->cwszText) + ); + } + } + nt = Util_VfsReadFile_FromPBYTE(sz, cbMax - 1, pb, cb, pcbRead, cbOffset - cStart * cbLINELENGTH); + LocalFree(sz); + return nt; +} + +/* +* Read : function as specified by the module manager. The module manager will +* call into this callback function whenever a read shall occur from a "file". +* -- ctx +* -- pb +* -- cb +* -- pcbRead +* -- cbOffset +* -- return +*/ +_Success_(return == 0) +NTSTATUS MemMap_Read(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset) +{ + NTSTATUS nt = VMMDLL_STATUS_FILE_INVALID; + PVMMOB_MAP_PTE pObMemMapPte = NULL; + PVMMOB_MAP_VAD pObMemMapVad = NULL; + // read page table memory map. + if(!_wcsicmp(ctx->wszPath, L"pte.txt")) { + if(VmmMap_GetPte(ctx->pProcess, &pObMemMapPte, TRUE)) { + nt = MemMap_Read_PteMap(pObMemMapPte, pb, cb, pcbRead, cbOffset); + Ob_DECREF(pObMemMapPte); + } + return nt; + } + if(!_wcsicmp(ctx->wszPath, L"vad.txt")) { + if(VmmMap_GetVad(ctx->pProcess, &pObMemMapVad, TRUE)) { + nt = MemMap_Read_VadMap(pObMemMapVad, pb, cb, pcbRead, cbOffset); + Ob_DECREF(pObMemMapVad); + } + return nt; + } + return nt; +} + +/* +* List : function as specified by the module manager. The module manager will +* call into this callback function whenever a list directory shall occur from +* the given module. +* -- ctx +* -- pFileList +* -- return +*/ +BOOL MemMap_List(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _Inout_ PHANDLE pFileList) +{ + PVMMOB_MAP_PTE pObMemMapPte = NULL; + PVMMOB_MAP_VAD pObMemMapVad = NULL; + // list page table memory map. + if(VmmMap_GetPte(ctx->pProcess, &pObMemMapPte, FALSE)) { + VMMDLL_VfsList_AddFileEx(pFileList, "pte.txt", NULL, pObMemMapPte->cMap * (ctxVmm->f32 ? MEMMAP_PTE_LINELENGTH_X86 : MEMMAP_PTE_LINELENGTH_X64), NULL); + Ob_DECREF_NULL(&pObMemMapPte); + } + // list vad memory map. + if(VmmMap_GetVad(ctx->pProcess, &pObMemMapVad, FALSE)) { + VMMDLL_VfsList_AddFileEx(pFileList, "vad.txt", NULL, pObMemMapVad->cMap * (ctxVmm->f32 ? MEMMAP_VAD_LINELENGTH_X86 : MEMMAP_VAD_LINELENGTH_X64), NULL); + Ob_DECREF_NULL(&pObMemMapVad); + } + return TRUE; +} + +/* +* Initialization function. The module manager shall call into this function +* when the module shall be initialized. If the module wish to initialize it +* shall call the supplied pfnPluginManager_Register function. +* NB! the module does not have to register itself - for example if the target +* operating system or architecture is unsupported. +* -- pPluginRegInfo +*/ +VOID M_MemMap_Initialize(_Inout_ PVMMDLL_PLUGIN_REGINFO pRI) +{ + PVMMOB_PHYS2VIRT_INFORMATION pObPhys2Virt = NULL; + if((pRI->magic != VMMDLL_PLUGIN_REGINFO_MAGIC) || (pRI->wVersion != VMMDLL_PLUGIN_REGINFO_VERSION)) { return; } + if(!((pRI->tpMemoryModel == VMM_MEMORYMODEL_X64) || (pRI->tpMemoryModel == VMM_MEMORYMODEL_X86) || (pRI->tpMemoryModel == VMM_MEMORYMODEL_X86PAE))) { return; } + wcscpy_s(pRI->reg_info.wszModuleName, 32, L"memmap"); // module name + pRI->reg_info.fRootModule = FALSE; // module shows in root directory + pRI->reg_info.fProcessModule = TRUE; // module shows in process directory + pRI->reg_fn.pfnList = MemMap_List; // List function supported + pRI->reg_fn.pfnRead = MemMap_Read; // Read function supported + pRI->pfnPluginManager_Register(pRI); +} diff --git a/vmm/m_modules.h b/vmm/m_modules.h new file mode 100644 index 00000000..fb33d880 --- /dev/null +++ b/vmm/m_modules.h @@ -0,0 +1,71 @@ +// m_modules.h : definitions related to initialization of built-in modules. +// +// (c) Ulf Frisk, 2018-2019 +// Author: Ulf Frisk, pcileech@frizk.net +// +#ifndef __M_MODULES_H__ +#define __M_MODULES_H__ +#include +#include "vmmdll.h" + +/* +* Initialization function for the built-in handle info module. +* -- pPluginRegInfo +*/ +VOID M_HandleInfo_Initialize(_Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); + +/* +* Initialization function for the built-in LdrModules module. +* -- pPluginRegInfo +*/ +VOID M_LdrModules_Initialize(_Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); + +/* +* Initialization function for the built-in memmap module. +* -- pPluginRegInfo +*/ +VOID M_MemMap_Initialize(_Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); + +/* +* Initialization function for the built-in pedump module. +* -- pPluginRegInfo +*/ +VOID M_PEDump_Initialize(_Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); + +/* +* Initialization function for the built-in phys2virt module. +* -- pPluginRegInfo +*/ +VOID M_Phys2Virt_Initialize(_Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); + +/* +* Initialization function for the built-in status module. +* -- pPluginRegInfo +*/ +VOID M_Status_Initialize(_Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); + +/* +* Initialization function for the built-in SysInfo module. +* -- pPluginRegInfo +*/ +VOID M_SysInfo_Initialize(_Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); + +/* +* Initialization function for the built-in thread info module. +* -- pPluginRegInfo +*/ +VOID M_ThreadInfo_Initialize(_Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); + +/* +* Initialization function for the built-in virt2phys module. +* -- pPluginRegInfo +*/ +VOID M_Virt2Phys_Initialize(_Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); + +/* +* Initialization function for the built-in WinReg module. +* -- pPluginRegInfo +*/ +VOID M_WinReg_Initialize(_Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); + +#endif /* __M_MODULES_H__ */ diff --git a/vmm/m_pedump.c b/vmm/m_pedump.c index a3685485..a3c19267 100644 --- a/vmm/m_pedump.c +++ b/vmm/m_pedump.c @@ -3,18 +3,14 @@ // (c) Ulf Frisk, 2019 // Author: Ulf Frisk, pcileech@frizk.net // -#include "m_ldrmodules.h" +#include "m_modules.h" #include "pluginmanager.h" #include "vmm.h" -#include "vmmproc.h" -#include "vmmwin.h" -#include "vmmvfs.h" -#include "util.h" #include "pe.h" typedef struct tdPEDUMP_FILENTRY { DWORD cb; - CHAR szName[32]; + WCHAR wszName[MAX_PATH]; } PEDUMP_FILENTRY; typedef struct tdOBPEDUMP_MODULECACHE { @@ -37,7 +33,7 @@ BOOL M_PEDump_List(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _Inout_ PHANDLE pFileList) { BOOL result = FALSE; DWORD iModule; - PVMMOB_MODULEMAP pObModuleMap = NULL; + PVMMOB_MAP_MODULE pObModuleMap = NULL; POBPEDUMP_MODULECACHE pObCache = NULL; POB_VSET pObVSet_ModuleBaseAddresses = NULL; PVMM_PROCESS pProcess = (PVMM_PROCESS)ctx->pProcess; @@ -48,36 +44,36 @@ BOOL M_PEDump_List(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _Inout_ PHANDLE pFileList) pObCache = ObContainer_GetOb(pProcess->Plugin.pObCPeDumpDirCache); // 2: set up cache (if needed) if(!pObCache) { - if(!VmmProc_ModuleMapGet(pProcess, &pObModuleMap)) { return FALSE; } - pObCache = Ob_Alloc('MP', LMEM_ZEROINIT, sizeof(OBPEDUMP_MODULECACHE) + pObModuleMap->cMap * sizeof(PEDUMP_FILENTRY), NULL, NULL); + if(!VmmMap_GetModule(pProcess, &pObModuleMap)) { return FALSE; } + pObCache = Ob_Alloc('MPeD', LMEM_ZEROINIT, sizeof(OBPEDUMP_MODULECACHE) + pObModuleMap->cMap * sizeof(PEDUMP_FILENTRY), NULL, NULL); if(!pObCache) { goto fail; } // Load module bases (PE header) memory into cache with one single call. if(!(pObVSet_ModuleBaseAddresses = ObVSet_New())) { goto fail; } for(iModule = 0; iModule < pObModuleMap->cMap; iModule++) { - ObVSet_Push(pObVSet_ModuleBaseAddresses, pObModuleMap->pMap[iModule].BaseAddress); + ObVSet_Push(pObVSet_ModuleBaseAddresses, pObModuleMap->pMap[iModule].vaBase); } - VmmCachePrefetchPages(pProcess, pObVSet_ModuleBaseAddresses); + VmmCachePrefetchPages(pProcess, pObVSet_ModuleBaseAddresses, 0); // Build file listing information cache (only from in-cache items). ZeroMemory(pbPage, 0x1000); for(iModule = 0; iModule < pObModuleMap->cMap; iModule++) { - VmmReadEx(pProcess, pObModuleMap->pMap[iModule].BaseAddress, pbPage, 0x1000, &cbPageRead, VMM_FLAG_FORCECACHE_READ); + VmmReadEx(pProcess, pObModuleMap->pMap[iModule].vaBase, pbPage, 0x1000, &cbPageRead, VMM_FLAG_FORCECACHE_READ); if(cbPageRead != 0x1000) { - vmmprintfvv_fn("Skipping module: '%s' - paged/invalid?\n", pObModuleMap->pMap[iModule].szName); + vmmprintfvv_fn("Skipping module: '%S' - paged/invalid?\n", pObModuleMap->pMap[iModule].wszText); continue; } - pObCache->File[pObCache->cFiles].cb = PE_FileRaw_Size(pProcess, pObModuleMap->pMap[iModule].BaseAddress, pbPage); + pObCache->File[pObCache->cFiles].cb = PE_FileRaw_Size(pProcess, pObModuleMap->pMap[iModule].vaBase, pbPage); if(!pObCache->File[pObCache->cFiles].cb) { - vmmprintfvv_fn("Skipping module: '%s' - paged/invalid?\n", pObModuleMap->pMap[iModule].szName); + vmmprintfvv_fn("Skipping module: '%S' - paged/invalid?\n", pObModuleMap->pMap[iModule].wszText); continue; } - memcpy(pObCache->File[pObCache->cFiles].szName, pObModuleMap->pMap[iModule].szName, 32); + wcsncpy_s(pObCache->File[pObCache->cFiles].wszName, MAX_PATH, pObModuleMap->pMap[iModule].wszText, _TRUNCATE); pObCache->cFiles++; } ObContainer_SetOb(pProcess->Plugin.pObCPeDumpDirCache, pObCache); } // 3: show results and return for(iModule = 0; iModule < pObCache->cFiles; iModule++) { - VMMDLL_VfsList_AddFile(pFileList, pObCache->File[iModule].szName, pObCache->File[iModule].cb); + VMMDLL_VfsList_AddFileEx(pFileList, NULL, pObCache->File[iModule].wszName, pObCache->File[iModule].cb, NULL); } result = TRUE; fail: @@ -99,15 +95,15 @@ BOOL M_PEDump_List(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _Inout_ PHANDLE pFileList) */ NTSTATUS M_PEDump_Read(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset) { - BOOL result; - PVMM_MODULEMAP_ENTRY pModule = NULL; - PVMMOB_MODULEMAP pObModuleMap = NULL; - result = - (cbOffset <= 0x02000000) && - VmmProc_ModuleMapGetSingleEntry((PVMM_PROCESS)ctx->pProcess, ctx->wszPath, &pObModuleMap, &pModule) && - PE_FileRaw_Read(ctx->pProcess, pModule->BaseAddress, pb, cb, pcbRead, (DWORD)cbOffset); + BOOL f; + PVMM_MAP_MODULEENTRY pModule = NULL; + PVMMOB_MAP_MODULE pObModuleMap = NULL; + f = (cbOffset <= 0x02000000) && + VmmMap_GetModule((PVMM_PROCESS)ctx->pProcess, &pObModuleMap) && + (pModule = VmmMap_GetModuleEntry(pObModuleMap, ctx->wszPath)) && + PE_FileRaw_Read(ctx->pProcess, pModule->vaBase, pb, cb, pcbRead, (DWORD)cbOffset); Ob_DECREF(pObModuleMap); - return result ? VMMDLL_STATUS_SUCCESS : VMMDLL_STATUS_FILE_INVALID; + return f ? VMMDLL_STATUS_SUCCESS : VMMDLL_STATUS_FILE_INVALID; } /* @@ -127,15 +123,15 @@ NTSTATUS M_PEDump_Read(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _Out_ PBYTE pb, _In_ DWO */ NTSTATUS M_PEDump_Write(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbWrite, _In_ QWORD cbOffset) { - BOOL result; - PVMM_MODULEMAP_ENTRY pModule = NULL; - PVMMOB_MODULEMAP pObModuleMap = NULL; - result = - (cbOffset <= 0x02000000) && - VmmProc_ModuleMapGetSingleEntry((PVMM_PROCESS)ctx->pProcess, ctx->wszPath, &pObModuleMap, &pModule) && - PE_FileRaw_Write(ctx->pProcess, pModule->BaseAddress, pb, cb, pcbWrite, (DWORD)cbOffset); + BOOL f; + PVMM_MAP_MODULEENTRY pModule = NULL; + PVMMOB_MAP_MODULE pObModuleMap = NULL; + f = (cbOffset <= 0x02000000) && + VmmMap_GetModule((PVMM_PROCESS)ctx->pProcess, &pObModuleMap) && + (pModule = VmmMap_GetModuleEntry(pObModuleMap, ctx->wszPath)) && + PE_FileRaw_Write(ctx->pProcess, pModule->vaBase, pb, cb, pcbWrite, (DWORD)cbOffset); Ob_DECREF(pObModuleMap); - return result ? VMMDLL_STATUS_SUCCESS : VMMDLL_STATUS_FILE_INVALID; + return f ? VMMDLL_STATUS_SUCCESS : VMMDLL_STATUS_FILE_INVALID; } /* diff --git a/vmm/m_pedump.h b/vmm/m_pedump.h deleted file mode 100644 index 7e2eda86..00000000 --- a/vmm/m_pedump.h +++ /dev/null @@ -1,17 +0,0 @@ -// m_pedump.h : definitions related to the pedump built-in module. -// -// (c) Ulf Frisk, 2019 -// Author: Ulf Frisk, pcileech@frizk.net -// -#ifndef __M_PEDUMP_H__ -#define __M_PEDUMP_H__ -#include -#include "vmmdll.h" - -/* -* Initialization function for the built-in pedump module. -* -- pPluginRegInfo -*/ -VOID M_PEDump_Initialize(_Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); - -#endif /* __M_PEDUMP_H__ */ diff --git a/vmm/m_phys2virt.c b/vmm/m_phys2virt.c index 93fa9660..86c1e3fc 100644 --- a/vmm/m_phys2virt.c +++ b/vmm/m_phys2virt.c @@ -3,7 +3,7 @@ // (c) Ulf Frisk, 2019 // Author: Ulf Frisk, pcileech@frizk.net // -#include "m_phys2virt.h" +#include "m_modules.h" #include "pluginmanager.h" #include "util.h" #include "vmm.h" diff --git a/vmm/m_phys2virt.h b/vmm/m_phys2virt.h deleted file mode 100644 index a0aa2df1..00000000 --- a/vmm/m_phys2virt.h +++ /dev/null @@ -1,17 +0,0 @@ -// m_phys2virt.h : definitions related to the phys2virt built-in module. -// -// (c) Ulf Frisk, 2019 -// Author: Ulf Frisk, pcileech@frizk.net -// -#ifndef __M_PHYS2VIRT_H__ -#define __M_PHYS2VIRT_H__ -#include -#include "vmmdll.h" - -/* -* Initialization function for the built-in phys2virt module. -* -- pPluginRegInfo -*/ -VOID M_Phys2Virt_Initialize(_Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); - -#endif /* __M_PHYS2VIRT_H__ */ diff --git a/vmm/m_status.c b/vmm/m_status.c index 7b1cbe77..d9fe1e44 100644 --- a/vmm/m_status.c +++ b/vmm/m_status.c @@ -7,16 +7,14 @@ /* * The m_status module registers itself with the name '.status' with the plugin manager. * -* The module showcases both a "root" "process" directory module as well as a -* stateless module. It neither holds state in its "global" HandleModule context -* nor in the per-process specific HandleProcess contexts. +* The module showcase a "root" directory module as well as a stateless module. * * The module implements listing of directories as well as read and write. * Read/Write happens, if allowed, to various configuration and status settings * related to the VMM and Memory Process File System. */ -#include "m_virt2phys.h" +#include "m_modules.h" #include "pdb.h" #include "pluginmanager.h" #include "util.h" @@ -38,126 +36,122 @@ */ NTSTATUS MStatus_Read(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset) { - PVMM_PROCESS pProcess = (PVMM_PROCESS)ctx->pProcess; DWORD cchBuffer; CHAR szBuffer[0x800]; DWORD cbCallStatistics = 0; PBYTE pbCallStatistics = NULL; QWORD cPageReadTotal, cPageFailTotal; NTSTATUS nt; - // "PROCESS" - if(pProcess) { - if(!_wcsicmp(ctx->wszPath, L"cache_file_enable")) { - return Util_VfsReadFile_FromBOOL(!pProcess->fFileCacheDisabled, pb, cb, pcbRead, cbOffset); - } + if(!_wcsicmp(ctx->wszPath, L"config_process_show_terminated")) { + return Util_VfsReadFile_FromBOOL(ctxVmm->flags & VMM_FLAG_PROCESS_SHOW_TERMINATED, pb, cb, pcbRead, cbOffset); } - // "ROOT" - if(!pProcess) { - if(!_wcsicmp(ctx->wszPath, L"config_process_show_terminated")) { - return Util_VfsReadFile_FromBOOL(ctxVmm->flags & VMM_FLAG_PROCESS_SHOW_TERMINATED, pb, cb, pcbRead, cbOffset); - } - if(!_wcsicmp(ctx->wszPath, L"config_cache_enable")) { - return Util_VfsReadFile_FromBOOL(!(ctxVmm->flags & VMM_FLAG_NOCACHE), pb, cb, pcbRead, cbOffset); - } - if(!_wcsicmp(ctx->wszPath, L"config_paging_enable")) { - return Util_VfsReadFile_FromBOOL(!(ctxVmm->flags & VMM_FLAG_NOPAGING), pb, cb, pcbRead, cbOffset); - } - if(!_wcsicmp(ctx->wszPath, L"config_statistics_fncall")) { - return Util_VfsReadFile_FromBOOL(Statistics_CallGetEnabled(), pb, cb, pcbRead, cbOffset); - } - if(!_wcsicmp(ctx->wszPath, L"config_refresh_enable")) { - return Util_VfsReadFile_FromBOOL(ctxVmm->ThreadProcCache.fEnabled, pb, cb, pcbRead, cbOffset); - } - if(!_wcsicmp(ctx->wszPath, L"config_refresh_tick_period_ms")) { - return Util_VfsReadFile_FromDWORD(ctxVmm->ThreadProcCache.cMs_TickPeriod, pb, cb, pcbRead, cbOffset, FALSE); - } - if(!_wcsicmp(ctx->wszPath, L"config_refresh_read")) { - return Util_VfsReadFile_FromDWORD(ctxVmm->ThreadProcCache.cTick_Phys, pb, cb, pcbRead, cbOffset, FALSE); - } - if(!_wcsicmp(ctx->wszPath, L"config_refresh_tlb")) { - return Util_VfsReadFile_FromDWORD(ctxVmm->ThreadProcCache.cTick_TLB, pb, cb, pcbRead, cbOffset, FALSE); - } - if(!_wcsicmp(ctx->wszPath, L"config_refresh_proc_partial")) { - return Util_VfsReadFile_FromDWORD(ctxVmm->ThreadProcCache.cTick_ProcPartial, pb, cb, pcbRead, cbOffset, FALSE); - } - if(!_wcsicmp(ctx->wszPath, L"config_refresh_proc_total")) { - return Util_VfsReadFile_FromDWORD(ctxVmm->ThreadProcCache.cTick_ProcTotal, pb, cb, pcbRead, cbOffset, FALSE); - } - if(!_wcsicmp(ctx->wszPath, L"config_refresh_registry")) { - return Util_VfsReadFile_FromDWORD(ctxVmm->ThreadProcCache.cTick_Registry, pb, cb, pcbRead, cbOffset, FALSE); - } - if(!_wcsicmp(ctx->wszPath, L"statistics")) { - cPageReadTotal = ctxVmm->stat.cPageReadSuccessCacheHit + ctxVmm->stat.cPageReadSuccessCompressed + ctxVmm->stat.cPageReadSuccessDemandZero; - cPageFailTotal = ctxVmm->stat.cPageReadFailedCacheHit + ctxVmm->stat.cPageReadFailedCompressed + ctxVmm->stat.cPageReadFailed; - cchBuffer = snprintf(szBuffer, 0x800, - "VMM STATISTICS (4kB PAGES / COUNTS - HEXADECIMAL)\n" \ - "===================================================\n" \ - "PHYSICAL MEMORY: \n" \ - " READ CACHE HIT: %16llx\n" \ - " READ RETRIEVED: %16llx\n" \ - " READ FAIL: %16llx\n" \ - " WRITE: %16llx\n" \ - "PAGED VIRTUAL MEMORY: \n" \ - " READ SUCCESS: %16llx\n" \ - " Cache hit: %16llx\n" \ - " Compressed: %16llx\n" \ - " DemandZero: %16llx\n" \ - " READ FAIL: %16llx\n" \ - " Cache hit: %16llx\n" \ - " Compressed: %16llx\n" \ - "TLB (PAGE TABLES): \n" \ - " CACHE HIT: %16llx\n" \ - " RETRIEVED: %16llx\n" \ - " FAILED: %16llx\n" \ - "PHYSICAL MEMORY REFRESH: %16llx\n" \ - "TLB MEMORY REFRESH: %16llx\n" \ - "PROCESS PARTIAL REFRESH: %16llx\n" \ - "PROCESS FULL REFRESH: %16llx\n", - ctxVmm->stat.cPhysCacheHit, ctxVmm->stat.cPhysReadSuccess, ctxVmm->stat.cPhysReadFail, ctxVmm->stat.cPhysWrite, - cPageReadTotal, ctxVmm->stat.cPageReadSuccessCacheHit, ctxVmm->stat.cPageReadSuccessCompressed, ctxVmm->stat.cPageReadSuccessDemandZero, - cPageFailTotal, ctxVmm->stat.cPageReadFailedCacheHit, ctxVmm->stat.cPageReadFailedCompressed, - ctxVmm->stat.cTlbCacheHit, ctxVmm->stat.cTlbReadSuccess, ctxVmm->stat.cTlbReadFail, - ctxVmm->stat.cPhysRefreshCache, ctxVmm->stat.cTlbRefreshCache, ctxVmm->stat.cProcessRefreshPartial, ctxVmm->stat.cProcessRefreshFull - ); - return Util_VfsReadFile_FromPBYTE(szBuffer, cchBuffer, pb, cb, pcbRead, cbOffset); - } - if(!_wcsicmp(ctx->wszPath, L"statistics_fncall")) { - Statistics_CallToString(NULL, 0, &cbCallStatistics); - pbCallStatistics = LocalAlloc(0, cbCallStatistics); - if(!pbCallStatistics) { return VMMDLL_STATUS_FILE_INVALID; } - Statistics_CallToString(pbCallStatistics, cbCallStatistics, &cbCallStatistics); - nt = Util_VfsReadFile_FromPBYTE(pbCallStatistics, cbCallStatistics, pb, cb, pcbRead, cbOffset); - LocalFree(pbCallStatistics); - return nt; - } - if(!_wcsicmp(ctx->wszPath, L"config_printf_enable")) { - return Util_VfsReadFile_FromBOOL(ctxMain->cfg.fVerboseDll, pb, cb, pcbRead, cbOffset); - } - if(!_wcsicmp(ctx->wszPath, L"config_printf_v")) { - return Util_VfsReadFile_FromBOOL(ctxMain->cfg.fVerbose, pb, cb, pcbRead, cbOffset); - } - if(!_wcsicmp(ctx->wszPath, L"config_printf_vv")) { - return Util_VfsReadFile_FromBOOL(ctxMain->cfg.fVerboseExtra, pb, cb, pcbRead, cbOffset); + if(!_wcsicmp(ctx->wszPath, L"config_cache_enable")) { + return Util_VfsReadFile_FromBOOL(!(ctxVmm->flags & VMM_FLAG_NOCACHE), pb, cb, pcbRead, cbOffset); + } + if(!_wcsicmp(ctx->wszPath, L"config_paging_enable")) { + return Util_VfsReadFile_FromBOOL(!(ctxVmm->flags & VMM_FLAG_NOPAGING), pb, cb, pcbRead, cbOffset); + } + if(!_wcsicmp(ctx->wszPath, L"config_statistics_fncall")) { + return Util_VfsReadFile_FromBOOL(Statistics_CallGetEnabled(), pb, cb, pcbRead, cbOffset); + } + if(!_wcsicmp(ctx->wszPath, L"config_refresh_enable")) { + return Util_VfsReadFile_FromBOOL(ctxVmm->ThreadProcCache.fEnabled, pb, cb, pcbRead, cbOffset); + } + if(!_wcsicmp(ctx->wszPath, L"config_refresh_tick_period_ms")) { + return Util_VfsReadFile_FromDWORD(ctxVmm->ThreadProcCache.cMs_TickPeriod, pb, cb, pcbRead, cbOffset, FALSE); + } + if(!_wcsicmp(ctx->wszPath, L"config_refresh_read")) { + return Util_VfsReadFile_FromDWORD(ctxVmm->ThreadProcCache.cTick_Phys, pb, cb, pcbRead, cbOffset, FALSE); + } + if(!_wcsicmp(ctx->wszPath, L"config_refresh_tlb")) { + return Util_VfsReadFile_FromDWORD(ctxVmm->ThreadProcCache.cTick_TLB, pb, cb, pcbRead, cbOffset, FALSE); + } + if(!_wcsicmp(ctx->wszPath, L"config_refresh_proc_partial")) { + return Util_VfsReadFile_FromDWORD(ctxVmm->ThreadProcCache.cTick_ProcPartial, pb, cb, pcbRead, cbOffset, FALSE); + } + if(!_wcsicmp(ctx->wszPath, L"config_refresh_proc_total")) { + return Util_VfsReadFile_FromDWORD(ctxVmm->ThreadProcCache.cTick_ProcTotal, pb, cb, pcbRead, cbOffset, FALSE); + } + if(!_wcsicmp(ctx->wszPath, L"config_refresh_registry")) { + return Util_VfsReadFile_FromDWORD(ctxVmm->ThreadProcCache.cTick_Registry, pb, cb, pcbRead, cbOffset, FALSE); + } + if(!_wcsicmp(ctx->wszPath, L"statistics")) { + cPageReadTotal = ctxVmm->stat.page.cPrototype + ctxVmm->stat.page.cTransition + ctxVmm->stat.page.cDemandZero + ctxVmm->stat.page.cVAD + ctxVmm->stat.page.cCacheHit + ctxVmm->stat.page.cPageFile + ctxVmm->stat.page.cCompressed; + cPageFailTotal = ctxVmm->stat.page.cFailCacheHit + ctxVmm->stat.page.cFailVAD + ctxVmm->stat.page.cFailPageFile + ctxVmm->stat.page.cFailCompressed + ctxVmm->stat.page.cFail; + cchBuffer = snprintf(szBuffer, 0x800, + "VMM STATISTICS (4kB PAGES / COUNTS - HEXADECIMAL)\n" \ + "===================================================\n" \ + "PHYSICAL MEMORY: \n" \ + " READ CACHE HIT: %16llx\n" \ + " READ RETRIEVED: %16llx\n" \ + " READ FAIL: %16llx\n" \ + " WRITE: %16llx\n" \ + "PAGED VIRTUAL MEMORY: \n" \ + " READ SUCCESS: %16llx\n" \ + " Prototype: %16llx\n" \ + " Transition: %16llx\n" \ + " DemandZero: %16llx\n" \ + " VAD: %16llx\n" \ + " Cache: %16llx\n" \ + " PageFile: %16llx\n" \ + " Compressed: %16llx\n" \ + " READ FAIL: %16llx\n" \ + " Cache: %16llx\n" \ + " VAD: %16llx\n" \ + " PageFile: %16llx\n" \ + " Compressed: %16llx\n" \ + "TLB (PAGE TABLES): \n" \ + " CACHE HIT: %16llx\n" \ + " RETRIEVED: %16llx\n" \ + " FAILED: %16llx\n" \ + "PHYSICAL MEMORY REFRESH: %16llx\n" \ + "TLB MEMORY REFRESH: %16llx\n" \ + "PROCESS PARTIAL REFRESH: %16llx\n" \ + "PROCESS FULL REFRESH: %16llx\n", + ctxVmm->stat.cPhysCacheHit, ctxVmm->stat.cPhysReadSuccess, ctxVmm->stat.cPhysReadFail, ctxVmm->stat.cPhysWrite, + cPageReadTotal, ctxVmm->stat.page.cPrototype, ctxVmm->stat.page.cTransition, ctxVmm->stat.page.cDemandZero, ctxVmm->stat.page.cVAD, ctxVmm->stat.page.cCacheHit, ctxVmm->stat.page.cPageFile, ctxVmm->stat.page.cCompressed, + cPageFailTotal, ctxVmm->stat.page.cFailCacheHit, ctxVmm->stat.page.cFailVAD, ctxVmm->stat.page.cFailPageFile, ctxVmm->stat.page.cFailCompressed, + ctxVmm->stat.cTlbCacheHit, ctxVmm->stat.cTlbReadSuccess, ctxVmm->stat.cTlbReadFail, + ctxVmm->stat.cPhysRefreshCache, ctxVmm->stat.cTlbRefreshCache, ctxVmm->stat.cProcessRefreshPartial, ctxVmm->stat.cProcessRefreshFull + ); + return Util_VfsReadFile_FromPBYTE(szBuffer, cchBuffer, pb, cb, pcbRead, cbOffset); + } + if(!_wcsicmp(ctx->wszPath, L"statistics_fncall")) { + Statistics_CallToString(NULL, 0, &cbCallStatistics); + pbCallStatistics = LocalAlloc(0, cbCallStatistics); + if(!pbCallStatistics) { return VMMDLL_STATUS_FILE_INVALID; } + Statistics_CallToString(pbCallStatistics, cbCallStatistics, &cbCallStatistics); + nt = Util_VfsReadFile_FromPBYTE(pbCallStatistics, cbCallStatistics, pb, cb, pcbRead, cbOffset); + LocalFree(pbCallStatistics); + return nt; + } + if(!_wcsicmp(ctx->wszPath, L"config_printf_enable")) { + return Util_VfsReadFile_FromBOOL(ctxMain->cfg.fVerboseDll, pb, cb, pcbRead, cbOffset); + } + if(!_wcsicmp(ctx->wszPath, L"config_printf_v")) { + return Util_VfsReadFile_FromBOOL(ctxMain->cfg.fVerbose, pb, cb, pcbRead, cbOffset); + } + if(!_wcsicmp(ctx->wszPath, L"config_printf_vv")) { + return Util_VfsReadFile_FromBOOL(ctxMain->cfg.fVerboseExtra, pb, cb, pcbRead, cbOffset); + } + if(!_wcsicmp(ctx->wszPath, L"config_printf_vvv")) { + return Util_VfsReadFile_FromBOOL(ctxMain->cfg.fVerboseExtraTlp, pb, cb, pcbRead, cbOffset); + } + if(!_wcsicmp(ctx->wszPath, L"native_max_address")) { + return Util_VfsReadFile_FromQWORD(ctxMain->dev.paMaxNative, pb, cb, pcbRead, cbOffset, FALSE); + } + if(!_wcsnicmp(ctx->wszPath, L"config_symbol", 13)) { + if(!_wcsicmp(ctx->wszPath, L"config_symbol_enable")) { + return Util_VfsReadFile_FromBOOL(ctxMain->pdb.fEnable, pb, cb, pcbRead, cbOffset); } - if(!_wcsicmp(ctx->wszPath, L"config_printf_vvv")) { - return Util_VfsReadFile_FromBOOL(ctxMain->cfg.fVerboseExtraTlp, pb, cb, pcbRead, cbOffset); + if(!_wcsicmp(ctx->wszPath, L"config_symbolcache")) { + return Util_VfsReadFile_FromPBYTE(ctxMain->pdb.szLocal, strlen(ctxMain->pdb.szLocal), pb, cb, pcbRead, cbOffset); } - if(!_wcsicmp(ctx->wszPath, L"native_max_address")) { - return Util_VfsReadFile_FromQWORD(ctxMain->dev.paMaxNative, pb, cb, pcbRead, cbOffset, FALSE); + if(!_wcsicmp(ctx->wszPath, L"config_symbolserver")) { + return Util_VfsReadFile_FromPBYTE(ctxMain->pdb.szServer, strlen(ctxMain->pdb.szServer), pb, cb, pcbRead, cbOffset); } - if(!_wcsnicmp(ctx->wszPath, L"config_symbol", 13)) { - if(!_wcsicmp(ctx->wszPath, L"config_symbol_enable")) { - return Util_VfsReadFile_FromBOOL(ctxMain->pdb.fEnable, pb, cb, pcbRead, cbOffset); - } - if(!_wcsicmp(ctx->wszPath, L"config_symbolcache")) { - return Util_VfsReadFile_FromPBYTE(ctxMain->pdb.szLocal, strlen(ctxMain->pdb.szLocal), pb, cb, pcbRead, cbOffset); - } - if(!_wcsicmp(ctx->wszPath, L"config_symbolserver")) { - return Util_VfsReadFile_FromPBYTE(ctxMain->pdb.szServer, strlen(ctxMain->pdb.szServer), pb, cb, pcbRead, cbOffset); - } - if(!_wcsicmp(ctx->wszPath, L"config_symbolserver_enable")) { - return Util_VfsReadFile_FromBOOL(ctxMain->pdb.fServerEnable, pb, cb, pcbRead, cbOffset); - } + if(!_wcsicmp(ctx->wszPath, L"config_symbolserver_enable")) { + return Util_VfsReadFile_FromBOOL(ctxMain->pdb.fServerEnable, pb, cb, pcbRead, cbOffset); } } return VMMDLL_STATUS_FILE_INVALID; @@ -183,105 +177,90 @@ NTSTATUS MStatus_Write_NotifyVerbosityChange(_In_ NTSTATUS nt) */ NTSTATUS MStatus_Write(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbWrite, _In_ QWORD cbOffset) { - PVMM_PROCESS pProcess = (PVMM_PROCESS)ctx->pProcess; NTSTATUS nt; BOOL fEnable = FALSE; - // "PROCESS" - if(pProcess) { - if(!_wcsicmp(ctx->wszPath, L"cache_file_enable")) { - if((cbOffset == 0) && (cb > 0)) { - if(((PCHAR)pb)[0] == '1') { pProcess->fFileCacheDisabled = FALSE; } - if(((PCHAR)pb)[0] == '0') { pProcess->fFileCacheDisabled = TRUE; } - } - *pcbWrite = cb; - return VMMDLL_STATUS_SUCCESS; + if(!_wcsicmp(ctx->wszPath, L"config_process_show_terminated")) { + nt = Util_VfsWriteFile_BOOL(&fEnable, pb, cb, pcbWrite, cbOffset); + if(nt == VMMDLL_STATUS_SUCCESS) { + ctxVmm->flags &= ~VMM_FLAG_PROCESS_SHOW_TERMINATED; + ctxVmm->flags |= fEnable ? VMM_FLAG_PROCESS_SHOW_TERMINATED : 0; } + return nt; } - // "ROOT" - if(!pProcess) { - - if(!_wcsicmp(ctx->wszPath, L"config_process_show_terminated")) { - nt = Util_VfsWriteFile_BOOL(&fEnable, pb, cb, pcbWrite, cbOffset); - if(nt == VMMDLL_STATUS_SUCCESS) { - ctxVmm->flags &= ~VMM_FLAG_PROCESS_SHOW_TERMINATED; - ctxVmm->flags |= fEnable ? VMM_FLAG_PROCESS_SHOW_TERMINATED : 0; - } - return nt; - } - if(!_wcsicmp(ctx->wszPath, L"config_cache_enable")) { - nt = Util_VfsWriteFile_BOOL(&fEnable, pb, cb, pcbWrite, cbOffset); - if(nt == VMMDLL_STATUS_SUCCESS) { - ctxVmm->flags &= ~VMM_FLAG_NOCACHE; - ctxVmm->flags |= fEnable ? 0 : VMM_FLAG_NOCACHE; - } - return nt; - } - if(!_wcsicmp(ctx->wszPath, L"config_paging_enable")) { - nt = Util_VfsWriteFile_BOOL(&fEnable, pb, cb, pcbWrite, cbOffset); - if(nt == VMMDLL_STATUS_SUCCESS) { - ctxVmm->flags &= ~VMM_FLAG_NOPAGING; - ctxVmm->flags |= fEnable ? 0 : VMM_FLAG_NOPAGING; - } - return nt; - } - if(!_wcsicmp(ctx->wszPath, L"config_statistics_fncall")) { - nt = Util_VfsWriteFile_BOOL(&fEnable, pb, cb, pcbWrite, cbOffset); - if(nt == VMMDLL_STATUS_SUCCESS) { - Statistics_CallSetEnabled(fEnable); - } - return nt; - } - if(!_wcsicmp(ctx->wszPath, L"config_refresh_tick_period_ms")) { - return Util_VfsWriteFile_DWORD(&ctxVmm->ThreadProcCache.cMs_TickPeriod, pb, cb, pcbWrite, cbOffset, 50); - } - if(!_wcsicmp(ctx->wszPath, L"config_refresh_read")) { - return Util_VfsWriteFile_DWORD(&ctxVmm->ThreadProcCache.cTick_Phys, pb, cb, pcbWrite, cbOffset, 1); - } - if(!_wcsicmp(ctx->wszPath, L"config_refresh_tlb")) { - return Util_VfsWriteFile_DWORD(&ctxVmm->ThreadProcCache.cTick_TLB, pb, cb, pcbWrite, cbOffset, 1); - } - if(!_wcsicmp(ctx->wszPath, L"config_refresh_proc_partial")) { - return Util_VfsWriteFile_DWORD(&ctxVmm->ThreadProcCache.cTick_ProcPartial, pb, cb, pcbWrite, cbOffset, 1); - } - if(!_wcsicmp(ctx->wszPath, L"config_refresh_proc_total")) { - return Util_VfsWriteFile_DWORD(&ctxVmm->ThreadProcCache.cTick_ProcTotal, pb, cb, pcbWrite, cbOffset, 1); + if(!_wcsicmp(ctx->wszPath, L"config_cache_enable")) { + nt = Util_VfsWriteFile_BOOL(&fEnable, pb, cb, pcbWrite, cbOffset); + if(nt == VMMDLL_STATUS_SUCCESS) { + ctxVmm->flags &= ~VMM_FLAG_NOCACHE; + ctxVmm->flags |= fEnable ? 0 : VMM_FLAG_NOCACHE; } - if(!_wcsicmp(ctx->wszPath, L"config_refresh_registry")) { - VmmWinReg_Refresh(); - return Util_VfsWriteFile_DWORD(&ctxVmm->ThreadProcCache.cTick_Registry, pb, cb, pcbWrite, cbOffset, 1); + return nt; + } + if(!_wcsicmp(ctx->wszPath, L"config_paging_enable")) { + nt = Util_VfsWriteFile_BOOL(&fEnable, pb, cb, pcbWrite, cbOffset); + if(nt == VMMDLL_STATUS_SUCCESS) { + ctxVmm->flags &= ~VMM_FLAG_NOPAGING; + ctxVmm->flags |= fEnable ? 0 : VMM_FLAG_NOPAGING; } - if(!_wcsicmp(ctx->wszPath, L"config_printf_enable")) { - return MStatus_Write_NotifyVerbosityChange( - Util_VfsWriteFile_BOOL(&ctxMain->cfg.fVerboseDll, pb, cb, pcbWrite, cbOffset)); + return nt; + } + if(!_wcsicmp(ctx->wszPath, L"config_statistics_fncall")) { + nt = Util_VfsWriteFile_BOOL(&fEnable, pb, cb, pcbWrite, cbOffset); + if(nt == VMMDLL_STATUS_SUCCESS) { + Statistics_CallSetEnabled(fEnable); } - if(!_wcsicmp(ctx->wszPath, L"config_printf_v")) { - return MStatus_Write_NotifyVerbosityChange( - Util_VfsWriteFile_BOOL(&ctxMain->cfg.fVerbose, pb, cb, pcbWrite, cbOffset)); + return nt; + } + if(!_wcsicmp(ctx->wszPath, L"config_refresh_tick_period_ms")) { + return Util_VfsWriteFile_DWORD(&ctxVmm->ThreadProcCache.cMs_TickPeriod, pb, cb, pcbWrite, cbOffset, 50); + } + if(!_wcsicmp(ctx->wszPath, L"config_refresh_read")) { + return Util_VfsWriteFile_DWORD(&ctxVmm->ThreadProcCache.cTick_Phys, pb, cb, pcbWrite, cbOffset, 1); + } + if(!_wcsicmp(ctx->wszPath, L"config_refresh_tlb")) { + return Util_VfsWriteFile_DWORD(&ctxVmm->ThreadProcCache.cTick_TLB, pb, cb, pcbWrite, cbOffset, 1); + } + if(!_wcsicmp(ctx->wszPath, L"config_refresh_proc_partial")) { + return Util_VfsWriteFile_DWORD(&ctxVmm->ThreadProcCache.cTick_ProcPartial, pb, cb, pcbWrite, cbOffset, 1); + } + if(!_wcsicmp(ctx->wszPath, L"config_refresh_proc_total")) { + return Util_VfsWriteFile_DWORD(&ctxVmm->ThreadProcCache.cTick_ProcTotal, pb, cb, pcbWrite, cbOffset, 1); + } + if(!_wcsicmp(ctx->wszPath, L"config_refresh_registry")) { + VmmWinReg_Refresh(); + return Util_VfsWriteFile_DWORD(&ctxVmm->ThreadProcCache.cTick_Registry, pb, cb, pcbWrite, cbOffset, 1); + } + if(!_wcsicmp(ctx->wszPath, L"config_printf_enable")) { + return MStatus_Write_NotifyVerbosityChange( + Util_VfsWriteFile_BOOL(&ctxMain->cfg.fVerboseDll, pb, cb, pcbWrite, cbOffset)); + } + if(!_wcsicmp(ctx->wszPath, L"config_printf_v")) { + return MStatus_Write_NotifyVerbosityChange( + Util_VfsWriteFile_BOOL(&ctxMain->cfg.fVerbose, pb, cb, pcbWrite, cbOffset)); + } + if(!_wcsicmp(ctx->wszPath, L"config_printf_vv")) { + return MStatus_Write_NotifyVerbosityChange( + Util_VfsWriteFile_BOOL(&ctxMain->cfg.fVerboseExtra, pb, cb, pcbWrite, cbOffset)); + } + if(!_wcsicmp(ctx->wszPath, L"config_printf_vvv")) { + return MStatus_Write_NotifyVerbosityChange( + Util_VfsWriteFile_BOOL(&ctxMain->cfg.fVerboseExtraTlp, pb, cb, pcbWrite, cbOffset)); + } + if(!_wcsnicmp(ctx->wszPath, L"config_symbol", 13)) { + nt = VMMDLL_STATUS_FILE_INVALID; + if(!_wcsicmp(ctx->wszPath, L"config_symbol_enable")) { + nt = Util_VfsWriteFile_DWORD(&ctxMain->pdb.fEnable, pb, cb, pcbWrite, cbOffset, 1); } - if(!_wcsicmp(ctx->wszPath, L"config_printf_vv")) { - return MStatus_Write_NotifyVerbosityChange( - Util_VfsWriteFile_BOOL(&ctxMain->cfg.fVerboseExtra, pb, cb, pcbWrite, cbOffset)); + if(!_wcsicmp(ctx->wszPath, L"config_symbolcache")) { + nt = Util_VfsWriteFile_PBYTE(ctxMain->pdb.szLocal, _countof(ctxMain->pdb.szLocal) - 1, pb, cb, pcbWrite, cbOffset, TRUE); } - if(!_wcsicmp(ctx->wszPath, L"config_printf_vvv")) { - return MStatus_Write_NotifyVerbosityChange( - Util_VfsWriteFile_BOOL(&ctxMain->cfg.fVerboseExtraTlp, pb, cb, pcbWrite, cbOffset)); + if(!_wcsicmp(ctx->wszPath, L"config_symbolserver")) { + nt = Util_VfsWriteFile_PBYTE(ctxMain->pdb.szServer, _countof(ctxMain->pdb.szServer) - 1, pb, cb, pcbWrite, cbOffset, TRUE); } - if(!_wcsnicmp(ctx->wszPath, L"config_symbol", 13)) { - if(!_wcsicmp(ctx->wszPath, L"config_symbol_enable")) { - nt = Util_VfsWriteFile_DWORD(&ctxMain->pdb.fEnable, pb, cb, pcbWrite, cbOffset, 1); - } - if(!_wcsicmp(ctx->wszPath, L"config_symbolcache")) { - nt = Util_VfsWriteFile_PBYTE(ctxMain->pdb.szLocal, _countof(ctxMain->pdb.szLocal) - 1, pb, cb, pcbWrite, cbOffset, TRUE); - } - if(!_wcsicmp(ctx->wszPath, L"config_symbolserver")) { - nt = Util_VfsWriteFile_PBYTE(ctxMain->pdb.szServer, _countof(ctxMain->pdb.szServer) - 1, pb, cb, pcbWrite, cbOffset, TRUE); - } - if(!_wcsicmp(ctx->wszPath, L"config_symbolserver_enable")) { - nt = Util_VfsWriteFile_DWORD(&ctxMain->pdb.fServerEnable, pb, cb, pcbWrite, cbOffset, 1); - } - PDB_ConfigChange(); - return nt; + if(!_wcsicmp(ctx->wszPath, L"config_symbolserver_enable")) { + nt = Util_VfsWriteFile_DWORD(&ctxMain->pdb.fServerEnable, pb, cb, pcbWrite, cbOffset, 1); } + PDB_ConfigChange(); + return nt; } return VMMDLL_STATUS_FILE_INVALID; } @@ -297,7 +276,6 @@ NTSTATUS MStatus_Write(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ PBYTE pb, _In_ DWOR BOOL MStatus_List(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _Inout_ PHANDLE pFileList) { DWORD cbCallStatistics = 0; - PVMM_PROCESS pProcess = (PVMM_PROCESS)ctx->pProcess; // not module root directory -> fail! if(ctx->wszPath[0]) { return FALSE; } // "root" view @@ -326,10 +304,6 @@ BOOL MStatus_List(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _Inout_ PHANDLE pFileList) Statistics_CallToString(NULL, 0, &cbCallStatistics); VMMDLL_VfsList_AddFile(pFileList, "statistics_fncall", cbCallStatistics); } - // "process" view - if(pProcess) { - VMMDLL_VfsList_AddFile(pFileList, "cache_file_enable", 1); - } return TRUE; } @@ -347,7 +321,6 @@ VOID M_Status_Initialize(_Inout_ PVMMDLL_PLUGIN_REGINFO pRI) // .status module is always valid - no check against pPluginRegInfo->tpMemoryModel, tpSystem wcscpy_s(pRI->reg_info.wszModuleName, 32, L".status"); // module name pRI->reg_info.fRootModule = TRUE; // module shows in root directory - pRI->reg_info.fProcessModule = TRUE; // module shows in process directory pRI->reg_fn.pfnList = MStatus_List; // List function supported pRI->reg_fn.pfnRead = MStatus_Read; // Read function supported pRI->reg_fn.pfnWrite = MStatus_Write; // Write function supported diff --git a/vmm/m_status.h b/vmm/m_status.h deleted file mode 100644 index 630aa5ea..00000000 --- a/vmm/m_status.h +++ /dev/null @@ -1,17 +0,0 @@ -// m_status.h : definitions related to the .status built-in module. -// -// (c) Ulf Frisk, 2018-2019 -// Author: Ulf Frisk, pcileech@frizk.net -// -#ifndef __M_STATUS_H__ -#define __M_STATUS_H__ -#include -#include "vmmdll.h" - -/* -* Initialization function for the built-in status module. -* -- pPluginRegInfo -*/ -VOID M_Status_Initialize(_Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); - -#endif /* __M_STATUS_H__ */ diff --git a/vmm/m_sysinfo.c b/vmm/m_sysinfo.c index 489851bf..fb2edc84 100644 --- a/vmm/m_sysinfo.c +++ b/vmm/m_sysinfo.c @@ -17,7 +17,7 @@ // Author: Ulf Frisk, pcileech@frizk.net // #include -#include "m_sysinfo.h" +#include "m_modules.h" #include "vmm.h" #include "vmmwin.h" #include "vmmwintcpip.h" @@ -83,7 +83,7 @@ DWORD MSysInfo_ProcTree_ProcessItems(_In_ PMSYSINFO_PROCTREE_ENTRY pProcessEntry fStateTerminated = (pProcessEntry->pObProcess->dwState != 0); fWinNativeProc = (pProcessEntry->dwPID == 4) || (pProcessEntry->dwPPID == 4); for(i = 0; !fWinNativeProc && (i < (sizeof(szMSYSINFO_WHITELIST_WINDOWS_PATHS_AND_BINARIES) / sizeof(LPSTR))); i++) { - fWinNativeProc = (NULL != strstr(pProcessEntry->pObProcess->pObProcessPersistent->szPathKernel, szMSYSINFO_WHITELIST_WINDOWS_PATHS_AND_BINARIES[i])); + fWinNativeProc = (NULL != strstr(pProcessEntry->pObProcess->pObPersistent->szPathKernel, szMSYSINFO_WHITELIST_WINDOWS_PATHS_AND_BINARIES[i])); } o = snprintf( pb, @@ -97,18 +97,18 @@ DWORD MSysInfo_ProcTree_ProcessItems(_In_ PMSYSINFO_PROCTREE_ENTRY pProcessEntry pProcessEntry->dwPPID, fStateTerminated ? 'T' : ' ', fWinNativeProc ? ' ' : '*', - fVerbose ? pProcessEntry->pObProcess->pObProcessPersistent->szPathKernel : "" + fVerbose ? pProcessEntry->pObProcess->pObPersistent->szPathKernel : "" ); if(fVerbose) { - if(pProcessEntry->pObProcess->pObProcessPersistent->UserProcessParams.szImagePathName) { + if(pProcessEntry->pObProcess->pObPersistent->UserProcessParams.szImagePathName) { o += snprintf(pb + o, cb - o, "%44s%-*s\n", "", - pProcessEntry->pObProcess->pObProcessPersistent->UserProcessParams.cchImagePathName, - pProcessEntry->pObProcess->pObProcessPersistent->UserProcessParams.szImagePathName); + pProcessEntry->pObProcess->pObPersistent->UserProcessParams.cchImagePathName, + pProcessEntry->pObProcess->pObPersistent->UserProcessParams.szImagePathName); } - if(pProcessEntry->pObProcess->pObProcessPersistent->UserProcessParams.szCommandLine) { + if(pProcessEntry->pObProcess->pObPersistent->UserProcessParams.szCommandLine) { o += snprintf(pb + o, cb - o, "%44s%-*s\n", "", - pProcessEntry->pObProcess->pObProcessPersistent->UserProcessParams.cchCommandLine, - pProcessEntry->pObProcess->pObProcessPersistent->UserProcessParams.szCommandLine); + pProcessEntry->pObProcess->pObPersistent->UserProcessParams.cchCommandLine, + pProcessEntry->pObProcess->pObPersistent->UserProcessParams.szCommandLine); } o += snprintf(pb + o, cb - o, "\n"); } @@ -209,7 +209,7 @@ NTSTATUS MSysInfo_Read_ProcTree(_In_ LPWSTR wszPath, _Out_ PBYTE pb, _In_ DWORD VOID MSysInfo_List_ProcTree_ProcessUserParams_CallbackAction(_In_ PVMM_PROCESS pProcess, _In_ PDWORD pcTotalBytes) { PVMMWIN_USER_PROCESS_PARAMETERS pu = VmmWin_UserProcessParameters_Get(pProcess); - DWORD c = MSYSINFO_PROCTREE_LINE_LENGTH_BASE + pProcess->pObProcessPersistent->cchPathKernel + 1; + DWORD c = MSYSINFO_PROCTREE_LINE_LENGTH_BASE + pProcess->pObPersistent->cchPathKernel + 1; if(pu->szImagePathName) { c += MSYSINFO_PROCTREE_LINE_LENGTH_BASE + pu->cchImagePathName; } @@ -325,7 +325,7 @@ BOOL MSysInfo_GetNetContext_ToString(_In_ PVMMWIN_TCPIP_ENTRY pTcpE, _In_ DWORD szTime, pE->dwPID, (pObProcess ? pObProcess->szName : "***"), - (pObProcess ? pObProcess->pObProcessPersistent->szPathKernel : "***") + (pObProcess ? pObProcess->pObPersistent->szPathKernel : "***") ); Ob_DECREF_NULL(&pObProcess); } @@ -363,7 +363,7 @@ PMSYSINFO_OB_NET_CONTEXT MSysInfo_GetNetContext() // 2: replace with new version if(!VmmWinTcpIp_TcpE_Get(&pTcpE, &cTcpE)) { goto finish; } Ob_DECREF_NULL(&gp_MSYSINFO_OB_NETCONTEXT); - pObCtx = gp_MSYSINFO_OB_NETCONTEXT = Ob_Alloc('IP', LMEM_ZEROINIT, sizeof(MSYSINFO_OB_NET_CONTEXT), MSysInfo_ObNetContext_CallbackRefCount1, NULL); + pObCtx = gp_MSYSINFO_OB_NETCONTEXT = Ob_Alloc('IP__', LMEM_ZEROINIT, sizeof(MSYSINFO_OB_NET_CONTEXT), MSysInfo_ObNetContext_CallbackRefCount1, NULL); if(!pObCtx) { goto finish; } // alloc failed - should not happen -> finish and return NULL MSysInfo_GetNetContext_ToString(pTcpE, cTcpE, &pObCtx->pbFile, &pObCtx->cbFile, &pObCtx->pbFileVerbose, &pObCtx->cbFileVerbose); pObCtx->qwCreateTimeTickCount64 = GetTickCount64(); diff --git a/vmm/m_sysinfo.h b/vmm/m_sysinfo.h deleted file mode 100644 index 86794c18..00000000 --- a/vmm/m_sysinfo.h +++ /dev/null @@ -1,30 +0,0 @@ -// m_sysinfo.h : definitions related to the SysInfo built-in module. -// -// The SysInfo module is responsible for displaying various informational files -// at the path /sysinfo/ -// -// Functionality includes: -// ProcTree - process tree listing showing parent processes - files: -// "proctree" -// "proctree-v" -// Version - operating system version information - files: -// "version" -// "version-major" -// "version-minor" -// "version-build" -// -// (c) Ulf Frisk, 2019 -// Author: Ulf Frisk, pcileech@frizk.net -// -#ifndef __M_SYSINFO_H__ -#define __M_SYSINFO_H__ -#include -#include "vmmdll.h" - -/* -* Initialization function for the built-in SysInfo module. -* -- pPluginRegInfo -*/ -VOID M_SysInfo_Initialize(_Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); - -#endif /* __M_SYSINFO_H__ */ diff --git a/vmm/m_threadinfo.c b/vmm/m_threadinfo.c new file mode 100644 index 00000000..314a175a --- /dev/null +++ b/vmm/m_threadinfo.c @@ -0,0 +1,300 @@ +// m_threadinfo.c : implementation of the thread info built-in module. +// +// (c) Ulf Frisk, 2019 +// Author: Ulf Frisk, pcileech@frizk.net +// +#include "m_modules.h" +#include "pluginmanager.h" +#include "util.h" +#include "vmm.h" +#include "vmmdll.h" +#include "vmmvfs.h" + +#define THREADINFO_LINELENGTH 186ULL +#define THREADINFO_INFOFILE_LENGTH 582ULL + +_Success_(return == 0) +NTSTATUS ThreadInfo_Read_ThreadInfo(_In_ PVMM_MAP_THREADENTRY pThreadEntry, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset) +{ + DWORD o; + CHAR sz[THREADINFO_INFOFILE_LENGTH + 1]; + CHAR szTimeCreate[32] = { 0 }, szTimeExit[32] = { 0 }; + Util_FileTime2String((PFILETIME)&pThreadEntry->ftCreateTime, szTimeCreate); + Util_FileTime2String((PFILETIME)&pThreadEntry->ftExitTime, szTimeExit); + o = snprintf( + sz, + THREADINFO_INFOFILE_LENGTH + 1, + "PID: %21i\n" \ + "TID: %21i\n" \ + "ExitStatus: %21x\n" \ + "State: %21x\n" \ + "Running: %21x\n" \ + "Priority: %21x\n" \ + "BasePriority: %21x\n" \ + "ETHREAD: %21llx\n" \ + "TEB: %21llx\n" \ + "StartAddress: %16llx\n" \ + "UserStackBase: %16llx\n" \ + "UserStackLimit: %16llx\n" \ + "KernelStackBase: %16llx\n" \ + "KernelStackLimit: %16llx\n" \ + "CreateTime: %-26s\n" \ + "ExitTime: %-26s\n", + pThreadEntry->dwPID, + pThreadEntry->dwTID, + pThreadEntry->dwExitStatus, + pThreadEntry->bState, + pThreadEntry->bRunning, + pThreadEntry->bPriority, + pThreadEntry->bBasePriority, + pThreadEntry->vaETHREAD, + pThreadEntry->vaTeb, + pThreadEntry->vaStartAddress, + pThreadEntry->vaStackBaseUser, + pThreadEntry->vaStackLimitUser, + pThreadEntry->vaStackBaseKernel, + pThreadEntry->vaStackLimitKernel, + szTimeCreate, + szTimeExit + ); + return Util_VfsReadFile_FromPBYTE(sz, THREADINFO_INFOFILE_LENGTH, pb, cb, pcbRead, cbOffset); +} + +_Success_(return == 0) +NTSTATUS ThreadInfo_Read_ThreadMap(_In_ PVMMOB_MAP_THREAD pThreadMap, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset) +{ + NTSTATUS nt; + LPSTR sz; + QWORD i, o = 0, cbMax, cStart, cEnd, cbLINELENGTH; + PVMM_MAP_THREADENTRY pT; + CHAR szTimeCreate[MAX_PATH] = { 0 }, szTimeExit[MAX_PATH] = { 0 }; + cbLINELENGTH = THREADINFO_LINELENGTH; + cStart = (DWORD)(cbOffset / cbLINELENGTH); + cEnd = (DWORD)min(pThreadMap->cMap - 1, (cb + cbOffset + cbLINELENGTH - 1) / cbLINELENGTH); + cbMax = 1 + (1 + cEnd - cStart) * cbLINELENGTH; + if(!pThreadMap->cMap || (cStart > pThreadMap->cMap)) { return VMMDLL_STATUS_END_OF_FILE; } + if(!(sz = LocalAlloc(LMEM_ZEROINIT, cbMax))) { return VMMDLL_STATUS_FILE_INVALID; } + for(i = cStart; i <= cEnd; i++) { + pT = pThreadMap->pMap + i; + Util_FileTime2String((PFILETIME)&pT->ftCreateTime, szTimeCreate); + Util_FileTime2String((PFILETIME)&pT->ftExitTime, szTimeExit); + o += snprintf( + sz + o, + cbMax - o, + "%04x%7i%8i %16llx %2x %2x %2x %2x %8x %16llx -- %16llx : %16llx > %16llx [%s :: %s]\n", + (DWORD)i, + pT->dwPID, + pT->dwTID, + pT->vaETHREAD, + pT->bState, + pT->bRunning, + pT->bBasePriority, + pT->bPriority, + pT->dwExitStatus, + pT->vaStartAddress, + pT->vaTeb, + pT->vaStackBaseUser, + pT->vaStackLimitUser, + szTimeCreate, + szTimeExit + ); + } + nt = Util_VfsReadFile_FromPBYTE(sz, cbMax - 1, pb, cb, pcbRead, cbOffset - cStart * cbLINELENGTH); + LocalFree(sz); + return nt; +} + +/* +* Read : function as specified by the module manager. The module manager will +* call into this callback function whenever a read shall occur from a "file". +* -- ctx +* -- pb +* -- cb +* -- pcbRead +* -- cbOffset +* -- return +*/ +_Success_(return == 0) +NTSTATUS ThreadInfo_Read(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset) +{ + NTSTATUS nt = VMMDLL_STATUS_FILE_INVALID; + PVMMOB_MAP_THREAD pObThreadMap = NULL; + PVMM_PROCESS pObSystemProcess = NULL; + WCHAR wszThreadName[16 + 1]; + LPWSTR wszSubPath; + DWORD dwTID; + PVMM_MAP_THREADENTRY pe; + if(!VmmMap_GetThread(ctx->pProcess, &pObThreadMap)) { return VMMDLL_STATUS_FILE_INVALID; } + // module root - thread info file + if(!_wcsicmp(ctx->wszPath, L"threads.txt")) { + nt = ThreadInfo_Read_ThreadMap(pObThreadMap, pb, cb, pcbRead, cbOffset); + goto finish; + } + // individual thread file + wszSubPath = Util_PathSplit2_ExWCHAR(ctx->wszPath, wszThreadName, _countof(wszThreadName)); + if(wszSubPath && (dwTID = (DWORD)Util_GetNumericW(ctx->wszPath)) && (pe = VmmMap_GetThreadEntry(pObThreadMap, dwTID))) { + if(!_wcsicmp(wszSubPath, L"info.txt")) { + nt = ThreadInfo_Read_ThreadInfo(pe, pb, cb, pcbRead, cbOffset); + goto finish; + } + // individual thread files backed by user-mode memory below: + if(!_wcsicmp(wszSubPath, L"teb")) { + nt = VmmReadAsFile((PVMM_PROCESS)ctx->pProcess, pe->vaTeb, 0x1000, pb, cb, pcbRead, cbOffset); + goto finish; + } + if(!_wcsicmp(wszSubPath, L"stack")) { + nt = VmmReadAsFile((PVMM_PROCESS)ctx->pProcess, pe->vaStackLimitUser, pe->vaStackBaseUser - pe->vaStackLimitUser, pb, cb, pcbRead, cbOffset); + goto finish; + } + // individual thread files backed by kernel memory below: + if(!(pObSystemProcess = VmmProcessGet(4))) { goto finish; } + if(!_wcsicmp(wszSubPath, L"ethread")) { + nt = VmmReadAsFile(pObSystemProcess, pe->vaETHREAD, ctxVmm->kernel.ThreadInfo.oMax, pb, cb, pcbRead, cbOffset); + goto finish; + } + if(!_wcsicmp(wszSubPath, L"kstack")) { + nt = VmmReadAsFile(pObSystemProcess, pe->vaStackLimitKernel, pe->vaStackBaseKernel - pe->vaStackLimitKernel, pb, cb, pcbRead, cbOffset); + goto finish; + } + } +finish: + Ob_DECREF(pObSystemProcess); + Ob_DECREF(pObThreadMap); + return nt; +} + +/* +* Write : function as specified by the module manager. The module manager will +* call into this callback function whenever a write shall occur from a "file". +* -- ctx +* -- pb +* -- cb +* -- pcbWrite +* -- cbOffset +* -- return +*/ +NTSTATUS ThreadInfo_Write(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbWrite, _In_ QWORD cbOffset) +{ + NTSTATUS nt = VMMDLL_STATUS_FILE_INVALID; + PVMMOB_MAP_THREAD pObThreadMap = NULL; + PVMM_PROCESS pObSystemProcess = NULL; + WCHAR wszThreadName[16 + 1]; + LPWSTR wszSubPath; + DWORD dwTID; + PVMM_MAP_THREADENTRY pe; + if(!VmmMap_GetThread(ctx->pProcess, &pObThreadMap)) { return VMMDLL_STATUS_FILE_INVALID; } + // individual thread file + wszSubPath = Util_PathSplit2_ExWCHAR(ctx->wszPath, wszThreadName, _countof(wszThreadName)); + if(wszSubPath && (dwTID = (DWORD)Util_GetNumericW(ctx->wszPath)) && (pe = VmmMap_GetThreadEntry(pObThreadMap, dwTID))) { + // individual thread files backed by user-mode memory below: + if(!_wcsicmp(wszSubPath, L"teb")) { + nt = VmmWriteAsFile((PVMM_PROCESS)ctx->pProcess, pe->vaTeb, 0x1000, pb, cb, pcbWrite, cbOffset); + goto finish; + } + if(!_wcsicmp(wszSubPath, L"stack")) { + nt = VmmWriteAsFile((PVMM_PROCESS)ctx->pProcess, pe->vaStackLimitUser, pe->vaStackBaseUser - pe->vaStackLimitUser, pb, cb, pcbWrite, cbOffset); + goto finish; + } + // individual thread files backed by kernel memory below: + if(!(pObSystemProcess = VmmProcessGet(4))) { goto finish; } + if(!_wcsicmp(wszSubPath, L"ethread")) { + nt = VmmWriteAsFile(pObSystemProcess, pe->vaETHREAD, ctxVmm->kernel.ThreadInfo.oMax, pb, cb, pcbWrite, cbOffset); + goto finish; + } + if(!_wcsicmp(wszSubPath, L"kstack")) { + nt = VmmWriteAsFile(pObSystemProcess, pe->vaStackLimitKernel, pe->vaStackBaseKernel - pe->vaStackLimitKernel, pb, cb, pcbWrite, cbOffset); + goto finish; + } + } +finish: + Ob_DECREF(pObSystemProcess); + Ob_DECREF(pObThreadMap); + return nt; +} + +/* +* Set file timestamp into the ExInfo struct. +* -- pThreadEntry +* -- pExInfo +*/ +VOID ThreadInfo_List_TimeStampFile(_In_ PVMM_MAP_THREADENTRY pThreadEntry, _Out_ PVMMDLL_VFS_FILELIST_EXINFO pExInfo) +{ + pExInfo->dwVersion = VMMDLL_VFS_FILELIST_EXINFO_VERSION; + pExInfo->qwCreationTime = pThreadEntry->ftCreateTime; + pExInfo->qwLastWriteTime = pThreadEntry->ftExitTime; + if(!pExInfo->qwLastWriteTime) { + pExInfo->qwLastWriteTime = pExInfo->qwCreationTime; + } +} + +/* +* List : function as specified by the module manager. The module manager will +* call into this callback function whenever a list directory shall occur from +* the given module. +* -- ctx +* -- pFileList +* -- return +*/ +BOOL ThreadInfo_List(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _Inout_ PHANDLE pFileList) +{ + DWORD i, dwTID, cbStack; + CHAR szBuffer[32] = { 0 }; + PVMMOB_MAP_THREAD pObThreadMap = NULL; + PVMM_MAP_THREADENTRY pe; + VMMDLL_VFS_FILELIST_EXINFO ExInfo = { 0 }; + if(!VmmMap_GetThread(ctx->pProcess, &pObThreadMap)) { goto fail; } + // module root - list thread map + if(!ctx->wszPath[0]) { + for(i = 0; i < pObThreadMap->cMap; i++) { + pe = pObThreadMap->pMap + i; + ThreadInfo_List_TimeStampFile(pe, &ExInfo); + snprintf(szBuffer, 32, "%i", pe->dwTID); + VMMDLL_VfsList_AddDirectoryEx(pFileList, szBuffer, NULL, &ExInfo); + } + VMMDLL_VfsList_AddFileEx(pFileList, "threads.txt", NULL, pObThreadMap->cMap * THREADINFO_LINELENGTH, NULL); + Ob_DECREF_NULL(&pObThreadMap); + return TRUE; + } + // specific thread + if(!(dwTID = (DWORD)Util_GetNumericW(ctx->wszPath))) { goto fail; } + if(!(pe = VmmMap_GetThreadEntry(pObThreadMap, dwTID))) { goto fail; } + ThreadInfo_List_TimeStampFile(pe, &ExInfo); + VMMDLL_VfsList_AddFileEx(pFileList, "info.txt", NULL, THREADINFO_INFOFILE_LENGTH, &ExInfo); + VMMDLL_VfsList_AddFileEx(pFileList, "ethread", NULL, ctxVmm->kernel.ThreadInfo.oMax, &ExInfo); + if(pe->vaTeb) { + VMMDLL_VfsList_AddFileEx(pFileList, "teb", NULL, 0x1000, &ExInfo); + } + if(pe->vaStackBaseUser && pe->vaStackLimitUser && (pe->vaStackLimitUser < pe->vaStackBaseUser)) { + cbStack = (DWORD)(pe->vaStackBaseUser - pe->vaStackLimitUser); + VMMDLL_VfsList_AddFileEx(pFileList, "stack", NULL, cbStack, &ExInfo); + } + if(pe->vaStackBaseKernel && pe->vaStackLimitKernel && (pe->vaStackLimitKernel < pe->vaStackBaseKernel)) { + cbStack = (DWORD)(pe->vaStackBaseKernel - pe->vaStackLimitKernel); + VMMDLL_VfsList_AddFileEx(pFileList, "kstack", NULL, cbStack, &ExInfo); + } +fail: + Ob_DECREF_NULL(&pObThreadMap); + return TRUE; +} + +/* +* Initialization function. The module manager shall call into this function +* when the module shall be initialized. If the module wish to initialize it +* shall call the supplied pfnPluginManager_Register function. +* NB! the module does not have to register itself - for example if the target +* operating system or architecture is unsupported. +* -- pPluginRegInfo +*/ +VOID M_ThreadInfo_Initialize(_Inout_ PVMMDLL_PLUGIN_REGINFO pRI) +{ + PVMMOB_PHYS2VIRT_INFORMATION pObPhys2Virt = NULL; + if((pRI->magic != VMMDLL_PLUGIN_REGINFO_MAGIC) || (pRI->wVersion != VMMDLL_PLUGIN_REGINFO_VERSION)) { return; } + if(!((pRI->tpSystem == VMM_SYSTEM_WINDOWS_X64) || (pRI->tpSystem == VMM_SYSTEM_WINDOWS_X86))) { return; } + wcscpy_s(pRI->reg_info.wszModuleName, 32, L"threads"); // module name + pRI->reg_info.fRootModule = FALSE; // module shows in root directory + pRI->reg_info.fProcessModule = TRUE; // module shows in process directory + pRI->reg_fn.pfnList = ThreadInfo_List; // List function supported + pRI->reg_fn.pfnRead = ThreadInfo_Read; // Read function supported + pRI->reg_fn.pfnWrite = ThreadInfo_Write; // Write function supported + pRI->pfnPluginManager_Register(pRI); +} diff --git a/vmm/m_virt2phys.c b/vmm/m_virt2phys.c index e802cfdf..9005282a 100644 --- a/vmm/m_virt2phys.c +++ b/vmm/m_virt2phys.c @@ -3,7 +3,7 @@ // (c) Ulf Frisk, 2018-2019 // Author: Ulf Frisk, pcileech@frizk.net // -#include "m_virt2phys.h" +#include "m_modules.h" #include "pluginmanager.h" #include "util.h" #include "vmm.h" @@ -39,7 +39,7 @@ NTSTATUS Virt2Phys_Read(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _Out_ PBYTE pb, _In_ DW PVMMOB_MEM pObPT = NULL; PVMM_PROCESS pProcess = (PVMM_PROCESS)ctx->pProcess; VMM_VIRT2PHYS_INFORMATION Virt2PhysInfo = { 0 }; - Virt2PhysInfo.va = pProcess->pObProcessPersistent->Plugin.vaVirt2Phys; + Virt2PhysInfo.va = pProcess->pObPersistent->Plugin.vaVirt2Phys; VmmVirt2PhysGetInformation(pProcess, &Virt2PhysInfo); if(!_wcsicmp(ctx->wszPath, L"readme")) { return Util_VfsReadFile_FromPBYTE((PBYTE)szMVIRT2PHYS_README, strlen(szMVIRT2PHYS_README), pb, cb, pcbRead, cbOffset); @@ -147,18 +147,18 @@ NTSTATUS Virt2Phys_WriteVA(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ PBYTE pb, _In_ VMM_MEMORYMODEL_TP tp = ctxVmm->tpMemoryModel; if((tp == VMM_MEMORYMODEL_X64) && (cbOffset < 16)) { *pcbWrite = cb; - snprintf(pbBuffer, 17, "%016llx", pProcess->pObProcessPersistent->Plugin.vaVirt2Phys); + snprintf(pbBuffer, 17, "%016llx", pProcess->pObPersistent->Plugin.vaVirt2Phys); cb = (DWORD)min(16 - cbOffset, cb); memcpy(pbBuffer + cbOffset, pb, cb); pbBuffer[16] = 0; - pProcess->pObProcessPersistent->Plugin.vaVirt2Phys = strtoull(pbBuffer, NULL, 16); + pProcess->pObPersistent->Plugin.vaVirt2Phys = strtoull(pbBuffer, NULL, 16); } else if ((tp == VMM_MEMORYMODEL_X86) || (tp == VMM_MEMORYMODEL_X86PAE)) { *pcbWrite = cb; - snprintf(pbBuffer, 9, "%08x", (DWORD)pProcess->pObProcessPersistent->Plugin.vaVirt2Phys); + snprintf(pbBuffer, 9, "%08x", (DWORD)pProcess->pObPersistent->Plugin.vaVirt2Phys); cb = (DWORD)min(8 - cbOffset, cb); memcpy(pbBuffer + cbOffset, pb, cb); pbBuffer[8] = 0; - pProcess->pObProcessPersistent->Plugin.vaVirt2Phys = strtoul(pbBuffer, NULL, 16); + pProcess->pObPersistent->Plugin.vaVirt2Phys = strtoul(pbBuffer, NULL, 16); } else { *pcbWrite = 0; } @@ -183,7 +183,7 @@ NTSTATUS Virt2Phys_Write(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ PBYTE pb, _In_ DW if(!_wcsicmp(ctx->wszPath, L"virt")) { return Virt2Phys_WriteVA(ctx, pb, cb, pcbWrite, cbOffset); } - Virt2PhysInfo.va = pProcess->pObProcessPersistent->Plugin.vaVirt2Phys; + Virt2PhysInfo.va = pProcess->pObPersistent->Plugin.vaVirt2Phys; VmmVirt2PhysGetInformation(pProcess, &Virt2PhysInfo); i = 0xff; if(!_wcsicmp(ctx->wszPath, L"pt_pml4")) { i = 4; } diff --git a/vmm/m_virt2phys.h b/vmm/m_virt2phys.h deleted file mode 100644 index d77b0dc1..00000000 --- a/vmm/m_virt2phys.h +++ /dev/null @@ -1,17 +0,0 @@ -// m_virt2phys.h : definitions related to the virt2phys built-in module. -// -// (c) Ulf Frisk, 2018-2019 -// Author: Ulf Frisk, pcileech@frizk.net -// -#ifndef __M_VIRT2PHYS_H__ -#define __M_VIRT2PHYS_H__ -#include -#include "vmmdll.h" - -/* -* Initialization function for the built-in virt2phys module. -* -- pPluginRegInfo -*/ -VOID M_Virt2Phys_Initialize(_Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); - -#endif /* __M_VIRT2PHYS_H__ */ diff --git a/vmm/m_vmmvfs_dump.c b/vmm/m_vmmvfs_dump.c index 38497b96..a8661c5b 100644 --- a/vmm/m_vmmvfs_dump.c +++ b/vmm/m_vmmvfs_dump.c @@ -6,7 +6,7 @@ // (c) Ulf Frisk, 2019 // Author: Ulf Frisk, pcileech@frizk.net // -#include "m_phys2virt.h" +#include "m_modules.h" #include "pluginmanager.h" #include "pe.h" #include "sysquery.h" @@ -171,29 +171,28 @@ VOID MVmmVfsDump_EnsureProcessorContext0(_In_ PVMM_PROCESS pSystemProcess, _In_ { BOOL f; QWORD va, vaContextKPRCB; - if(!ctxVmm->f32) { - f = (va = *(PQWORD)(ctx->KDBG.pb + KDBG64_KiProcessorBlock)) && - VMM_KADDR64_16(va) && - VmmRead(pSystemProcess, va, (PBYTE)&va, sizeof(QWORD)) && - VMM_KADDR64_16(va) && - (va = va + *(PWORD)(ctx->KDBG.pb + KDBG64_ContextKPRCB)) && - VmmRead(pSystemProcess, va, (PBYTE)&vaContextKPRCB, sizeof(QWORD)) && - VMM_KADDR64_16(vaContextKPRCB); - if(f) { - if(VmmVirt2Phys(pSystemProcess, vaContextKPRCB + 0x038, &ctx->KiInitialPCR_Context.pa)) { - ctx->KiInitialPCR_Context.cb = 0x10; - *(PWORD)(ctx->KiInitialPCR_Context.pb + 0x00) = 0x10; // SegCs - *(PWORD)(ctx->KiInitialPCR_Context.pb + 0x02) = 0x2b; // SegDs - *(PWORD)(ctx->KiInitialPCR_Context.pb + 0x04) = 0x2b; // SegEs - *(PWORD)(ctx->KiInitialPCR_Context.pb + 0x06) = 0x53; // SegFs - *(PWORD)(ctx->KiInitialPCR_Context.pb + 0x08) = 0x2b; // SegGs - *(PWORD)(ctx->KiInitialPCR_Context.pb + 0x0a) = 0x00; // SegSs - } - // set physical memory overlay struct for processor context - ctx->OVERLAY[2].pa = ctx->KiInitialPCR_Context.pa; - ctx->OVERLAY[2].cb = ctx->KiInitialPCR_Context.cb; - ctx->OVERLAY[2].pb = ctx->KiInitialPCR_Context.pb; + if(ctxVmm->f32) { return; } + f = (va = *(PQWORD)(ctx->KDBG.pb + KDBG64_KiProcessorBlock)) && + VMM_KADDR64_16(va) && + VmmRead(pSystemProcess, va, (PBYTE)&va, sizeof(QWORD)) && + VMM_KADDR64_16(va) && + (va = va + *(PWORD)(ctx->KDBG.pb + KDBG64_ContextKPRCB)) && + VmmRead(pSystemProcess, va, (PBYTE)&vaContextKPRCB, sizeof(QWORD)) && + VMM_KADDR64_16(vaContextKPRCB); + if(f) { + if(VmmVirt2Phys(pSystemProcess, vaContextKPRCB + 0x038, &ctx->KiInitialPCR_Context.pa)) { + ctx->KiInitialPCR_Context.cb = 0x10; + *(PWORD)(ctx->KiInitialPCR_Context.pb + 0x00) = 0x10; // SegCs + *(PWORD)(ctx->KiInitialPCR_Context.pb + 0x02) = 0x2b; // SegDs + *(PWORD)(ctx->KiInitialPCR_Context.pb + 0x04) = 0x2b; // SegEs + *(PWORD)(ctx->KiInitialPCR_Context.pb + 0x06) = 0x53; // SegFs + *(PWORD)(ctx->KiInitialPCR_Context.pb + 0x08) = 0x2b; // SegGs + *(PWORD)(ctx->KiInitialPCR_Context.pb + 0x0a) = 0x00; // SegSs } + // set physical memory overlay struct for processor context + ctx->OVERLAY[2].pa = ctx->KiInitialPCR_Context.pa; + ctx->OVERLAY[2].cb = ctx->KiInitialPCR_Context.cb; + ctx->OVERLAY[2].pb = ctx->KiInitialPCR_Context.pb; } } @@ -282,7 +281,7 @@ VOID MVmmVfsDump_InitializeDumpContext64(_In_ PVMM_PROCESS pSystemProcess, _In_ pd->PsLoadedModuleList = ctxVmm->kernel.opt.vaPsLoadedModuleListExp; pd->PsActiveProcessHead = pSystemProcess->win.EPROCESS.va; pd->MachineImageType = IMAGE_FILE_MACHINE_AMD64; - pd->NumberProcessors = ctxVmm->kernel.opt.cCPUs; + pd->NumberProcessors = max(1, ctxVmm->kernel.opt.cCPUs); pd->BugCheckCode = 0xDEADDEAD; pd->BugCheckParameter1 = 1; pd->BugCheckParameter2 = 2; @@ -333,7 +332,7 @@ VOID MVmmVfsDump_InitializeDumpContext32(PVMM_PROCESS pSystemProcess, POB_VMMVFS pd->PsLoadedModuleList = (DWORD)ctxVmm->kernel.opt.vaPsLoadedModuleListExp; pd->PsActiveProcessHead = (DWORD)pSystemProcess->win.EPROCESS.va; pd->MachineImageType = IMAGE_FILE_MACHINE_I386; - pd->NumberProcessors = ctxVmm->kernel.opt.cCPUs; + pd->NumberProcessors = max(1, ctxVmm->kernel.opt.cCPUs); pd->BugCheckCode = 0xDEADDEAD; pd->BugCheckParameter1 = 1; pd->BugCheckParameter2 = 2; @@ -542,8 +541,5 @@ NTSTATUS MVmmVfsDump_Write(_In_ LPCWSTR wcsFileName, _In_reads_(cb) PBYTE pb, _I VOID MVmmVfsDump_List(_Inout_ PHANDLE pFileList) { VMMDLL_VfsList_AddFile(pFileList, "memory.pmem", ctxMain->dev.paMax); - if(ctxVmm->kernel.dwVersionBuild >= 7600) { - // Memory dump files compatible with WinDbg are supported on Win7 and later. - VMMDLL_VfsList_AddFile(pFileList, "memory.dmp", ctxMain->dev.paMax + (ctxVmm->f32 ? 0x1000 : 0x2000)); - } + VMMDLL_VfsList_AddFile(pFileList, "memory.dmp", ctxMain->dev.paMax + (ctxVmm->f32 ? 0x1000 : 0x2000)); } diff --git a/vmm/m_winreg.c b/vmm/m_winreg.c index 433b2323..8ad6d90c 100644 --- a/vmm/m_winreg.c +++ b/vmm/m_winreg.c @@ -3,7 +3,7 @@ // (c) Ulf Frisk, 2019 // Author: Ulf Frisk, pcileech@frizk.net // -#include "m_winreg.h" +#include "m_modules.h" #include "vmm.h" #include "vmmwinreg.h" #include "util.h" diff --git a/vmm/m_winreg.h b/vmm/m_winreg.h deleted file mode 100644 index 465bde8d..00000000 --- a/vmm/m_winreg.h +++ /dev/null @@ -1,17 +0,0 @@ -// m_winreg.h : definitions related to the WinReg built-in module. -// -// (c) Ulf Frisk, 2019 -// Author: Ulf Frisk, pcileech@frizk.net -// -#ifndef __M_WINREG_H__ -#define __M_WINREG_H__ -#include -#include "vmmdll.h" - -/* -* Initialization function for the built-in WinReg module. -* -- pPluginRegInfo -*/ -VOID M_WinReg_Initialize(_Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); - -#endif /* __M_WINREG_H__ */ diff --git a/vmm/mm.h b/vmm/mm.h new file mode 100644 index 00000000..03ad77be --- /dev/null +++ b/vmm/mm.h @@ -0,0 +1,65 @@ +// mm.h : definitions related to the core memory manager. +// +// (c) Ulf Frisk, 2018-2019 +// Author: Ulf Frisk, pcileech@frizk.net +// +#ifndef __MM_H__ +#define __MM_H__ +#include "vmm.h" + +/* +* Initialize the X86 32-bit protected mode memory model. +* If a previous memory model exists that memory model is first closed before +* the new X86 memory model is initialized. +*/ +VOID MmX86_Initialize(); + +/* +* Initialize the X86 PAE 32-bit protected mode memory model. +* If a previous memory model exists that memory model is first closed before +* the new X86 PAE memory model is initialized. +*/ +VOID MmX86PAE_Initialize(); + +/* +* Initialize the X64 / IA32e / Long-Mode paging / memory model. +* If a previous memory model exists that memory model is first closed before +* the new X64 memory model is initialized. +*/ +VOID MmX64_Initialize(); + +/* +* Initialize the paging sub-system for Windows in a limited or full fashion. +* In full mode Win10 memory decompression will be initialized. +* -- fModeFull +*/ +VOID MmWin_PagingInitialize(_In_ BOOL fModeFull); + +/* +* Close / Shutdown the paging subsystem. This function should not be called +* when there is an active thread executing inside the sub-system - ideally +* it should only be called on shutdown. +*/ +VOID MmWin_PagingClose(); + +/* +* Initialize / Ensure that a VAD map is initialized for the specific process. +* -- pProcess +* -- fExtendedText = also fetch extended info such as module names. +* -- fVmmRead = VMM_FLAGS_* flags. +* -- return +*/ +_Success_(return) +BOOL MmVad_MapInitialize(_In_ PVMM_PROCESS pProcess, _In_ BOOL fExtendedText, _In_ QWORD fVmmRead); + +/* +* Try to read a prototype page table entry (PTE). +* -- pProcess +* -- va +* -- pfInRange +* -- fVmmRead = VMM_FLAGS_* flags. +* -- return = prototype pte or zero on fail. +*/ +QWORD MmVad_PrototypePte(_In_ PVMM_PROCESS pProcess, _In_ QWORD va, _Out_opt_ PBOOL pfInRange, _In_ QWORD fVmmRead); + +#endif /* __MM_H__ */ diff --git a/vmm/mm_vad.c b/vmm/mm_vad.c new file mode 100644 index 00000000..969d343e --- /dev/null +++ b/vmm/mm_vad.c @@ -0,0 +1,1157 @@ +// mm_vad.c : implementation of Windows VAD (virtual address descriptor) functionality. +// +// (c) Ulf Frisk, 2019 +// Author: Ulf Frisk, pcileech@frizk.net +// +#include "vmm.h" +#include "mm.h" + +#define MMVAD_POOLTAG_VAD 'Vad ' +#define MMVAD_POOLTAG_VADF 'VadF' +#define MMVAD_POOLTAG_VADS 'VadS' +#define MMVAD_POOLTAG_VADL 'Vadl' +#define MMVAD_POOLTAG_VADM 'Vadm' + +#define MMVAD_PTESIZE ((ctxVmm->tpMemoryModel == VMM_MEMORYMODEL_X86) ? 4 : 8) + +// ---------------------------------------------------------------------------- +// DEFINES OF VAD STRUCTS FOR DIFFRENT WINDOWS VERSIONS +// Define the VADs here statically rather than parse offsets it from PDBs +// to keep the VAD functionality fast and independent of PDBs. +// ---------------------------------------------------------------------------- + +typedef enum _tdMMVAD_TYPE { + VadNone = 0, + VadDevicePhysicalMemory = 1, + VadImageMap = 2, + VadAwe = 3, + VadWriteWatch = 4, + VadLargePages = 5, + VadRotatePhysical = 6, + VadLargePageSection = 7 +} _MMVAD_TYPE; + +// WinXP 32-bit +typedef struct _tdMMVAD32_XP { + DWORD _Dummy1; + DWORD PoolTag; + // _MMVAD_SHORT + DWORD StartingVpn; + DWORD EndingVpn; + DWORD Parent; + DWORD LeftChild; + DWORD RightChild; + union { + struct { + DWORD CommitCharge : 19; // Pos 0 + DWORD PhysicalMapping : 1; // Pos 19 + DWORD ImageMap : 1; // Pos 20 + DWORD UserPhysicalPages : 1; // Pos 21 + DWORD NoChange : 1; // Pos 22 + DWORD WriteWatch : 1; // Pos 23 + DWORD Protection : 5; // Pos 24 + DWORD LargePages : 1; // Pos 29 + DWORD MemCommit : 1; // Pos 30 + DWORD PrivateMemory : 1; // Pos 31 + }; + DWORD u; + }; + // _MMVAD + DWORD ControlArea; + DWORD FirstPrototypePte; + DWORD LastContiguousPte; + union { + struct { + DWORD FileOffset : 24; // Pos 0 + DWORD SecNoChange : 1; // Pos 24 + DWORD OneSecured : 1; // Pos 25 + DWORD MultipleSecured : 1; // Pos 26 + DWORD ReadOnly : 1; // Pos 27 + DWORD LongVad : 1; // Pos 28 + DWORD ExtendableFile : 1; // Pos 29 + DWORD Inherit : 1; // Pos 30 + DWORD CopyOnWrite : 1; // Pos 31 + }; + DWORD u2; + }; +} _MMVAD32_XP; + +// Vista/7 32-bit +typedef struct _tdMMVAD32_7 { + DWORD _Dummy1; + DWORD PoolTag; + // _MMVAD_SHORT + DWORD u1; + DWORD LeftChild; + DWORD RightChild; + DWORD StartingVpn; + DWORD EndingVpn; + union { + struct { + DWORD CommitCharge : 19; // Pos 0 + DWORD NoChange : 1; // Pos 51 + DWORD VadType : 3; // Pos 52 + DWORD MemCommit : 1; // Pos 55 + DWORD Protection : 5; // Pos 56 + DWORD _Spare1 : 2; // Pos 61 + DWORD PrivateMemory : 1; // Pos 63 + }; + DWORD u; + }; + DWORD PushLock; + DWORD u5; + // _MMVAD + union { + struct { + DWORD FileOffset : 24; // Pos 0 + DWORD SecNoChange : 1; // Pos 24 + DWORD OneSecured : 1; // Pos 25 + DWORD MultipleSecured : 1; // Pos 26 + DWORD _Spare2 : 1; // Pos 27 + DWORD LongVad : 1; // Pos 28 + DWORD ExtendableFile : 1; // Pos 29 + DWORD Inherit : 1; // Pos 30 + DWORD CopyOnWrite : 1; // Pos 31 + }; + DWORD u2; + }; + DWORD Subsection; + DWORD FirstPrototypePte; + DWORD LastContiguousPte; + DWORD ViewLinks[2]; + DWORD VadsProcess; +} _MMVAD32_7; + +// Vista/7 64-bit +typedef struct _tdMMVAD64_7 { + DWORD _Dummy1; + DWORD PoolTag; + QWORD _Dummy2; + // _MMVAD_SHORT + QWORD u1; + QWORD LeftChild; + QWORD RightChild; + QWORD StartingVpn; + QWORD EndingVpn; + union { + struct { + QWORD CommitCharge : 51; // Pos 0 + QWORD NoChange : 1; // Pos 51 + QWORD VadType : 3; // Pos 52 + QWORD MemCommit : 1; // Pos 55 + QWORD Protection : 5; // Pos 56 + QWORD _Spare1 : 2; // Pos 61 + QWORD PrivateMemory : 1; // Pos 63 + }; + QWORD u; + }; + QWORD PushLock; + QWORD u5; + // _MMVAD + union { + struct { + DWORD FileOffset : 24; // Pos 0 + DWORD SecNoChange : 1; // Pos 24 + DWORD OneSecured : 1; // Pos 25 + DWORD MultipleSecured : 1; // Pos 26 + DWORD _Spare2 : 1; // Pos 27 + DWORD LongVad : 1; // Pos 28 + DWORD ExtendableFile : 1; // Pos 29 + DWORD Inherit : 1; // Pos 30 + DWORD CopyOnWrite : 1; // Pos 31 + }; + QWORD u2; + }; + QWORD Subsection; + QWORD FirstPrototypePte; + QWORD LastContiguousPte; + QWORD ViewLinks[2]; + QWORD VadsProcess; +} _MMVAD64_7; + +// Win8.0 32-bit +typedef struct _tdMMVAD32_80 { + DWORD PoolTag; + QWORD _Dummy2; + // _MMVAD_SHORT + DWORD __u1; + DWORD LeftChild; + DWORD RightChild; + DWORD StartingVpn; + DWORD EndingVpn; + DWORD PushLock; + union { + struct { + DWORD VadType : 3; // Pos 0 + DWORD Protection : 5; // Pos 3 + DWORD PreferredNode : 6; // Pos 8 + DWORD NoChange : 1; // Pos 14 + DWORD PrivateMemory : 1; // Pos 15 + DWORD Teb : 1; // Pos 16 + DWORD PrivateFixup : 1; // Pos 17 + DWORD _Spare1 : 13; // Pos 18 + DWORD DeleteInProgress : 1; // Pos 31 + }; + DWORD u; + }; + union { + struct { + DWORD CommitCharge : 31; // Pos 0 + DWORD MemCommit : 1; // Pos 31 + }; + DWORD u1; + }; + DWORD EventList; + DWORD ReferenceCount; + // _MMVAD + union { + struct { + DWORD FileOffset : 24; // Pos 0 + DWORD Large : 1; // Pos 24 + DWORD TrimBehind : 1; // Pos 25 + DWORD Inherit : 1; // Pos 26 + DWORD CopyOnWrite : 1; // Pos 27 + DWORD NoValidationNeeded : 1; // Pos 28 + DWORD _Spare2 : 3; // Pos 29 + }; + DWORD u2; + }; + DWORD Subsection; + DWORD FirstPrototypePte; + DWORD LastContiguousPte; + DWORD ViewLinks[2]; + DWORD VadsProcess; + DWORD u4; +} _MMVAD32_80; + +// Win8.0 64-bit +typedef struct _tdMMVAD64_80 { + DWORD _Dummy1; + DWORD PoolTag; + QWORD _Dummy2; + // _MMVAD_SHORT + QWORD __u1; + QWORD LeftChild; + QWORD RightChild; + DWORD StartingVpn; + DWORD EndingVpn; + QWORD PushLock; + union { + struct { + DWORD VadType : 3; // Pos 0 + DWORD Protection : 5; // Pos 3 + DWORD PreferredNode : 6; // Pos 8 + DWORD NoChange : 1; // Pos 14 + DWORD PrivateMemory : 1; // Pos 15 + DWORD Teb : 1; // Pos 16 + DWORD PrivateFixup : 1; // Pos 17 + DWORD _Spare1 : 13; // Pos 18 + DWORD DeleteInProgress : 1; // Pos 31 + }; + DWORD u; + }; + union { + struct { + DWORD CommitCharge : 31; // Pos 0 + DWORD MemCommit : 1; // Pos 31 + }; + DWORD u1; + }; + QWORD EventList; + DWORD ReferenceCount; + DWORD _Filler; + // _MMVAD + union { + struct { + DWORD FileOffset : 24; // Pos 0 + DWORD Large : 1; // Pos 24 + DWORD TrimBehind : 1; // Pos 25 + DWORD Inherit : 1; // Pos 26 + DWORD CopyOnWrite : 1; // Pos 27 + DWORD NoValidationNeeded : 1; // Pos 28 + DWORD _Spare2 : 3; // Pos 29 + }; + QWORD u2; + }; + QWORD Subsection; + QWORD FirstPrototypePte; + QWORD LastContiguousPte; + QWORD ViewLinks[2]; + QWORD VadsProcess; + QWORD u4; +} _MMVAD64_80; + +// Win8.1/10 32-bit +typedef struct _tdMMVAD32_10 { + DWORD _Dummy1; + DWORD PoolTag; + // _MMVAD_SHORT + DWORD Children[2]; + DWORD ParentValue; + DWORD StartingVpn; + DWORD EndingVpn; + DWORD ReferenceCount; + DWORD PushLock; + DWORD u; // no struct - bit order varies too much in Win10 + union { + struct { + DWORD CommitCharge : 31; // Pos 0 + DWORD MemCommit : 1; // Pos 31 + }; + DWORD u1; + }; + DWORD EventList; + // _MMVAD + union { + struct { + DWORD FileOffset : 24; // Pos 0 + DWORD Large : 1; // Pos 24 + DWORD TrimBehind : 1; // Pos 25 + DWORD Inherit : 1; // Pos 26 + DWORD CopyOnWrite : 1; // Pos 27 + DWORD NoValidationNeeded : 1; // Pos 28 + DWORD _Spare2 : 3; // Pos 29 + }; + DWORD u2; + }; + DWORD Subsection; + DWORD FirstPrototypePte; + DWORD LastContiguousPte; + DWORD ViewLinks[2]; + DWORD VadsProcess; + DWORD u4; + DWORD FileObject; +} _MMVAD32_10; + +// Win8.1/10 64-bit +typedef struct _tdMMVAD64_10 { + DWORD _Dummy1; + DWORD PoolTag; + QWORD _Dummy2; + // _MMVAD_SHORT + QWORD Children[2]; + QWORD ParentValue; + DWORD StartingVpn; + DWORD EndingVpn; + BYTE StartingVpnHigh; + BYTE EndingVpnHigh; + BYTE CommitChargeHigh; + BYTE SpareNT64VadUChar; + QWORD PushLock; + DWORD u; // no struct - bit order varies too much in Win10 + union { + struct { + DWORD CommitCharge : 31; // Pos 0 + DWORD MemCommit : 1; // Pos 31 + }; + DWORD u1; + }; + QWORD EventList; + // _MMVAD + union { + struct { + DWORD FileOffset : 24; // Pos 0 + DWORD Large : 1; // Pos 24 + DWORD TrimBehind : 1; // Pos 25 + DWORD Inherit : 1; // Pos 26 + DWORD CopyOnWrite : 1; // Pos 27 + DWORD NoValidationNeeded : 1; // Pos 28 + DWORD _Spare2 : 3; // Pos 29 + }; + QWORD u2; + }; + QWORD Subsection; + QWORD FirstPrototypePte; + QWORD LastContiguousPte; + QWORD ViewLinks[2]; + QWORD VadsProcess; + QWORD u4; + QWORD FileObject; +} _MMVAD64_10; + +/* +* Object manager callback function for object cleanup tasks. +*/ +VOID VmmVad_MemMapVad_CloseObCallback(_In_ PVOID pVmmOb) +{ + PVMMOB_MAP_VAD pOb = (PVMMOB_MAP_VAD)pVmmOb; + LocalFree(pOb->wszMultiText); +} + +// ---------------------------------------------------------------------------- +// IMPLEMENTATION OF VAD PARSING FUNCTIONALITY FOR DIFFERENT WINDOWS VERSIONS: +// ---------------------------------------------------------------------------- + +/* +* Comparator / Sorting function for qsort of VMM_VADMAP_ENTRIES. +* -- v1 +* -- v2 +* -- return +*/ +int MmVad_CmpVadEntry(const void *v1, const void *v2) +{ + return + (*(PQWORD)v1 < *(PQWORD)v2) ? -1 : + (*(PQWORD)v1 > * (PQWORD)v2) ? 1 : 0; +} + +BOOL MmVad_Spider_PoolTagAny(_In_ DWORD dwPoolTag, _In_ DWORD cPoolTag, ...) +{ + va_list argp; + dwPoolTag = _byteswap_ulong(dwPoolTag); + va_start(argp, cPoolTag); + while(cPoolTag) { + if(dwPoolTag == va_arg(argp, DWORD)) { + va_end(argp); + return TRUE; + } + cPoolTag--; + } + va_end(argp); + return FALSE; +} + +PVMM_MAP_VADENTRY MmVad_Spider_MMVAD32_XP(_In_ PVMM_PROCESS pSystemProcess, _In_ QWORD va, _In_ PVMMOB_MAP_VAD pmVad, _In_ POB_VSET psAll, _In_ POB_VSET psTry1, _In_opt_ POB_VSET psTry2, _In_ QWORD fVmmRead, _In_ DWORD dwReserved) +{ + _MMVAD32_XP v = { 0 }; + PVMM_MAP_VADENTRY e; + if(!VmmRead2(pSystemProcess, va, (PBYTE)&v, sizeof(_MMVAD32_XP), fVmmRead | VMM_FLAG_FORCECACHE_READ)) { + ObVSet_Push(psTry2, va); + return NULL; + } + if((v.EndingVpn < v.StartingVpn) || !MmVad_Spider_PoolTagAny(v.PoolTag, 5, MMVAD_POOLTAG_VADS, MMVAD_POOLTAG_VAD, MMVAD_POOLTAG_VADL, MMVAD_POOLTAG_VADM, MMVAD_POOLTAG_VADF)) { + return NULL; + } + // short vad + e = &pmVad->pMap[pmVad->cMap++]; + if(VMM_KADDR32_8(v.LeftChild)) { + ObVSet_Push(psAll, v.LeftChild - 8); + ObVSet_Push(psTry1, v.LeftChild - 8); + } + if(VMM_KADDR32_8(v.RightChild)) { + ObVSet_Push(psAll, v.RightChild - 8); + ObVSet_Push(psTry1, v.RightChild - 8); + } + e->vaStart = (QWORD)v.StartingVpn << 12; + e->vaEnd = ((QWORD)v.EndingVpn << 12) | 0xfff; + e->CommitCharge = v.CommitCharge; + e->MemCommit = v.MemCommit; + e->VadType = 0; + e->Protection = v.Protection; + e->fPrivateMemory = v.PrivateMemory; + if(VMM_POOLTAG(v.PoolTag, MMVAD_POOLTAG_VADL)) { e->VadType = VadLargePages; } + // full vad + if(v.PoolTag == MMVAD_POOLTAG_VADS) { return e; } + e->vaSubsection = v.ControlArea; + if(VMM_KADDR32_4(v.FirstPrototypePte)) { + e->vaPrototypePte = v.FirstPrototypePte; + e->cbPrototypePte = (DWORD)(v.LastContiguousPte - v.FirstPrototypePte + MMVAD_PTESIZE); + } + return e; +} + +PVMM_MAP_VADENTRY MmVad_Spider_MMVAD32_7(_In_ PVMM_PROCESS pSystemProcess, _In_ QWORD va, _In_ PVMMOB_MAP_VAD pmVad, _In_ POB_VSET psAll, _In_ POB_VSET psTry1, _In_opt_ POB_VSET psTry2, _In_ QWORD fVmmRead, _In_ DWORD dwReserved) +{ + _MMVAD32_7 v = { 0 }; + PVMM_MAP_VADENTRY e; + if(!VmmRead2(pSystemProcess, va, (PBYTE)&v, sizeof(_MMVAD32_7), fVmmRead | VMM_FLAG_FORCECACHE_READ)) { + ObVSet_Push(psTry2, va); + return NULL; + } + if((v.EndingVpn < v.StartingVpn) || !MmVad_Spider_PoolTagAny(v.PoolTag, 5, MMVAD_POOLTAG_VADS, MMVAD_POOLTAG_VAD, MMVAD_POOLTAG_VADL, MMVAD_POOLTAG_VADM, MMVAD_POOLTAG_VADF)) { + return NULL; + } + // short vad + e = &pmVad->pMap[pmVad->cMap++]; + if(VMM_KADDR32_8(v.LeftChild)) { + ObVSet_Push(psAll, v.LeftChild - 8); + ObVSet_Push(psTry1, v.LeftChild - 8); + } + if(VMM_KADDR32_8(v.RightChild)) { + ObVSet_Push(psAll, v.RightChild - 8); + ObVSet_Push(psTry1, v.RightChild - 8); + } + e->vaStart = (QWORD)v.StartingVpn << 12; + e->vaEnd = ((QWORD)v.EndingVpn << 12) | 0xfff; + e->CommitCharge = v.CommitCharge; + e->MemCommit = v.MemCommit; + e->VadType = v.VadType; + e->Protection = v.Protection; + e->fPrivateMemory = v.PrivateMemory; + // full vad + if(v.PoolTag == MMVAD_POOLTAG_VADS) { return e; } + e->vaSubsection = v.Subsection; + if(VMM_KADDR32_4(v.FirstPrototypePte)) { + e->vaPrototypePte = v.FirstPrototypePte; + e->cbPrototypePte = (DWORD)(v.LastContiguousPte - v.FirstPrototypePte + MMVAD_PTESIZE); + } + return e; +} + +PVMM_MAP_VADENTRY MmVad_Spider_MMVAD64_7(_In_ PVMM_PROCESS pSystemProcess, _In_ QWORD va, _In_ PVMMOB_MAP_VAD pmVad, _In_ POB_VSET psAll, _In_ POB_VSET psTry1, _In_opt_ POB_VSET psTry2, _In_ QWORD fVmmRead, _In_ DWORD dwReserved) +{ + _MMVAD64_7 v = { 0 }; + PVMM_MAP_VADENTRY e; + if(!VmmRead2(pSystemProcess, va, (PBYTE)&v, sizeof(_MMVAD64_7), fVmmRead | VMM_FLAG_FORCECACHE_READ)) { + ObVSet_Push(psTry2, va); + return NULL; + } + if((v.EndingVpn < v.StartingVpn) || !MmVad_Spider_PoolTagAny(v.PoolTag, 5, MMVAD_POOLTAG_VADS, MMVAD_POOLTAG_VAD, MMVAD_POOLTAG_VADL, MMVAD_POOLTAG_VADM, MMVAD_POOLTAG_VADF)) { + return NULL; + } + // short vad + e = &pmVad->pMap[pmVad->cMap++]; + if(VMM_KADDR64_16(v.LeftChild)) { + ObVSet_Push(psAll, v.LeftChild - 0x10); + ObVSet_Push(psTry1, v.LeftChild - 0x10); + } + if(VMM_KADDR64_16(v.RightChild)) { + ObVSet_Push(psAll, v.RightChild - 0x10); + ObVSet_Push(psTry1, v.RightChild - 0x10); + } + e->vaStart = (QWORD)v.StartingVpn << 12; + e->vaEnd = ((QWORD)v.EndingVpn << 12) | 0xfff; + e->CommitCharge = (DWORD)v.CommitCharge; + e->MemCommit = (DWORD)v.MemCommit; + e->VadType = (DWORD)v.VadType; + e->Protection = (DWORD)v.Protection; + e->fPrivateMemory = (DWORD)v.PrivateMemory; + // full vad + if(v.PoolTag == MMVAD_POOLTAG_VADS) { return e; } + e->vaSubsection = v.Subsection; + if(VMM_KADDR64_8(v.FirstPrototypePte)) { + e->vaPrototypePte = v.FirstPrototypePte; + e->cbPrototypePte = (DWORD)(v.LastContiguousPte - v.FirstPrototypePte + 8); + } + return e; +} + +PVMM_MAP_VADENTRY MmVad_Spider_MMVAD32_80(_In_ PVMM_PROCESS pSystemProcess, _In_ QWORD va, _In_ PVMMOB_MAP_VAD pmVad, _In_ POB_VSET psAll, _In_ POB_VSET psTry1, _In_opt_ POB_VSET psTry2, _In_ QWORD fVmmRead, _In_ DWORD dwReserved) +{ + _MMVAD32_80 v = { 0 }; + PVMM_MAP_VADENTRY e; + if(!VmmRead2(pSystemProcess, va, (PBYTE)&v, sizeof(_MMVAD32_80), fVmmRead | VMM_FLAG_FORCECACHE_READ)) { + ObVSet_Push(psTry2, va); + return NULL; + } + if((v.EndingVpn < v.StartingVpn) || !MmVad_Spider_PoolTagAny(v.PoolTag, 5, MMVAD_POOLTAG_VADS, MMVAD_POOLTAG_VAD, MMVAD_POOLTAG_VADL, MMVAD_POOLTAG_VADM, MMVAD_POOLTAG_VADF)) { + return NULL; + } + // short vad + e = &pmVad->pMap[pmVad->cMap++]; + if(VMM_KADDR64_16(v.LeftChild)) { + ObVSet_Push(psAll, v.LeftChild - 8); + ObVSet_Push(psTry1, v.LeftChild - 8); + } + if(VMM_KADDR64_16(v.RightChild)) { + ObVSet_Push(psAll, v.RightChild - 8); + ObVSet_Push(psTry1, v.RightChild - 8); + } + e->vaStart = (QWORD)v.StartingVpn << 12; + e->vaEnd = ((QWORD)v.EndingVpn << 12) | 0xfff; + e->CommitCharge = (DWORD)v.CommitCharge; + e->MemCommit = (DWORD)v.MemCommit; + e->VadType = (DWORD)v.VadType; + e->Protection = (DWORD)v.Protection; + e->fPrivateMemory = (DWORD)v.PrivateMemory; + // full vad + if(v.PoolTag == MMVAD_POOLTAG_VADS) { return e; } + e->flags[2] = v.u2; + e->vaSubsection = v.Subsection; + if(VMM_KADDR32_8(v.FirstPrototypePte)) { + e->vaPrototypePte = v.FirstPrototypePte; + e->cbPrototypePte = (DWORD)(v.LastContiguousPte - v.FirstPrototypePte + 8); + } + return e; +} + +PVMM_MAP_VADENTRY MmVad_Spider_MMVAD64_80(_In_ PVMM_PROCESS pSystemProcess, _In_ QWORD va, _In_ PVMMOB_MAP_VAD pmVad, _In_ POB_VSET psAll, _In_ POB_VSET psTry1, _In_opt_ POB_VSET psTry2, _In_ QWORD fVmmRead, _In_ DWORD dwReserved) +{ + _MMVAD64_80 v = { 0 }; + PVMM_MAP_VADENTRY e; + if(!VmmRead2(pSystemProcess, va, (PBYTE)&v, sizeof(_MMVAD64_80), fVmmRead | VMM_FLAG_FORCECACHE_READ)) { + ObVSet_Push(psTry2, va); + return NULL; + } + if((v.EndingVpn < v.StartingVpn) || !MmVad_Spider_PoolTagAny(v.PoolTag, 5, MMVAD_POOLTAG_VADS, MMVAD_POOLTAG_VAD, MMVAD_POOLTAG_VADL, MMVAD_POOLTAG_VADM, MMVAD_POOLTAG_VADF)) { + return NULL; + } + // short vad + e = &pmVad->pMap[pmVad->cMap++]; + if(VMM_KADDR64_16(v.LeftChild)) { + ObVSet_Push(psAll, v.LeftChild - 0x10); + ObVSet_Push(psTry1, v.LeftChild - 0x10); + } + if(VMM_KADDR64_16(v.RightChild)) { + ObVSet_Push(psAll, v.RightChild - 0x10); + ObVSet_Push(psTry1, v.RightChild - 0x10); + } + e->vaStart = (QWORD)v.StartingVpn << 12; + e->vaEnd = ((QWORD)v.EndingVpn << 12) | 0xfff; + e->CommitCharge = v.CommitCharge; + e->MemCommit = v.MemCommit; + e->VadType = v.VadType; + e->Protection = v.Protection; + e->fPrivateMemory = v.PrivateMemory; + // full vad + if(v.PoolTag == MMVAD_POOLTAG_VADS) { return e; } + e->flags[2] = (DWORD)v.u2; + e->vaSubsection = v.Subsection; + if(VMM_KADDR64_8(v.FirstPrototypePte)) { + e->vaPrototypePte = v.FirstPrototypePte; + e->cbPrototypePte = (DWORD)(v.LastContiguousPte - v.FirstPrototypePte); + } + return e; +} + +PVMM_MAP_VADENTRY MmVad_Spider_MMVAD32_10(_In_ PVMM_PROCESS pSystemProcess, _In_ QWORD va, _In_ PVMMOB_MAP_VAD pmVad, _In_ POB_VSET psAll, _In_ POB_VSET psTry1, _In_opt_ POB_VSET psTry2, _In_ QWORD fVmmRead, _In_ DWORD dwFlagsBitMask) +{ + _MMVAD32_10 v = { 0 }; + PVMM_MAP_VADENTRY e; + if(!VmmRead2(pSystemProcess, va, (PBYTE)&v, sizeof(_MMVAD32_10), fVmmRead | VMM_FLAG_FORCECACHE_READ)) { + ObVSet_Push(psTry2, va); + return NULL; + } + if((v.EndingVpn < v.StartingVpn) || !MmVad_Spider_PoolTagAny(v.PoolTag, 5, MMVAD_POOLTAG_VADS, MMVAD_POOLTAG_VAD, MMVAD_POOLTAG_VADL, MMVAD_POOLTAG_VADM, MMVAD_POOLTAG_VADF)) { + return NULL; + } + // short vad + e = &pmVad->pMap[pmVad->cMap++]; + if(VMM_KADDR32_8(v.Children[0])) { + ObVSet_Push(psAll, v.Children[0] - 8); + ObVSet_Push(psTry1, v.Children[0] - 8); + } + if(VMM_KADDR32_8(v.Children[1])) { + ObVSet_Push(psAll, v.Children[1] - 8); + ObVSet_Push(psTry1, v.Children[1] - 8); + } + e->vaStart = (QWORD)v.StartingVpn << 12; + e->vaEnd = ((QWORD)v.EndingVpn << 12) | 0xfff; + e->CommitCharge = v.CommitCharge; + e->MemCommit = v.MemCommit; + e->VadType = 0x07 & (v.u >> (dwFlagsBitMask & 0xff)); + e->Protection = 0x1f & (v.u >> ((dwFlagsBitMask >> 8) & 0xff)); + e->fPrivateMemory = 0x01 & (v.u >> ((dwFlagsBitMask >> 16) & 0xff)); + // full vad + if(v.PoolTag == MMVAD_POOLTAG_VADS) { return e; } + e->flags[2] = v.u2; + e->vaSubsection = v.Subsection; + if(VMM_KADDR32_4(v.FirstPrototypePte)) { + e->vaPrototypePte = v.FirstPrototypePte; + e->cbPrototypePte = (DWORD)(v.LastContiguousPte - v.FirstPrototypePte); + } + return e; +} + +PVMM_MAP_VADENTRY MmVad_Spider_MMVAD64_10(_In_ PVMM_PROCESS pSystemProcess, _In_ QWORD va, _In_ PVMMOB_MAP_VAD pmVad, _In_ POB_VSET psAll, _In_ POB_VSET psTry1, _In_opt_ POB_VSET psTry2, _In_ QWORD fVmmRead, _In_ DWORD dwFlagsBitMask) +{ + _MMVAD64_10 v = { 0 }; + PVMM_MAP_VADENTRY e; + if(!VmmRead2(pSystemProcess, va, (PBYTE)&v, sizeof(_MMVAD64_10), fVmmRead | VMM_FLAG_FORCECACHE_READ)) { + ObVSet_Push(psTry2, va); + return NULL; + } + if((v.EndingVpnHigh < v.StartingVpnHigh) || (v.EndingVpn < v.StartingVpn) || !MmVad_Spider_PoolTagAny(v.PoolTag, 5, MMVAD_POOLTAG_VADS, MMVAD_POOLTAG_VAD, MMVAD_POOLTAG_VADL, MMVAD_POOLTAG_VADM, MMVAD_POOLTAG_VADF)) { + return NULL; + } + // short vad + e = &pmVad->pMap[pmVad->cMap++]; + if(VMM_KADDR64_16(v.Children[0])) { + ObVSet_Push(psAll, v.Children[0] - 0x10); + ObVSet_Push(psTry1, v.Children[0] - 0x10); + } + if(VMM_KADDR64_16(v.Children[1])) { + ObVSet_Push(psAll, v.Children[1] - 0x10); + ObVSet_Push(psTry1, v.Children[1] - 0x10); + } + e->vaStart = ((QWORD)v.StartingVpnHigh << (32 + 12)) | ((QWORD)v.StartingVpn << 12); + e->vaEnd = ((QWORD)v.EndingVpnHigh << (32 + 12)) | ((QWORD)v.EndingVpn << 12) | 0xfff; + e->CommitCharge = (DWORD)v.CommitCharge; + e->MemCommit = (DWORD)v.MemCommit; + e->VadType = 0x07 & (v.u >> (dwFlagsBitMask & 0xff)); + e->Protection = 0x1f & (v.u >> ((dwFlagsBitMask >> 8) & 0xff)); + e->fPrivateMemory = 0x01 & (v.u >> ((dwFlagsBitMask >> 16) & 0xff)); + // full vad + if(v.PoolTag == MMVAD_POOLTAG_VADS) { return e; } + e->flags[2] = (DWORD)v.u2; + e->vaSubsection = v.Subsection; + if(VMM_KADDR64_8(v.FirstPrototypePte)) { + e->vaPrototypePte = v.FirstPrototypePte; + e->cbPrototypePte = (DWORD)(v.LastContiguousPte - v.FirstPrototypePte + 8); + } + return e; +} + +VOID MmVad_Spider_DoWork(_In_ PVMM_PROCESS pSystemProcess, _In_ PVMM_PROCESS pProcess, _In_ QWORD fVmmRead) +{ + BOOL f; + QWORD i, va; + DWORD cMax, cVads, dwFlagsBitMask = 0; + PVMM_MAP_VADENTRY eVad; + PVMMOB_MAP_VAD pmObVad = NULL, pmObVadTemp; + POB_VSET psObAll = NULL, psObTry1 = NULL, psObTry2 = NULL, psObPrefetch = NULL; + PVMM_MAP_VADENTRY(*pfnMmVad_Spider)(PVMM_PROCESS, QWORD, PVMMOB_MAP_VAD, POB_VSET, POB_VSET, POB_VSET, QWORD, DWORD); + if(!(ctxVmm->tpSystem == VMM_SYSTEM_WINDOWS_X64 || ctxVmm->tpSystem == VMM_SYSTEM_WINDOWS_X86)) { goto fail; } + // 1: retrieve # of VAD entries and sanity check. + if(ctxVmm->kernel.dwVersionBuild >= 9600) { + // Win8.1 and later -> fetch # of RtlBalancedNode from EPROCESS. + cVads = (DWORD)VMM_EPROCESS_PTR(pProcess, ctxVmm->kernel.OffsetEPROCESS.VadRoot + (ctxVmm->f32 ? 8 : 0x10)); + } else if(ctxVmm->kernel.dwVersionBuild >= 6000) { + // WinVista::Win8.0 -> fetch # of AvlNode from EPROCESS. + i = (ctxVmm->kernel.dwVersionBuild < 9200) ? (ctxVmm->f32 ? 0x14 : 0x28) : (ctxVmm->f32 ? 0x1c : 0x18); + cVads = ((DWORD)VMM_EPROCESS_PTR(pProcess, ctxVmm->kernel.OffsetEPROCESS.VadRoot + i) >> 8); + } else { + // WinXP + cVads = (DWORD)VMM_EPROCESS_DWORD(pProcess, 0x240); + } + if(cVads > 0x1000) { + vmmprintfv_fn("WARNING: BAD #VAD VALUE- PID: %i #VAD: %x\n", pProcess->dwPID, cVads); + cVads = 0x1000; + } + // 2: allocate and retrieve objects required for processing + if(!(pmObVad = Ob_Alloc(OB_TAG_MAP_VAD, LMEM_ZEROINIT, sizeof(VMMOB_MAP_VAD) + cVads * sizeof(VMM_MAP_VADENTRY), VmmVad_MemMapVad_CloseObCallback, NULL))) { goto fail; } + if(cVads == 0) { // No VADs + vmmprintfvv_fn("WARNING: NO VAD FOR PROCESS - PID: %i STATE: %i NAME: %s\n", pProcess->dwPID, pProcess->dwState, pProcess->szName); + pProcess->Map.pObVad = Ob_INCREF(pmObVad); + goto fail; + } + cMax = cVads; + if(!(psObAll = ObVSet_New())) { goto fail; } + if(!(psObTry1 = ObVSet_New())) { goto fail; } + if(!(psObTry2 = ObVSet_New())) { goto fail; } + // 3: retrieve initial VAD node entry + f = ((ctxVmm->kernel.dwVersionBuild >= 6000) && (ctxVmm->kernel.dwVersionBuild < 9600)); // AvlTree (Vista::Win8.0 + for(i = (f ? 1 : 0); i < (f ? 4 : 1); i++) { + va = VMM_EPROCESS_PTR(pProcess, ctxVmm->kernel.OffsetEPROCESS.VadRoot + i * (ctxVmm->f32 ? 4 : 8)); + if(ctxVmm->f32 && !VMM_KADDR32_8(va)) { continue; } + if(!ctxVmm->f32 && !VMM_KADDR64_16(va)) { continue; } + va -= ctxVmm->f32 ? 8 : 0x10; + ObVSet_Push(psObAll, va); + ObVSet_Push(psObTry2, va); + } + if(!ObVSet_Size(psObTry2)) { goto fail; } + if(ctxVmm->kernel.dwVersionBuild >= 9600) { + // Win8.1 and later + pfnMmVad_Spider = ctxVmm->f32 ? MmVad_Spider_MMVAD32_10 : MmVad_Spider_MMVAD64_10; + if(ctxVmm->kernel.dwVersionBuild >= 18362) { // bitmask offset for empty:PrivateMemory:Protection:VadType + dwFlagsBitMask = 0x00140704; + } else if(ctxVmm->kernel.dwVersionBuild >= 17134) { + dwFlagsBitMask = 0x000e0300; + } else { + dwFlagsBitMask = 0x000f0300; + } + } else if(ctxVmm->kernel.dwVersionBuild >= 9200) { + // Win8.0 + pfnMmVad_Spider = ctxVmm->f32 ? MmVad_Spider_MMVAD32_80 : MmVad_Spider_MMVAD64_80; + } else if(ctxVmm->kernel.dwVersionBuild >= 6000) { + // WinVista :: Win7 + pfnMmVad_Spider = ctxVmm->f32 ? MmVad_Spider_MMVAD32_7 : MmVad_Spider_MMVAD64_7; + } else { + // WinXP + pfnMmVad_Spider = MmVad_Spider_MMVAD32_XP; + } + // 4: cache: prefetch previous addresses + if((psObPrefetch = ObContainer_GetOb(pProcess->pObPersistent->pObCMapVadPrefetch))) { + VmmCachePrefetchPages3(pSystemProcess, psObPrefetch, sizeof(_MMVAD64_10), fVmmRead); + Ob_DECREF_NULL(&psObPrefetch); + } + // 5: spider vad tree in an efficient way (minimize non-cached reads) + while((pmObVad->cMap < cMax) && ObVSet_Size(psObTry2)) { + // fetch vad entries 2nd attempt + VmmCachePrefetchPages3(pSystemProcess, psObTry2, sizeof(_MMVAD64_10), fVmmRead); + while((pmObVad->cMap < cMax) && (va = ObVSet_Pop(psObTry2))) { + if((eVad = pfnMmVad_Spider(pSystemProcess, va, pmObVad, psObAll, psObTry1, NULL, fVmmRead, dwFlagsBitMask))) { + eVad->vaVad = va; + eVad->wszText = &ctxVmm->_EmptyWCHAR; + if(eVad->cbPrototypePte > 0x01000000) { eVad->cbPrototypePte = MMVAD_PTESIZE * (DWORD)((0x1000 + eVad->vaEnd - eVad->vaStart) >> 12); } + } + } + // fetch vad entries 1st attempt + while((pmObVad->cMap < cMax) && (va = ObVSet_Pop(psObTry1))) { + if((eVad = pfnMmVad_Spider(pSystemProcess, va, pmObVad, psObAll, psObTry1, psObTry2, fVmmRead, dwFlagsBitMask))) { + eVad->vaVad = va; + eVad->wszText = &ctxVmm->_EmptyWCHAR; + if(eVad->cbPrototypePte > 0x01000000) { eVad->cbPrototypePte = MMVAD_PTESIZE * (DWORD)((0x1000 + eVad->vaEnd - eVad->vaStart) >> 12); } + } + } + } + // 6: sort result + if(pmObVad->cMap > 1) { + qsort(pmObVad->pMap, pmObVad->cMap, sizeof(VMM_MAP_VADENTRY), MmVad_CmpVadEntry); + } + // 7: cache: update + ObContainer_SetOb(pProcess->pObPersistent->pObCMapVadPrefetch, psObAll); + // 8: shrink oversized result object (if sufficiently too large) + if(pmObVad->cMap + 0x10 < cMax) { + pmObVadTemp = pmObVad; + if(!(pmObVad = Ob_Alloc(OB_TAG_MAP_VAD, 0, sizeof(VMMOB_MAP_VAD) + pmObVadTemp->cMap * sizeof(VMM_MAP_VADENTRY), VmmVad_MemMapVad_CloseObCallback, NULL))) { goto fail; } + memcpy(((POB_DATA)pmObVad)->pb, ((POB_DATA)pmObVadTemp)->pb, pmObVad->ObHdr.cbData); + Ob_DECREF_NULL(&pmObVadTemp); + } + pProcess->Map.pObVad = Ob_INCREF(pmObVad); +fail: + Ob_DECREF(pmObVad); + Ob_DECREF(psObAll); + Ob_DECREF(psObTry1); + Ob_DECREF(psObTry2); +} + +/* +* Fetch extended information such as file and image names into the buffer +* pProcess->pObMemMapVad->wszText which will be allocated by the function +* and must be free'd upon cleanup of pObMemMapVad. +* NB! MUST BE CALLED IN THREAD-SAFE WAY AND MUST NOT HAVE A PREVIOUS BUFFER! +* -- pSystemProcess +* -- pProcess +* -- fVmmRead +*/ +VOID MmVad_TextFetch(_In_ PVMM_PROCESS pSystemProcess, _In_ PVMM_PROCESS pProcess, _In_ QWORD fVmmRead) +{ + BOOL fResult = FALSE; + BOOL f, f2, f32 = ctxVmm->f32; + DWORD oControlAreaFilePointer, oMultiText = 1, cwszMultiText = 2, dwTID; + BYTE pb[MAX_PATH*2+2]; + PQWORD pva = NULL; + LPWSTR wszMultiText = NULL; + QWORD i, j, va, cVads = 0; + PVMM_MAP_VADENTRY pVad, *ppVads; + PVMMOB_MAP_HEAP pObHeapMap = NULL; + PVMMOB_MAP_THREAD pObThreadMap = NULL; + VmmMap_GetThreadAsync(pProcess); // thread map async initialization to speed up later retrieval. + // count max potential vads and allocate. + { + for(i = 0; i < pProcess->Map.pObVad->cMap; i++) { + va = pProcess->Map.pObVad->pMap[i].vaSubsection; + if(f32 ? VMM_KADDR32_8(va) : VMM_KADDR64_16(va)) { + cVads++; + } + } + if(!cVads || !(pva = LocalAlloc(LMEM_ZEROINIT, cVads * 0x10))) { goto fail; } + ppVads = (PVMM_MAP_VADENTRY*)(pva + cVads); + } + // get subsection addresses from vad. + { + for(i = 0, j = 0; (i < pProcess->Map.pObVad->cMap) && (j < cVads); i++) { + va = pProcess->Map.pObVad->pMap[i].vaSubsection; + if(f32 ? VMM_KADDR32_8(va) : VMM_KADDR64_16(va)) { + ppVads[j] = pProcess->Map.pObVad->pMap + i; + pva[j++] = va; + } + } + } + // fetch subsection -> pointer to control area (1st address ptr in subsection) + if((ctxVmm->kernel.dwVersionBuild >= 6000)) { // Not WinXP (ControlArea already in map subsection field). + VmmCachePrefetchPages4(pSystemProcess, (DWORD)cVads, pva, 8, fVmmRead); + for(i = 0, f2 = FALSE, va = 0; i < cVads; i++) { + f = pva[i] && + VmmRead2(pSystemProcess, pva[i], (PBYTE)&va, f32 ? 4 : 8, fVmmRead | VMM_FLAG_FORCECACHE_READ) && + (f32 ? VMM_KADDR32_8(va) : VMM_KADDR64_16(va)); + f2 = f2 | f; + pva[i] = f ? va : 0; + } + if(!f2) { goto fail; } + } + // fetch control area -> pointer to file object. + { + VmmCachePrefetchPages4(pSystemProcess, (DWORD)cVads, pva, 0x50, fVmmRead); + oControlAreaFilePointer = f32 ? + ((ctxVmm->kernel.dwVersionBuild <= 7601) ? 0x24 : 0x20) : // 32-bit win7sp1- or win8.0+ + ((ctxVmm->kernel.dwVersionBuild <= 6000) ? 0x30 : 0x40); // 64-bit vistasp0- or vistasp1+ + for(i = 0, f2 = FALSE, va = 0; i < cVads; i++) { + f = pva[i] && + VmmRead2(pSystemProcess, pva[i] - 0x10, pb, 0x60, fVmmRead | VMM_FLAG_FORCECACHE_READ) && + (VMM_POOLTAG_PREPENDED(pb, 0x10, 'MmCa') || VMM_POOLTAG_PREPENDED(pb, 0x10, 'MmCi')) && + (va = f32 ? (~7 & *(PDWORD)(pb + 0x10 + oControlAreaFilePointer)) : (~0xf & *(PQWORD)(pb + 0x10 + oControlAreaFilePointer))) && + (f32 ? VMM_KADDR32_8(va) : VMM_KADDR64_16(va)); + f2 = f2 | f; + if(pva[i] && !f && VMM_POOLTAG_PREPENDED(pb, 0x10, 'MmCa')) { ppVads[i]->fPageFile = 1; } + if(f && VMM_POOLTAG_PREPENDED(pb, 0x10, 'MmCa')) { ppVads[i]->fFile = 1; } + if(f && VMM_POOLTAG_PREPENDED(pb, 0x10, 'MmCi')) { ppVads[i]->fImage = 1; } + pva[i] = f ? va : 0; + } + if(!f2) { goto fail; } + } + // fetch file object -> unicode string size and pointer to text. + { + VmmCachePrefetchPages4(pSystemProcess, (DWORD)cVads, pva, 0x68, fVmmRead); + for(i = 0, f2 = FALSE, va = 0; i < cVads; i++) { + f = pva[i] && + VmmRead2(pSystemProcess, pva[i] + (f32 ? 0x30 : 0x58), pb, 0x10, fVmmRead | VMM_FLAG_FORCECACHE_READ) && + *(PWORD)(pb) && (*(PWORD)(pb) <= *(PWORD)(pb + 2)) && + (va = f32 ? *(PDWORD)(pb + 4) : *(PQWORD)(pb + 8)) && + (f32 ? VMM_KADDR32_8(va) : VMM_KADDR64_16(va)); + f2 = f2 | f; + pva[i] = f ? va : 0; + if(f) { + ppVads[i]->cwszText = min(0xff, *(PWORD)(pb) >> 1); + cwszMultiText += ppVads[i]->cwszText + 1; + } + } + if(!f2) { goto fail; } + } + // [ heap map fetch reference ] + if(VmmMap_GetHeap(pProcess, &pObHeapMap)) { + cwszMultiText += 8 * pObHeapMap->cMap; // 7 WCHAR + NULL per heap entry + } + // [ thread map fetch reference ] + if(VmmMap_GetThread(pProcess, &pObThreadMap)) { + cwszMultiText += 20 * pObThreadMap->cMap; // 8(TEB) + 10(STACK) WCHAR + 2 NULL per thread entry + } + // fetch and parse unicode strings. + { + if(!(wszMultiText = LocalAlloc(LMEM_ZEROINIT, (QWORD)cwszMultiText << 1))) { goto fail; } + VmmCachePrefetchPages4(pSystemProcess, (DWORD)cVads, pva, MAX_PATH * 2, fVmmRead); + for(i = 0, f2 = FALSE; i < cVads; i++) { + f = pva[i] && VmmRead2(pSystemProcess, pva[i], (PBYTE)(wszMultiText + oMultiText), ppVads[i]->cwszText << 1, fVmmRead | VMM_FLAG_FORCECACHE_READ); + if(f) { + f2 = TRUE; + ppVads[i]->wszText = wszMultiText + oMultiText; + oMultiText += 1 + ppVads[i]->cwszText; + } + } + if(!f2) { goto fail; } + } + // [ heap map parse ] + if(pObHeapMap) { + for(i = 0; i < pObHeapMap->cMap; i++) { + if((pVad = VmmMap_GetVadEntry(pProcess->Map.pObVad, pObHeapMap->pMap[i].vaHeapSegment))) { + pVad->fHeap = 1; + pVad->HeapNum = pObHeapMap->pMap[i].HeapId; + if(!pVad->cwszText) { + swprintf_s(wszMultiText + oMultiText, cwszMultiText - oMultiText, L"HEAP-%02X", pVad->HeapNum); + pVad->wszText = wszMultiText + oMultiText; + pVad->cwszText = 7; + oMultiText += 8; + } + } + } + } + // [ thread map parse ] + if(pObThreadMap) { + for(i = 0; i < pObThreadMap->cMap; i++) { + if((pVad = VmmMap_GetVadEntry(pProcess->Map.pObVad, pObThreadMap->pMap[i].vaTeb))) { + pVad->fTeb = TRUE; + if(!pVad->cwszText) { + dwTID = min(0xffff, pObThreadMap->pMap[i].dwTID); + swprintf_s(wszMultiText + oMultiText, cwszMultiText - oMultiText, L"TEB-%04X", (WORD)min(0xffff, pObThreadMap->pMap[i].dwTID)); + pVad->wszText = wszMultiText + oMultiText; + pVad->cwszText = 8; + oMultiText += 9; + } + } + if((pVad = VmmMap_GetVadEntry(pProcess->Map.pObVad, pObThreadMap->pMap[i].vaStackLimitUser))) { + pVad->fStack = TRUE; + if(!pVad->cwszText) { + dwTID = min(0xffff, pObThreadMap->pMap[i].dwTID); + swprintf_s(wszMultiText + oMultiText, cwszMultiText - oMultiText, L"STACK-%04X", (WORD)min(0xffff, pObThreadMap->pMap[i].dwTID)); + pVad->wszText = wszMultiText + oMultiText; + pVad->cwszText = 10; + oMultiText += 11; + } + } + } + } + // cleanup + pProcess->Map.pObVad->cbMultiText = cwszMultiText << 1; + pProcess->Map.pObVad->wszMultiText = wszMultiText; + fResult = TRUE; +fail: + if(!fResult) { LocalFree(wszMultiText); } + Ob_DECREF(pObThreadMap); + Ob_DECREF(pObHeapMap); + LocalFree(pva); +} + +_Success_(return) +BOOL MmVad_PrototypePteArray_FetchNew_PoolHdrVerify(_In_ PBYTE pb, _In_ DWORD cbDataOffsetPoolHdr) +{ + DWORD o; + if(cbDataOffsetPoolHdr < 0x10) { + return !cbDataOffsetPoolHdr || ('tSmM' == *(PDWORD)pb); + } + for(o = 0; o < cbDataOffsetPoolHdr; o += 4) { + if('tSmM' == *(PDWORD)(pb + o)) { return TRUE; } // check for MmSt pool header in various locations + } + return FALSE; +} + +/* +* Fetch an array of prototype pte's into the cache. +* -- pSystemProcess +* -- pVad +* -- fVmmRead +*/ +VOID MmVad_PrototypePteArray_FetchNew(_In_ PVMM_PROCESS pSystemProcess, _In_ PVMM_MAP_VADENTRY pVad, _In_ QWORD fVmmRead) +{ + PBYTE pbData; + POB_DATA e = NULL; + DWORD cbData, cbDataOffsetPoolHdr = 0; + cbData = pVad->cbPrototypePte; + // 1: santity check size + if(cbData > 0x00010000) { // most probably an error, file > 32MB + cbData = MMVAD_PTESIZE * (DWORD)((0x1000 + pVad->vaEnd - pVad->vaStart) >> 12); + if(cbData > 0x00010000) { return; } + } + // 2: pool header offset (if any) + if(pVad->vaPrototypePte & 0xfff) { + if(ctxVmm->kernel.dwVersionBuild >= 9200) { // WIN8.0 and later + cbDataOffsetPoolHdr = ctxVmm->f32 ? 0x04 : 0x0c; + } else { + // WinXP to Win7 - pool header seems to be varying between these zero and these offsets, check for them all... + cbDataOffsetPoolHdr = ctxVmm->f32 ? 0x34 : 0x5c; + if((pVad->vaStart & 0xfff) < cbDataOffsetPoolHdr) { cbDataOffsetPoolHdr = 0; } + } + cbData += cbDataOffsetPoolHdr; + } + // 3: fetch prototype page table entries + if(!(pbData = LocalAlloc(0, cbData))) { return; } + if(VmmRead2(pSystemProcess, pVad->vaPrototypePte - cbDataOffsetPoolHdr, pbData, cbData, fVmmRead)) { + if(MmVad_PrototypePteArray_FetchNew_PoolHdrVerify(pbData, cbDataOffsetPoolHdr)) { + if((e = Ob_Alloc('MmSt', 0, sizeof(OB) + cbData - cbDataOffsetPoolHdr, NULL, NULL))) { + memcpy(e->pb, pbData + cbDataOffsetPoolHdr, cbData - cbDataOffsetPoolHdr); + } + } + } + if(!e) { + e = Ob_Alloc('MmSt', 0, sizeof(OB), NULL, NULL); + } + if(e) { + ObMap_Push(ctxVmm->Cache.pmPrototypePte, pVad->vaPrototypePte, e); + Ob_DECREF(e); + } + LocalFree(pbData); +} + +/* +* Retrieve an object manager object containing the prototype pte's. THe object +* will be retrieved from cache if possible, otherwise a read will be attempted +* provided that the fVmmRead flags allows for it. +* CALLER DECREF: return +* -- pVad +* -- fVmmRead +* -- return +*/ +POB_DATA MmVad_PrototypePteArray_Get(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MAP_VADENTRY pVad, _In_ QWORD fVmmRead) +{ + QWORD i, va; + POB_DATA e = NULL; + POB_VSET psObPrefetch = NULL; + PVMM_PROCESS pObSystemProcess = NULL; + PVMMOB_MAP_VAD pVadMap; + if(!pVad->vaPrototypePte || !pVad->cbPrototypePte) { return NULL; } + if((e = ObMap_GetByKey(ctxVmm->Cache.pmPrototypePte, pVad->vaPrototypePte))) { return e; } + EnterCriticalSection(&pProcess->LockUpdate); + if((e = ObMap_GetByKey(ctxVmm->Cache.pmPrototypePte, pVad->vaPrototypePte))) { + LeaveCriticalSection(&pProcess->LockUpdate); + return e; + } + if((pObSystemProcess = VmmProcessGet(4))) { + if(!pProcess->Map.pObVad->fSpiderPrototypePte && pVad->cbPrototypePte < 0x1000 && (psObPrefetch = ObVSet_New())) { + pVadMap = pProcess->Map.pObVad; + // spider all prototype pte's less than 0x1000 in size into the cache + pVadMap->fSpiderPrototypePte = TRUE; + for(i = 0; i < pVadMap->cMap; i++) { + va = pVadMap->pMap[i].vaPrototypePte; + if(va && (pVadMap->pMap[i].cbPrototypePte < 0x1000) && !ObMap_ExistsKey(ctxVmm->Cache.pmPrototypePte, va)) { + ObVSet_Push(psObPrefetch, va); + } + } + VmmCachePrefetchPages3(pObSystemProcess, psObPrefetch, 0x1000, fVmmRead); + for(i = 0; i < pVadMap->cMap; i++) { + va = pVadMap->pMap[i].vaPrototypePte; + if(va && (pVadMap->pMap[i].cbPrototypePte < 0x1000) && !ObMap_ExistsKey(ctxVmm->Cache.pmPrototypePte, va)) { + MmVad_PrototypePteArray_FetchNew(pObSystemProcess, pVadMap->pMap + i, fVmmRead | VMM_FLAG_FORCECACHE_READ); + } + } + Ob_DECREF(psObPrefetch); + } else { + // fetch single vad prototypte pte array into the cache + MmVad_PrototypePteArray_FetchNew(pObSystemProcess, pVad, fVmmRead); + } + Ob_DECREF(pObSystemProcess); + } + LeaveCriticalSection(&pProcess->LockUpdate); + return ObMap_GetByKey(ctxVmm->Cache.pmPrototypePte, pVad->vaPrototypePte); +} + + + +// ---------------------------------------------------------------------------- +// IMPLEMENTATION OF VAD RELATED GENERAL FUNCTIONALITY BELOW: +// ---------------------------------------------------------------------------- + +/* +* Try to read a prototype page table entry (PTE). +* -- pProcess +* -- va +* -- pfInRange +* -- fVmmRead = VMM_FLAGS_* flags. +* -- return = prototype pte or zero on fail. +*/ +QWORD MmVad_PrototypePte(_In_ PVMM_PROCESS pProcess, _In_ QWORD va, _Out_opt_ PBOOL pfInRange, _In_ QWORD fVmmRead) +{ + QWORD iPrototypePte, qwPrototypePte = 0; + POB_DATA pObPteArray = NULL; + PVMM_MAP_VADENTRY pVad = NULL; + if(MmVad_MapInitialize(pProcess, FALSE, fVmmRead) && (pVad = VmmMap_GetVadEntry(pProcess->Map.pObVad, va)) && (pObPteArray = MmVad_PrototypePteArray_Get(pProcess, pVad, fVmmRead))) { + iPrototypePte = (va - pVad->vaStart) >> 12; + if(ctxVmm->tpMemoryModel == VMM_MEMORYMODEL_X86) { + if(pObPteArray->ObHdr.cbData > (iPrototypePte * 4)) { + qwPrototypePte = pObPteArray->pdw[iPrototypePte]; + } + } else { + if(pObPteArray->ObHdr.cbData > (iPrototypePte * 8)) { + qwPrototypePte = pObPteArray->pqw[iPrototypePte]; + } + } + Ob_DECREF(pObPteArray); + } + if(pfInRange) { *pfInRange = pVad ? TRUE : FALSE; } + return qwPrototypePte; +} + +_Success_(return) +BOOL MmVad_MapInitialize_Core(_In_ PVMM_PROCESS pProcess, _In_ QWORD fVmmRead) +{ + PVMM_PROCESS pObSystemProcess; + if(pProcess->Map.pObVad) { return TRUE; } + EnterCriticalSection(&pProcess->LockUpdate); + if(!pProcess->Map.pObVad && (pObSystemProcess = VmmProcessGet(4))) { + MmVad_Spider_DoWork(pObSystemProcess, pProcess, fVmmRead | VMM_FLAG_NOVAD); + if(!pProcess->Map.pObVad) { + pProcess->Map.pObVad = Ob_Alloc(OB_TAG_MAP_VAD, LMEM_ZEROINIT, sizeof(VMMOB_MAP_VAD), VmmVad_MemMapVad_CloseObCallback, NULL); + } + Ob_DECREF(pObSystemProcess); + } + LeaveCriticalSection(&pProcess->LockUpdate); + return pProcess->Map.pObVad ? TRUE : FALSE; +} + +_Success_(return) +BOOL MmVad_MapInitialize_Text(_In_ PVMM_PROCESS pProcess, _In_ QWORD fVmmRead) +{ + PVMM_PROCESS pObSystemProcess; + if(pProcess->Map.pObVad->wszMultiText) { return TRUE; } + EnterCriticalSection(&pProcess->Map.LockUpdateExtendedInfo); + if(!pProcess->Map.pObVad->wszMultiText && (pObSystemProcess = VmmProcessGet(4))) { + MmVad_TextFetch(pObSystemProcess, pProcess, fVmmRead | VMM_FLAG_NOVAD); + Ob_DECREF(pObSystemProcess); + } + LeaveCriticalSection(&pProcess->Map.LockUpdateExtendedInfo); + return pProcess->Map.pObVad->wszMultiText ? TRUE : FALSE; +} + +/* +* Initialize / Ensure that a VAD map is initialized for the specific process. +* -- pProcess +* -- fExtendedText = also fetch extended info such as module names. +* -- fVmmRead = VMM_FLAGS_* flags. +* -- return +*/ +_Success_(return) +BOOL MmVad_MapInitialize(_In_ PVMM_PROCESS pProcess, _In_ BOOL fExtendedText, _In_ QWORD fVmmRead) +{ + if(pProcess->Map.pObVad && (!fExtendedText || pProcess->Map.pObVad->wszMultiText)) { return TRUE; } + VmmTlbSpider(pProcess); + return MmVad_MapInitialize_Core(pProcess, fVmmRead) && (!fExtendedText || MmVad_MapInitialize_Text(pProcess, fVmmRead)); +} diff --git a/vmm/mm_win.c b/vmm/mm_win.c new file mode 100644 index 00000000..f6fa97dd --- /dev/null +++ b/vmm/mm_win.c @@ -0,0 +1,1390 @@ +// mm_win.c : implementation of functionality related to the windows paging subsystem. +// +// (c) Ulf Frisk, 2019 +// Author: Ulf Frisk, pcileech@frizk.net +// +#include "vmm.h" +#include "mm.h" +#include "pdb.h" +#include "pe.h" +#include "statistics.h" +#include "util.h" + +#define MM_BUILD_17134_LATER (ctxVmm->kernel.dwVersionBuild >= 17134) +#define MM_LOOP_PROTECT_ADD(flags) ((flags & ~0x00ff0000) | ((((flags >> 16) & 0xff) + 1) << 16)) +#define MM_LOOP_PROTECT_MAX(flags) (((flags >> 16) & 0xff) > 4) + +#define PTE_SWIZZLE_BIT 0x10 +#define PTE_SWIZZLE_MASK 0x00002000 + +#define MMWINX86_PTE_IS_HARDWARE(pte) (pte & 0x01) +#define MMWINX86_PTE_TRANSITION(pte) (((pte & 0x0c01) == 0x0800) ? ((pte & 0xfffff000) | 0x005) : 0) +#define MMWINX86_PTE_PROTOTYPE(pte) (((pte & 0x00000407) == 0x00000400) ? (0x80000000 | ((pte >> 1) & 0x7ffffc00) | ((pte << 1) & 0x3ff)) : 0) +#define MMWINX86_PTE_PAGE_FILE_NUMBER(pte) ((pte >> 1) & 0x0f) +#define MMWINX86_PTE_PAGE_FILE_OFFSET(pte) (pte >> 12) + +#define MMWINX86PAE_PTE_IS_HARDWARE(pte) (pte & 0x01) +#define MMWINX86PAE_PTE_TRANSITION(pte) (((pte & 0x0c01) == 0x0800) ? ((pte & 0x0000003f'fffff000) | 0x005) : 0) +#define MMWINX86PAE_PTE_PROTOTYPE(pte) (((pte & 0x80000007'00000401) == 0x80000000'00000400) ? (pte >> 32) : 0) +#define MMWINX86PAE_PTE_PAGE_FILE_NUMBER(pte) ((pte >> (((ctxVmm->kernel.dwVersionBuild >= 17134) ? 12 : 1))) & 0x0f) +#define MMWINX86PAE_PTE_PAGE_FILE_OFFSET(pte) ((pte >> 32) ^ ((!(pte & PTE_SWIZZLE_BIT) && MM_BUILD_17134_LATER) ? PTE_SWIZZLE_MASK : 0)) +#define MMWINX86PAE_PTE_PAGE_KEY_COMPRESSED(pte) (DWORD)(((MMWINX86PAE_PTE_PAGE_FILE_NUMBER(pte) << 0x1c) | MMWINX86PAE_PTE_PAGE_FILE_OFFSET(pte))) + +#define MMWINX64_PTE_IS_HARDWARE(pte) (pte & 0x01) +#define MMWINX64_PTE_TRANSITION(pte) (((pte & 0x0c01) == 0x0800) ? ((pte & 0xffffdfff'fffff000) | 0x005) : 0) +#define MMWINX64_PTE_PROTOTYPE(pte) (((pte & 0x80000000'00070401) == 0x80000000'00000400) ? ((pte >> 16) | 0xffff0000'00000000) : 0) +#define MMWINX64_PTE_PAGE_FILE_NUMBER(pte) ((pte >> (((ctxVmm->kernel.dwVersionBuild >= 17134) ? 12 : 1))) & 0x0f) +#define MMWINX64_PTE_PAGE_FILE_OFFSET(pte) ((pte >> 32) ^ ((!(pte & PTE_SWIZZLE_BIT) && MM_BUILD_17134_LATER) ? PTE_SWIZZLE_MASK : 0)) +#define MMWINX64_PTE_PAGE_KEY_COMPRESSED(pte) (DWORD)(((MMWINX64_PTE_PAGE_FILE_NUMBER(pte) << 0x1c) | MMWINX64_PTE_PAGE_FILE_OFFSET(pte))) + +typedef struct tdMMWIN_MEMCOMPRESS_OFFSET { + BOOL _fValid; + BOOL _fProcessedTry; + WORD _Size; + struct { + WORD PagesTree; + WORD SmkmStore; + WORD ChunkMetaData; + WORD RegionSizeMask; + WORD RegionIndexMask; + WORD CompressionAlgorithm; + WORD CompressedRegionPtrArray; + WORD OwnerProcess; + } SMKM_STORE; +} MMWIN_MEMCOMPRESS_OFFSET, *PMMWIN_MEMCOMPRESS_OFFSET; + +typedef struct tdMMWIN_MEMCOMPRESS_CONTEXT { + QWORD vaEPROCESS; + DWORD dwPid; + DWORD dwPageFileNumber; + BOOL fValid; + BOOL fInitialized; + QWORD vaSmGlobals; + QWORD vaKeyToStoreTree; + MMWIN_MEMCOMPRESS_OFFSET O; +} MMWIN_MEMCOMPRESS_CONTEXT, *PMMWIN_MEMCOMPRESS_CONTEXT; + +typedef struct tdMMWIN_CONTEXT { + CRITICAL_SECTION Lock; + FILE *pPageFile[10]; + MMWIN_MEMCOMPRESS_CONTEXT MemCompress; +} MMWIN_CONTEXT, *PMMWIN_CONTEXT; + +//----------------------------------------------------------------------------- +// BTREE FUNCTIONALITY BELOW: +//----------------------------------------------------------------------------- + +typedef struct td_BTREE_LEAF_ENTRY { + DWORD k; + DWORD v; +} _BTREE_LEAF_ENTRY; + +typedef struct td_BTREE_NODE_ENTRY32 { + DWORD k; + DWORD vaLeaf; +} _BTREE_NODE_ENTRY32; + +typedef struct td_BTREE_NODE_ENTRY64 { + DWORD k; + QWORD vaLeaf; +} _BTREE_NODE_ENTRY64; + +typedef struct td_BTREE32 { + WORD cEntries; + BYTE cLevel; + BYTE fLeaf; + DWORD vaLeftChild; + union { + _BTREE_LEAF_ENTRY LeafEntries[]; + _BTREE_NODE_ENTRY32 NodeEntries[]; + }; +} _BTREE32, *P_BTREE32; + +typedef struct td_BTREE64 { + WORD cEntries; + BYTE cLevel; + BYTE fLeaf; + QWORD vaLeftChild; + union { + _BTREE_LEAF_ENTRY LeafEntries[]; + _BTREE_NODE_ENTRY64 NodeEntries[]; + }; +} _BTREE64, *P_BTREE64; + +_Success_(return) +BOOL MmWin_BTree32_Search(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaTree, _In_ DWORD dwKey, _Out_ PDWORD pdwValue, _In_ QWORD fVmmRead); + +_Success_(return) +BOOL MmWin_BTree64_Search(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaTree, _In_ DWORD dwKey, _Out_ PDWORD pdwValue, _In_ QWORD fVmmRead); + +_Success_(return) +BOOL MmWin_BTree32_SearchLeaf(_In_ PVMM_PROCESS pSystemProcess, _In_ P_BTREE32 pT, _In_ DWORD dwKey, _Out_ PDWORD pdwValue, _In_ QWORD fVmmRead) +{ + BOOL fSearchPreFail = FALSE; + DWORD i, dwSearchStep, dwSearchIndex = 1, dwSearchCount = 0; + // 2: search tree for leaf + for(i = 1; (i < 12) && ((pT->cEntries - 1) >> i); i++); + dwSearchIndex = dwSearchStep = min(1 << (i - 1), pT->cEntries); + while(TRUE) { + dwSearchCount++; + dwSearchStep = dwSearchStep >> 1; + if(pT->LeafEntries[dwSearchIndex].k == dwKey) { + *pdwValue = pT->LeafEntries[dwSearchIndex].v; + return TRUE; + } + if(dwSearchStep == 0) { + if(fSearchPreFail) { + return FALSE; + } + fSearchPreFail = TRUE; + dwSearchStep = 1; + } + if(pT->LeafEntries[dwSearchIndex].k < dwKey) { + if(dwSearchIndex + dwSearchStep < pT->cEntries) { + dwSearchIndex += dwSearchStep; + } + } else { + if(dwSearchStep <= dwSearchIndex) { + dwSearchIndex -= dwSearchStep; + } + } + } +} + +_Success_(return) +BOOL MmWin_BTree64_SearchLeaf(_In_ PVMM_PROCESS pSystemProcess, _In_ P_BTREE64 pT, _In_ DWORD dwKey, _Out_ PDWORD pdwValue, _In_ QWORD fVmmRead) +{ + BOOL fSearchPreFail = FALSE; + DWORD i, dwSearchStep, dwSearchIndex = 1; + // 2: search tree for leaf + for(i = 1; (i < 12) && ((pT->cEntries - 1) >> i); i++); + dwSearchIndex = dwSearchStep = min(1 << (i - 1), pT->cEntries); + while(TRUE) { + dwSearchStep = dwSearchStep >> 1; + if(pT->LeafEntries[dwSearchIndex].k == dwKey) { + *pdwValue = pT->LeafEntries[dwSearchIndex].v; + return TRUE; + } + if(dwSearchStep == 0) { + if(fSearchPreFail) { + return FALSE; + } + fSearchPreFail = TRUE; + dwSearchStep = 1; + } + if(pT->LeafEntries[dwSearchIndex].k < dwKey) { + if(dwSearchIndex + dwSearchStep < pT->cEntries) { + dwSearchIndex += dwSearchStep; + } + } else { + if(dwSearchStep <= dwSearchIndex) { + dwSearchIndex -= dwSearchStep; + } + } + } +} + +_Success_(return) +BOOL MmWin_BTree32_SearchNode(_In_ PVMM_PROCESS pSystemProcess, _In_ P_BTREE32 pT, _In_ DWORD dwKey, _Out_ PDWORD pdwValue, _In_ QWORD fVmmRead) +{ + BOOL fSearchPreFail = FALSE; + DWORD i, dwSearchStep, dwSearchIndex = 1; + QWORD vaSubTree = 0; + // 2: search tree for entry + for(i = 1; (i < 12) && ((pT->cEntries - 1) >> i); i++); + dwSearchIndex = dwSearchStep = min(1 << (i - 1), pT->cEntries - 1); + while(TRUE) { + dwSearchStep = dwSearchStep >> 1; + if((dwSearchStep == 0) && !fSearchPreFail) { + fSearchPreFail = TRUE; + dwSearchStep = 1; + } + if((dwSearchStep == 0) || ((pT->NodeEntries[dwSearchIndex].k <= dwKey) && ((dwSearchIndex + 1 == pT->cEntries) || (pT->NodeEntries[dwSearchIndex + 1].k > dwKey)))) { + if((dwSearchIndex == 0) && (pT->NodeEntries[0].k > dwKey)) { + vaSubTree = pT->vaLeftChild; + } else { + vaSubTree = pT->NodeEntries[dwSearchIndex].vaLeaf; + } + return MmWin_BTree32_Search(pSystemProcess, vaSubTree, dwKey, pdwValue, MM_LOOP_PROTECT_ADD(fVmmRead)); + } else if(pT->NodeEntries[dwSearchIndex].k < dwKey) { + if(dwSearchIndex + dwSearchStep < pT->cEntries) { + dwSearchIndex += dwSearchStep; + } + } else { + if(dwSearchStep <= dwSearchIndex) { + dwSearchIndex -= dwSearchStep; + } + } + } +} + +_Success_(return) +BOOL MmWin_BTree64_SearchNode(_In_ PVMM_PROCESS pSystemProcess, _In_ P_BTREE64 pT, _In_ DWORD dwKey, _Out_ PDWORD pdwValue, _In_ QWORD fVmmRead) +{ + BOOL fSearchPreFail = FALSE; + DWORD i, dwSearchStep, dwSearchIndex = 1, dwSearchCount = 0; + QWORD vaSubTree = 0; + // 2: search tree for entry + for(i = 1; (i < 12) && ((pT->cEntries - 1) >> i); i++); + dwSearchIndex = dwSearchStep = min(1 << (i - 1), pT->cEntries - 1); + while(TRUE) { + dwSearchCount++; + dwSearchStep = dwSearchStep >> 1; + if((dwSearchStep == 0) && !fSearchPreFail) { + fSearchPreFail = TRUE; + dwSearchStep = 1; + } + if((dwSearchStep == 0) || ((pT->NodeEntries[dwSearchIndex].k <= dwKey) && ((dwSearchIndex + 1 == pT->cEntries) || (pT->NodeEntries[dwSearchIndex + 1].k > dwKey)))) { + if((dwSearchIndex == 0) && (pT->NodeEntries[0].k > dwKey)) { + vaSubTree = pT->vaLeftChild; + } else { + vaSubTree = pT->NodeEntries[dwSearchIndex].vaLeaf; + } + return MmWin_BTree64_Search(pSystemProcess, vaSubTree, dwKey, pdwValue, MM_LOOP_PROTECT_ADD(fVmmRead)); + } else if(pT->NodeEntries[dwSearchIndex].k < dwKey) { + if(dwSearchIndex + dwSearchStep < pT->cEntries) { + dwSearchIndex += dwSearchStep; + } + } else { + if(dwSearchStep <= dwSearchIndex) { + dwSearchIndex -= dwSearchStep; + } + } + } +} + +_Success_(return) +BOOL MmWin_BTree32_Search(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaTree, _In_ DWORD dwKey, _Out_ PDWORD pdwValue, _In_ QWORD fVmmRead) +{ + BOOL f; + BYTE pbBuffer[0x1000]; + P_BTREE32 pT = (P_BTREE32)pbBuffer; + // 1: read tree + f = !MM_LOOP_PROTECT_MAX(fVmmRead) && + VMM_KADDR32_PAGE(vaTree) && + VmmRead2(pProcess, vaTree, pbBuffer, 0x1000, fVmmRead) && + pT->cEntries; + if(!f) { return FALSE; } + if(pT->fLeaf) { + // Leaf + if(pT->cEntries > 0x1ff) { return FALSE; } + return MmWin_BTree32_SearchLeaf(pProcess, pT, dwKey, pdwValue, fVmmRead); + } else { + // Node + if(pT->cEntries > 0x1ff) { return FALSE; } + return MmWin_BTree32_SearchNode(pProcess, pT, dwKey, pdwValue, fVmmRead); + } +} + +_Success_(return) +BOOL MmWin_BTree64_Search(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaTree, _In_ DWORD dwKey, _Out_ PDWORD pdwValue, _In_ QWORD fVmmRead) +{ + BOOL f; + BYTE pbBuffer[0x1000]; + P_BTREE64 pT = (P_BTREE64)pbBuffer; + // 1: read tree + f = !MM_LOOP_PROTECT_MAX(fVmmRead) && + VMM_KADDR64_PAGE(vaTree) && + VmmRead2(pProcess, vaTree, pbBuffer, 0x1000, fVmmRead) && + pT->cEntries; + if(!f) { return FALSE; } + if(pT->fLeaf) { + // Leaf + if(pT->cEntries > 0x1ff) { return FALSE; } + return MmWin_BTree64_SearchLeaf(pProcess, pT, dwKey, pdwValue, fVmmRead); + } else { + // Node + if(pT->cEntries > 0xff) { return FALSE; } + return MmWin_BTree64_SearchNode(pProcess, pT, dwKey, pdwValue, fVmmRead); + } +} + +_Success_(return) +BOOL MmWin_BTree_Search(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaTree, _In_ DWORD dwKey, _Out_ PDWORD pdwValue, _In_ QWORD fVmmRead) +{ + return ctxVmm->f32 ? MmWin_BTree32_Search(pProcess, vaTree, dwKey, pdwValue, fVmmRead) : MmWin_BTree64_Search(pProcess, vaTree, dwKey, pdwValue, fVmmRead); +} + + +//----------------------------------------------------------------------------- +// MEMCOMPRESSION INITIALIZATION FUNCTIONALITY BELOW: +//----------------------------------------------------------------------------- + +/* +* Initialize offsets in _SMKM_STORE / _ST_STORE / _ST_DATA_MGR +*/ +VOID MmWin_MemCompress_InitializeOffsets32() +{ + PMMWIN_MEMCOMPRESS_OFFSET po = &((PMMWIN_CONTEXT)ctxVmm->pMmContext)->MemCompress.O; + po->SMKM_STORE.PagesTree = 0x38 + 0x0; // static = ok + po->SMKM_STORE.ChunkMetaData = 0x38 + 0x6C; // static = ok + po->SMKM_STORE.SmkmStore = 0x38 + 0x1C0; // static = ok + po->SMKM_STORE.RegionSizeMask = 0x38 + 0x1C4; // static = ok + po->SMKM_STORE.RegionIndexMask = 0x38 + 0x1C8; // static = ok + po->SMKM_STORE.CompressionAlgorithm = 0x38 + 0x224; // 1709+ + po->SMKM_STORE.CompressedRegionPtrArray = 0x1184; // 1709+ + po->SMKM_STORE.OwnerProcess = 0x1254; // 1709+ + if(ctxVmm->kernel.dwVersionBuild == 15063) { // 1703 + po->SMKM_STORE.CompressionAlgorithm = 0x38 + 0x220; + po->SMKM_STORE.CompressedRegionPtrArray = 0x1174; + po->SMKM_STORE.OwnerProcess = 0x1244; + } + if(ctxVmm->kernel.dwVersionBuild == 14393) { // 1607 + po->SMKM_STORE.CompressionAlgorithm = 0x38 + 0x220; + po->SMKM_STORE.CompressedRegionPtrArray = 0x1124; + po->SMKM_STORE.OwnerProcess = 0x1204; + } + po->_Size = po->SMKM_STORE.OwnerProcess + 8; + po->_fProcessedTry = TRUE; + po->_fValid = TRUE; +} + +VOID MmWin_MemCompress_InitializeOffsets64() +{ + PMMWIN_MEMCOMPRESS_OFFSET po = &((PMMWIN_CONTEXT)ctxVmm->pMmContext)->MemCompress.O; + po->SMKM_STORE.PagesTree = 0x50 + 0x0; // static = ok + po->SMKM_STORE.ChunkMetaData = 0x50 + 0xC0; // static = ok + po->SMKM_STORE.SmkmStore = 0x50 + 0x320; // static = ok + po->SMKM_STORE.RegionSizeMask = 0x50 + 0x328; // static = ok + po->SMKM_STORE.RegionIndexMask = 0x50 + 0x32C; // static = ok + po->SMKM_STORE.CompressionAlgorithm = 0x50 + 0x3E0; // 1709+ + po->SMKM_STORE.CompressedRegionPtrArray = 0x1848; // 1709+ + po->SMKM_STORE.OwnerProcess = 0x19A8; // 1709+ + if(ctxVmm->kernel.dwVersionBuild == 15063) { // 1703 + po->SMKM_STORE.CompressionAlgorithm = 0x50 + 0x3D0; + po->SMKM_STORE.CompressedRegionPtrArray = 0x1828; + po->SMKM_STORE.OwnerProcess = 0x1988; + } + if(ctxVmm->kernel.dwVersionBuild == 14393) { // 1607 + po->SMKM_STORE.CompressionAlgorithm = 0x50 + 0x3D0; + po->SMKM_STORE.CompressedRegionPtrArray = 0x17A8; + po->SMKM_STORE.OwnerProcess = 0x1918; + } + po->_Size = po->SMKM_STORE.OwnerProcess + 8; + po->_fProcessedTry = TRUE; + po->_fValid = TRUE; +} + +/* +* Retrieve the page file number of the virtual store. This will be '2' on a +* standard system, but if paging are configured in a non-standard way this +* number may differ. +* Walk nt!MiSystemPartition/nt!.data section for candidate pointers to +* nt!_MMPAGING_FILE which have pool header: 'Mm '. The page file number and +* the virtual store flag is contained at same bits in all known versions with +* MemCompression as per below (for 64-bit): +* dt nt!_MMPAGING_FILE +* +0x0cc PageFileNumber : Pos 0, 4 Bits +* +0x0cc VirtualStorePagefile : Pos 6, 1 Bit +* If this function fails it will automatically fallback to the default number +* of 2. +*/ +VOID MmWin_MemCompress_InitializeVirtualStorePageFileNumber() +{ + BOOL f; + BYTE pbMm[0x100] = { 0 }; + QWORD j, va = 0; + DWORD i, cb, cbRead, oPoolHdr, oPfNum; + PBYTE pb = NULL; + PVMM_PROCESS pObSystemProcess = NULL; + IMAGE_SECTION_HEADER oSectionHeader; + POB_VSET pObSet = NULL; + PMMWIN_CONTEXT ctx = (PMMWIN_CONTEXT)ctxVmm->pMmContext; + ctx->MemCompress.dwPageFileNumber = 2; + // 1: SetUp and locate nt!MiSystemPartition/nt!.data + if(!(pObSet = ObVSet_New())) { goto finish; } + if(!(pObSystemProcess = VmmProcessGet(4))) { goto finish; } + if(PDB_GetSymbolAddress(VMMWIN_PDB_HANDLE_KERNEL, "MiSystemPartition", &va) && va) { + cb = 0x3000; + } else { + if(!PE_SectionGetFromName(pObSystemProcess, ctxVmm->kernel.vaBase, ".data", &oSectionHeader)) { + vmmprintfv_fn("CANNOT READ ntoskrnl.exe .data SECTION from PE header.\n"); + goto finish; + } + if(oSectionHeader.Misc.VirtualSize > 0x00100000) { goto finish; } + va = ctxVmm->kernel.vaBase + oSectionHeader.VirtualAddress; + cb = oSectionHeader.Misc.VirtualSize; + } + if(!(pb = LocalAlloc(0, cb))) { goto finish; } + if(!VmmRead(pObSystemProcess, va, pb, cb)) { + vmmprintfv_fn("CANNOT READ ntoskrnl.exe .data SECTION.\n"); + goto finish; + } + if(ctxVmm->f32) { + // 32-bit + // 2: Search for candidate pointers + for(i = 0; i < cb - 0x90; i += 4) { + f = (*(PDWORD)(pb + i + 0x004) == 1) && + *(PDWORD)(pb + i + 0x000) && + (*(PDWORD)(pb + i + 0x000) < 16) && + VMM_KADDR32_8(*(PDWORD)(pb + i + 0x008)) && + VMM_KADDR32_8(*(PDWORD)(pb + i + 0x00c)); + if(f) { + for(j = 0; j < *(PDWORD)(pb + i + 0x000); j++) { + va = *(PDWORD)(pb + i + 0x008 + j * 4); + if(VMM_KADDR32_8(va)) { + ObVSet_Push(pObSet, va); + } + } + } + } + oPoolHdr = 12; + oPfNum = 0x74; + } else { + // 64-bit + // 2: Search for candidate pointers + for(i = 0; i < cb - 0x90; i += 8) { + f = (*(PDWORD)(pb + i + 0x004) == 1) && + *(PDWORD)(pb + i + 0x000) && + (*(PDWORD)(pb + i + 0x000) < 16) && + VMM_KADDR64_16(*(PQWORD)(pb + i + 0x008)) && + VMM_KADDR64_16(*(PQWORD)(pb + i + 0x010)) && + ((*(PQWORD)(pb + i + 0x008) >> 32) == (*(PQWORD)(pb + i + 0x010) >> 32)); + if(f) { + for(j = 0; j < *(PDWORD)(pb + i + 0x000); j++) { + va = *(PQWORD)(pb + i + 0x008 + j * 8); + if(VMM_KADDR64_16(va)) { + ObVSet_Push(pObSet, va); + } + } + } + } + oPoolHdr = 4; + oPfNum = 0xcc; + } + // 3: Verify nt!dt _MMPAGING_FILE by looking at pool header and VirtualStorePagefile bit + VmmCachePrefetchPages(pObSystemProcess, pObSet, 0); + while((va = ObVSet_Pop(pObSet))) { + VmmReadEx(pObSystemProcess, va - 0x10, pbMm, 0x100, &cbRead, VMM_FLAG_FORCECACHE_READ); + if((*(PDWORD)(pbMm + oPoolHdr) == ' mM') && (*(PBYTE)(pbMm + 0x10 + oPfNum) & 0x40)) { + ctx->MemCompress.dwPageFileNumber = (*(PBYTE)(pbMm + 0x10 + oPfNum) & 0x0f); + goto finish; + } + } + vmmprintfv_fn("WARN! did not find virtual store number - fallback to default.\n"); +finish: + LocalFree(pb); + Ob_DECREF(pObSystemProcess); + Ob_DECREF(pObSet); +} + +/* +* Locate SmGlobals in 1st page of ntoskrnl.exe!CACHEALI section by looking for +* pointers to SMKM_STORE_METADATA (pool hdr: 'SmSa'). +* SMGLOBALS (_SMKM_STORE_MGR)+000 = Smkm Metadata (_SMKM sSmkm) +* _SMKM: PTR[32] to _SMKM_STORE_METADATA: (pool hdr smSa) +* _SMKM_STORE_METADATA: sizeof(_SMKM_STORE_METADATA) = 0x28 +* +000 = PTR to SMKM_STORE +* +018 = PTR to EPROCESS +* SMGLOBALS (_SMKM_STORE_MGR)+1C0 = KeyToStoreTree (B_TREE sGlobalTree) +* -- pSystemProcess +*/ +VOID MmWin_MemCompress_Initialize_NoPdb64() +{ + BOOL f, f32 = ctxVmm->f32; + BYTE pbPage[0x1000] = { 0 }; + DWORD i, dwSmsaPoolHdr = 0, cbRead; + QWORD vaSmGlobals, vaSmsa, vaKeyToStoreTree; + IMAGE_SECTION_HEADER oSectionHeader; + PVMM_PROCESS pObSystemProcess = NULL; + POB_VSET pObSet = NULL; + PMMWIN_CONTEXT ctx = (PMMWIN_CONTEXT)ctxVmm->pMmContext; + EnterCriticalSection(&ctxVmm->MasterLock); + if(ctx->MemCompress.fInitialized || (ctxVmm->kernel.dwVersionBuild < 14393)) { goto finish; } + // 1: Locate SmGlobals candidates in ntoskrnl.exe!CACHEALI section + if(!(pObSystemProcess = VmmProcessGet(4))) { goto finish; } + if(!(pObSet = ObVSet_New())) { goto finish; } + if(!PE_SectionGetFromName(pObSystemProcess, ctxVmm->kernel.vaBase, "CACHEALI", &oSectionHeader)) { + vmmprintfv_fn("CANNOT READ ntoskrnl.exe CACHEALI SECTION from PE header.\n"); + goto finish; + } + if(!VmmRead(pObSystemProcess, ctxVmm->kernel.vaBase + oSectionHeader.VirtualAddress, pbPage, 0x1000)) { + vmmprintfv_fn("CANNOT READ ntoskrnl.exe CACHEALI SECTION.\n"); + goto finish; + } + // 2: Verify SMGLOBALS / _SMKM_STORE_METADATA (pool hdr: 'smSa') + for(i = 0; i < 0x1000; i += 8) { + vaSmGlobals = ctxVmm->kernel.vaBase + oSectionHeader.VirtualAddress + i; + vaSmsa = *(PQWORD)(pbPage + i); + vaKeyToStoreTree = *(PQWORD)(pbPage + i + 0x1c0); + f = VMM_KADDR64_PAGE(vaKeyToStoreTree) && + VMM_KADDR64_16(vaSmsa); + if(f) { + ObVSet_Push(pObSet, vaSmGlobals); + ObVSet_Push(pObSet, vaKeyToStoreTree); + ObVSet_Push(pObSet, vaSmsa); + } + } + // 2: Verify SMGLOBALS / _SMKM_STORE_METADATA (pool hdr: 'smSa') + VmmCachePrefetchPages(pObSystemProcess, pObSet, 0); + while(ObVSet_Size(pObSet)) { + vaSmsa = ObVSet_Pop(pObSet); + vaKeyToStoreTree = ObVSet_Pop(pObSet); + vaSmGlobals = ObVSet_Pop(pObSet); + VmmReadEx(pObSystemProcess, vaSmsa - 12, (PBYTE)&dwSmsaPoolHdr, sizeof(DWORD), &cbRead, VMM_FLAG_FORCECACHE_READ); + if(dwSmsaPoolHdr == 'aSms') { + MmWin_MemCompress_InitializeOffsets64(); + ctx->MemCompress.fValid = TRUE; + ctx->MemCompress.vaSmGlobals = vaSmGlobals; + ctx->MemCompress.vaKeyToStoreTree = vaKeyToStoreTree; + MmWin_MemCompress_InitializeVirtualStorePageFileNumber(); + vmmprintfv("Windows 10 Memory Compression Initialize #1 - SmGlobals located at: %16llx Pf: %i \n", ctx->MemCompress.vaSmGlobals, ctx->MemCompress.dwPageFileNumber); + break; + } + } +finish: + LeaveCriticalSection(&ctxVmm->MasterLock); + ctx->MemCompress.fInitialized = TRUE; + Ob_DECREF(pObSystemProcess); + Ob_DECREF(pObSet); +} + +VOID MmWin_MemCompress_Initialize() +{ + DWORD vaKeyToStoreTree32; + QWORD vaKeyToStoreTree64; + PVMM_PROCESS pObSystemProcess = NULL, pObProcess = NULL; + PMMWIN_CONTEXT ctx = (PMMWIN_CONTEXT)ctxVmm->pMmContext; + if(ctxVmm->kernel.dwVersionMajor < 10) { goto fail; } + // Retrieve MemCompression process PID and vaEPROCESS + while((pObProcess = VmmProcessGetNext(pObProcess, 0))) { + if((pObProcess->dwPPID == 4) && !memcmp("MemCompression", pObProcess->szName, 15)) { + ctx->MemCompress.dwPid = pObProcess->dwPID; + ctx->MemCompress.vaEPROCESS = pObProcess->win.EPROCESS.va; + } + } + // Retrieve SmGlobals address + if(!PDB_GetSymbolAddress(VMMWIN_PDB_HANDLE_KERNEL, "SmGlobals", &ctx->MemCompress.vaSmGlobals)) { + if(ctxVmm->tpMemoryModel == VMM_MEMORYMODEL_X64) { + MmWin_MemCompress_Initialize_NoPdb64(); + } + goto fail; + } + if(!(pObSystemProcess = VmmProcessGet(4))) { goto fail; } + if(ctxVmm->f32) { + MmWin_MemCompress_InitializeOffsets32(); + if(!VmmRead(pObSystemProcess, ctx->MemCompress.vaSmGlobals + 0x0f4, (PBYTE)&vaKeyToStoreTree32, sizeof(DWORD))) { goto fail; } + if(!VMM_KADDR32_PAGE(vaKeyToStoreTree32)) { goto fail; } + ctx->MemCompress.vaKeyToStoreTree = vaKeyToStoreTree32; + } else { + MmWin_MemCompress_InitializeOffsets64(); + if(!VmmRead(pObSystemProcess, ctx->MemCompress.vaSmGlobals + 0x1c0, (PBYTE)&vaKeyToStoreTree64, sizeof(QWORD))) { goto fail; } + if(!VMM_KADDR64_PAGE(vaKeyToStoreTree64)) { goto fail; } + ctx->MemCompress.vaKeyToStoreTree = vaKeyToStoreTree64; + } + MmWin_MemCompress_InitializeVirtualStorePageFileNumber(); + ctx->MemCompress.fValid = TRUE; +fail: + Ob_DECREF(pObSystemProcess); +} + + +//----------------------------------------------------------------------------- +// COMPRESSED STORE FUNCTIONALITY BELOW: +//----------------------------------------------------------------------------- + +#define COMPRESS_ALGORITHM_INVALID 0 +#define COMPRESS_ALGORITHM_NULL 1 +#define COMPRESS_ALGORITHM_MSZIP 2 +#define COMPRESS_ALGORITHM_XPRESS 3 +#define COMPRESS_ALGORITHM_XPRESS_HUFF 4 +#define COMPRESS_ALGORITHM_LZMS 5 +#define COMPRESS_ALGORITHM_MAX 6 +#define COMPRESS_RAW (1 << 29) + +typedef struct tdMMWINX64_COMPRESS_CONTEXT { + QWORD fVmmRead; + PVMM_PROCESS pProcess; + PVMM_PROCESS pSystemProcess; + PVMM_PROCESS pProcessMemCompress; + // per page items + struct { + QWORD va; + QWORD PTE; + DWORD dwPageKey; + DWORD iSmkm; // index into 32x32 array in SmGlobals/SMKM_STORE_METADATA + QWORD vaSmkmStore; + QWORD vaEPROCESS; + QWORD vaOwnerEPROCESS; + BYTE pbSmkm[0x2000]; + DWORD dwRegionKey; + QWORD vaPageRecord; + QWORD vaRegion; + DWORD cbRegionOffset; + DWORD cbCompressedData; + BYTE pbCompressedData[0x1000]; + } e; +} MMWINX64_COMPRESS_CONTEXT, *PMMWINX64_COMPRESS_CONTEXT; + +typedef struct td_SMKM_STORE_METADATA32 { + DWORD vaSmkmStore; + DWORD Reserved1[2]; + DWORD vaEPROCESS; + DWORD Reserved2; +} _SMKM_STORE_METADATA32; + +typedef struct td_SMKM_STORE_METADATA64 { + QWORD vaSmkmStore; + QWORD Reserved1[2]; + QWORD vaEPROCESS; + QWORD Reserved2; +} _SMKM_STORE_METADATA64; + +typedef struct td_SMHP_CHUNK_METADATA32 { + DWORD avaChunkPtr[32]; + QWORD Reserved1; + DWORD dwBitValue; + DWORD dwPageRecordsPerChunkMask; + DWORD dwPageRecordSize; + DWORD Reserved2; + DWORD dwChunkPageHeaderSize; +} _SMHP_CHUNK_METADATA32, *P_SMHP_CHUNK_METADATA32; + +typedef struct td_SMHP_CHUNK_METADATA64 { + QWORD avaChunkPtr[32]; + QWORD Reserved1; + DWORD dwBitValue; + DWORD dwPageRecordsPerChunkMask; + DWORD dwPageRecordSize; + DWORD Reserved2; + DWORD dwChunkPageHeaderSize; +} _SMHP_CHUNK_METADATA64, *P_SMHP_CHUNK_METADATA64; + +typedef struct td_ST_PAGE_RECORD { + DWORD Key; + DWORD CompressedSize; + DWORD NextKey; +} _ST_PAGE_RECORD, *P_ST_PAGE_RECORD; + +BOOL MmWin_MemCompress_LogError(_In_ PMMWINX64_COMPRESS_CONTEXT ctx, _In_ LPSTR sz) +{ + vmmprintfvv( + "MmWin_CompressedPage: FAIL: %s\n" \ + " va= %016llx ep= %016llx pgk=%08x ism=%04x vas=%016llx \n" \ + " pte=%016llx oep=%016llx rgk=%08x pid=%04x \n" \ + " pgr=%016llx rgn=%016llx rgo=%08x cbc=%04x rga=%016llx\n", + sz, + ctx->e.va, ctx->e.vaEPROCESS, ctx->e.dwPageKey, ctx->e.iSmkm, ctx->e.vaSmkmStore, + ctx->e.PTE, ctx->e.vaOwnerEPROCESS, ctx->e.dwRegionKey, ctx->pProcess->dwPID, + ctx->e.vaPageRecord, ctx->e.vaRegion, ctx->e.cbRegionOffset, ctx->e.cbCompressedData, (ctx->e.vaRegion + ctx->e.cbRegionOffset) + ); + return FALSE; +} + +/* +* Retrieve the index of the 32x32 array in SmGlobals/SMKM_STORE_METADATA which +* points to the SmkmStore. The index is retrieved from the KeyToStoreTree BTree +* pointed by SmGlobals/SMKM_STORE_METADATA. +* -- ctx +* -- pwSmkmStoreIndex +* -- return +*/ +_Success_(return) +BOOL MmWin_MemCompress1_SmkmStoreIndex(_In_ PMMWINX64_COMPRESS_CONTEXT ctx) +{ + DWORD v; + if(!MmWin_BTree_Search(ctx->pSystemProcess, ((PMMWIN_CONTEXT)ctxVmm->pMmContext)->MemCompress.vaKeyToStoreTree, ctx->e.dwPageKey, &v, ctx->fVmmRead)) { + return MmWin_MemCompress_LogError(ctx, "#11 BTreeSearch"); + } + if(v & 0x01000000) { return MmWin_MemCompress_LogError(ctx, "#12 InvalidValue"); } + ctx->e.iSmkm = 0x3ff & v; + return TRUE; +} + +/* +* Retrieve the virtual address to the SmkmStore and the EPROCESS of the process +* by walking the 32x32 array in SmGlobals. +* -- ctx +* -- return +*/ +_Success_(return) +BOOL MmWin_MemCompress2_SmkmStoreMetadata32(_In_ PMMWINX64_COMPRESS_CONTEXT ctx) +{ + DWORD va; + _SMKM_STORE_METADATA32 MetaData; + // 1: 1st level fetch virtual address to 2nd level of 32x32 array + if(!VmmRead2(ctx->pSystemProcess, ((PMMWIN_CONTEXT)ctxVmm->pMmContext)->MemCompress.vaSmGlobals + (ctx->e.iSmkm >> 5) * sizeof(DWORD), (PBYTE)&va, sizeof(DWORD), ctx->fVmmRead)) { return MmWin_MemCompress_LogError(ctx, "#21 Read"); } + if(!VMM_KADDR32_8(va)) { return MmWin_MemCompress_LogError(ctx, "#22 NoKADDR"); } + // 2: 2nd fetch values (_SMKM_STORE_METADATA) from 2nd level of 32x32 array. + if(!VmmRead2(ctx->pSystemProcess, va + (ctx->e.iSmkm & 0x1f) * sizeof(_SMKM_STORE_METADATA32), (PBYTE)&MetaData, sizeof(_SMKM_STORE_METADATA32), ctx->fVmmRead)) { return MmWin_MemCompress_LogError(ctx, "#23 Read"); } + if(MetaData.vaEPROCESS && !VMM_KADDR32_8(MetaData.vaEPROCESS)) { return MmWin_MemCompress_LogError(ctx, "#24 NoKADDR"); } + if(!VMM_KADDR32_PAGE(MetaData.vaSmkmStore)) { return MmWin_MemCompress_LogError(ctx, "#25 NoKADDR"); } + ctx->e.vaSmkmStore = MetaData.vaSmkmStore; + ctx->e.vaEPROCESS = MetaData.vaEPROCESS; + return TRUE; +} + +_Success_(return) +BOOL MmWin_MemCompress2_SmkmStoreMetadata64(_In_ PMMWINX64_COMPRESS_CONTEXT ctx) +{ + QWORD va; + _SMKM_STORE_METADATA64 MetaData; + // 1: 1st level fetch virtual address to 2nd level of 32x32 array + if(!VmmRead2(ctx->pSystemProcess, ((PMMWIN_CONTEXT)ctxVmm->pMmContext)->MemCompress.vaSmGlobals + (ctx->e.iSmkm >> 5) * sizeof(QWORD), (PBYTE)&va, sizeof(QWORD), ctx->fVmmRead)) { return MmWin_MemCompress_LogError(ctx, "#21 Read"); } + if(!VMM_KADDR64_16(va)) { return MmWin_MemCompress_LogError(ctx, "#22 NoKADDR"); } + // 2: 2nd fetch values (_SMKM_STORE_METADATA) from 2nd level of 32x32 array. + if(!VmmRead2(ctx->pSystemProcess, va + (ctx->e.iSmkm & 0x1f) * sizeof(_SMKM_STORE_METADATA64), (PBYTE)&MetaData, sizeof(_SMKM_STORE_METADATA64), ctx->fVmmRead)) { return MmWin_MemCompress_LogError(ctx, "#23 Read"); } + if(MetaData.vaEPROCESS && !VMM_KADDR64_16(MetaData.vaEPROCESS)) { return MmWin_MemCompress_LogError(ctx, "#24 NoKADDR"); } + if(!VMM_KADDR64_PAGE(MetaData.vaSmkmStore)) { return MmWin_MemCompress_LogError(ctx, "#25 NoKADDR"); } + ctx->e.vaSmkmStore = MetaData.vaSmkmStore; + ctx->e.vaEPROCESS = MetaData.vaEPROCESS; + return TRUE; +} + +/* +* Retrieve the SmkmStore and the PageRecord. +* -- ctx +* -- return +*/ +_Success_(return) +BOOL MmWin_MemCompress3_SmkmStoreAndPageRecord32(_In_ PMMWINX64_COMPRESS_CONTEXT ctx) +{ + DWORD vaPageRecordArray; + DWORD i, dwEncodedMetadata, iChunkPtr = 0, iChunkArray, dwPoolHdr = 0; + P_SMHP_CHUNK_METADATA32 pc; + PMMWIN_MEMCOMPRESS_OFFSET po = &((PMMWIN_CONTEXT)ctxVmm->pMmContext)->MemCompress.O; + // 1: Load SmkmStore + if(!VmmRead2(ctx->pSystemProcess, ctx->e.vaSmkmStore, ctx->e.pbSmkm, sizeof(ctx->e.pbSmkm), ctx->fVmmRead)) { + return MmWin_MemCompress_LogError(ctx, "#31 ReadSmkmStore"); + } + // 2: Validate + if(!VMM_KADDR32_8(*(PDWORD)(ctx->e.pbSmkm + po->SMKM_STORE.PagesTree))) { + return MmWin_MemCompress_LogError(ctx, "#32 PagesTreePtrNoKADDR"); + } + if(COMPRESS_ALGORITHM_XPRESS != *(PWORD)(ctx->e.pbSmkm + po->SMKM_STORE.CompressionAlgorithm)) { + return MmWin_MemCompress_LogError(ctx, "#33 InvalidCompressionAlgorithm"); + } + // 3: Get region key + if(!MmWin_BTree_Search(ctx->pSystemProcess, *(PDWORD)(ctx->e.pbSmkm + po->SMKM_STORE.PagesTree), ctx->e.dwPageKey, &ctx->e.dwRegionKey, ctx->fVmmRead)) { + return MmWin_MemCompress_LogError(ctx, "#34 RegionKeyBTreeSearch"); + } + // 4: Get page record and calculate: + // - chunk "encoded metadata" + // - index into chunk metadata array (= highest non-zero bit position of encoded_metadata) + // - index into chunk array (pointed to by chunk metadata array) + pc = (P_SMHP_CHUNK_METADATA32)(ctx->e.pbSmkm + po->SMKM_STORE.ChunkMetaData); + dwEncodedMetadata = ctx->e.dwRegionKey >> (pc->dwBitValue & 0xff); + for(i = 0; i < 32; i++) { + if(!(dwEncodedMetadata >> i)) { break; } + iChunkPtr = i; + } + iChunkArray = (1 << iChunkPtr) ^ dwEncodedMetadata; + // 5: Validate and fetch page record address + if(iChunkArray > 0x400) { + return MmWin_MemCompress_LogError(ctx, "#35 ChunkArrayTooLarge"); + } + if(!VMM_KADDR32_8(pc->avaChunkPtr[iChunkPtr])) { + return MmWin_MemCompress_LogError(ctx, "#36 ChunkPtrNoKADDR"); + } + if(pc->avaChunkPtr[iChunkPtr] & 0xfff) { + if(!VmmRead2(ctx->pSystemProcess, pc->avaChunkPtr[iChunkPtr] - 4, (PBYTE)&dwPoolHdr, 4, ctx->fVmmRead) || (dwPoolHdr != 'ABms')) { + return MmWin_MemCompress_LogError(ctx, "#37 ChunkBadPoolHdr"); + } + } + if(!VmmRead2(ctx->pSystemProcess, pc->avaChunkPtr[iChunkPtr] + 0x0cULL * iChunkArray, (PBYTE)&vaPageRecordArray, sizeof(DWORD), ctx->fVmmRead) || !VMM_KADDR32_PAGE(vaPageRecordArray)) { + return MmWin_MemCompress_LogError(ctx, "#38 PageRecordArray"); + } + ctx->e.vaPageRecord = (DWORD)((QWORD)vaPageRecordArray + pc->dwChunkPageHeaderSize + ((QWORD)pc->dwPageRecordSize * (ctx->e.dwRegionKey & pc->dwPageRecordsPerChunkMask))); + // 6: Get owner EPROCESS + ctx->e.vaOwnerEPROCESS = *(PDWORD)(ctx->e.pbSmkm + po->SMKM_STORE.OwnerProcess); + if(ctx->e.vaOwnerEPROCESS != ((PMMWIN_CONTEXT)ctxVmm->pMmContext)->MemCompress.vaEPROCESS) { + return MmWin_MemCompress_LogError(ctx, "#39 OwnerEPROCESS"); + } + return TRUE; +} + +/* +* Retrieve the SmkmStore and the PageRecord. +* -- ctx +* -- return +*/ +_Success_(return) +BOOL MmWin_MemCompress3_SmkmStoreAndPageRecord64(_In_ PMMWINX64_COMPRESS_CONTEXT ctx) +{ + QWORD vaPageRecordArray; + DWORD i, dwEncodedMetadata, iChunkPtr = 0, iChunkArray, dwPoolHdr = 0; + P_SMHP_CHUNK_METADATA64 pc; + PMMWIN_MEMCOMPRESS_OFFSET po = &((PMMWIN_CONTEXT)ctxVmm->pMmContext)->MemCompress.O; + // 1: Load SmkmStore + if(!VmmRead2(ctx->pSystemProcess, ctx->e.vaSmkmStore, ctx->e.pbSmkm, sizeof(ctx->e.pbSmkm), ctx->fVmmRead)) { + return MmWin_MemCompress_LogError(ctx, "#31 ReadSmkmStore"); + } + // 2: Validate + if(!VMM_KADDR64_16(*(PQWORD)(ctx->e.pbSmkm + po->SMKM_STORE.PagesTree))) { + return MmWin_MemCompress_LogError(ctx, "#32 PagesTreePtrNoKADDR"); + } + if(COMPRESS_ALGORITHM_XPRESS != *(PWORD)(ctx->e.pbSmkm + po->SMKM_STORE.CompressionAlgorithm)) { + return MmWin_MemCompress_LogError(ctx, "#33 InvalidCompressionAlgorithm"); + } + // 3: Get region key + if(!MmWin_BTree_Search(ctx->pSystemProcess, *(PQWORD)(ctx->e.pbSmkm + po->SMKM_STORE.PagesTree), ctx->e.dwPageKey, &ctx->e.dwRegionKey, ctx->fVmmRead)) { + return MmWin_MemCompress_LogError(ctx, "#34 RegionKeyBTreeSearch"); + } + // 4: Get page record and calculate: + // - chunk "encoded metadata" + // - index into chunk metadata array (= highest non-zero bit position of encoded_metadata) + // - index into chunk array (pointed to by chunk metadata array) + pc = (P_SMHP_CHUNK_METADATA64)(ctx->e.pbSmkm + po->SMKM_STORE.ChunkMetaData); + dwEncodedMetadata = ctx->e.dwRegionKey >> (pc->dwBitValue & 0xff); + for(i = 0; i < 32; i++) { + if(!(dwEncodedMetadata >> i)) { break; } + iChunkPtr = i; + } + iChunkArray = (1 << iChunkPtr) ^ dwEncodedMetadata; + // 5: Validate and fetch page record address + if(iChunkArray > 0x400) { + return MmWin_MemCompress_LogError(ctx, "#35 ChunkArrayTooLarge"); + } + if(!VMM_KADDR64_16(pc->avaChunkPtr[iChunkPtr])) { + return MmWin_MemCompress_LogError(ctx, "#36 ChunkPtrNoKADDR"); + } + if(pc->avaChunkPtr[iChunkPtr] & 0xfff) { + if(!VmmRead2(ctx->pSystemProcess, pc->avaChunkPtr[iChunkPtr] - 12, (PBYTE)&dwPoolHdr, 4, ctx->fVmmRead) || (dwPoolHdr != 'ABms')) { + return MmWin_MemCompress_LogError(ctx, "#37 ChunkBadPoolHdr"); + } + } + if(!VmmRead2(ctx->pSystemProcess, pc->avaChunkPtr[iChunkPtr] + 0x10ULL * iChunkArray, (PBYTE)&vaPageRecordArray, sizeof(QWORD), ctx->fVmmRead) || !VMM_KADDR64_PAGE(vaPageRecordArray)) { + return MmWin_MemCompress_LogError(ctx, "#38 PageRecordArray"); + } + ctx->e.vaPageRecord = (QWORD)(vaPageRecordArray + pc->dwChunkPageHeaderSize + ((QWORD)pc->dwPageRecordSize * (ctx->e.dwRegionKey & pc->dwPageRecordsPerChunkMask))); + // 6: Get owner EPROCESS + ctx->e.vaOwnerEPROCESS = *(PQWORD)(ctx->e.pbSmkm + po->SMKM_STORE.OwnerProcess); + if(ctx->e.vaOwnerEPROCESS != ((PMMWIN_CONTEXT)ctxVmm->pMmContext)->MemCompress.vaEPROCESS) { + return MmWin_MemCompress_LogError(ctx, "#39 OwnerEPROCESS"); + } + return TRUE; +} + +/* +* Retrieve the region address / data containing the compressed process. +* -- ctx +* -- return +*/ +_Success_(return) +BOOL MmWin_MemCompress4_CompressedRegionData(_In_ PMMWINX64_COMPRESS_CONTEXT ctx) +{ + QWORD vaRegionPtr = 0; + DWORD dwRegionIndexMask, dwRegionIndex; + _ST_PAGE_RECORD PageRecord; + PMMWIN_MEMCOMPRESS_OFFSET po = &((PMMWIN_CONTEXT)ctxVmm->pMmContext)->MemCompress.O; + // 1: Read page record + if(!VmmRead2(ctx->pSystemProcess, ctx->e.vaPageRecord, (PBYTE)&PageRecord, sizeof(PageRecord), ctx->fVmmRead)) { + return MmWin_MemCompress_LogError(ctx, "#41 ReadPageRecord"); + } + if(PageRecord.Key == 0xffffffff) { + // TODO: implement support + return MmWin_MemCompress_LogError(ctx, "#42 InvalidPageRecord"); + } + ctx->e.cbCompressedData = (PageRecord.CompressedSize == 0x1000) ? 0x1000 : PageRecord.CompressedSize & 0xfff; + if(ctxVmm->f32) { + // 2: Get pointer to region (32-bit) + dwRegionIndexMask = *(PDWORD)(ctx->e.pbSmkm + po->SMKM_STORE.RegionIndexMask) & 0xff; + dwRegionIndex = PageRecord.Key >> dwRegionIndexMask; + vaRegionPtr = *(PDWORD)(ctx->e.pbSmkm + po->SMKM_STORE.CompressedRegionPtrArray) + dwRegionIndex * sizeof(DWORD); + // 3: Get region and offset (32-bit) + if(!VmmRead2(ctx->pSystemProcess, vaRegionPtr, (PBYTE)&ctx->e.vaRegion, sizeof(DWORD), ctx->fVmmRead)) { + return MmWin_MemCompress_LogError(ctx, "#43 ReadRegionVA"); + } + if(!ctx->e.vaRegion || (ctx->e.vaRegion & 0x8000ffff)) { + return MmWin_MemCompress_LogError(ctx, "#44 InvalidRegionVA"); + } + } else { + // 2: Get pointer to region (64-bit) + dwRegionIndexMask = *(PDWORD)(ctx->e.pbSmkm + po->SMKM_STORE.RegionIndexMask) & 0xff; + dwRegionIndex = PageRecord.Key >> dwRegionIndexMask; + vaRegionPtr = *(PQWORD)(ctx->e.pbSmkm + po->SMKM_STORE.CompressedRegionPtrArray) + dwRegionIndex * sizeof(QWORD); + // 3: Get region and offset (64-bit) + if(!VmmRead2(ctx->pSystemProcess, vaRegionPtr, (PBYTE)&ctx->e.vaRegion, sizeof(QWORD), ctx->fVmmRead)) { + return MmWin_MemCompress_LogError(ctx, "#45 ReadRegionVA"); + } + if(!ctx->e.vaRegion || (ctx->e.vaRegion & 0xffff8000'0000ffff)) { + return MmWin_MemCompress_LogError(ctx, "#46 InvalidRegionVA"); + } + } + ctx->e.cbRegionOffset = (PageRecord.Key & *(PDWORD)(ctx->e.pbSmkm + po->SMKM_STORE.RegionSizeMask)) << 4; + return TRUE; +} + +/* +* Decompress a compressed page +* -- ctx +* -- pbDecompressedPage +* -- return +*/ +_Success_(return) +BOOL MmWin_MemCompress5_DecompressPage(_In_ PMMWINX64_COMPRESS_CONTEXT ctx, _Out_writes_(4096) PBYTE pbDecompressedPage) +{ + DWORD cbDecompressed; + // 1: Read compressed data + if(!VmmRead2(ctx->pProcessMemCompress, ctx->e.vaRegion + ctx->e.cbRegionOffset, ctx->e.pbCompressedData, ctx->e.cbCompressedData, ctx->fVmmRead)) { + MmWin_MemCompress_LogError(ctx, "#51 Read"); + return FALSE; + } + // 2: Decompress data + if(ctx->e.cbCompressedData == 0x1000) { + memcpy(pbDecompressedPage, ctx->e.pbCompressedData, 0x1000); + } else { + if((VMM_STATUS_SUCCESS != ctxVmm->fn.RtlDecompressBuffer(COMPRESS_ALGORITHM_XPRESS, pbDecompressedPage, 0x1000, ctx->e.pbCompressedData, ctx->e.cbCompressedData, &cbDecompressed)) || (cbDecompressed != 0x1000)) { + return MmWin_MemCompress_LogError(ctx, "#52 Decompress"); + } + } + return TRUE; +} + +/* +* Decompress a page. +* -- pProcess +* -- pMEM +* -- fVmmRead = flags to VmmRead function calls. +* -- return +*/ +_Success_(return) +BOOL MmWin_MemCompress(_In_ PVMM_PROCESS pProcess, _In_ QWORD va, _In_ QWORD pte, _Out_writes_(4096) PBYTE pbPage, _In_ QWORD fVmmRead) +{ + BOOL fResult = FALSE; + PMMWINX64_COMPRESS_CONTEXT ctx = NULL; + PVMM_PROCESS pObSystemProcess = NULL, pObMemCompressProcess = NULL; + QWORD tm = Statistics_CallStart(); + if(!(ctx = LocalAlloc(LMEM_ZEROINIT, sizeof(MMWINX64_COMPRESS_CONTEXT)))) { goto fail; } + ctx->fVmmRead = fVmmRead; + ctx->e.va = va; + ctx->e.PTE = pte; + if(ctxVmm->f32) { + // 32-bit system + ctx->e.dwPageKey = MMWINX86PAE_PTE_PAGE_KEY_COMPRESSED(pte); + fResult = + (ctx->pProcess = pProcess) && + (ctx->pSystemProcess = pObSystemProcess = VmmProcessGet(4)) && + (ctx->pProcessMemCompress = pObMemCompressProcess = VmmProcessGet(((PMMWIN_CONTEXT)ctxVmm->pMmContext)->MemCompress.dwPid)) && + MmWin_MemCompress1_SmkmStoreIndex(ctx) && + MmWin_MemCompress2_SmkmStoreMetadata32(ctx) && + MmWin_MemCompress3_SmkmStoreAndPageRecord32(ctx) && + MmWin_MemCompress4_CompressedRegionData(ctx) && + MmWin_MemCompress5_DecompressPage(ctx, pbPage); + } else { + // 64-bit system + ctx->e.dwPageKey = MMWINX64_PTE_PAGE_KEY_COMPRESSED(pte); + fResult = + (ctx->pProcess = pProcess) && + (ctx->pSystemProcess = pObSystemProcess = VmmProcessGet(4)) && + (ctx->pProcessMemCompress = pObMemCompressProcess = VmmProcessGet(((PMMWIN_CONTEXT)ctxVmm->pMmContext)->MemCompress.dwPid)) && + MmWin_MemCompress1_SmkmStoreIndex(ctx) && + MmWin_MemCompress2_SmkmStoreMetadata64(ctx) && + MmWin_MemCompress3_SmkmStoreAndPageRecord64(ctx) && + MmWin_MemCompress4_CompressedRegionData(ctx) && + MmWin_MemCompress5_DecompressPage(ctx, pbPage); + } +fail: + LocalFree(ctx); + Ob_DECREF(pObSystemProcess); + Ob_DECREF(pObMemCompressProcess); + Statistics_CallEnd(STATISTICS_ID_VMM_PagedCompressedMemory, tm); + return fResult; +} + + +//----------------------------------------------------------------------------- +// PAGE FILE FUNCTIONALITY BELOW: +//----------------------------------------------------------------------------- + +_Success_(return) +BOOL MmWin_PfReadFile(_In_ DWORD dwPfNumber, _In_ DWORD dwPfOffset, _Out_writes_(4096) PBYTE pbPage) +{ + PMMWIN_CONTEXT ctx = (PMMWIN_CONTEXT)ctxVmm->pMmContext; + DWORD cb = 0; + if(!ctx || !ctx->pPageFile[dwPfNumber]) { return FALSE; } + EnterCriticalSection(&ctx->Lock); + if(!_fseeki64(ctx->pPageFile[dwPfNumber], (QWORD)dwPfOffset << 12, SEEK_SET)) { + cb = (DWORD)fread(pbPage, 1, 0x1000, ctx->pPageFile[dwPfNumber]); + } + LeaveCriticalSection(&ctx->Lock); + return cb == 0x1000; +} + +_Success_(return) +BOOL MmWin_PfRead(_In_ PVMM_PROCESS pProcess, _In_ QWORD va, _In_ QWORD pte, _In_ QWORD fVmmRead, _In_ DWORD dwPfNumber, _In_ DWORD dwPfOffset, _Out_writes_(4096) PBYTE pbPage) +{ + BOOL fResult; + PVMMOB_MEM pObCacheEntry; + // cached page? + if((pObCacheEntry = VmmCacheGet(VMM_CACHE_TAG_PAGING, pte))) { + memcpy(pbPage, pObCacheEntry->pb, 0x1000); + Ob_DECREF(pObCacheEntry); + InterlockedIncrement64(&ctxVmm->stat.page.cCacheHit); + return TRUE; + } + // cached failed page? + if(ObVSet_Exists(ctxVmm->Cache.PAGING_FAILED, pte)) { + InterlockedIncrement64(&ctxVmm->stat.page.cFailCacheHit); + return FALSE; + } + // check flags: NoPagingIo, ForceCache and santity checks. + if(fVmmRead & (VMM_FLAG_NOPAGING_IO | VMM_FLAG_FORCECACHE_READ)) { return FALSE; } + if(!ctxVmm->pMmContext || (dwPfNumber >= 10)) { return FALSE; } + // dispatch to page file or compressed virtual store + if(((PMMWIN_CONTEXT)ctxVmm->pMmContext)->MemCompress.fValid && (dwPfNumber == ((PMMWIN_CONTEXT)ctxVmm->pMmContext)->MemCompress.dwPageFileNumber)) { + fResult = MmWin_MemCompress(pProcess, va, pte, pbPage, fVmmRead); + if(fResult) { + InterlockedIncrement64(&ctxVmm->stat.page.cCompressed); + } else { + InterlockedIncrement64(&ctxVmm->stat.page.cFailCompressed); + } + } else { + fResult = MmWin_PfReadFile(dwPfNumber, dwPfOffset, pbPage); + if(fResult) { + InterlockedIncrement64(&ctxVmm->stat.page.cPageFile); + } else { + InterlockedIncrement64(&ctxVmm->stat.page.cFailPageFile); + } + } + // update cache + if(fResult) { + if((pObCacheEntry = VmmCacheReserve(VMM_CACHE_TAG_PAGING))) { + pObCacheEntry->h.qwA = pte; + pObCacheEntry->h.cb = 0x1000; + memcpy(pObCacheEntry->pb, pbPage, 0x1000); + VmmCacheReserveReturn(pObCacheEntry); + } + return TRUE; + } + ObVSet_Push(ctxVmm->Cache.PAGING_FAILED, pte); + return FALSE; +} + + +//----------------------------------------------------------------------------- +// X86 VIRTUAL MEMORY BELOW: +//----------------------------------------------------------------------------- + +/* +* Fetch PTE from a prototype PTE. The returned PTE may be zero = fail, hardware or software PTE. +* -- pte +* -- fVmmRead = flags to VmmRead function calls. +* -- return +*/ +DWORD MmWinX86_Prototype(_In_ DWORD pte, _In_ QWORD fVmmRead) +{ + PVMM_PROCESS pObSystemProcess; + DWORD cbRead, dwPtePage = 0; + if(!(pObSystemProcess = VmmProcessGet(4))) { goto fail; } + VmmReadEx(pObSystemProcess, MMWINX86_PTE_PROTOTYPE(pte), (PBYTE)&dwPtePage, 4, &cbRead, fVmmRead); + if(cbRead != 4) { goto fail; } + if((MMWINX86_PTE_IS_HARDWARE(dwPtePage) && (dwPtePage >= ctxMain->dev.paMax)) || MMWINX86_PTE_PROTOTYPE(dwPtePage)) { + dwPtePage = 0; + } +fail: + Ob_DECREF(pObSystemProcess); + return dwPtePage; +} + +/* +* Read a 'paged' page from virtual memory. +* -- pProcess +* -- va +* -- pte +* -- pbPage +* -- ppa +* -- return +*/ +_Success_(return) +BOOL MmWinX86_ReadPaged(_In_ PVMM_PROCESS pProcess, _In_ DWORD va, _In_ DWORD pte, _Out_writes_(4096) PBYTE pbPage, _Out_ PQWORD ppa, _In_ QWORD flags) +{ + BOOL f; + DWORD dwPfNumber, dwPfOffset; + *ppa = 0; + if(MMWINX86_PTE_IS_HARDWARE(pte) || MM_LOOP_PROTECT_MAX(flags)) { goto fail; } + flags = MM_LOOP_PROTECT_ADD(flags); + // prototype page [ nt!_MMPTE_PROTOTYPE ] + if(!(flags & VMM_FLAG_NOPAGING_IO) && MMWINX86_PTE_PROTOTYPE(pte)) { + InterlockedIncrement64(&ctxVmm->stat.page.cPrototype); + pte = MmWinX86_Prototype(pte, flags); + if(MMWINX86_PTE_IS_HARDWARE(pte)) { + *ppa = pte & 0xfffff000; + return FALSE; + } + // prototype pte points to software pte -> use it as new pte and continue + } + // transition page [ nt!_MMPTE_TRANSITION ] + if(MMWINX86_PTE_TRANSITION(pte)) { + pte = MMWINX86_PTE_TRANSITION(pte); + if((pte & 0xfffff000) < ctxMain->dev.paMax) { + *ppa = pte & 0xfffff000; + InterlockedIncrement64(&ctxVmm->stat.page.cTransition); + } + return FALSE; + } + dwPfNumber = MMWINX86_PTE_PAGE_FILE_NUMBER(pte); + dwPfOffset = MMWINX86_PTE_PAGE_FILE_OFFSET(pte); + // Potentially VAD-backed virtual memory + if(!VMM_KADDR32(va) && !(flags & VMM_FLAG_NOVAD) && (!pte || (dwPfOffset == 0x000fffff))) { + pte = (DWORD)MmVad_PrototypePte(pProcess, va, &f, flags); + if(!pte) { + if(f) { InterlockedIncrement64(&ctxVmm->stat.page.cFailVAD); } + return FALSE; + } + InterlockedIncrement64(&ctxVmm->stat.page.cVAD); + if(MMWINX86_PTE_IS_HARDWARE(pte)) { + *ppa = pte & 0xfffff000; + return FALSE; + } + return MmWinX86_ReadPaged(pProcess, va, pte, pbPage, ppa, flags | VMM_FLAG_NOVAD); + } + if(!pte) { return FALSE; } + // demand zero virtual memory [ nt!_MMPTE_SOFTWARE ] + if(!dwPfNumber && !dwPfOffset) { + ZeroMemory(pbPage, 0x1000); + InterlockedIncrement64(&ctxVmm->stat.page.cDemandZero); + return TRUE; + } + // retrive from page file or compressed store + return MmWin_PfRead(pProcess, va, pte, flags, dwPfNumber, dwPfOffset, pbPage); +fail: + InterlockedIncrement64(&ctxVmm->stat.page.cFail); + return FALSE; +} + + +//----------------------------------------------------------------------------- +// X86PAE VIRTUAL MEMORY BELOW: +//----------------------------------------------------------------------------- + +/* +* Fetch PTE from a prototype PTE. The returned PTE may be zero = fail, hardware or software PTE. +* -- pte +* -- fVmmRead = flags to VmmRead function calls. +* -- return +*/ +QWORD MmWinX86PAE_Prototype(_In_ QWORD pte, _In_ QWORD fVmmRead) +{ + DWORD cbRead; + QWORD qwPtePage = 0; + PVMM_PROCESS pObSystemProcess; + if(!(pObSystemProcess = VmmProcessGet(4))) { goto fail; } + VmmReadEx(pObSystemProcess, MMWINX86PAE_PTE_PROTOTYPE(pte), (PBYTE)&qwPtePage, 8, &cbRead, fVmmRead); + if(cbRead != 8) { goto fail; } + if((MMWINX86PAE_PTE_IS_HARDWARE(qwPtePage) && ((qwPtePage & 0x0000003f'fffff000) >= ctxMain->dev.paMax)) || MMWINX86PAE_PTE_PROTOTYPE(qwPtePage)) { + qwPtePage = 0; + } +fail: + Ob_DECREF(pObSystemProcess); + return qwPtePage; +} + +/* +* Read a 'paged' page from virtual memory. +* -- pProcess +* -- va +* -- pte +* -- pbPage +* -- ppa +* -- return +*/ +_Success_(return) +BOOL MmWinX86PAE_ReadPaged(_In_ PVMM_PROCESS pProcess, _In_ DWORD va, _In_ QWORD pte, _Out_writes_(4096) PBYTE pbPage, _Out_ PQWORD ppa, _In_ QWORD flags) +{ + BOOL f; + DWORD dwPfNumber, dwPfOffset; + *ppa = 0; + if(MMWINX86PAE_PTE_IS_HARDWARE(pte) || MM_LOOP_PROTECT_MAX(flags)) { goto fail; } + flags = MM_LOOP_PROTECT_ADD(flags); + // prototype page [ nt!_MMPTE_PROTOTYPE ] + if(!(flags & VMM_FLAG_NOPAGING_IO) && MMWINX86PAE_PTE_PROTOTYPE(pte)) { + InterlockedIncrement64(&ctxVmm->stat.page.cPrototype); + pte = MmWinX86PAE_Prototype(pte, flags); + if(MMWINX86PAE_PTE_IS_HARDWARE(pte)) { + *ppa = pte & 0x0000003f'fffff000; + return FALSE; + } + // prototype pte points to software pte -> use it as new pte and continue + } + // transition page [ nt!_MMPTE_TRANSITION ] + if(MMWINX86PAE_PTE_TRANSITION(pte)) { + pte = MMWINX86PAE_PTE_TRANSITION(pte); + if((pte & 0x0000003f'fffff000) < ctxMain->dev.paMax) { + *ppa = pte & 0x0000003f'fffff000; + InterlockedIncrement64(&ctxVmm->stat.page.cTransition); + } + return FALSE; + } + dwPfNumber = MMWINX86PAE_PTE_PAGE_FILE_NUMBER(pte); + dwPfOffset = MMWINX86PAE_PTE_PAGE_FILE_OFFSET(pte); + // Potentially VAD-backed virtual memory + if(!VMM_KADDR32(va) && !(flags & VMM_FLAG_NOVAD) && (!pte || (dwPfOffset == 0xffffffff))) { + pte = MmVad_PrototypePte(pProcess, va, &f, flags); + if(!pte) { + if(f) { InterlockedIncrement64(&ctxVmm->stat.page.cFailVAD); } + return FALSE; + } + InterlockedIncrement64(&ctxVmm->stat.page.cVAD); + if(MMWINX86PAE_PTE_IS_HARDWARE(pte)) { + *ppa = pte & 0x0000003f'fffff000; + return FALSE; + } + return MmWinX86PAE_ReadPaged(pProcess, va, pte, pbPage, ppa, flags | VMM_FLAG_NOVAD); + } + if(!pte) { return FALSE; } + // demand zero virtual memory [ nt!_MMPTE_SOFTWARE ] + if(!dwPfNumber && !dwPfOffset) { + ZeroMemory(pbPage, 0x1000); + InterlockedIncrement64(&ctxVmm->stat.page.cDemandZero); + return TRUE; + } + // retrive from page file or compressed store + return MmWin_PfRead(pProcess, va, pte, flags, dwPfNumber, dwPfOffset, pbPage); +fail: + InterlockedIncrement64(&ctxVmm->stat.page.cFail); + return FALSE; +} + + +//----------------------------------------------------------------------------- +// X64 VIRTUAL MEMORY BELOW: +//----------------------------------------------------------------------------- + +/* +* Fetch PTE from a prototype PTE. The returned PTE may be zero = fail, hardware or software PTE. +* -- pte +* -- fVmmRead = flags to VmmRead function calls. +* -- return +*/ +QWORD MmWinX64_Prototype(_In_ QWORD pte, _In_ QWORD fVmmRead) +{ + DWORD cbRead; + QWORD qwPtePage = 0; + PVMM_PROCESS pObSystemProcess; + if(!(pObSystemProcess = VmmProcessGet(4))) { goto fail; } + VmmReadEx(pObSystemProcess, MMWINX64_PTE_PROTOTYPE(pte), (PBYTE)&qwPtePage, 8, &cbRead, fVmmRead); + if(cbRead != 8) { goto fail; } + if((MMWINX64_PTE_IS_HARDWARE(qwPtePage) && ((qwPtePage & 0x0000ffff'fffff000) >= ctxMain->dev.paMax)) || MMWINX64_PTE_PROTOTYPE(qwPtePage)) { + qwPtePage = 0; + } +fail: + Ob_DECREF(pObSystemProcess); + return qwPtePage; +} + +/* +* Read a 'paged' page from virtual memory. +* -- pProcess +* -- va +* -- pte +* -- pbPage +* -- ppa +* -- flags +* -- return +*/ +_Success_(return) +BOOL MmWinX64_ReadPaged(_In_ PVMM_PROCESS pProcess, _In_ QWORD va, _In_ QWORD pte, _Out_writes_(4096) PBYTE pbPage, _Out_ PQWORD ppa, _In_ QWORD flags) +{ + BOOL f; + DWORD dwPfNumber, dwPfOffset; + *ppa = 0; + if(MMWINX64_PTE_IS_HARDWARE(pte) || MM_LOOP_PROTECT_MAX(flags)) { goto fail; } + flags = MM_LOOP_PROTECT_ADD(flags); + // prototype page + if(!(flags & VMM_FLAG_NOPAGING_IO) && MMWINX64_PTE_PROTOTYPE(pte)) { + InterlockedIncrement64(&ctxVmm->stat.page.cPrototype); + pte = MmWinX64_Prototype(pte, flags); + if(MMWINX64_PTE_IS_HARDWARE(pte)) { + *ppa = pte & 0x0000ffff'fffff000; + return FALSE; + } + // prototype pte points to software pte -> use it as new pte and continue + } + // transition page + if(MMWINX64_PTE_TRANSITION(pte)) { + pte = MMWINX64_PTE_TRANSITION(pte); + if((pte & 0x0000ffff'fffff000) < ctxMain->dev.paMax) { + *ppa = pte & 0x0000ffff'fffff000; + InterlockedIncrement64(&ctxVmm->stat.page.cTransition); + } + return FALSE; + } + dwPfNumber = MMWINX64_PTE_PAGE_FILE_NUMBER(pte); + dwPfOffset = MMWINX64_PTE_PAGE_FILE_OFFSET(pte); + // Potentially VAD-backed virtual memory + if(!VMM_KADDR64(va) && !(flags & VMM_FLAG_NOVAD) && (!pte || (dwPfOffset == 0xffffffff))) { + pte = MmVad_PrototypePte(pProcess, va, &f, flags); + if(!pte) { + if(f) { InterlockedIncrement64(&ctxVmm->stat.page.cFailVAD); } + return FALSE; + } + InterlockedIncrement64(&ctxVmm->stat.page.cVAD); + if(MMWINX64_PTE_IS_HARDWARE(pte)) { + *ppa = pte & 0x0000ffff'fffff000; + return FALSE; + } + return MmWinX64_ReadPaged(pProcess, va, pte, pbPage, ppa, flags | VMM_FLAG_NOVAD); + } + if(!pte) { return FALSE; } + // demand zero virtual memory [ nt!_MMPTE_SOFTWARE ] + if(!dwPfNumber && !dwPfOffset) { + ZeroMemory(pbPage, 0x1000); + InterlockedIncrement64(&ctxVmm->stat.page.cDemandZero); + return TRUE; + } + // retrive from page file or compressed store + return MmWin_PfRead(pProcess, va, pte, flags, dwPfNumber, dwPfOffset, pbPage); +fail: + InterlockedIncrement64(&ctxVmm->stat.page.cFail); + return FALSE; +} + + +//----------------------------------------------------------------------------- +// INITIALIZATION FUNCTIONALITY BELOW: +//----------------------------------------------------------------------------- + +VOID MmWin_PagingClose() +{ + PMMWIN_CONTEXT ctx = (PMMWIN_CONTEXT)ctxVmm->pMmContext; + DWORD i; + if(ctx) { + ctxVmm->pMmContext = NULL; + for(i = 0; i < 10; i++) { + if(ctx->pPageFile[i]) { + fclose(ctx->pPageFile[i]); + } + } + LocalFree(ctx); + } +} + +VOID MmWin_PagingInitialize(_In_ BOOL fModeFull) +{ + PMMWIN_CONTEXT ctx = (PMMWIN_CONTEXT)ctxVmm->pMmContext; + DWORD i; + // 1: Initialize Paging + switch(ctxVmm->tpMemoryModel) { + case VMM_MEMORYMODEL_X64: + ctxVmm->fnMemoryModel.pfnPagedRead = MmWinX64_ReadPaged; + break; + case VMM_MEMORYMODEL_X86PAE: + ctxVmm->fnMemoryModel.pfnPagedRead = (BOOL(*)(PVMM_PROCESS, QWORD, QWORD, PBYTE, PQWORD, QWORD))MmWinX86PAE_ReadPaged; + break; + case VMM_MEMORYMODEL_X86: + ctxVmm->fnMemoryModel.pfnPagedRead = (BOOL(*)(PVMM_PROCESS, QWORD, QWORD, PBYTE, PQWORD, QWORD))MmWinX86_ReadPaged; + break; + default: + return; + } + // 2: Initialize Page Files (if any) + if(!ctx) { + ctx = LocalAlloc(LMEM_ZEROINIT, sizeof(MMWIN_CONTEXT)); + if(!ctx) { return; } + InitializeCriticalSection(&ctx->Lock); + for(i = 0; i < 10; i++) { + if(ctxMain->cfg.szPageFile[i][0]) { + if(fopen_s(&ctx->pPageFile[i], ctxMain->cfg.szPageFile[i], "rb")) { + vmmprintfv("WARNING: CANNOT OPEN PAGE FILE #%i '%s'\n", i, ctxMain->cfg.szPageFile[i]); + } else { + vmmprintfvv("Successfully opened page file #%i '%s'\n", i, ctxMain->cfg.szPageFile[i]); + } + } + } + ctxVmm->pMmContext = ctx; + } + if(!fModeFull) { return; } + // 3: Initialize Memory DeCompression + MmWin_MemCompress_Initialize(); +} diff --git a/vmm/mm_x64.c b/vmm/mm_x64.c index 69cc8b2c..b4949f15 100644 --- a/vmm/mm_x64.c +++ b/vmm/mm_x64.c @@ -96,12 +96,12 @@ const QWORD MMX64_PAGETABLEMAP_PML_REGION_SIZE[5] = { 0, 12, 21, 30, 39 }; const QWORD MMX64_PAGETABLEMAP_PML_REGION_MASK_PG[5] = { 0, 0x0000fffffffff000, 0x0000ffffffe00000, 0x0000ffffc0000000, 0 }; const QWORD MMX64_PAGETABLEMAP_PML_REGION_MASK_AD[5] = { 0, 0xfff, 0x1fffff, 0x3fffffff, 0 }; -VOID MmX64_MapInitialize_Index(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MEMMAP_ENTRY pMemMap, _In_ PDWORD pcMemMap, _In_ QWORD vaBase, _In_ BYTE iPML, _In_ QWORD PTEs[512], _In_ BOOL fSupervisorPML, _In_ QWORD paMax) +VOID MmX64_MapInitialize_Index(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MAP_PTEENTRY pMemMap, _In_ PDWORD pcMemMap, _In_ QWORD vaBase, _In_ BYTE iPML, _In_ QWORD PTEs[512], _In_ BOOL fSupervisorPML, _In_ QWORD paMax) { PVMMOB_MEM pObNextPT; QWORD i, pte, va; BOOL fUserOnly, fNextSupervisorPML, fTransition = FALSE; - PVMM_MEMMAP_ENTRY pMemMapEntry = pMemMap + *pcMemMap - 1; + PVMM_MAP_PTEENTRY pMemMapEntry = pMemMap + *pcMemMap - 1; if(!pProcess->fTlbSpiderDone) { VmmTlbSpider(pProcess); } @@ -125,10 +125,10 @@ VOID MmX64_MapInitialize_Index(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MEMMAP_ENTR if(iPML == 4) { continue; } // not supported - PML4 cannot map page directly if((*pcMemMap == 0) || ((pMemMapEntry->fPage != (pte & VMM_MEMMAP_PAGE_MASK)) && !fTransition) || - (va != pMemMapEntry->AddrBase + (pMemMapEntry->cPages << 12))) { + (va != pMemMapEntry->vaBase + (pMemMapEntry->cPages << 12))) { if(*pcMemMap + 1 >= VMM_MEMMAP_ENTRIES_MAX) { return; } pMemMapEntry = pMemMap + *pcMemMap; - pMemMapEntry->AddrBase = va; + pMemMapEntry->vaBase = va; pMemMapEntry->fPage = pte & VMM_MEMMAP_PAGE_MASK; pMemMapEntry->cPages = 1ULL << (MMX64_PAGETABLEMAP_PML_REGION_SIZE[iPML] - 12); *pcMemMap = *pcMemMap + 1; @@ -141,7 +141,7 @@ VOID MmX64_MapInitialize_Index(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MEMMAP_ENTR continue; } // optimization - same PT in multiple consecutive PDe - if((iPML == 2) && i && (pte == PTEs[i - 1]) && (pMemMapEntry->cPages >= 512) && (va == pMemMapEntry->AddrBase + (pMemMapEntry->cPages << 12))) { + if((iPML == 2) && i && (pte == PTEs[i - 1]) && (pMemMapEntry->cPages >= 512) && (va == pMemMapEntry->vaBase + (pMemMapEntry->cPages << 12))) { pMemMapEntry->cPages += 1ULL << (MMX64_PAGETABLEMAP_PML_REGION_SIZE[iPML] - 12); continue; } @@ -155,214 +155,54 @@ VOID MmX64_MapInitialize_Index(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MEMMAP_ENTR } } -VOID MmX64_MapCloseObCallback(_In_ PVOID pVmmOb) -{ - PVMMOB_MEMMAP pObMemMap = (PVMMOB_MEMMAP)pVmmOb; - if(pObMemMap->pObDisplay) { - Ob_DECREF(pObMemMap->pObDisplay); - } -} - _Success_(return) -BOOL MmX64_MapInitialize(_In_ PVMM_PROCESS pProcess) +BOOL MmX64_PteMapInitialize(_In_ PVMM_PROCESS pProcess) { QWORD i; DWORD cMemMap = 0; PVMMOB_MEM pObPML4; - PVMM_MEMMAP_ENTRY pMemMap = NULL; - PVMMOB_MEMMAP pObMemMap = NULL; + PVMM_MAP_PTEENTRY pMemMap = NULL; + PVMMOB_MAP_PTE pObMap = NULL; // already existing? - if(pProcess && pProcess->pObMemMap) { - return pProcess->pObMemMap->fValid; - } + if(pProcess->Map.pObPte) { return TRUE; } EnterCriticalSection(&pProcess->LockUpdate); - if(pProcess && pProcess->pObMemMap) { + if(pProcess->Map.pObPte) { LeaveCriticalSection(&pProcess->LockUpdate); - return pProcess->pObMemMap->fValid; + return TRUE; } // allocate temporary buffer and walk page tables pObPML4 = VmmTlbGetPageTable(pProcess->paDTB, FALSE); if(pObPML4) { - pMemMap = (PVMM_MEMMAP_ENTRY)LocalAlloc(LMEM_ZEROINIT, VMM_MEMMAP_ENTRIES_MAX * sizeof(VMM_MEMMAP_ENTRY)); + pMemMap = (PVMM_MAP_PTEENTRY)LocalAlloc(LMEM_ZEROINIT, VMM_MEMMAP_ENTRIES_MAX * sizeof(VMM_MAP_PTEENTRY)); if(pMemMap) { MmX64_MapInitialize_Index(pProcess, pMemMap, &cMemMap, 0, 4, pObPML4->pqw, FALSE, ctxMain->dev.paMax); for(i = 0; i < cMemMap; i++) { // fixup sign extension for kernel addresses - if(pMemMap[i].AddrBase & 0x0000800000000000) { - pMemMap[i].AddrBase |= 0xffff000000000000; + if(pMemMap[i].vaBase & 0x0000800000000000) { + pMemMap[i].vaBase |= 0xffff000000000000; } } } Ob_DECREF(pObPML4); } // allocate VmmOb depending on result - pObMemMap = Ob_Alloc('MM', 0, sizeof(VMMOB_MEMMAP) + cMemMap * sizeof(VMM_MEMMAP_ENTRY), MmX64_MapCloseObCallback, NULL); - if(!pObMemMap) { + pObMap = Ob_Alloc(OB_TAG_MAP_PTE, 0, sizeof(VMMOB_MAP_PTE) + cMemMap * sizeof(VMM_MAP_PTEENTRY), NULL, NULL); + if(!pObMap) { + pProcess->Map.pObPte = Ob_Alloc(OB_TAG_MAP_PTE, LMEM_ZEROINIT, sizeof(VMMOB_MAP_PTE), NULL, NULL); LeaveCriticalSection(&pProcess->LockUpdate); LocalFree(pMemMap); - return FALSE; - } - pObMemMap->fValid = cMemMap > 0; - pObMemMap->fTagModules = FALSE; - pObMemMap->fTagScan = FALSE; - pObMemMap->cMap = cMemMap; - pObMemMap->cbDisplay = cMemMap * MMX64_MEMMAP_DISPLAYBUFFER_LINE_LENGTH; - pObMemMap->pObDisplay = NULL; - if(cMemMap > 0) { - memcpy(pObMemMap->pMap, pMemMap, cMemMap * sizeof(VMM_MEMMAP_ENTRY)); + return TRUE; } + pObMap->wszMultiText = NULL; + pObMap->cbMultiText = 0; + pObMap->fTagScan = FALSE; + pObMap->cMap = cMemMap; + memcpy(pObMap->pMap, pMemMap, cMemMap * sizeof(VMM_MAP_PTEENTRY)); LocalFree(pMemMap); - pProcess->pObMemMap = pObMemMap; + pProcess->Map.pObPte = pObMap; LeaveCriticalSection(&pProcess->LockUpdate); - return pObMemMap->fValid; -} - -/* -* Map a tag into the sorted memory map in O(log2) operations. Supply only one of szTag or wszTag. -* -- pProcess -* -- vaBase -* -- vaLimit = limit == vaBase + size (== top address in range +1) -* -- szTag -* -- wszTag -* -- fWoW64 -* -- fOverwrite -*/ -VOID MmX64_MapTag(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaBase, _In_ QWORD vaLimit, _In_opt_ LPSTR szTag, _In_opt_ LPWSTR wszTag, _In_ BOOL fWoW64, _In_ BOOL fOverwrite) -{ - // NB! update here may take placey without acquiring the process 'LockUpdate' - // Data is not super important so it should be ok. Also, in many cases the - // lock will already be acquired by MapGetEntries function. - PVMM_MEMMAP_ENTRY pMap; - QWORD i, lvl, cMap; - BOOL fCompleteOccupancy; - if(!MmX64_MapInitialize(pProcess)) { return; } - pMap = pProcess->pObMemMap->pMap; - cMap = pProcess->pObMemMap->cMap; - if(!pMap || !cMap) { return; } - // 1: locate base - lvl = 1; - i = cMap >> lvl; - while(TRUE) { - lvl++; - if((cMap >> lvl) == 0) { - break; - } - if(pMap[i].AddrBase > vaBase) { - i -= (cMap >> lvl); - } else { - i += (cMap >> lvl); - } - } - // 2: scan back if needed - while(i && (pMap[i].AddrBase > vaBase)) { - i--; - } - // 3.1: fill in tag - for(; i < cMap; i++) { - if(pMap[i].AddrBase >= vaLimit) { break; } // outside scope - if(pMap[i].AddrBase + (pMap[i].cPages << 12) <= vaBase) { continue; } // outside scope - fCompleteOccupancy = (pMap[i].AddrBase + (pMap[i].cPages << 12) <= vaLimit) && (pMap[i].AddrBase >= vaBase); - if(pMap[i].szTag[0] && (!fOverwrite || !fCompleteOccupancy)) { continue; } - pMap[i].fWoW64 = fWoW64; - if(wszTag) { - snprintf(pMap[i].szTag, 31, fCompleteOccupancy ? "%S" : "[%S]", wszTag); - } - if(szTag) { - snprintf(pMap[i].szTag, 31, fCompleteOccupancy ? "%s" : "[%s]", szTag); - } - } -} - -_Success_(return) -BOOL MmX64_MapGetEntries(_In_ PVMM_PROCESS pProcess, _In_ DWORD flags, _Out_ PVMMOB_MEMMAP *ppObMemMap) -{ - DWORD i; - PVMM_MODULEMAP_ENTRY pModule; - PVMMOB_MODULEMAP pObModuleMap; - if(!MmX64_MapInitialize(pProcess)) { return FALSE; } - if((!pProcess->pObMemMap->fTagModules && (flags & VMM_MEMMAP_FLAG_MODULES)) || (!pProcess->pObMemMap->fTagScan && (flags & VMM_MEMMAP_FLAG_SCAN))) { - EnterCriticalSection(&pProcess->LockUpdate); - if(!pProcess->pObMemMap->fTagModules && (flags & VMM_MEMMAP_FLAG_MODULES)) { - pProcess->pObMemMap->fTagModules = TRUE; - if(VmmProc_ModuleMapGet(pProcess, &pObModuleMap)) { - // update memory map with names - for(i = 0; i < pObModuleMap->cMap; i++) { - pModule = pObModuleMap->pMap + i; - MmX64_MapTag(pProcess, pModule->BaseAddress, pModule->BaseAddress + pModule->SizeOfImage, pModule->szName, NULL, pModule->fWoW64, FALSE); - } - Ob_DECREF(pObModuleMap); - } - } - if(!pProcess->pObMemMap->fTagScan && (flags & VMM_MEMMAP_FLAG_SCAN)) { - pProcess->pObMemMap->fTagScan = TRUE; - VmmProc_ScanTagsMemMap(pProcess); - } - LeaveCriticalSection(&pProcess->LockUpdate); - } - *ppObMemMap = Ob_INCREF(pProcess->pObMemMap); return TRUE; } -_Success_(return) -BOOL MmX64_MapGetDisplay(_In_ PVMM_PROCESS pProcess, _In_ DWORD flags, _Out_ PVMMOB_DATA *ppObDisplay) -{ - DWORD i, o = 0; - PVMMOB_MEMMAP pObMemMap = NULL; - PVMMOB_DATA pObDisplay = NULL; - // memory map display data already exists - if(!MmX64_MapInitialize(pProcess)) { return FALSE; } - if(pProcess->pObMemMap->pObDisplay) { - *ppObDisplay = Ob_INCREF(pProcess->pObMemMap->pObDisplay); - return TRUE; - } - // create new memory map display data - EnterCriticalSection(&pProcess->LockUpdate); - if(!pProcess->pObMemMap->pObDisplay) { - if(MmX64_MapGetEntries(pProcess, flags, &pObMemMap)) { - pObDisplay = Ob_Alloc('MD', LMEM_ZEROINIT, sizeof(VMMOB_DATA) + pObMemMap->cbDisplay, NULL, NULL); - if(pObDisplay) { - for(i = 0; i < pObMemMap->cMap; i++) { - if(o + MMX64_MEMMAP_DISPLAYBUFFER_LINE_LENGTH > pObMemMap->cbDisplay) { - vmmprintf_fn("ERROR: SHOULD NOT HAPPEN! LENGTH DIFFERS #1: %i %i\n", o + MMX64_MEMMAP_DISPLAYBUFFER_LINE_LENGTH, pObMemMap->cbDisplay); - Ob_DECREF(pObDisplay); - pObDisplay = NULL; - goto fail; - } - o += snprintf( - pObDisplay->pbData + o, - pObMemMap->cbDisplay - o, - "%04x %8x %016llx-%016llx %sr%s%s%s%-32s\n", - i, - (DWORD)pObMemMap->pMap[i].cPages, - pObMemMap->pMap[i].AddrBase, - pObMemMap->pMap[i].AddrBase + (pObMemMap->pMap[i].cPages << 12) - 1, - pObMemMap->pMap[i].fPage & VMM_MEMMAP_PAGE_NS ? "-" : "s", - pObMemMap->pMap[i].fPage & VMM_MEMMAP_PAGE_W ? "w" : "-", - pObMemMap->pMap[i].fPage & VMM_MEMMAP_PAGE_NX ? "-" : "x", - pObMemMap->pMap[i].szTag[0] ? (pObMemMap->pMap[i].fWoW64 ? " 32 " : " ") : " ", - pObMemMap->pMap[i].szTag - ); - } - if(o != pObMemMap->cbDisplay) { - vmmprintf_fn("ERROR: SHOULD NOT HAPPEN! LENGTH DIFFERS #2: %i %i\n", o, pObMemMap->cbDisplay); - Ob_DECREF(pObDisplay); - pObDisplay = NULL; - goto fail; - } - pObDisplay->pbData[o - 1] = '\n'; - } - } - pProcess->pObMemMap->pObDisplay = pObDisplay; - } -fail: - Ob_DECREF(pObMemMap); - LeaveCriticalSection(&pProcess->LockUpdate); - if(pProcess->pObMemMap->pObDisplay) { - *ppObDisplay = Ob_INCREF(pProcess->pObMemMap->pObDisplay); - return TRUE; - } - return FALSE; -} - _Success_(return) BOOL MmX64_Virt2Phys(_In_ QWORD paPT, _In_ BOOL fUserOnly, _In_ BYTE iPML, _In_ QWORD va, _Out_ PQWORD ppa) { @@ -375,12 +215,8 @@ BOOL MmX64_Virt2Phys(_In_ QWORD paPT, _In_ BOOL fUserOnly, _In_ BYTE iPML, _In_ pte = pObPTEs->pqw[i]; Ob_DECREF(pObPTEs); if(!MMX64_PTE_IS_VALID(pte, iPML)) { - if(pte && MMX64_PTE_IS_TRANSITION(pte, iPML)) { - pte = MMX64_PTE_IS_TRANSITION(pte, iPML); // TRANSITION - } else { - if(iPML == 1) { *ppa = pte; } // NOT VALID - return FALSE; - } + if(iPML == 1) { *ppa = pte; } // NOT VALID + return FALSE; } if(fUserOnly && !(pte & 0x04)) { return FALSE; } // SUPERVISOR PAGE & USER MODE REQ if(pte & 0x000f000000000000) { return FALSE; } // RESERVED @@ -498,10 +334,7 @@ VOID MmX64_Initialize() ctxVmm->fnMemoryModel.pfnVirt2Phys = MmX64_Virt2Phys; ctxVmm->fnMemoryModel.pfnVirt2PhysGetInformation = MmX64_Virt2PhysGetInformation; ctxVmm->fnMemoryModel.pfnPhys2VirtGetInformation = MmX64_Phys2VirtGetInformation; - ctxVmm->fnMemoryModel.pfnMapInitialize = MmX64_MapInitialize; - ctxVmm->fnMemoryModel.pfnMapTag = MmX64_MapTag; - ctxVmm->fnMemoryModel.pfnMapGetEntries = MmX64_MapGetEntries; - ctxVmm->fnMemoryModel.pfnMapGetDisplay = MmX64_MapGetDisplay; + ctxVmm->fnMemoryModel.pfnPteMapInitialize = MmX64_PteMapInitialize; ctxVmm->fnMemoryModel.pfnTlbSpider = MmX64_TlbSpider; ctxVmm->fnMemoryModel.pfnTlbPageTableVerify = MmX64_TlbPageTableVerify; ctxVmm->tpMemoryModel = VMM_MEMORYMODEL_X64; diff --git a/vmm/mm_x64.h b/vmm/mm_x64.h deleted file mode 100644 index e2e11263..00000000 --- a/vmm/mm_x64.h +++ /dev/null @@ -1,17 +0,0 @@ -// mm_x64.h : definitions related to the x64 / IA32e / long-mode paging / memory model. -// -// (c) Ulf Frisk, 2018-2019 -// Author: Ulf Frisk, pcileech@frizk.net -// -#ifndef __MM_X64_H__ -#define __MM_X64_H__ -#include "vmm.h" - -/* -* Initialize the X64 / IA32e / Long-Mode paging / memory model. -* If a previous memory model exists that memory model is first closed before -* the new X64 memory model is initialized. -*/ -VOID MmX64_Initialize(); - -#endif /* __MM_X64_H__ */ diff --git a/vmm/mm_x64_page_win.c b/vmm/mm_x64_page_win.c deleted file mode 100644 index 6ad2c694..00000000 --- a/vmm/mm_x64_page_win.c +++ /dev/null @@ -1,632 +0,0 @@ -// mm_x64_page_win.c : implementation related to the x64 windows paging subsystem -// (including paged out virtual/compressed virtual memory). -// -// (c) Ulf Frisk, 2019 -// Author: Ulf Frisk, pcileech@frizk.net -// -#include "mm_x64_page_win.h" -#include "pe.h" -#include "statistics.h" -#include "util.h" - -//----------------------------------------------------------------------------- -// CUSTOM GENERAL FUNCTIONS BELOW: -//----------------------------------------------------------------------------- - -/* -* Read memory with option VMM_FLAG_NOPAGED to avoid circular reads back into -* the paging subsystem. Otherwise identical to the VmmRead function. -*/ -_Success_(return) -BOOL MmX64PageWin_VmmRead(_In_opt_ PVMM_PROCESS pProcess, _In_ QWORD qwA, _Out_ PBYTE pb, _In_ DWORD cb) -{ - DWORD cbRead; - VmmReadEx(pProcess, qwA, pb, cb, &cbRead, VMM_FLAG_NOPAGING); - return (cbRead == cb); -} - -//----------------------------------------------------------------------------- -// BTREE FUNCTIONALITY BELOW: -//----------------------------------------------------------------------------- - -typedef struct td_BTREE_LEAF_NODE { - DWORD k; - DWORD v; -} _BTREE_LEAF_NODE; - -typedef struct td_BTREE_LEAF { - WORD cEntries; - BYTE cLevel; - BYTE fLeaf; - QWORD vaLeftChild; - _BTREE_LEAF_NODE Entries[]; -} _BTREE_LEAF, *P_BTREE_LEAF; - -typedef struct td_BTREE_NODE { - DWORD k; - QWORD vaLeaf; -} _BTREE_NODE; - -typedef struct td_BTREE { - WORD cEntries; - BYTE cLevel; - BYTE fLeaf; - QWORD vaLeftChild; - _BTREE_NODE Entries[]; -} _BTREE, *P_BTREE; - -_Success_(return) -BOOL MmX64PageWin_BTreeSearch_Leaf(_In_ PVMM_PROCESS pSystemProcess, _In_ QWORD vaLeaf, _In_ DWORD dwKey, _Out_ PDWORD pdwValue) -{ - BOOL f, fSearchPreFail = FALSE; - DWORD i, dwSearchStep, dwSearchIndex = 1, dwSearchCount = 0; - BYTE pbBuffer[0x1000]; - P_BTREE_LEAF pT = (P_BTREE_LEAF)pbBuffer; - // 1: read tree leaf page - f = VMM_KADDR64_PAGE(vaLeaf) && - MmX64PageWin_VmmRead(pSystemProcess, vaLeaf, pbBuffer, 0x1000) && - pT->fLeaf && - pT->cEntries && - (pT->cEntries <= 0x1ff); - if(!f) { return FALSE; } - // 2: search tree for leaf - for(i = 1; (i < 12) && ((pT->cEntries - 1) >> i); i++); - dwSearchIndex = dwSearchStep = min(1 << (i - 1), pT->cEntries); - while(TRUE) { - dwSearchCount++; - dwSearchStep = dwSearchStep >> 1; - if(pT->Entries[dwSearchIndex].k == dwKey) { - *pdwValue = pT->Entries[dwSearchIndex].v; - return TRUE; - } - if(dwSearchStep == 0) { - if(fSearchPreFail) { - return FALSE; - } - fSearchPreFail = TRUE; - dwSearchStep = 1; - } - if(pT->Entries[dwSearchIndex].k < dwKey) { - if(dwSearchIndex + dwSearchStep < pT->cEntries) { - dwSearchIndex += dwSearchStep; - } - } else { - if(dwSearchStep <= dwSearchIndex) { - dwSearchIndex -= dwSearchStep; - } - } - } -} - -_Success_(return) -BOOL MmX64PageWin_BTreeSearch(_In_ PVMM_PROCESS pSystemProcess, _In_ QWORD vaTreeRoot, _In_ DWORD dwKey, _Out_ PDWORD pdwValue) -{ - BOOL f, fSearchPreFail = FALSE; - DWORD i, dwSearchStep, dwSearchIndex = 1, dwSearchCount = 0; - QWORD vaEntryLeaf = 0; - BYTE pbBuffer[0x1000]; - P_BTREE pT = (P_BTREE)pbBuffer; - // 1: read tree root - f = VMM_KADDR64_PAGE(vaTreeRoot) && - MmX64PageWin_VmmRead(pSystemProcess, vaTreeRoot, pbBuffer, 0x1000) && - !pT->fLeaf && - pT->cEntries && - (pT->cEntries <= 0xff); - if(!f) { return FALSE; } - // 2: search tree for leaf - for(i = 1; (i < 12) && ((pT->cEntries - 1) >> i); i++); - dwSearchIndex = dwSearchStep = min(1 << (i - 1), pT->cEntries); - while(TRUE) { - dwSearchCount++; - dwSearchStep = dwSearchStep >> 1; - if((dwSearchStep == 0) && !fSearchPreFail) { - fSearchPreFail = TRUE; - dwSearchStep = 1; - } - if((dwSearchStep == 0) || ((pT->Entries[dwSearchIndex].k <= dwKey) && ((dwSearchIndex + 1 == pT->cEntries) || (pT->Entries[dwSearchIndex + 1].k > dwKey)))) { - if((dwSearchIndex == 0) && (pT->Entries[0].k > dwKey)) { - vaEntryLeaf = pT->vaLeftChild; - } else { - vaEntryLeaf = pT->Entries[dwSearchIndex].vaLeaf; - } - return MmX64PageWin_BTreeSearch_Leaf(pSystemProcess, vaEntryLeaf, dwKey, pdwValue); - } else if(pT->Entries[dwSearchIndex].k < dwKey) { - if(dwSearchIndex + dwSearchStep < pT->cEntries) { - dwSearchIndex += dwSearchStep; - } - } else { - if(dwSearchStep <= dwSearchIndex) { - dwSearchIndex -= dwSearchStep; - } - } - } -} - -//----------------------------------------------------------------------------- -// INITIALIZATION FUNCTIONALITY BELOW: -//----------------------------------------------------------------------------- - -/* -* Fuzz offsets in _SMKM_STORE / _ST_STORE / _ST_DATA_MGR -* NB! this function does not do any real fuzzing it rather queries the OS build -* number / version to determine correct offsets. -*/ -VOID MmX64PageWin_MemCompress_Fuzz() -{ - PVMMWIN_MEMCOMPRESS_OFFSET po = &ctxVmm->kernel.MemCompress.O; - po->SMKM_STORE.PagesTree = 0x50 + 0x0; // static = ok - po->SMKM_STORE.ChunkMetaData = 0x50 + 0xC0; // static = ok - po->SMKM_STORE.SmkmStore = 0x50 + 0x320; // static = ok - po->SMKM_STORE.RegionSizeMask = 0x50 + 0x328; // static = ok - po->SMKM_STORE.RegionIndexMask = 0x50 + 0x32C; // static = ok - po->SMKM_STORE.CompressionAlgorithm = 0x50 + 0x3E0; // 1709+ - po->SMKM_STORE.CompressedRegionPtrArray = 0x1848; // 1709+ - po->SMKM_STORE.OwnerProcess = 0x19A8; // 1709+ - if(ctxVmm->kernel.dwVersionBuild == 15063) { // 1703 - po->SMKM_STORE.CompressionAlgorithm = 0x50 + 0x3D0; - po->SMKM_STORE.CompressedRegionPtrArray = 0x1828; - po->SMKM_STORE.OwnerProcess = 0x1988; - } - if(ctxVmm->kernel.dwVersionBuild == 14393) { // 1607 - po->SMKM_STORE.CompressionAlgorithm = 0x50 + 0x3D0; - po->SMKM_STORE.CompressedRegionPtrArray = 0x17A8; - po->SMKM_STORE.OwnerProcess = 0x1918; - } - po->_Size = po->SMKM_STORE.OwnerProcess + 8; - po->_fProcessedTry = TRUE; - po->_fValid = TRUE; -} - -/* -* Retrieve the page file number of the virtual store. This will be '2' on a -* standard system, but if paging are configured in a non-standard way this -* number may differ. -* Walk ntoskrnl.exe!.data section for candidate pointers to nt!_MMPAGING_FILE -* which have pool header: 'Mm '. The page file number and the virtual store -* flag is contained at same bits in all known versions with MemCompression -* as per below: -* dt nt!_MMPAGING_FILE -* +0x0cc PageFileNumber : Pos 0, 4 Bits -* +0x0cc VirtualStorePagefile : Pos 6, 1 Bit -* If this function fails it will automatically fallback to the default number -* of 2. -*/ -VOID MmX64PageWin_MemCompress_InitializeVirtualStorePageFileNumber() -{ - BOOL f; - BYTE pbMm[0x100] = { 0 }; - QWORD j, va = 0; - DWORD i, cb, cbRead; - PBYTE pb = NULL; - PVMM_PROCESS pObSystemProcess = NULL; - IMAGE_SECTION_HEADER oSectionHeader; - POB_VSET pObSet = NULL; - ctxVmm->kernel.MemCompress.dwPageFileNumber = 2; - // 1: Locate candicate pointers to 'nt!_MMPAGING_FILE' candidates in ntoskrnl.exe!.data section - if(!(pObSystemProcess = VmmProcessGet(4))) { goto finish; } - if(!(pObSet = ObVSet_New())) { goto finish; } - if(!PE_SectionGetFromName(pObSystemProcess, ctxVmm->kernel.vaBase, ".data", &oSectionHeader)) { - vmmprintfv_fn("CANNOT READ ntoskrnl.exe .data SECTION from PE header.\n"); - goto finish; - } - if(oSectionHeader.Misc.VirtualSize > 0x00100000) { goto finish; } - cb = oSectionHeader.Misc.VirtualSize; - if(!(pb = LocalAlloc(0, cb))) { goto finish; } - if(!VmmRead(pObSystemProcess, ctxVmm->kernel.vaBase + oSectionHeader.VirtualAddress, pb, cb)) { - vmmprintfv_fn("CANNOT READ ntoskrnl.exe .data SECTION.\n"); - goto finish; - } - for(i = 0; i < cb - 0x90; i += 8) { - f = (*(PDWORD)(pb + i + 0x004) == 1) && - *(PDWORD)(pb + i + 0x000) && - (*(PDWORD)(pb + i + 0x000) < 16) && - VMM_KADDR64_16(*(PQWORD)(pb + i + 0x008)) && - VMM_KADDR64_16(*(PQWORD)(pb + i + 0x010)) && - ((*(PQWORD)(pb + i + 0x008) >> 32) == (*(PQWORD)(pb + i + 0x010) >> 32)); - if(f) { - for(j = 0; j < *(PDWORD)(pb + i + 0x000); j++) { - va = *(PQWORD)(pb + i + 0x008 + j * 8); - if(VMM_KADDR64_16(va)) { - ObVSet_Push(pObSet, va); - } - } - } - } - // 2: Verify nt!dt _MMPAGING_FILE by looking at pool header and VirtualStorePagefile bit - VmmCachePrefetchPages(pObSystemProcess, pObSet); - while((va = ObVSet_Pop(pObSet))) { - VmmReadEx(pObSystemProcess, va - 0x10, pbMm, 0x100, &cbRead, VMM_FLAG_FORCECACHE_READ); - if((*(PDWORD)(pbMm + 4) == ' mM') && (*(PBYTE)(pbMm + 0x10 + 0xcc) & 0x40)) { - ctxVmm->kernel.MemCompress.dwPageFileNumber = (*(PBYTE)(pbMm + 0x10 + 0xcc) & 0x0f); - goto finish; - } - } - vmmprintfv_fn("WARN! did not find virtual store number - fallback to default.\n"); -finish: - LocalFree(pb); - Ob_DECREF(pObSystemProcess); - Ob_DECREF(pObSet); -} - -/* -* Locate SmGlobals in 1st page of ntoskrnl.exe!CACHEALI section by looking for -* pointers to SMKM_STORE_METADATA (pool hdr: 'SmSa'). -* SMGLOBALS (_SMKM_STORE_MGR)+000 = Smkm Metadata (_SMKM sSmkm) -* _SMKM: PTR[32] to _SMKM_STORE_METADATA: (pool hdr smSa) -* _SMKM_STORE_METADATA: sizeof(_SMKM_STORE_METADATA) = 0x28 -* +000 = PTR to SMKM_STORE -* +018 = PTR to EPROCESS -* SMGLOBALS (_SMKM_STORE_MGR)+1C0 = KeyToStoreTree (B_TREE sGlobalTree) -* -- pSystemProcess -*/ -VOID MmX64PageWin_MemCompress_InitializeLocateSMGLOBALS() -{ - BOOL f; - BYTE pbPage[0x1000] = { 0 }; - DWORD i, dwSmsaPoolHdr = 0, cbRead; - QWORD vaSmGlobals, vaSmsa, vaKeyToStoreTree; - IMAGE_SECTION_HEADER oSectionHeader; - PVMM_PROCESS pObSystemProcess = NULL; - POB_VSET pObSet = NULL; - EnterCriticalSection(&ctxVmm->MasterLock); - if(ctxVmm->kernel.MemCompress.fInitialized || (ctxVmm->kernel.dwVersionBuild < 14393)) { goto finish; } - // 1: Locate SmGlobals candidates in ntoskrnl.exe!CACHEALI section - if(!(pObSystemProcess = VmmProcessGet(4))) { goto finish; } - if(!(pObSet = ObVSet_New())) { goto finish; } - if(!PE_SectionGetFromName(pObSystemProcess, ctxVmm->kernel.vaBase, "CACHEALI", &oSectionHeader)) { - vmmprintfv_fn("CANNOT READ ntoskrnl.exe CACHEALI SECTION from PE header.\n"); - goto finish; - } - if(!VmmRead(pObSystemProcess, ctxVmm->kernel.vaBase + oSectionHeader.VirtualAddress, pbPage, 0x1000)) { - vmmprintfv_fn("CANNOT READ ntoskrnl.exe CACHEALI SECTION.\n"); - goto finish; - } - for(i = 0; i < 0x1000; i += 8) { - vaSmGlobals = ctxVmm->kernel.vaBase + oSectionHeader.VirtualAddress + i; - vaSmsa = *(PQWORD)(pbPage + i); - vaKeyToStoreTree = *(PQWORD)(pbPage + i + 0x1c0); - f = VMM_KADDR64_PAGE(vaKeyToStoreTree) && - VMM_KADDR64_16(vaSmsa) && - (vaSmsa > 0xffff8fff'ffffffff); - if(f) { - ObVSet_Push(pObSet, vaSmGlobals); - ObVSet_Push(pObSet, vaKeyToStoreTree); - ObVSet_Push(pObSet, vaSmsa); - } - } - // 2: Verify SMGLOBALS / _SMKM_STORE_METADATA (pool hdr: 'smSa') - VmmCachePrefetchPages(pObSystemProcess, pObSet); - while(ObVSet_Size(pObSet)) { - vaSmsa = ObVSet_Pop(pObSet); - vaKeyToStoreTree = ObVSet_Pop(pObSet); - vaSmGlobals = ObVSet_Pop(pObSet); - VmmReadEx(pObSystemProcess, vaSmsa - 12, (PBYTE)&dwSmsaPoolHdr, sizeof(DWORD), &cbRead, VMM_FLAG_FORCECACHE_READ); - if(dwSmsaPoolHdr == 'aSms') { - MmX64PageWin_MemCompress_Fuzz(); - ctxVmm->kernel.MemCompress.fValid = TRUE; - ctxVmm->kernel.MemCompress.vaSmGlobals = vaSmGlobals; - ctxVmm->kernel.MemCompress.vaKeyToStoreTree = vaKeyToStoreTree; - MmX64PageWin_MemCompress_InitializeVirtualStorePageFileNumber(); - vmmprintfv("Windows 10 Memory Compression Initialize #1 - SmGlobals located at: %16llx Pf: %i \n", ctxVmm->kernel.MemCompress.vaSmGlobals, ctxVmm->kernel.MemCompress.dwPageFileNumber); - break; - } - } -finish: - LeaveCriticalSection(&ctxVmm->MasterLock); - ctxVmm->kernel.MemCompress.fInitialized = TRUE; - Ob_DECREF(pObSystemProcess); - Ob_DECREF(pObSet); -} - -//----------------------------------------------------------------------------- -// COMPRESSED STORE FUNCTIONALITY BELOW: -//----------------------------------------------------------------------------- - -#define COMPRESS_ALGORITHM_INVALID 0 -#define COMPRESS_ALGORITHM_NULL 1 -#define COMPRESS_ALGORITHM_MSZIP 2 -#define COMPRESS_ALGORITHM_XPRESS 3 -#define COMPRESS_ALGORITHM_XPRESS_HUFF 4 -#define COMPRESS_ALGORITHM_LZMS 5 -#define COMPRESS_ALGORITHM_MAX 6 -#define COMPRESS_RAW (1 << 29) - -typedef struct tdMMX64WINPAGED_CONTEXT { - PVMM_PROCESS pProcess; - PVMM_PROCESS pSystemProcess; - PVMM_PROCESS pProcessMemCompress; - // per page items - struct { - QWORD va; - QWORD PTE; - DWORD dwPageKey; - DWORD iSmkm; // index into 32x32 array in SmGlobals/SMKM_STORE_METADATA - QWORD vaSmkmStore; - QWORD vaEPROCESS; - QWORD vaOwnerEPROCESS; - BYTE pbSmkm[0x2000]; - DWORD dwRegionKey; - QWORD vaPageRecord; - QWORD vaRegion; - DWORD cbRegionOffset; - DWORD cbCompressedData; - BYTE pbCompressedData[0x1000]; - } e; -} MMX64WINPAGED_CONTEXT, *PMMX64WINPAGED_CONTEXT; - -#define PAGE_FILE_NUMBER_FROM_PTE(pte) ((pte >> (((ctxVmm->kernel.dwVersionBuild >= 17134) ? 12 : 1))) & 0x0f) -#define PAGE_FILE_OFFSET_FROM_PTE(pte) ((pte >> 32) & ((!(pte & 0x10) && (ctxVmm->kernel.dwVersionBuild >= 17134)) ? 0xffffdfff : 0xffffffff)) -#define PAGE_KEY_COMPRESSED_FROM_PTE(pte) (DWORD)(((PAGE_FILE_NUMBER_FROM_PTE(pte) << 0x1c) | PAGE_FILE_OFFSET_FROM_PTE(pte))) - -typedef struct td_SMKM_STORE_METADATA { - QWORD vaSmkmStore; - QWORD Reserved1[2]; - QWORD vaEPROCESS; - QWORD Reserved2; -} _SMKM_STORE_METADATA; - -typedef struct td_SMHP_CHUNK_METADATA { - QWORD avaChunkPtr[32]; - QWORD Reserved1; - DWORD dwBitValue; - DWORD dwPageRecordsPerChunkMask; - DWORD dwPageRecordSize; - DWORD Reserved2; - DWORD dwChunkPageHeaderSize; -} _SMHP_CHUNK_METADATA, *P_SMHP_CHUNK_METADATA; - -typedef struct td_ST_PAGE_RECORD { - DWORD Key; - DWORD CompressedSize; - DWORD NextKey; -} _ST_PAGE_RECORD, *P_ST_PAGE_RECORD; - -BOOL MmX64PageWin_MemCompress_LogError(_In_ PMMX64WINPAGED_CONTEXT ctx, _In_ LPSTR sz) -{ - vmmprintfvv( - "MmX64PageWin: FAIL: %s\n" \ - " va= %016llx ep= %016llx pgk=%08x ism=%04x vas=%016llx \n" \ - " pte=%016llx oep=%016llx rgk=%08x pid=%04x \n" \ - " pgr=%016llx rgn=%016llx rgo=%08x cbc=%04x rga=%016llx\n", - sz, - ctx->e.va, ctx->e.vaEPROCESS, ctx->e.dwPageKey, ctx->e.iSmkm, ctx->e.vaSmkmStore, - ctx->e.PTE, ctx->e.vaOwnerEPROCESS, ctx->e.dwRegionKey, ctx->pProcess->dwPID, - ctx->e.vaPageRecord, ctx->e.vaRegion, ctx->e.cbRegionOffset, ctx->e.cbCompressedData, (ctx->e.vaRegion + ctx->e.cbRegionOffset) - ); - return FALSE; -} - -/* -* Retrieve the index of the 32x32 array in SmGlobals/SMKM_STORE_METADATA which -* points to the SmkmStore. The index is retrieved from the KeyToStoreTree BTree -* pointed by SmGlobals/SMKM_STORE_METADATA. -* -- ctx -* -- pwSmkmStoreIndex -* -- return -*/ -_Success_(return) -BOOL MmX64PageWin_MemCompress1_SmkmStoreIndex(_In_ PMMX64WINPAGED_CONTEXT ctx) -{ - DWORD v; - if(!MmX64PageWin_BTreeSearch(ctx->pSystemProcess, ctxVmm->kernel.MemCompress.vaKeyToStoreTree, ctx->e.dwPageKey, &v)) { return MmX64PageWin_MemCompress_LogError(ctx, "#11 BTreeSearch"); } - if(v & 0x01000000) { return MmX64PageWin_MemCompress_LogError(ctx, "#12 InvalidValue"); } - ctx->e.iSmkm = 0x3ff & v; - return TRUE; -} - -/* -* Retrieve the virtual address to the SmkmStore and the EPROCESS of the process -* by walking the 32x32 array in SmGlobals. -* -- ctx -* -- return -*/ -_Success_(return) -BOOL MmX64PageWin_MemCompress2_SmkmStoreMetadata(_In_ PMMX64WINPAGED_CONTEXT ctx) -{ - QWORD va; - _SMKM_STORE_METADATA MetaData; - // 1: 1st level fetch virtual address to 2nd level of 32x32 array - if(!MmX64PageWin_VmmRead(ctx->pSystemProcess, ctxVmm->kernel.MemCompress.vaSmGlobals + (ctx->e.iSmkm >> 5) * sizeof(QWORD), (PBYTE)&va, sizeof(QWORD))) { return MmX64PageWin_MemCompress_LogError(ctx, "#21 Read"); } - if(!VMM_KADDR64_16(va)) { return MmX64PageWin_MemCompress_LogError(ctx, "#22 NoKADDR"); } - // 2: 2nd fetch values (_SMKM_STORE_METADATA) from 2nd level of 32x32 array. - if(!MmX64PageWin_VmmRead(ctx->pSystemProcess, va + (ctx->e.iSmkm & 0x1f) * sizeof(_SMKM_STORE_METADATA), (PBYTE)&MetaData, sizeof(_SMKM_STORE_METADATA))) { return MmX64PageWin_MemCompress_LogError(ctx, "#23 Read"); } - if(MetaData.vaEPROCESS && !VMM_KADDR64_16(MetaData.vaEPROCESS)) { return MmX64PageWin_MemCompress_LogError(ctx, "#24 NoKADDR"); } - if(!VMM_KADDR64_PAGE(MetaData.vaSmkmStore)) { return MmX64PageWin_MemCompress_LogError(ctx, "#25 NoKADDR"); } - ctx->e.vaSmkmStore = MetaData.vaSmkmStore; - ctx->e.vaEPROCESS = MetaData.vaEPROCESS; - return TRUE; -} - -/* -* Retrieve the SmkmStore and the PageRecord. -* -- ctx -* -- return -*/ -_Success_(return) -BOOL MmX64PageWin_MemCompress3_SmkmStoreAndPageRecord(_In_ PMMX64WINPAGED_CONTEXT ctx) -{ - QWORD vaPageRecordArray; - DWORD i, dwEncodedMetadata, iChunkPtr = 0, iChunkArray, dwPoolHdr = 0; - P_SMHP_CHUNK_METADATA pc; - PVMMWIN_MEMCOMPRESS_OFFSET po = &ctxVmm->kernel.MemCompress.O; - // 1: Load SmkmStore - if(!MmX64PageWin_VmmRead(ctx->pSystemProcess, ctx->e.vaSmkmStore, ctx->e.pbSmkm, sizeof(ctx->e.pbSmkm))) { - return MmX64PageWin_MemCompress_LogError(ctx, "#31 ReadSmkmStore"); - } - // 2: Validate - if(!VMM_KADDR64_16(*(PQWORD)(ctx->e.pbSmkm + po->SMKM_STORE.PagesTree))) { - return MmX64PageWin_MemCompress_LogError(ctx, "#32 PagesTreePtrNoKADDR"); - } - if(COMPRESS_ALGORITHM_XPRESS != *(PWORD)(ctx->e.pbSmkm + po->SMKM_STORE.CompressionAlgorithm)) { - return MmX64PageWin_MemCompress_LogError(ctx, "#33 InvalidCompressionAlgorithm"); - } - // 3: Get region key - if(!MmX64PageWin_BTreeSearch(ctx->pSystemProcess, *(PQWORD)(ctx->e.pbSmkm + po->SMKM_STORE.PagesTree), ctx->e.dwPageKey, &ctx->e.dwRegionKey)) { - return MmX64PageWin_MemCompress_LogError(ctx, "#34 RegionKeyBTreeSearch"); - } - // 4: Get page record and calculate: - // - chunk "encoded metadata" - // - index into chunk metadata array (= highest non-zero bit position of encoded_metadata) - // - index into chunk array (pointed to by chunk metadata array) - pc = (P_SMHP_CHUNK_METADATA)(ctx->e.pbSmkm + po->SMKM_STORE.ChunkMetaData); - dwEncodedMetadata = ctx->e.dwRegionKey >> (pc->dwBitValue & 0xff); - for(i = 0; i < 32; i++) { - if(!(dwEncodedMetadata >> i)) { break; } - iChunkPtr = i; - } - iChunkArray = (1 << iChunkPtr) ^ dwEncodedMetadata; - // 5: Validate and fetch page record address - if(iChunkArray > 0x400) { - return MmX64PageWin_MemCompress_LogError(ctx, "#35 ChunkArrayTooLarge"); - } - if(!VMM_KADDR64_16(pc->avaChunkPtr[iChunkPtr])) { - return MmX64PageWin_MemCompress_LogError(ctx, "#36 ChunkPtrNoKADDR"); - } - if(!MmX64PageWin_VmmRead(ctx->pSystemProcess, pc->avaChunkPtr[iChunkPtr] - 12, (PBYTE)&dwPoolHdr, 4) || (dwPoolHdr != 'ABms')) { - return MmX64PageWin_MemCompress_LogError(ctx, "#37 ChunkBadPoolHdr"); - } - if(!MmX64PageWin_VmmRead(ctx->pSystemProcess, pc->avaChunkPtr[iChunkPtr] + 0x10ULL * iChunkArray, (PBYTE)&vaPageRecordArray, 8) || !VMM_KADDR64_PAGE(vaPageRecordArray)) { - return MmX64PageWin_MemCompress_LogError(ctx, "#38 PageRecordArray"); - } - ctx->e.vaPageRecord = vaPageRecordArray + pc->dwChunkPageHeaderSize + ((QWORD)pc->dwPageRecordSize * (ctx->e.dwRegionKey & pc->dwPageRecordsPerChunkMask)); - // 6: Get owner EPROCESS - ctx->e.vaOwnerEPROCESS = *(PQWORD)(ctx->e.pbSmkm + po->SMKM_STORE.OwnerProcess); - if(ctx->e.vaOwnerEPROCESS != ctxVmm->kernel.MemCompress.vaEPROCESS) { - return MmX64PageWin_MemCompress_LogError(ctx, "#39 OwnerEPROCESS"); - } - return TRUE; -} - -/* -* Retrieve the region address / data containing the compressed process. -* -- ctx -* -- return -*/ -_Success_(return) -BOOL MmX64PageWin_MemCompress4_CompressedRegionData(_In_ PMMX64WINPAGED_CONTEXT ctx) -{ - QWORD vaRegionPtr; - DWORD dwRegionIndexMask, dwRegionIndex; - _ST_PAGE_RECORD PageRecord; - PVMMWIN_MEMCOMPRESS_OFFSET po = &ctxVmm->kernel.MemCompress.O; - // 1: Read page record - if(!MmX64PageWin_VmmRead(ctx->pSystemProcess, ctx->e.vaPageRecord, (PBYTE)&PageRecord, sizeof(PageRecord))) { - return MmX64PageWin_MemCompress_LogError(ctx, "#41 ReadPageRecord"); - } - if(PageRecord.Key == 0xffffffff) { - return MmX64PageWin_MemCompress_LogError(ctx, "#42 InvalidPageRecord"); - } - ctx->e.cbCompressedData = (PageRecord.CompressedSize == 0x1000) ? 0x1000 : PageRecord.CompressedSize & 0xfff; - // 2: Get pointer to region - dwRegionIndexMask = *(PDWORD)(ctx->e.pbSmkm + po->SMKM_STORE.RegionIndexMask) & 0xff; - dwRegionIndex = PageRecord.Key >> dwRegionIndexMask; - vaRegionPtr = *(PQWORD)(ctx->e.pbSmkm + po->SMKM_STORE.CompressedRegionPtrArray) + dwRegionIndex * sizeof(QWORD); - // 3: Get region and offset - if(!MmX64PageWin_VmmRead(ctx->pSystemProcess, vaRegionPtr, (PBYTE)&ctx->e.vaRegion, sizeof(QWORD))) { - return MmX64PageWin_MemCompress_LogError(ctx, "#42 ReadRegionVA"); - } - if(!ctx->e.vaRegion || (ctx->e.vaRegion & 0xffff8000'0000ffff)) { - return MmX64PageWin_MemCompress_LogError(ctx, "#42 InvalidRegionVA"); - } - ctx->e.cbRegionOffset = (PageRecord.Key & *(PDWORD)(ctx->e.pbSmkm + po->SMKM_STORE.RegionSizeMask)) << 4; - return TRUE; -} - -/* -* Decompress a compressed page -* -- ctx -* -- pbDecompressedPage -* -- return -*/ -_Success_(return) -BOOL MmX64PageWin_MemCompress5_DecompressPage(_In_ PMMX64WINPAGED_CONTEXT ctx, _Out_writes_(4096) PBYTE pbDecompressedPage) -{ - DWORD cbDecompressed; - // 1: Read compressed data - if(!MmX64PageWin_VmmRead(ctx->pProcessMemCompress, ctx->e.vaRegion + ctx->e.cbRegionOffset, ctx->e.pbCompressedData, ctx->e.cbCompressedData)) { - MmX64PageWin_MemCompress_LogError(ctx, "#51 Read"); - return FALSE; - } - // 2: Decompress data - if(ctx->e.cbCompressedData == 0x1000) { - memcpy(pbDecompressedPage, ctx->e.pbCompressedData, 0x1000); - } else { - if((VMM_STATUS_SUCCESS != ctxVmm->fn.RtlDecompressBuffer(COMPRESS_ALGORITHM_XPRESS, pbDecompressedPage, 0x1000, ctx->e.pbCompressedData, ctx->e.cbCompressedData, &cbDecompressed)) || (cbDecompressed != 0x1000)) { - return MmX64PageWin_MemCompress_LogError(ctx, "#52 Decompress"); - } - } - return TRUE; -} - -/* -* Decompress a page. -* -- pProcess -* -- pMEM -*/ -VOID MmX64PageWin_MemCompress(_In_ PVMM_PROCESS pProcess, PMEM_IO_SCATTER_HEADER pMEM) -{ - BOOL f; - PMMX64WINPAGED_CONTEXT ctx = NULL; - PVMM_PROCESS pObSystemProcess = NULL, pObMemCompressProcess = NULL; - QWORD tm = Statistics_CallStart(); - if(!(ctx = LocalAlloc(LMEM_ZEROINIT, sizeof(MMX64WINPAGED_CONTEXT)))) { return; } - if(pMEM->pvReserved1) { - ctx->e.va = ((PMEM_IO_SCATTER_HEADER)pMEM->pvReserved1)->qwA; - } - ctx->e.PTE = pMEM->qwA; - ctx->e.dwPageKey = PAGE_KEY_COMPRESSED_FROM_PTE(ctx->e.PTE); - f = (ctx->pProcess = pProcess) && - (ctx->pSystemProcess = pObSystemProcess = VmmProcessGet(4)) && - (ctx->pProcessMemCompress = pObMemCompressProcess = VmmProcessGet(ctxVmm->kernel.MemCompress.dwPid)) && - MmX64PageWin_MemCompress1_SmkmStoreIndex(ctx) && - MmX64PageWin_MemCompress2_SmkmStoreMetadata(ctx) && - MmX64PageWin_MemCompress3_SmkmStoreAndPageRecord(ctx) && - MmX64PageWin_MemCompress4_CompressedRegionData(ctx) && - MmX64PageWin_MemCompress5_DecompressPage(ctx, pMEM->pb); - if(f) { - pMEM->cb = 0x1000; - } - LocalFree(ctx); - Ob_DECREF(pObSystemProcess); - Ob_DECREF(pObMemCompressProcess); - Statistics_CallEnd(STATISTICS_ID_VMM_PagedCompressedMemory, tm); -} - -VOID MmX64PageWin_ReadScatterPaged(_In_ PVMM_PROCESS pProcess, _Inout_ PPMEM_IO_SCATTER_HEADER ppMEMsPaged, _In_ DWORD cpMEMsPaged) -{ - DWORD iPG; - QWORD pte; - PMEM_IO_SCATTER_HEADER pMEM; - for(iPG = 0; iPG < cpMEMsPaged; iPG++) { - pMEM = ppMEMsPaged[iPG]; - pte = pMEM->qwA; - // no processing of physical, cached, pre-failed or non-paged entries - if((pte & 0x01) || (pMEM->cb == 0x1000) || (QWORD)pMEM->pvReserved2) { - continue; - } - // demand zero virtual memory - if(!PAGE_FILE_NUMBER_FROM_PTE(pte) && !PAGE_FILE_OFFSET_FROM_PTE(pte)) { - pMEM->cb = 0x1000; - ZeroMemory(pMEM->pb, 0x1000); - InterlockedIncrement64(&ctxVmm->stat.cPageReadSuccessDemandZero); - continue; - } - // potentially compressed virtual memory - if(ctxVmm->kernel.MemCompress.dwPageFileNumber == PAGE_FILE_NUMBER_FROM_PTE(pte)) { - if(!ctxVmm->kernel.MemCompress.fValid && !ctxVmm->kernel.MemCompress.fInitialized) { - MmX64PageWin_MemCompress_InitializeLocateSMGLOBALS(); - } - if(ctxVmm->kernel.MemCompress.fValid) { - MmX64PageWin_MemCompress(pProcess, pMEM); - if(pMEM->cb == 0x1000) { - InterlockedIncrement64(&ctxVmm->stat.cPageReadSuccessCompressed); - } else { - ObVSet_Push(ctxVmm->Cache.PAGING_FAILED, pte); - InterlockedIncrement64(&ctxVmm->stat.cPageReadFailedCompressed); - } - continue; - } - } - InterlockedIncrement64(&ctxVmm->stat.cPageReadFailed); - } -} diff --git a/vmm/mm_x64_page_win.h b/vmm/mm_x64_page_win.h deleted file mode 100644 index 89f28e9e..00000000 --- a/vmm/mm_x64_page_win.h +++ /dev/null @@ -1,23 +0,0 @@ -// mm_x64_page_win.h : definitions related to the x64 windows paging subsystem -// (including paged out virtual/compressed virtual memory). -// -// (c) Ulf Frisk, 2019 -// Author: Ulf Frisk, pcileech@frizk.net -// -#ifndef __MM_X64_PAGE_WIN_H__ -#define __MM_X64_PAGE_WIN_H__ -#include "vmm.h" - -/* -* Scatter read paged out virtual memory. Non contiguous 4096-byte pages. -* -- pProcess -* -- ppMEMsPaged -* -- cpMEMsPaged -*/ -VOID MmX64PageWin_ReadScatterPaged( - _In_ PVMM_PROCESS pProcess, - _Inout_ PPMEM_IO_SCATTER_HEADER ppMEMsPaged, - _In_ DWORD cpMEMsPaged -); - -#endif /* __MM_X64_PAGE_WIN_H__ */ diff --git a/vmm/mm_x86.c b/vmm/mm_x86.c index b31c76cb..ee58cb3c 100644 --- a/vmm/mm_x86.c +++ b/vmm/mm_x86.c @@ -47,12 +47,12 @@ VOID MmX86_TlbSpider(_In_ PVMM_PROCESS pProcess) const DWORD MMX86_PAGETABLEMAP_PML_REGION_SIZE[3] = { 0, 12, 22 }; -VOID MmX86_MapInitialize_Index(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MEMMAP_ENTRY pMemMap, _In_ PDWORD pcMemMap, _In_ DWORD vaBase, _In_ BYTE iPML, _In_ DWORD PTEs[1024], _In_ BOOL fSupervisorPML, _In_ QWORD paMax) +VOID MmX86_MapInitialize_Index(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MAP_PTEENTRY pMemMap, _In_ PDWORD pcMemMap, _In_ DWORD vaBase, _In_ BYTE iPML, _In_ DWORD PTEs[1024], _In_ BOOL fSupervisorPML, _In_ QWORD paMax) { PVMMOB_MEM pObNextPT; DWORD i, va, pte; BOOL fUserOnly, fNextSupervisorPML, fTransition = FALSE; - PVMM_MEMMAP_ENTRY pMemMapEntry = pMemMap + *pcMemMap - 1; + PVMM_MAP_PTEENTRY pMemMapEntry = pMemMap + *pcMemMap - 1; fUserOnly = pProcess->fUserOnly; for(i = 0; i < 1024; i++) { pte = PTEs[i]; @@ -71,10 +71,10 @@ VOID MmX86_MapInitialize_Index(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MEMMAP_ENTR if((iPML == 1) || (pte & 0x80) /* PS */) { if((*pcMemMap == 0) || ((pMemMapEntry->fPage != (pte & VMM_MEMMAP_PAGE_MASK)) && !fTransition) || - (va != pMemMapEntry->AddrBase + (pMemMapEntry->cPages << 12))) { + (va != pMemMapEntry->vaBase + (pMemMapEntry->cPages << 12))) { if(*pcMemMap + 1 >= VMM_MEMMAP_ENTRIES_MAX) { return; } pMemMapEntry = pMemMap + *pcMemMap; - pMemMapEntry->AddrBase = va; + pMemMapEntry->vaBase = va; pMemMapEntry->fPage = pte & VMM_MEMMAP_PAGE_MASK; pMemMapEntry->cPages = 1ULL << (MMX86_PAGETABLEMAP_PML_REGION_SIZE[iPML] - 12); *pcMemMap = *pcMemMap + 1; @@ -94,205 +94,49 @@ VOID MmX86_MapInitialize_Index(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MEMMAP_ENTR } } -VOID MmX86_MapCloseObCallback(_In_ PVOID pVmmOb) -{ - PVMMOB_MEMMAP pObMemMap = (PVMMOB_MEMMAP)pVmmOb; - if(pObMemMap->pObDisplay) { - Ob_DECREF(pObMemMap->pObDisplay); - } -} - _Success_(return) -BOOL MmX86_MapInitialize(_In_ PVMM_PROCESS pProcess) +BOOL MmX86_PteMapInitialize(_In_ PVMM_PROCESS pProcess) { PVMMOB_MEM pObPD; DWORD cMemMap = 0; - PVMM_MEMMAP_ENTRY pMemMap = NULL; - PVMMOB_MEMMAP pObMemMap = NULL; + PVMM_MAP_PTEENTRY pMemMap = NULL; + PVMMOB_MAP_PTE pObMap = NULL; // already existing? - if(pProcess && pProcess->pObMemMap) { - return pProcess->pObMemMap->fValid; - } + if(pProcess->Map.pObPte) { TRUE; } EnterCriticalSection(&pProcess->LockUpdate); - if(pProcess && pProcess->pObMemMap) { + if(pProcess->Map.pObPte) { LeaveCriticalSection(&pProcess->LockUpdate); - return pProcess->pObMemMap->fValid; + return TRUE; } // allocate temporary buffer and walk page tables VmmTlbSpider(pProcess); pObPD = VmmTlbGetPageTable(pProcess->paDTB & 0xfffff000, FALSE); if(pObPD) { - pMemMap = (PVMM_MEMMAP_ENTRY)LocalAlloc(LMEM_ZEROINIT, VMM_MEMMAP_ENTRIES_MAX * sizeof(VMM_MEMMAP_ENTRY)); + pMemMap = (PVMM_MAP_PTEENTRY)LocalAlloc(LMEM_ZEROINIT, VMM_MEMMAP_ENTRIES_MAX * sizeof(VMM_MAP_PTEENTRY)); if(pMemMap) { MmX86_MapInitialize_Index(pProcess, pMemMap, &cMemMap, 0, 2, pObPD->pdw, FALSE, ctxMain->dev.paMax); } Ob_DECREF(pObPD); } // allocate VmmOb depending on result - pObMemMap = Ob_Alloc('MM', 0, sizeof(VMMOB_MEMMAP) + cMemMap * sizeof(VMM_MEMMAP_ENTRY), MmX86_MapCloseObCallback, NULL); - if(!pObMemMap) { + pObMap = Ob_Alloc(OB_TAG_MAP_PTE, 0, sizeof(VMMOB_MAP_PTE) + cMemMap * sizeof(VMM_MAP_PTEENTRY), NULL, NULL); + if(!pObMap) { + pProcess->Map.pObPte = Ob_Alloc(OB_TAG_MAP_PTE, LMEM_ZEROINIT, sizeof(VMMOB_MAP_PTE), NULL, NULL); LeaveCriticalSection(&pProcess->LockUpdate); LocalFree(pMemMap); - return FALSE; - } - pObMemMap->fValid = cMemMap > 0; - pObMemMap->fTagModules = FALSE; - pObMemMap->fTagScan = FALSE; - pObMemMap->cMap = cMemMap; - pObMemMap->cbDisplay = cMemMap * MMX86_MEMMAP_DISPLAYBUFFER_LINE_LENGTH; - pObMemMap->pObDisplay = NULL; - if(cMemMap > 0) { - memcpy(pObMemMap->pMap, pMemMap, cMemMap * sizeof(VMM_MEMMAP_ENTRY)); + return TRUE; } + pObMap->wszMultiText = NULL; + pObMap->cbMultiText = 0; + pObMap->fTagScan = FALSE; + pObMap->cMap = cMemMap; + memcpy(pObMap->pMap, pMemMap, cMemMap * sizeof(VMM_MAP_PTEENTRY)); LocalFree(pMemMap); - pProcess->pObMemMap = pObMemMap; + pProcess->Map.pObPte = pObMap; LeaveCriticalSection(&pProcess->LockUpdate); - return pObMemMap->fValid; -} - -/* -* Map a tag into the sorted memory map in O(log2) operations. Supply only one of szTag or wszTag. -* -- pProcess -* -- vaBase -* -- vaLimit = limit == vaBase + size (== top address in range +1) -* -- szTag -* -- wszTag -* -- fWoW64 -* -- fOverwrite -*/ -VOID MmX86_MapTag(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaBase, _In_ QWORD vaLimit, _In_opt_ LPSTR szTag, _In_opt_ LPWSTR wszTag, _In_ BOOL fWoW64, _In_ BOOL fOverwrite) -{ - // NB! update here may take placey without acquiring the process 'LockUpdate' - // Data is not super important so it should be ok. Also, in many cases the - // lock will already be acquired by MapGetEntries function. - PVMM_MEMMAP_ENTRY pMap; - QWORD i, lvl, cMap; - if(!MmX86_MapInitialize(pProcess)) { return; } - if((vaBase > 0xffffffff) || (vaLimit > 0xffffffff)) { return; } - pMap = pProcess->pObMemMap->pMap; - cMap = pProcess->pObMemMap->cMap; - if(!pMap || !cMap) { return; } - // 1: locate base - lvl = 1; - i = cMap >> lvl; - while(TRUE) { - lvl++; - if((cMap >> lvl) == 0) { - break; - } - if(pMap[i].AddrBase > vaBase) { - i -= (cMap >> lvl); - } else { - i += (cMap >> lvl); - } - } - // 2: scan back if needed - while(i && (pMap[i].AddrBase > vaBase)) { - i--; - } - // 3: fill in tag - while((i < cMap) && (pMap[i].AddrBase + (pMap[i].cPages << 12) <= vaLimit)) { - if((pMap[i].AddrBase >= vaBase) && (fOverwrite || !pMap[i].szTag[0])) { - if(wszTag) { - snprintf(pMap[i].szTag, 31, "%S", wszTag); - } - if(szTag) { - snprintf(pMap[i].szTag, 31, "%s", szTag); - } - } - i++; - } -} - -_Success_(return) -BOOL MmX86_MapGetEntries(_In_ PVMM_PROCESS pProcess, _In_ DWORD flags, _Out_ PVMMOB_MEMMAP *ppObMemMap) -{ - DWORD i; - PVMM_MODULEMAP_ENTRY pModule; - PVMMOB_MODULEMAP pObModuleMap; - if(!MmX86_MapInitialize(pProcess)) { return FALSE; } - if((!pProcess->pObMemMap->fTagModules && (flags & VMM_MEMMAP_FLAG_MODULES)) || (!pProcess->pObMemMap->fTagScan && (flags & VMM_MEMMAP_FLAG_SCAN))) { - EnterCriticalSection(&pProcess->LockUpdate); - if(!pProcess->pObMemMap->fTagModules && (flags & VMM_MEMMAP_FLAG_MODULES)) { - pProcess->pObMemMap->fTagModules = TRUE; - if(VmmProc_ModuleMapGet(pProcess, &pObModuleMap)) { - // update memory map with names - for(i = 0; i < pObModuleMap->cMap; i++) { - pModule = pObModuleMap->pMap + i; - MmX86_MapTag(pProcess, pModule->BaseAddress, pModule->BaseAddress + pModule->SizeOfImage, pModule->szName, NULL, FALSE, FALSE); - } - Ob_DECREF(pObModuleMap); - } - } - if(!pProcess->pObMemMap->fTagScan && (flags & VMM_MEMMAP_FLAG_SCAN)) { - pProcess->pObMemMap->fTagScan = TRUE; - VmmProc_ScanTagsMemMap(pProcess); - } - LeaveCriticalSection(&pProcess->LockUpdate); - } - *ppObMemMap = Ob_INCREF(pProcess->pObMemMap); return TRUE; } -_Success_(return) -BOOL MmX86_MapGetDisplay(_In_ PVMM_PROCESS pProcess, _In_ DWORD flags, _Out_ PVMMOB_DATA *ppObDisplay) -{ - DWORD i, o = 0; - PVMMOB_MEMMAP pObMemMap = NULL; - PVMMOB_DATA pObDisplay = NULL; - // memory map display data already exists - if(!MmX86_MapInitialize(pProcess)) { return FALSE; } - if(pProcess->pObMemMap->pObDisplay) { - *ppObDisplay = Ob_INCREF(pProcess->pObMemMap->pObDisplay); - return TRUE; - } - // create new memory map display data - EnterCriticalSection(&pProcess->LockUpdate); - if(!pProcess->pObMemMap->pObDisplay) { - if(MmX86_MapGetEntries(pProcess, flags, &pObMemMap)) { - pObDisplay = Ob_Alloc('MD', LMEM_ZEROINIT, sizeof(VMMOB_DATA) + pObMemMap->cbDisplay, NULL, NULL); - if(pObDisplay) { - for(i = 0; i < pObMemMap->cMap; i++) { - if(o + MMX86_MEMMAP_DISPLAYBUFFER_LINE_LENGTH > pObMemMap->cbDisplay) { - vmmprintf_fn("ERROR: SHOULD NOT HAPPEN! LENGTH DIFFERS #1: %i %i\n", o + MMX86_MEMMAP_DISPLAYBUFFER_LINE_LENGTH, pObMemMap->cbDisplay); - Ob_DECREF(pObDisplay); - pObDisplay = NULL; - goto fail; - } - o += snprintf( - pObDisplay->pbData + o, - pObMemMap->cbDisplay - o, - "%04x %8x %08x-%08x %sr%sx %-32s\n", - i, - (DWORD)pObMemMap->pMap[i].cPages, - (DWORD)pObMemMap->pMap[i].AddrBase, - (DWORD)(pObMemMap->pMap[i].AddrBase + (pObMemMap->pMap[i].cPages << 12) - 1), - pObMemMap->pMap[i].fPage & VMM_MEMMAP_PAGE_NS ? "-" : "s", - pObMemMap->pMap[i].fPage & VMM_MEMMAP_PAGE_W ? "w" : "-", - pObMemMap->pMap[i].szTag - ); - } - if(o != pObMemMap->cbDisplay) { - vmmprintf_fn("ERROR: SHOULD NOT HAPPEN! LENGTH DIFFERS #2: %i %i\n", o, pObMemMap->cbDisplay); - Ob_DECREF(pObDisplay); - pObDisplay = NULL; - goto fail; - } - pObDisplay->pbData[o - 1] = '\n'; - } - } - pProcess->pObMemMap->pObDisplay = pObDisplay; - } -fail: - Ob_DECREF(pObMemMap); - LeaveCriticalSection(&pProcess->LockUpdate); - if(pProcess->pObMemMap->pObDisplay) { - *ppObDisplay = Ob_INCREF(pProcess->pObMemMap->pObDisplay); - return TRUE; - } - return FALSE; -} - _Success_(return) BOOL MmX86_Virt2Phys(_In_ QWORD paPT, _In_ BOOL fUserOnly, _In_ BYTE iPML, _In_ QWORD va, _Out_ PQWORD ppa) { @@ -308,12 +152,8 @@ BOOL MmX86_Virt2Phys(_In_ QWORD paPT, _In_ BOOL fUserOnly, _In_ BYTE iPML, _In_ pte = pObPTEs->pdw[i]; Ob_DECREF(pObPTEs); if(!MMX86_PTE_IS_VALID(pte, iPML)) { - if(pte && MMX86_PTE_IS_TRANSITION(pte, iPML)) { - pte = MMX86_PTE_IS_TRANSITION(pte, iPML); // TRANSITION - } else { - if(iPML == 1) { *ppa = pte; } // NOT VALID - return FALSE; - } + if(iPML == 1) { *ppa = pte; } // NOT VALID + return FALSE; } if(fUserOnly && !(pte & 0x04)) { return FALSE; } // SUPERVISOR PAGE & USER MODE REQ if((iPML == 2) && !(pte & 0x80) /* PS */) { @@ -435,9 +275,7 @@ VOID MmX86_Initialize() ctxVmm->fnMemoryModel.pfnVirt2Phys = MmX86_Virt2Phys; ctxVmm->fnMemoryModel.pfnVirt2PhysGetInformation = MmX86_Virt2PhysGetInformation; ctxVmm->fnMemoryModel.pfnPhys2VirtGetInformation = MmX86_Phys2VirtGetInformation; - ctxVmm->fnMemoryModel.pfnMapTag = MmX86_MapTag; - ctxVmm->fnMemoryModel.pfnMapGetEntries = MmX86_MapGetEntries; - ctxVmm->fnMemoryModel.pfnMapGetDisplay = MmX86_MapGetDisplay; + ctxVmm->fnMemoryModel.pfnPteMapInitialize = MmX86_PteMapInitialize; ctxVmm->fnMemoryModel.pfnTlbSpider = MmX86_TlbSpider; ctxVmm->fnMemoryModel.pfnTlbPageTableVerify = MmX86_TlbPageTableVerify; ctxVmm->tpMemoryModel = VMM_MEMORYMODEL_X86; diff --git a/vmm/mm_x86.h b/vmm/mm_x86.h deleted file mode 100644 index a47962a3..00000000 --- a/vmm/mm_x86.h +++ /dev/null @@ -1,17 +0,0 @@ -// mm_x86.h : definitions related to the x86 32-bit protected mode memory model. -// -// (c) Ulf Frisk, 2018-2019 -// Author: Ulf Frisk, pcileech@frizk.net -// -#ifndef __MM_X86_H__ -#define __MM_X86_H__ -#include "vmm.h" - -/* -* Initialize the X86 32-bit protected mode memory model. -* If a previous memory model exists that memory model is first closed before -* the new X86 memory model is initialized. -*/ -VOID MmX86_Initialize(); - -#endif /* __MM_X86_H__ */ diff --git a/vmm/mm_x86pae.c b/vmm/mm_x86pae.c index 662c805c..58827076 100644 --- a/vmm/mm_x86pae.c +++ b/vmm/mm_x86pae.c @@ -87,13 +87,13 @@ const DWORD MMX86PAE_PAGETABLEMAP_PML_REGION_MASK_PG[4] = { 0, 0xfffff000, 0xffe const DWORD MMX86PAE_PAGETABLEMAP_PML_REGION_MASK_AD[4] = { 0, 0xfff, 0x1fffff, 0 }; -VOID MmX86PAE_MapInitialize_Index(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MEMMAP_ENTRY pMemMap, _In_ PDWORD pcMemMap, _In_ DWORD vaBase, _In_ BYTE iPML, _In_ QWORD PTEs[512], _In_ BOOL fSupervisorPML, _In_ QWORD paMax) +VOID MmX86PAE_MapInitialize_Index(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MAP_PTEENTRY pMemMap, _In_ PDWORD pcMemMap, _In_ DWORD vaBase, _In_ BYTE iPML, _In_ QWORD PTEs[512], _In_ BOOL fSupervisorPML, _In_ QWORD paMax) { PVMMOB_MEM pObNextPT; DWORD i, va; QWORD pte; BOOL fUserOnly, fNextSupervisorPML, fTransition = FALSE; - PVMM_MEMMAP_ENTRY pMemMapEntry = pMemMap + *pcMemMap - 1; + PVMM_MAP_PTEENTRY pMemMapEntry = pMemMap + *pcMemMap - 1; fUserOnly = pProcess->fUserOnly; for(i = 0; i < 512; i++) { if((iPML == 3) && (i > 3)) { break; } // MAX 4 ENTRIES IN PDPT @@ -120,10 +120,10 @@ VOID MmX86PAE_MapInitialize_Index(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MEMMAP_E if((iPML == 1) || (pte & 0x80) /* PS */) { if((*pcMemMap == 0) || (pMemMapEntry->fPage != (pte & VMM_MEMMAP_PAGE_MASK)) || - ((va != pMemMapEntry->AddrBase + (pMemMapEntry->cPages << 12))) && !fTransition) { + ((va != pMemMapEntry->vaBase + (pMemMapEntry->cPages << 12))) && !fTransition) { if(*pcMemMap + 1 >= VMM_MEMMAP_ENTRIES_MAX) { return; } pMemMapEntry = pMemMap + *pcMemMap; - pMemMapEntry->AddrBase = va; + pMemMapEntry->vaBase = va; pMemMapEntry->fPage = pte & VMM_MEMMAP_PAGE_MASK; pMemMapEntry->cPages = 1ULL << (MMX86PAE_PAGETABLEMAP_PML_REGION_SIZE[iPML] - 12); *pcMemMap = *pcMemMap + 1; @@ -144,36 +144,26 @@ VOID MmX86PAE_MapInitialize_Index(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MEMMAP_E } } -VOID MmX86PAE_MapCloseObCallback(_In_ PVOID pVmmOb) -{ - PVMMOB_MEMMAP pObMemMap = (PVMMOB_MEMMAP)pVmmOb; - if(pObMemMap->pObDisplay) { - Ob_DECREF(pObMemMap->pObDisplay); - } -} - _Success_(return) -BOOL MmX86PAE_MapInitialize(_In_ PVMM_PROCESS pProcess) +BOOL MmX86PAE_PteMapInitialize(_In_ PVMM_PROCESS pProcess) { DWORD cMemMap = 0; PBYTE pbPDPT; PVMMOB_MEM pObPDPT; - PVMM_MEMMAP_ENTRY pMemMap = NULL; - PVMMOB_MEMMAP pObMemMap = NULL; + PVMM_MAP_PTEENTRY pMemMap = NULL; + PVMMOB_MAP_PTE pObMap = NULL; // already existing? - if(pProcess && pProcess->pObMemMap) { - return pProcess->pObMemMap->fValid; - } + if(pProcess->Map.pObPte) { return TRUE; } EnterCriticalSection(&pProcess->LockUpdate); - if(pProcess && pProcess->pObMemMap) { + if(pProcess->Map.pObPte) { LeaveCriticalSection(&pProcess->LockUpdate); - return pProcess->pObMemMap->fValid; + return TRUE; } // allocate temporary buffer and walk page tables VmmTlbSpider(pProcess); pObPDPT = VmmTlbGetPageTable(pProcess->paDTB & 0xfffff000, FALSE); if(pObPDPT) { - pMemMap = (PVMM_MEMMAP_ENTRY)LocalAlloc(LMEM_ZEROINIT, VMM_MEMMAP_ENTRIES_MAX * sizeof(VMM_MEMMAP_ENTRY)); + pMemMap = (PVMM_MAP_PTEENTRY)LocalAlloc(LMEM_ZEROINIT, VMM_MEMMAP_ENTRIES_MAX * sizeof(VMM_MAP_PTEENTRY)); if(pMemMap) { pbPDPT = pObPDPT->pb + (pProcess->paDTB & 0xfe0); // ADJUST PDPT TO 32-BYTE BOUNDARY MmX86PAE_MapInitialize_Index(pProcess, pMemMap, &cMemMap, 0, 3, (PQWORD)pbPDPT, FALSE, ctxMain->dev.paMax); @@ -181,171 +171,24 @@ BOOL MmX86PAE_MapInitialize(_In_ PVMM_PROCESS pProcess) Ob_DECREF(pObPDPT); } // allocate VmmOb depending on result - pObMemMap = Ob_Alloc('MM', 0, sizeof(VMMOB_MEMMAP) + cMemMap * sizeof(VMM_MEMMAP_ENTRY), MmX86PAE_MapCloseObCallback, NULL); - if(!pObMemMap) { + pObMap = Ob_Alloc(OB_TAG_MAP_PTE, 0, sizeof(VMMOB_MAP_PTE) + cMemMap * sizeof(VMM_MAP_PTEENTRY), NULL, NULL); + if(!pObMap) { + pProcess->Map.pObPte = Ob_Alloc(OB_TAG_MAP_PTE, LMEM_ZEROINIT, sizeof(VMMOB_MAP_PTE), NULL, NULL); LeaveCriticalSection(&pProcess->LockUpdate); LocalFree(pMemMap); - return FALSE; - } - pObMemMap->fValid = cMemMap > 0; - pObMemMap->fTagModules = FALSE; - pObMemMap->fTagScan = FALSE; - pObMemMap->cMap = cMemMap; - pObMemMap->cbDisplay = cMemMap * MMX86PAE_MEMMAP_DISPLAYBUFFER_LINE_LENGTH; - pObMemMap->pObDisplay = NULL; - if(cMemMap > 0) { - memcpy(pObMemMap->pMap, pMemMap, cMemMap * sizeof(VMM_MEMMAP_ENTRY)); + return TRUE; } + pObMap->wszMultiText = NULL; + pObMap->cbMultiText = 0; + pObMap->fTagScan = FALSE; + pObMap->cMap = cMemMap; + memcpy(pObMap->pMap, pMemMap, cMemMap * sizeof(VMM_MAP_PTEENTRY)); LocalFree(pMemMap); - pProcess->pObMemMap = pObMemMap; + pProcess->Map.pObPte = pObMap; LeaveCriticalSection(&pProcess->LockUpdate); - return pObMemMap->fValid; -} - -/* -* Map a tag into the sorted memory map in O(log2) operations. Supply only one of szTag or wszTag. -* -- pProcess -* -- vaBase -* -- vaLimit = limit == vaBase + size (== top address in range +1) -* -- szTag -* -- wszTag -* -- fWoW64 -* -- fOverwrite -*/ -VOID MmX86PAE_MapTag(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaBase, _In_ QWORD vaLimit, _In_opt_ LPSTR szTag, _In_opt_ LPWSTR wszTag, _In_ BOOL fWoW64, _In_ BOOL fOverwrite) -{ - // NB! update here may take placey without acquiring the process 'LockUpdate' - // Data is not super important so it should be ok. Also, in many cases the - // lock will already be acquired by MapGetEntries function. - PVMM_MEMMAP_ENTRY pMap; - QWORD i, lvl, cMap; - if(!MmX86PAE_MapInitialize(pProcess)) { return; } - if((vaBase > 0xffffffff) || (vaLimit > 0xffffffff)) { return; } - pMap = pProcess->pObMemMap->pMap; - cMap = pProcess->pObMemMap->cMap; - if(!pMap || !cMap) { return; } - // 1: locate base - lvl = 1; - i = cMap >> lvl; - while(TRUE) { - lvl++; - if((cMap >> lvl) == 0) { - break; - } - if(pMap[i].AddrBase > vaBase) { - i -= (cMap >> lvl); - } else { - i += (cMap >> lvl); - } - } - // 2: scan back if needed - while(i && (pMap[i].AddrBase > vaBase)) { - i--; - } - // 3: fill in tag - while((i < cMap) && (pMap[i].AddrBase + (pMap[i].cPages << 12) <= vaLimit)) { - if((pMap[i].AddrBase >= vaBase) && (fOverwrite || !pMap[i].szTag[0])) { - if(wszTag) { - snprintf(pMap[i].szTag, 31, "%S", wszTag); - } - if(szTag) { - snprintf(pMap[i].szTag, 31, "%s", szTag); - } - } - i++; - } -} - -_Success_(return) -BOOL MmX86PAE_MapGetEntries(_In_ PVMM_PROCESS pProcess, _In_ DWORD flags, _Out_ PVMMOB_MEMMAP *ppObMemMap) -{ - DWORD i; - PVMM_MODULEMAP_ENTRY pModule; - PVMMOB_MODULEMAP pObModuleMap; - if(!MmX86PAE_MapInitialize(pProcess)) { return FALSE; } - if((!pProcess->pObMemMap->fTagModules && (flags & VMM_MEMMAP_FLAG_MODULES)) || (!pProcess->pObMemMap->fTagScan && (flags & VMM_MEMMAP_FLAG_SCAN))) { - EnterCriticalSection(&pProcess->LockUpdate); - if(!pProcess->pObMemMap->fTagModules && (flags & VMM_MEMMAP_FLAG_MODULES)) { - pProcess->pObMemMap->fTagModules = TRUE; - if(VmmProc_ModuleMapGet(pProcess, &pObModuleMap)) { - // update memory map with names - for(i = 0; i < pObModuleMap->cMap; i++) { - pModule = pObModuleMap->pMap + i; - MmX86PAE_MapTag(pProcess, pModule->BaseAddress, pModule->BaseAddress + pModule->SizeOfImage, pModule->szName, NULL, FALSE, FALSE); - } - Ob_DECREF(pObModuleMap); - } - } - if(!pProcess->pObMemMap->fTagScan && (flags & VMM_MEMMAP_FLAG_SCAN)) { - pProcess->pObMemMap->fTagScan = TRUE; - VmmProc_ScanTagsMemMap(pProcess); - } - LeaveCriticalSection(&pProcess->LockUpdate); - } - *ppObMemMap = Ob_INCREF(pProcess->pObMemMap); return TRUE; } -_Success_(return) -BOOL MmX86PAE_MapGetDisplay(_In_ PVMM_PROCESS pProcess, _In_ DWORD flags, _Out_ PVMMOB_DATA *ppObDisplay) -{ - DWORD i, o = 0; - PVMMOB_MEMMAP pObMemMap = NULL; - PVMMOB_DATA pObDisplay = NULL; - // memory map display data already exists - if(!MmX86PAE_MapInitialize(pProcess)) { return FALSE; } - if(pProcess->pObMemMap->pObDisplay) { - *ppObDisplay = Ob_INCREF(pProcess->pObMemMap->pObDisplay); - return TRUE; - } - // create new memory map display data - EnterCriticalSection(&pProcess->LockUpdate); - if(!pProcess->pObMemMap->pObDisplay) { - if(MmX86PAE_MapGetEntries(pProcess, flags, &pObMemMap)) { - pObDisplay = Ob_Alloc('MD', LMEM_ZEROINIT, sizeof(VMMOB_DATA) + pObMemMap->cbDisplay, NULL, NULL); - if(pObDisplay) { - for(i = 0; i < pObMemMap->cMap; i++) { - if(o + MMX86PAE_MEMMAP_DISPLAYBUFFER_LINE_LENGTH > pObMemMap->cbDisplay) { - vmmprintf_fn("ERROR: SHOULD NOT HAPPEN! LENGTH DIFFERS #1: %i %i\n", o + MMX86PAE_MEMMAP_DISPLAYBUFFER_LINE_LENGTH, pObMemMap->cbDisplay); - Ob_DECREF(pObDisplay); - pObDisplay = NULL; - goto fail; - } - o += snprintf( - pObDisplay->pbData + o, - pObMemMap->cbDisplay - o, - "%04x %8x %08x-%08x %sr%s%s %-32s\n", - i, - (DWORD)pObMemMap->pMap[i].cPages, - (DWORD)pObMemMap->pMap[i].AddrBase, - (DWORD)(pObMemMap->pMap[i].AddrBase + (pObMemMap->pMap[i].cPages << 12) - 1), - pObMemMap->pMap[i].fPage & VMM_MEMMAP_PAGE_NS ? "-" : "s", - pObMemMap->pMap[i].fPage & VMM_MEMMAP_PAGE_W ? "w" : "-", - pObMemMap->pMap[i].fPage & VMM_MEMMAP_PAGE_NX ? "-" : "x", - pObMemMap->pMap[i].szTag - ); - } - if(o != pObMemMap->cbDisplay) { - vmmprintf_fn("ERROR: SHOULD NOT HAPPEN! LENGTH DIFFERS #2: %i %i\n", o, pObMemMap->cbDisplay); - Ob_DECREF(pObDisplay); - pObDisplay = NULL; - goto fail; - } - pObDisplay->pbData[o - 1] = '\n'; - } - } - pProcess->pObMemMap->pObDisplay = pObDisplay; - } -fail: - Ob_DECREF(pObMemMap); - LeaveCriticalSection(&pProcess->LockUpdate); - if(pProcess->pObMemMap->pObDisplay) { - *ppObDisplay = Ob_INCREF(pProcess->pObMemMap->pObDisplay); - return TRUE; - } - return FALSE; -} - _Success_(return) BOOL MmX86PAE_Virt2Phys(_In_ QWORD paPT, _In_ BOOL fUserOnly, _In_ BYTE iPML, _In_ QWORD va, _Out_ PQWORD ppa) { @@ -374,12 +217,8 @@ BOOL MmX86PAE_Virt2Phys(_In_ QWORD paPT, _In_ BOOL fUserOnly, _In_ BYTE iPML, _I pte = pObPTEs->pqw[i]; Ob_DECREF(pObPTEs); if(!MMX86PAE_PTE_IS_VALID(pte, iPML)) { - if(pte && MMX86PAE_PTE_IS_TRANSITION(pte, iPML)) { - pte = MMX86PAE_PTE_IS_TRANSITION(pte, iPML); // TRANSITION - } else { - if(iPML == 1) { *ppa = pte; } // NOT VALID - return FALSE; - } + if(iPML == 1) { *ppa = pte; } // NOT VALID + return FALSE; } if(fUserOnly && !(pte & 0x04)) { return FALSE; } // SUPERVISOR PAGE & USER MODE REQ if(pte & 0x000f000000000000) { return FALSE; } // RESERVED @@ -511,10 +350,7 @@ VOID MmX86PAE_Initialize() ctxVmm->fnMemoryModel.pfnVirt2Phys = MmX86PAE_Virt2Phys; ctxVmm->fnMemoryModel.pfnVirt2PhysGetInformation = MmX86PAE_Virt2PhysGetInformation; ctxVmm->fnMemoryModel.pfnPhys2VirtGetInformation = MmX86PAE_Phys2VirtGetInformation; - ctxVmm->fnMemoryModel.pfnMapInitialize = MmX86PAE_MapInitialize; - ctxVmm->fnMemoryModel.pfnMapTag = MmX86PAE_MapTag; - ctxVmm->fnMemoryModel.pfnMapGetEntries = MmX86PAE_MapGetEntries; - ctxVmm->fnMemoryModel.pfnMapGetDisplay = MmX86PAE_MapGetDisplay; + ctxVmm->fnMemoryModel.pfnPteMapInitialize = MmX86PAE_PteMapInitialize; ctxVmm->fnMemoryModel.pfnTlbSpider = MmX86PAE_TlbSpider; ctxVmm->fnMemoryModel.pfnTlbPageTableVerify = MmX86PAE_TlbPageTableVerify; ctxVmm->tpMemoryModel = VMM_MEMORYMODEL_X86PAE; diff --git a/vmm/mm_x86pae.h b/vmm/mm_x86pae.h deleted file mode 100644 index 5d0dc16c..00000000 --- a/vmm/mm_x86pae.h +++ /dev/null @@ -1,17 +0,0 @@ -// mm_x86pae.h : definitions related to the x86 PAE (Physical Address Extension) 32-bit protected mode memory model. -// -// (c) Ulf Frisk, 2018-2019 -// Author: Ulf Frisk, pcileech@frizk.net -// -#ifndef __MM_X86PAE_H__ -#define __MM_X86PAE_H__ -#include "vmm.h" - -/* -* Initialize the X86 PAE 32-bit protected mode memory model. -* If a previous memory model exists that memory model is first closed before -* the new X86 PAE memory model is initialized. -*/ -VOID MmX86PAE_Initialize(); - -#endif /* __MM_X86PAE_H__ */ diff --git a/vmm/ob.h b/vmm/ob.h index 5b3b60e3..441d00e2 100644 --- a/vmm/ob.h +++ b/vmm/ob.h @@ -11,16 +11,23 @@ typedef unsigned __int64 QWORD, *PQWORD; #define OB_DEBUG #define OB_HEADER_MAGIC 0x0c0efefe -#define OB_TAG_CORE_CONTAINER 'OC' -#define OB_TAG_CORE_VSET 'OS' -#define OB_TAG_CORE_MAP 'OM' -#define OB_TAG_PDB_ENTRY 'DE' -#define OB_TAG_REG_HIVE 'RH' -#define OB_TAG_REG_KEY 'RK' -#define OB_TAG_REG_KEYVALUE 'RV' -#define OB_TAG_VMM_PROCESS 'PR' -#define OB_TAG_VMM_PROCESSTABLE 'PT' -#define OB_TAG_VMMVFS_DUMPCONTEXT 'CD' +#define OB_TAG_CORE_CONTAINER 'ObCo' +#define OB_TAG_CORE_DATA 'ObDA' +#define OB_TAG_CORE_VSET 'ObVS' +#define OB_TAG_CORE_MAP 'ObMA' +#define OB_TAG_MAP_PTE 'PteM' +#define OB_TAG_MAP_VAD 'VadM' +#define OB_TAG_MAP_MODULE 'ModM' +#define OB_TAG_MAP_THREAD 'ThrM' +#define OB_TAG_MAP_HANDLE 'HndM' +#define OB_TAG_PDB_ENTRY 'PdbE' +#define OB_TAG_REG_HIVE 'RegH' +#define OB_TAG_REG_KEY 'RegK' +#define OB_TAG_REG_KEYVALUE 'RegV' +#define OB_TAG_VMM_PROCESS 'Ps__' +#define OB_TAG_VMM_PROCESS_PERSISTENT 'PsSt' +#define OB_TAG_VMM_PROCESSTABLE 'PsTb' +#define OB_TAG_VMMVFS_DUMPCONTEXT 'CDmp' // ---------------------------------------------------------------------------- // OBJECT MANAGER CORE FUNCTIONALITY BELOW: @@ -43,10 +50,9 @@ typedef unsigned __int64 QWORD, *PQWORD; typedef struct tdOB { // internal object manager functionality below: (= do not use unless absolutely necessary) DWORD _magic; // magic value - OB_HEADER_MAGIC - WORD _Free; union { - WORD _tag; // tag - 2 chars, no null terminator - CHAR _tagCh[2]; + DWORD _tag; // tag - 2 chars, no null terminator + CHAR _tagCh[4]; }; VOID(*_pfnRef_0)(_In_ PVOID pOb); // callback - object specific cleanup before free VOID(*_pfnRef_1)(_In_ PVOID pOb); // callback - when object reach refcount 1 (not initial) @@ -66,7 +72,7 @@ typedef struct tdOB { * -- pfnRef_1 = optional callback for when object reach refcount = 1 at DECREF. * -- return = allocated object on success, with refcount = 1, - NULL on fail. */ -PVOID Ob_Alloc(_In_ WORD tag, _In_ UINT uFlags, _In_ SIZE_T uBytes, _In_opt_ VOID(*pfnRef_0)(_In_ PVOID pOb), _In_opt_ VOID(*pfnRef_1)(_In_ PVOID pOb)); +PVOID Ob_Alloc(_In_ DWORD tag, _In_ UINT uFlags, _In_ SIZE_T uBytes, _In_opt_ VOID(*pfnRef_0)(_In_ PVOID pOb), _In_opt_ VOID(*pfnRef_1)(_In_ PVOID pOb)); /* * Increase the reference count of a object by one. @@ -103,7 +109,23 @@ inline VOID Ob_DECREF_NULL(_In_opt_ PVOID *ppOb) * -- tag * -- return */ -BOOL Ob_VALID_TAG(_In_ PVOID pObIn, _In_ WORD tag); +BOOL Ob_VALID_TAG(_In_ PVOID pObIn, _In_ DWORD tag); + + + +// ---------------------------------------------------------------------------- +// OBJECT MANAGER COMMON/GENERIC OBJECTS BELOW: +// +// ---------------------------------------------------------------------------- + +typedef struct tdOB_DATA { + OB ObHdr; + union { + BYTE pb[]; + DWORD pdw[]; + QWORD pqw[]; + }; +} OB_DATA, * POB_DATA; @@ -334,8 +356,19 @@ BOOL ObMap_Push(_In_opt_ POB_MAP pm, _In_ QWORD qwKey, _In_ PVOID pvObject); * -- pm * -- return = success: object, fail: NULL. */ +_Success_(return != NULL) PVOID ObMap_Pop(_In_opt_ POB_MAP pm); +/* +* Remove the "last" object and return it and its key. +* CALLER DECREF(if OB): return +* -- pm +* -- pKey +* -- return = success: object, fail: NULL. +*/ +_Success_(return != NULL) +PVOID ObMap_PopWithKey(_In_opt_ POB_MAP pm, _Out_ PQWORD pKey); + /* * Remove an object from the ObMap. * NB! must not be called simultaneously while iterating with ObMap_GetByIndex/ObMap_GetNext. @@ -431,6 +464,16 @@ PVOID ObMap_GetByKey(_In_opt_ POB_MAP pm, _In_ QWORD qwKey); */ PVOID ObMap_GetByIndex(_In_opt_ POB_MAP pm, _In_ DWORD index); +/* +* Return all keys in the map in a POB_DATA object consisting of a QWORD array. +* Item 0 contains the number of items in the array (not including the 0th item) +* CALLER DECREF: return +* -- pm +* -- return = POB_DATA consisting of QWORD array, NULL if map is empty. +*/ +_Success_(return != NULL) +POB_DATA ObMap_GetTableKeys(_In_opt_ POB_MAP pm); + #endif /* __OB_H__ */ diff --git a/vmm/ob_container.c b/vmm/ob_container.c index c12617f6..494a9ecf 100644 --- a/vmm/ob_container.c +++ b/vmm/ob_container.c @@ -70,9 +70,11 @@ PVOID ObContainer_GetOb(_In_ POB_CONTAINER pObContainer) */ VOID ObContainer_SetOb(_In_ POB_CONTAINER pObContainer, _In_opt_ PVOID pOb) { + POB pObOld; if(!OB_CONTAINER_IS_VALID(pObContainer)) { return; } EnterCriticalSection(&pObContainer->Lock); - Ob_DECREF(pObContainer->pOb); + pObOld = pObContainer->pOb; pObContainer->pOb = Ob_INCREF(pOb); LeaveCriticalSection(&pObContainer->Lock); + Ob_DECREF(pObOld); } diff --git a/vmm/ob_core.c b/vmm/ob_core.c index 82981a0c..185419da 100644 --- a/vmm/ob_core.c +++ b/vmm/ob_core.c @@ -34,7 +34,7 @@ * -- pfnRef_1 = optional callback for when object reach refcount = 1 (excl. initial). * -- return = allocated object on success, with refcount = 1, - NULL on fail. */ -PVOID Ob_Alloc(_In_ WORD tag, _In_ UINT uFlags, _In_ SIZE_T uBytes, _In_opt_ VOID(*pfnRef_0)(_In_ PVOID pOb), _In_opt_ VOID(*pfnRef_1)(_In_ PVOID pOb)) +PVOID Ob_Alloc(_In_ DWORD tag, _In_ UINT uFlags, _In_ SIZE_T uBytes, _In_opt_ VOID(*pfnRef_0)(_In_ PVOID pOb), _In_opt_ VOID(*pfnRef_1)(_In_ PVOID pOb)) { POB pOb; if((uBytes > 0x40000000) || (uBytes < sizeof(OB))) { return NULL; } @@ -115,7 +115,7 @@ VOID Ob_DECREF(_In_opt_ PVOID pObIn) * -- tag * -- return */ -BOOL Ob_VALID_TAG(_In_ PVOID pObIn, _In_ WORD tag) +BOOL Ob_VALID_TAG(_In_ PVOID pObIn, _In_ DWORD tag) { POB pOb = (POB)pObIn; return pOb && (pOb->_magic == OB_HEADER_MAGIC) && (pOb->_tag = tag); diff --git a/vmm/ob_map.c b/vmm/ob_map.c index 0fca83ae..6c7f105d 100644 --- a/vmm/ob_map.c +++ b/vmm/ob_map.c @@ -82,14 +82,15 @@ VOID _ObMap_ObFreeAllObjects(_In_ POB_MAP pObMap) { DWORD i; POB_MAP_ENTRY pe; - if(pObMap->fObjectsOb || pObMap->fObjectsLocalFree) { + if(pObMap->fObjectsOb) { for(i = 1; i < pObMap->c; i++) { pe = &pObMap->Directory[OB_MAP_INDEX_DIRECTORY(i)][OB_MAP_INDEX_TABLE(i)][OB_MAP_INDEX_STORE(i)]; - if(pObMap->fObjectsOb) { - Ob_DECREF(pe->v); - } else if(pObMap->fObjectsLocalFree) { - LocalFree(pe->v); - } + Ob_DECREF(pe->v); + } + } else if(pObMap->fObjectsLocalFree) { + for(i = 1; i < pObMap->c; i++) { + pe = &pObMap->Directory[OB_MAP_INDEX_DIRECTORY(i)][OB_MAP_INDEX_TABLE(i)][OB_MAP_INDEX_STORE(i)]; + LocalFree(pe->v); } } } @@ -118,27 +119,6 @@ VOID _ObMap_ObCloseCallback(_In_ POB_MAP pObMap) } } -/* -* Create a new hashed value set. A hashed value set (ObVSet) provides atomic -* ways to store unique 64-bit (or smaller) numbers as a set. -* The ObVSet is an object manager object and must be DECREF'ed when required. -* CALLER DECREF: return -* -- return -*/ -/* -POB_VSET ObVSet_New() -{ - POB_VSET pObVSet = Ob_Alloc('VS', LMEM_ZEROINIT, sizeof(OB_VSET) - sizeof(OB), _ObVSet_ObCloseCallback, NULL); - if(!pObVSet) { return NULL; } - InitializeSRWLock(&pObVSet->LockSRW); - pObVSet->c = 1; // item zero is reserved - hence the initialization of count to 1 - pObVSet->cHashMax = 0x400; - pObVSet->cHashGrowThreshold = 0x300; - pObVSet->pTable0[0].pValues = pObVSet->pStore00; - return pObVSet; -} -*/ - inline POB_MAP_ENTRY _ObMap_GetFromIndex(_In_ POB_MAP pm, _In_ DWORD iEntry) { if(!iEntry || (iEntry >= pm->c)) { return NULL; } @@ -151,20 +131,6 @@ inline QWORD _ObMap_GetFromEntryIndex(_In_ POB_MAP pm, _In_ BOOL fValueHash, _In return pe ? (fValueHash ? (QWORD)pe->v : pe->k) : 0; } -/*inline VOID _ObMap_SetFromEntryIndex(_In_ POB_MAP pm, _In_ DWORD iEntry, _In_ QWORD qwKey, _In_ PVOID pvObject) -{ - POB_MAP_ENTRY pe = _ObMap_GetFromIndex(pm, iEntry); - if(pe) { - pe->k = qwKey; - pe->v = pvObject; - } -}*/ - -/*inline DWORD _ObMap_GetEntryIndexFromHashIndex(_In_ POB_MAP pm, _In_ BOOL fValueHash, _In_ DWORD iHash) -{ - return fValueHash ? pm->pHashMapValue[iHash] : pm->pHashMapKey[iHash]; -}*/ - inline VOID _ObMap_SetHashIndex(_In_ POB_MAP pm, _In_ BOOL fValueHash, _In_ DWORD iHash, _In_ DWORD iEntry) { if(fValueHash) { @@ -312,6 +278,20 @@ PVOID _ObMap_GetNextByKey(_In_ POB_MAP pm, _In_ QWORD qwKey, _In_opt_ PVOID pvOb return _ObMap_GetByEntryIndex(pm, iEntry + 1); } +// NB: CALLER LOCALFREE: return +_Success_(return != NULL) +POB_DATA _ObMap_GetTableKeys(_In_ POB_MAP pm) +{ + QWORD iEntry; + POB_DATA pObData; + if((pm->c <= 1) || !(pObData = Ob_Alloc(OB_TAG_CORE_DATA, 0, sizeof(OB) + pm->c * sizeof(QWORD), NULL, NULL))) { return NULL; } + for(iEntry = 1; iEntry < pm->c; iEntry++) { + pObData->pqw[iEntry] = pm->Directory[OB_MAP_INDEX_DIRECTORY(iEntry)][OB_MAP_INDEX_TABLE(iEntry)][OB_MAP_INDEX_STORE(iEntry)].k; + } + pObData->pqw[0] = pm->c - 1; + return pObData; +} + /* * Retrieve an object given an index (which is less than the amount of items * in the ObMap). @@ -397,6 +377,19 @@ QWORD ObMap_PeekKey(_In_opt_ POB_MAP pm) OB_MAP_CALL_SYNCHRONIZED_IMPLEMENTATION_READ(pm, QWORD, 0, _ObMap_GetFromEntryIndex(pm, FALSE, pm->c - 1)) } +/* +* Return all keys in the map in a POB_DATA object consisting of a QWORD array. +* Item 0 contains the number of items in the array (not including the 0th item) +* CALLER DECREF: return +* -- pm +* -- return = POB_DATA consisting of QWORD array, NULL if map is empty. +*/ +_Success_(return != NULL) +POB_DATA ObMap_GetTableKeys(_In_opt_ POB_MAP pm) +{ + OB_MAP_CALL_SYNCHRONIZED_IMPLEMENTATION_READ(pm, POB_DATA, NULL, _ObMap_GetTableKeys(pm)); +} + //----------------------------------------------------------------------------- @@ -407,7 +400,8 @@ QWORD ObMap_PeekKey(_In_opt_ POB_MAP pm) /* * CALLER DECREF: return */ -PVOID _ObMap_RetrieveAndRemoveByEntryIndex(_In_ POB_MAP pm, _In_ DWORD iEntry) +_Success_(return != NULL) +PVOID _ObMap_RetrieveAndRemoveByEntryIndex(_In_ POB_MAP pm, _In_ DWORD iEntry, _Out_opt_ PQWORD pKey) { POB_MAP_ENTRY pRemoveEntry, pLastEntry; QWORD qwRemoveKey, qwRemoveValue; @@ -427,6 +421,7 @@ PVOID _ObMap_RetrieveAndRemoveByEntryIndex(_In_ POB_MAP pm, _In_ DWORD iEntry) _ObMap_InsertHash(pm, TRUE, iEntry); } pm->c--; + if(pKey) { *pKey = qwRemoveKey; } return (PVOID)qwRemoveValue; } @@ -435,7 +430,7 @@ PVOID _ObMap_RemoveOrRemoveByKey(_In_ POB_MAP pm, _In_ BOOL fValueHash, _In_ QWO DWORD iEntry; if(fValueHash && !kv) { return NULL; } if(!_ObMap_GetEntryIndexFromKeyOrValue(pm, fValueHash, kv, &iEntry)) { return NULL; } - return _ObMap_RetrieveAndRemoveByEntryIndex(pm, iEntry); + return _ObMap_RetrieveAndRemoveByEntryIndex(pm, iEntry, NULL); } /* @@ -444,9 +439,23 @@ PVOID _ObMap_RemoveOrRemoveByKey(_In_ POB_MAP pm, _In_ BOOL fValueHash, _In_ QWO * -- pm * -- return = success: object, fail: NULL. */ +_Success_(return != NULL) PVOID ObMap_Pop(_In_opt_ POB_MAP pm) { - OB_MAP_CALL_SYNCHRONIZED_IMPLEMENTATION_WRITE(pm, PVOID, NULL, _ObMap_RetrieveAndRemoveByEntryIndex(pm, pm->c - 1)) + OB_MAP_CALL_SYNCHRONIZED_IMPLEMENTATION_WRITE(pm, PVOID, NULL, _ObMap_RetrieveAndRemoveByEntryIndex(pm, pm->c - 1, NULL)) +} + +/* +* Remove the "last" object and return it and its key. +* CALLER DECREF(if OB): return +* -- pm +* -- pKey +* -- return = success: object, fail: NULL. +*/ +_Success_(return != NULL) +PVOID ObMap_PopWithKey(_In_opt_ POB_MAP pm, _Out_ PQWORD pKey) +{ + OB_MAP_CALL_SYNCHRONIZED_IMPLEMENTATION_WRITE(pm, PVOID, NULL, _ObMap_RetrieveAndRemoveByEntryIndex(pm, pm->c - 1, pKey)) } /* diff --git a/vmm/pdb.c b/vmm/pdb.c index efd5c157..79038e92 100644 --- a/vmm/pdb.c +++ b/vmm/pdb.c @@ -74,6 +74,12 @@ typedef struct tdVMMWIN_PDB_CONTEXT { }; } VMMWIN_PDB_CONTEXT, *PVMMWIN_PDB_CONTEXT; +typedef struct tdVMMWIN_PDB_INITIALIZE_KERNEL_PARAMETERS { + PHANDLE phEventThreadStarted; + BOOL fPdbInfo; + IMAGE_DEBUG_TYPE_CODEVIEW_PDBINFO PdbInfo; +} VMMWIN_PDB_INITIALIZE_KERNEL_PARAMETERS, *PVMMWIN_PDB_INITIALIZE_KERNEL_PARAMETERS; + QWORD PDB_HashPdb(_In_ LPSTR szPdbName, _In_reads_(16) PBYTE pbPdbGUID, _In_ DWORD dwPdbAge) { QWORD qwHash = 0; @@ -374,6 +380,15 @@ BOOL PDB_GetTypeChildOffset(_In_opt_ VMMWIN_PDB_HANDLE hPDB, _In_ LPSTR szTypeNa return fResult; } +_Success_(return) +BOOL PDB_GetTypeChildOffsetShort(_In_opt_ VMMWIN_PDB_HANDLE hPDB, _In_ LPSTR szTypeName, _In_ LPWSTR wszTypeChildName, _Out_ PWORD pwTypeOffset) +{ + DWORD dwTypeOffset; + if(!PDB_GetTypeChildOffset(hPDB, szTypeName, wszTypeChildName, &dwTypeOffset) || (dwTypeOffset > 0xffff)) { return FALSE; } + if(pwTypeOffset) { *pwTypeOffset = (WORD)dwTypeOffset; } + return TRUE; +} + //----------------------------------------------------------------------------- @@ -398,6 +413,47 @@ VOID PDB_Close() Ob_DECREF(ctx->pmPdbByModule); if(ctx->hModuleDbgHelp) { FreeLibrary(ctx->hModuleDbgHelp); } if(ctx->hModuleSymSrv) { FreeLibrary(ctx->hModuleSymSrv); } + ZeroMemory(ctx, sizeof(VMMWIN_PDB_CONTEXT)); + ctxMain->pdb.fInitialized = FALSE; +} + +/* +* +*/ +_Success_(return) +BOOL PDB_Initialize_Async_Kernel_ScanForPdbInfo(_In_ PVMM_PROCESS pSystemProcess, _Out_writes_(MAX_PATH) LPSTR szPdbName, _Out_writes_(16) PBYTE pbGUID, _Out_ PDWORD pdwAge) +{ + PBYTE pb = NULL; + DWORD i, cbRead; + PIMAGE_DEBUG_TYPE_CODEVIEW_PDBINFO pPdb; + if(!ctxVmm->kernel.vaBase) { return FALSE; } + if(!(pb = LocalAlloc(0, 0x00800000))) { return FALSE; } + VmmReadEx(pSystemProcess, ctxVmm->kernel.vaBase, pb, 0x00800000, &cbRead, VMM_FLAG_ZEROPAD_ON_FAIL); + // 1: search for pdb debug information adn extract offset of PsInitialSystemProcess + for(i = 0; i < 0x00800000 - sizeof(IMAGE_DEBUG_TYPE_CODEVIEW_PDBINFO); i += 4) { + pPdb = (PIMAGE_DEBUG_TYPE_CODEVIEW_PDBINFO)(pb + i); + if(pPdb->Signature == 0x53445352) { + if(pPdb->Age > 0x20) { continue; } + if(memcmp("nt", pPdb->PdbFileName, 2)) { continue; } + if(memcmp(".pdb", pPdb->PdbFileName + 8, 5)) { continue; } + *pdwAge = pPdb->Age; + memcpy(pbGUID, pPdb->Guid, 16); + strncpy_s(szPdbName, MAX_PATH, pPdb->PdbFileName, sizeof(pPdb->PdbFileName)); + LocalFree(pb); + return TRUE; + } + } + LocalFree(pb); + return FALSE; +} + +VOID PDB_Initialize_WaitComplete() +{ + PVMMWIN_PDB_CONTEXT ctx = (PVMMWIN_PDB_CONTEXT)ctxVmm->pPdbContext; + if(ctxMain->pdb.fEnable) { + EnterCriticalSection(&ctx->Lock); + LeaveCriticalSection(&ctx->Lock); + } } /* @@ -409,21 +465,26 @@ VOID PDB_Close() * -- hEventThreadStart * -- return */ -DWORD PDB_Initialize_Async_Kernel(LPVOID hEventThreadStart) +DWORD PDB_Initialize_Async_Kernel(LPVOID lpParameter) { PVMMWIN_PDB_CONTEXT ctx = (PVMMWIN_PDB_CONTEXT)ctxVmm->pPdbContext; + PVMMWIN_PDB_INITIALIZE_KERNEL_PARAMETERS pKernelParameters = (PVMMWIN_PDB_INITIALIZE_KERNEL_PARAMETERS)lpParameter; DWORD dwReturnStatus = 0, dwPdbAge; BYTE pbPdbGUID[16]; - CHAR szPdbName[MAX_PATH]; + CHAR szPdbName[MAX_PATH] = { 0 }; PVMM_PROCESS pObSystemProcess = NULL; PPDB_ENTRY pObKernelEntry = NULL; QWORD qwPdbHash; if(!ctx) { return 0; } EnterCriticalSection(&ctx->Lock); InterlockedIncrement(&ctxVmm->ThreadWorkers.c); - SetEvent((HANDLE)hEventThreadStart); + SetEvent(*pKernelParameters->phEventThreadStarted); if(!(pObSystemProcess = VmmProcessGet(4))) { goto fail; } - if(!PE_GetPdbInfo(pObSystemProcess, ctxVmm->kernel.vaBase, NULL, szPdbName, pbPdbGUID, &dwPdbAge)) { + if(pKernelParameters->fPdbInfo) { + dwPdbAge = pKernelParameters->PdbInfo.Age; + memcpy(pbPdbGUID, pKernelParameters->PdbInfo.Guid, 16); + memcpy(szPdbName, pKernelParameters->PdbInfo.PdbFileName, sizeof(pKernelParameters->PdbInfo.PdbFileName)); + } else if(!PE_GetPdbInfo(pObSystemProcess, ctxVmm->kernel.vaBase, NULL, szPdbName, pbPdbGUID, &dwPdbAge) && !PDB_Initialize_Async_Kernel_ScanForPdbInfo(pObSystemProcess, szPdbName, pbPdbGUID, &dwPdbAge)) { vmmprintf("%s Reason: Unable to locate debugging information in kernel image.\n", VMMWIN_PDB_WARN_DEFAULT); goto fail; } @@ -443,12 +504,10 @@ DWORD PDB_Initialize_Async_Kernel(LPVOID hEventThreadStart) // fall-through to fail for cleanup fail: LeaveCriticalSection(&ctx->Lock); - if(dwReturnStatus) { - VmmWinInit_TryInitializeKernelOptionalValues(); - } Ob_DECREF(pObKernelEntry); Ob_DECREF(pObSystemProcess); InterlockedDecrement(&ctxVmm->ThreadWorkers.c); + LocalFree(pKernelParameters); return dwReturnStatus; } @@ -529,19 +588,21 @@ VOID PDB_ConfigChange() // refresh values and reload! EnterCriticalSection(&ctxVmm->MasterLock); PDB_Close(); - PDB_Initialize(); + PDB_Initialize(NULL, FALSE); LeaveCriticalSection(&ctxVmm->MasterLock); } /* * Initialize the PDB sub-system. This should ideally be done on Vmm Init() */ -VOID PDB_Initialize() +VOID PDB_Initialize(_In_opt_ PIMAGE_DEBUG_TYPE_CODEVIEW_PDBINFO pPdbInfoOpt, _In_ BOOL fInitializeKernelAsync) { HANDLE hThreadAsyncKernel, hEventThreadStarted = 0; PVMMWIN_PDB_CONTEXT ctx = NULL; DWORD i, dwSymOptions; CHAR szPathSymSrv[MAX_PATH], szPathDbgHelp[MAX_PATH]; + PVMMWIN_PDB_INITIALIZE_KERNEL_PARAMETERS pKernelParameters = NULL; + if(ctxMain->pdb.fInitialized) { return; } PDB_Initialize_InitialValues(); if(!ctxMain->pdb.fEnable) { goto fail; } if(!(ctx = LocalAlloc(LMEM_ZEROINIT, sizeof(VMMWIN_PDB_CONTEXT)))) { goto fail; } @@ -582,18 +643,28 @@ VOID PDB_Initialize() // success - finish up and load kernel .pdb async (to optimize startup time). // pdb subsystem won't be fully initialized until before the kernel is loaded. if(!(hEventThreadStarted = CreateEvent(NULL, TRUE, FALSE, NULL))) { goto fail; } + if(!(pKernelParameters = LocalAlloc(LMEM_ZEROINIT, sizeof(VMMWIN_PDB_INITIALIZE_KERNEL_PARAMETERS)))) { goto fail; } + pKernelParameters->phEventThreadStarted = &hEventThreadStarted; + if(pPdbInfoOpt) { + pKernelParameters->fPdbInfo = TRUE; + memcpy(&pKernelParameters->PdbInfo, pPdbInfoOpt, sizeof(IMAGE_DEBUG_TYPE_CODEVIEW_PDBINFO)); + } InitializeCriticalSection(&ctx->Lock); ctx->qwLoadAddressNext = VMMWIN_PDB_LOAD_ADDRESS_BASE; ctx->fDisabled = TRUE; ctxVmm->pPdbContext = ctx; - hThreadAsyncKernel = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PDB_Initialize_Async_Kernel, (LPVOID)hEventThreadStarted, 0, NULL); - if(!hThreadAsyncKernel) { - DeleteCriticalSection(&ctx->Lock); - goto fail; + if(fInitializeKernelAsync) { + hThreadAsyncKernel = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PDB_Initialize_Async_Kernel, (LPVOID)pKernelParameters, 0, NULL); + if(!hThreadAsyncKernel) { + DeleteCriticalSection(&ctx->Lock); + goto fail; + } + WaitForSingleObject(hEventThreadStarted, 500); // wait for async thread initialize thread to start (and acquire PDB lock). + CloseHandle(hThreadAsyncKernel); + } else { + PDB_Initialize_Async_Kernel(pKernelParameters); // synchronous call } - WaitForSingleObject(hEventThreadStarted, 500); // wait for async thread initialize thread to start (and acquire PDB lock). CloseHandle(hEventThreadStarted); - CloseHandle(hThreadAsyncKernel); return; fail: if(hEventThreadStarted) { CloseHandle(hEventThreadStarted); } @@ -607,4 +678,5 @@ VOID PDB_Initialize() if(ctx->hModuleSymSrv) { FreeLibrary(ctx->hModuleSymSrv); } } LocalFree(ctx); + LocalFree(pKernelParameters); } diff --git a/vmm/pdb.h b/vmm/pdb.h index f0933c02..d49ab4bf 100644 --- a/vmm/pdb.h +++ b/vmm/pdb.h @@ -8,13 +8,21 @@ #ifndef __PDB_H__ #define __PDB_H__ #include "vmm.h" +#include "pe.h" #define VMMWIN_PDB_HANDLE_KERNEL ((QWORD)-1) /* * Initialize the PDB sub-system. This should ideally be done on Vmm Init(). +* -- pPdbInfoOpt +* -- fInitializeKernelAsync */ -VOID PDB_Initialize(); +VOID PDB_Initialize(_In_opt_ PIMAGE_DEBUG_TYPE_CODEVIEW_PDBINFO pPdbInfoOpt, _In_ BOOL fInitializeKernelAsync); + +/* +* Wait for completion of initialization of the PDB sub-system. +*/ +VOID PDB_Initialize_WaitComplete(); /* * Cleanup the PDB sub-system. This should ideally be done on Vmm Close(). @@ -149,10 +157,13 @@ BOOL PDB_GetTypeSize(_In_opt_ VMMWIN_PDB_HANDLE hPDB, _In_ LPSTR szTypeName, _Ou * -- hPDB * -- szTypeName = wildcard type name. * -- wszTypeChildName = exact match of child name. -* -- pdwTypeOffset = offset relative to type base. +* -- pdwTypeOffset / pwTypeOffset = offset relative to type base. * -- return */ _Success_(return) BOOL PDB_GetTypeChildOffset(_In_opt_ VMMWIN_PDB_HANDLE hPDB, _In_ LPSTR szTypeName, _In_ LPWSTR wszTypeChildName, _Out_ PDWORD pdwTypeOffset); +_Success_(return) +BOOL PDB_GetTypeChildOffsetShort(_In_opt_ VMMWIN_PDB_HANDLE hPDB, _In_ LPSTR szTypeName, _In_ LPWSTR wszTypeChildName, _Out_ PWORD pwTypeOffset); + #endif /* __PDB_H__ */ diff --git a/vmm/pe.c b/vmm/pe.c index 8e1f8ef7..9cbe0204 100644 --- a/vmm/pe.c +++ b/vmm/pe.c @@ -332,14 +332,6 @@ BOOL PE_GetModuleNameEx(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaModuleBase, _In return FALSE; } -typedef struct tdIMAGE_DEBUG_TYPE_CODEVIEW_PDBINFO -{ - DWORD Signature; - BYTE Guid[16]; - DWORD Age; - CHAR PdbFileName[256 - 4 - 16 - 4]; -} IMAGE_DEBUG_TYPE_CODEVIEW_PDBINFO; - _Success_(return) BOOL PE_GetPdbInfo(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaModuleBase, _In_reads_opt_(0x1000) PBYTE pbModuleHeaderOpt, _Out_writes_(MAX_PATH) LPSTR szPdbName, _Out_writes_(16) PBYTE pbGUID, _Out_ PDWORD pdwAge) { @@ -378,7 +370,7 @@ BOOL PE_GetPdbInfo(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaModuleBase, _In_read (pDebugDirectory->SizeOfData < sizeof(IMAGE_DEBUG_TYPE_CODEVIEW_PDBINFO)) && (pDebugDirectory->SizeOfData > 24) && (pDebugDirectory->AddressOfRawData + pDebugDirectory->SizeOfData < cbImageSize) && - VmmRead(pProcess, vaModuleBase + pDebugDirectory->AddressOfRawData, (PBYTE)& PdbInfo, pDebugDirectory->SizeOfData) && + VmmRead(pProcess, vaModuleBase + pDebugDirectory->AddressOfRawData, (PBYTE)&PdbInfo, pDebugDirectory->SizeOfData) && (PdbInfo.Signature == 0x53445352); if(f) { *pdwAge = PdbInfo.Age; diff --git a/vmm/pe.h b/vmm/pe.h index a0643ded..056710ec 100644 --- a/vmm/pe.h +++ b/vmm/pe.h @@ -15,6 +15,13 @@ static const LPCSTR PE_DATA_DIRECTORIES[16] = { "EXPORT", "IMPORT", "RESOURCE", "EXCEPTION", "SECURITY", "BASERELOC", "DEBUG", "ARCHITECTURE", "GLOBALPTR", "TLS", "LOAD_CONFIG", "BOUND_IMPORT", "IAT", "DELAY_IMPORT", "COM_DESCRIPTOR", "RESERVED" }; +typedef struct tdIMAGE_DEBUG_TYPE_CODEVIEW_PDBINFO { + DWORD Signature; + BYTE Guid[16]; + DWORD Age; + CHAR PdbFileName[256 - 4 - 16 - 4]; +} IMAGE_DEBUG_TYPE_CODEVIEW_PDBINFO, *PIMAGE_DEBUG_TYPE_CODEVIEW_PDBINFO; + typedef struct tdPE_THUNKINFO_IAT { BOOL fValid; BOOL f32; // if TRUE fn is a 32-bit/4-byte entry, otherwise 64-bit/8-byte entry. diff --git a/vmm/pluginmanager.c b/vmm/pluginmanager.c index 51da0741..eb4f49e8 100644 --- a/vmm/pluginmanager.c +++ b/vmm/pluginmanager.c @@ -8,13 +8,7 @@ #include "util.h" #include "vmm.h" #include "vmmdll.h" -#include "m_ldrmodules.h" -#include "m_pedump.h" -#include "m_status.h" -#include "m_sysinfo.h" -#include "m_virt2phys.h" -#include "m_phys2virt.h" -#include "m_winreg.h" +#include "m_modules.h" // // This file contains functionality related to keeping track of plugins, both @@ -332,12 +326,18 @@ BOOL PluginManager_Initialize() PluginManager_Initialize_RegInfoInit(&ri, NULL); M_LdrModules_Initialize(&ri); PluginManager_Initialize_RegInfoInit(&ri, NULL); + M_MemMap_Initialize(&ri); + PluginManager_Initialize_RegInfoInit(&ri, NULL); M_Status_Initialize(&ri); PluginManager_Initialize_RegInfoInit(&ri, NULL); M_WinReg_Initialize(&ri); PluginManager_Initialize_RegInfoInit(&ri, NULL); M_PEDump_Initialize(&ri); PluginManager_Initialize_RegInfoInit(&ri, NULL); + M_HandleInfo_Initialize(&ri); + PluginManager_Initialize_RegInfoInit(&ri, NULL); + M_ThreadInfo_Initialize(&ri); + PluginManager_Initialize_RegInfoInit(&ri, NULL); M_SysInfo_Initialize(&ri); // 2: process dll modules Util_GetPathDll(szPath, NULL); diff --git a/vmm/statistics.c b/vmm/statistics.c index d10d9b31..3a265ac0 100644 --- a/vmm/statistics.c +++ b/vmm/statistics.c @@ -198,10 +198,13 @@ const LPSTR NAMES_VMM_STATISTICS_CALL[] = { "VMMDLL_PidGetFromName", "VMMDLL_ProcessGetInformation", "VMMDLL_ProcessGetInformationString", - "VMMDLL_ProcessGetMemoryMap", - "VMMDLL_ProcessGetMemoryMapEntry", - "VMMDLL_ProcessGetModuleMap", - "VMMDLL_ProcessGetModuleFromName", + "VMMDLL_ProcessMap_GetPte", + "VMMDLL_ProcessMap_GetVad", + "VMMDLL_ProcessMap_GetModule", + "VMMDLL_ProcessMap_GetModuleFromName", + "VMMDLL_ProcessMap_GetHeap", + "VMMDLL_ProcessMap_GetThread", + "VMMDLL_ProcessMap_GetHandle", "VMMDLL_ProcessGetDirectories", "VMMDLL_ProcessGetSections", "VMMDLL_ProcessGetEAT", diff --git a/vmm/statistics.h b/vmm/statistics.h index 9c8fd5ea..4e65c6ea 100644 --- a/vmm/statistics.h +++ b/vmm/statistics.h @@ -85,33 +85,36 @@ VOID PageStatUpdate(_In_opt_ PPAGE_STATISTICS pPageStat, _In_ QWORD qwAddr, _In_ #define STATISTICS_ID_VMMDLL_PidGetFromName 0x0f #define STATISTICS_ID_VMMDLL_ProcessGetInformation 0x10 #define STATISTICS_ID_VMMDLL_ProcessGetInformationString 0x11 -#define STATISTICS_ID_VMMDLL_ProcessGetMemoryMap 0x12 -#define STATISTICS_ID_VMMDLL_ProcessGetMemoryMapEntry 0x13 -#define STATISTICS_ID_VMMDLL_ProcessGetModuleMap 0x14 -#define STATISTICS_ID_VMMDLL_ProcessGetModuleFromName 0x15 -#define STATISTICS_ID_VMMDLL_ProcessGetDirectories 0x16 -#define STATISTICS_ID_VMMDLL_ProcessGetSections 0x17 -#define STATISTICS_ID_VMMDLL_ProcessGetEAT 0x18 -#define STATISTICS_ID_VMMDLL_ProcessGetIAT 0x19 -#define STATISTICS_ID_VMMDLL_ProcessGetProcAddress 0x1a -#define STATISTICS_ID_VMMDLL_ProcessGetModuleBase 0x1b -#define STATISTICS_ID_VMMDLL_WinGetThunkEAT 0x1c -#define STATISTICS_ID_VMMDLL_WinGetThunkIAT 0x1d -#define STATISTICS_ID_VMMDLL_WinMemCompression_DecompressPage 0x1e -#define STATISTICS_ID_VMMDLL_WinRegHive_List 0x1f -#define STATISTICS_ID_VMMDLL_WinRegHive_ReadEx 0x20 -#define STATISTICS_ID_VMMDLL_WinRegHive_Write 0x21 -#define STATISTICS_ID_VMMDLL_WinReg_EnumKeyExW 0x22 -#define STATISTICS_ID_VMMDLL_WinReg_EnumValueW 0x23 -#define STATISTICS_ID_VMMDLL_WinReg_QueryValueExW 0x24 -#define STATISTICS_ID_VMMDLL_WinNet_Get 0x25 -#define STATISTICS_ID_VMMDLL_Refresh 0x26 -#define STATISTICS_ID_VMMDLL_UtilFillHexAscii 0x27 -#define STATISTICS_ID_VMMDLL_PdbSymbolAddress 0x28 -#define STATISTICS_ID_VMMDLL_PdbTypeSize 0x29 -#define STATISTICS_ID_VMMDLL_PdbTypeChildOffset 0x2a -#define STATISTICS_ID_VMM_PagedCompressedMemory 0x2b -#define STATISTICS_ID_MAX 0x2b +#define STATISTICS_ID_VMMDLL_ProcessMap_GetPte 0x12 +#define STATISTICS_ID_VMMDLL_ProcessMap_GetVad 0x13 +#define STATISTICS_ID_VMMDLL_ProcessMap_GetModule 0x14 +#define STATISTICS_ID_VMMDLL_ProcessMap_GetModuleFromName 0x15 +#define STATISTICS_ID_VMMDLL_ProcessMap_GetHeap 0x16 +#define STATISTICS_ID_VMMDLL_ProcessMap_GetThread 0x17 +#define STATISTICS_ID_VMMDLL_ProcessMap_GetHandle 0x18 +#define STATISTICS_ID_VMMDLL_ProcessGetDirectories 0x19 +#define STATISTICS_ID_VMMDLL_ProcessGetSections 0x1a +#define STATISTICS_ID_VMMDLL_ProcessGetEAT 0x1b +#define STATISTICS_ID_VMMDLL_ProcessGetIAT 0x1c +#define STATISTICS_ID_VMMDLL_ProcessGetProcAddress 0x1d +#define STATISTICS_ID_VMMDLL_ProcessGetModuleBase 0x1e +#define STATISTICS_ID_VMMDLL_WinGetThunkEAT 0x1f +#define STATISTICS_ID_VMMDLL_WinGetThunkIAT 0x20 +#define STATISTICS_ID_VMMDLL_WinMemCompression_DecompressPage 0x21 +#define STATISTICS_ID_VMMDLL_WinRegHive_List 0x22 +#define STATISTICS_ID_VMMDLL_WinRegHive_ReadEx 0x23 +#define STATISTICS_ID_VMMDLL_WinRegHive_Write 0x24 +#define STATISTICS_ID_VMMDLL_WinReg_EnumKeyExW 0x25 +#define STATISTICS_ID_VMMDLL_WinReg_EnumValueW 0x26 +#define STATISTICS_ID_VMMDLL_WinReg_QueryValueExW 0x27 +#define STATISTICS_ID_VMMDLL_WinNet_Get 0x28 +#define STATISTICS_ID_VMMDLL_Refresh 0x29 +#define STATISTICS_ID_VMMDLL_UtilFillHexAscii 0x2a +#define STATISTICS_ID_VMMDLL_PdbSymbolAddress 0x2b +#define STATISTICS_ID_VMMDLL_PdbTypeSize 0x2c +#define STATISTICS_ID_VMMDLL_PdbTypeChildOffset 0x2d +#define STATISTICS_ID_VMM_PagedCompressedMemory 0x2e +#define STATISTICS_ID_MAX 0x2e #define STATISTICS_ID_NOLOG 0xffffffff VOID Statistics_CallSetEnabled(_In_ BOOL fEnabled); diff --git a/vmm/test_ob.c b/vmm/test_ob.c deleted file mode 100644 index e39adc78..00000000 --- a/vmm/test_ob.c +++ /dev/null @@ -1,50 +0,0 @@ -// test_ob.c : test cases related to object maanger objects -// -// (c) Ulf Frisk, 2019 -// Author: Ulf Frisk, pcileech@frizk.net -// - -#include "test_ob.h" - - - -VOID Test_ObMap_Insert(POB_MAP pm, DWORD c) -{ - DWORD i; - for(i = 1; i < c; i++) { - ObMap_Push(pm, i, (PVOID)(i | 0xffffffff'00000000)); - } -} - -VOID Test_ObMap() -{ - /* - QWORD qw1, qw2; - DWORD i; - POB_MAP pm = NULL; - pm = ObMap_New(OB_MAP_FLAGS_NOKEY); - Test_ObMap_Insert(pm, 0x001f0000); - qw1 = ObMap_Peek(pm); - qw2 = ObMap_PeekKey(pm); - qw1 = ObMap_Pop(pm); - qw1 = ObMap_Peek(pm); - qw2 = ObMap_PeekKey(pm); - qw1 = ObMap_Pop(pm); - qw1 = ObMap_Peek(pm); - qw2 = ObMap_PeekKey(pm); - qw1 = ObMap_GetByKey(pm, 0x200); - qw1 = ObMap_PeekKey(pm); - qw1 = ObMap_ExistsKey(pm, 0x200); - Ob_DECREF_NULL(&pm); - - for(i = 0; i < 10; i++) { - pm = ObMap_New(0); - Test_ObMap_Insert(pm, 0x007f0000); - ObMap_Clear(pm); - Test_ObMap_Insert(pm, 0x00020000); - Ob_DECREF_NULL(&pm); - } - - DWORD y = 0; - */ -} diff --git a/vmm/test_ob.h b/vmm/test_ob.h deleted file mode 100644 index 4b671e46..00000000 --- a/vmm/test_ob.h +++ /dev/null @@ -1,12 +0,0 @@ -// test_ob.h : test cases related to object maanger objects -// -// (c) Ulf Frisk, 2019 -// Author: Ulf Frisk, pcileech@frizk.net -// -#ifndef __TEST_OB_H__ -#define __TEST_OB_H__ -#include "ob.h" - -VOID Test_ObMap(); - -#endif /* __TEST_OB_H__ */ diff --git a/vmm/util.c b/vmm/util.c index 8155e091..6a45d6d5 100644 --- a/vmm/util.c +++ b/vmm/util.c @@ -44,6 +44,21 @@ DWORD Util_HashStringA(_In_opt_ LPCSTR sz) } } +DWORD Util_HashStringUpperW(_In_opt_ LPCWSTR wsz) +{ + WCHAR c; + DWORD i = 0, dwHash = 0; + if(!wsz) { return 0; } + while(TRUE) { + c = wsz[i++]; + if(!c) { return dwHash; } + if(c >= 'a' && c <= 'z') { + c += 'A' - 'a'; + } + dwHash = ((dwHash >> 13) | (dwHash << 19)) + c; + } +} + #define Util_2HexChar(x) (((((x) & 0xf) <= 9) ? '0' : ('a' - 10)) + ((x) & 0xf)) _Success_(return) @@ -235,6 +250,26 @@ int Util_wcsstrncmp(_In_ LPSTR sz, _In_ LPWSTR wsz, _In_opt_ DWORD cMax) return 0; } +_Success_(return >= 0) +DWORD Util_snprintf_ln( + _Out_writes_(min(cszBuffer, cszLineLength + 1)) LPSTR szBuffer, + _In_ QWORD cszBuffer, + _In_ QWORD cszLineLength, + _In_z_ _Printf_format_string_ LPSTR szFormat, + ... +) { + int status; + va_list arglist; + va_start(arglist, szFormat); + status = vsnprintf(szBuffer, min(cszBuffer, cszLineLength + 1), szFormat, arglist); + va_end(arglist); + if(status < 0) { + status = snprintf(szBuffer, cszBuffer, "%*s\n", (DWORD)(cszLineLength - 1), ""); + if(status < 0) { status = 0; } + } + return (DWORD)status; +} + VOID Util_GetPathDll(_Out_writes_(MAX_PATH) PCHAR szPath, _In_opt_ HMODULE hModule) { SIZE_T i; @@ -347,17 +382,17 @@ LPSTR Util_StrDupA(_In_opt_ LPSTR sz) return szDup; } -VOID Util_FileTime2String(_In_ PFILETIME pFileTime, _Out_writes_(MAX_PATH) LPSTR szTime) +VOID Util_FileTime2String(_In_ PFILETIME pFileTime, _Out_writes_(32) LPSTR szTime) { SYSTEMTIME SystemTime; if(!*(PQWORD)pFileTime) { - strcpy_s(szTime, MAX_PATH, " ***"); + strcpy_s(szTime, 32, " ***"); return; } FileTimeToSystemTime(pFileTime, &SystemTime); sprintf_s( szTime, - MAX_PATH, + 32, "%04i-%02i-%02i %02i:%02i:%02i UTC", SystemTime.wYear, SystemTime.wMonth, @@ -367,3 +402,37 @@ VOID Util_FileTime2String(_In_ PFILETIME pFileTime, _Out_writes_(MAX_PATH) LPSTR SystemTime.wSecond ); } + +PVOID Util_qfind(_In_ PVOID pvFind, _In_ DWORD cMap, _In_ PVOID pvMap, _In_ DWORD cbEntry, _In_ int(*pfnCmp)(_In_ PVOID pvFind, _In_ PVOID pvEntry)) +{ + int f; + DWORD i, cbSearch, cbStep, cbMap; + PBYTE pbMap = pvMap; + if(!cMap || !cbEntry) { return NULL; } + for(i = 1; ((cMap - 1) >> i); i++); + cbMap = cMap * cbEntry; + cbSearch = cbEntry * min(1UL << (i - 1), cMap - 1); + cbStep = max(cbEntry, cbSearch >> 1); + while(cbStep >= cbEntry) { + f = pfnCmp(pvFind, pbMap + cbSearch); + if(f < 0) { + cbSearch -= cbStep; + } else if(f > 0) { + if(cbSearch + cbStep < cbMap) { + cbSearch += cbStep; + } + } else { + return pbMap + cbSearch; + } + cbStep = cbStep >> 1; + } + if(cbSearch < cbMap) { + if(!pfnCmp(pvFind, pbMap + cbSearch)) { + return pbMap + cbSearch; + } + if((cbSearch >= cbEntry) && !pfnCmp(pvFind, pbMap + cbSearch - cbEntry)) { + return pbMap + cbSearch - cbEntry; + } + } + return NULL; +} diff --git a/vmm/util.h b/vmm/util.h index 355d9b91..e5397064 100644 --- a/vmm/util.h +++ b/vmm/util.h @@ -44,6 +44,13 @@ QWORD Util_GetNumericW(_In_ LPWSTR wsz); */ DWORD Util_HashStringA(_In_opt_ LPCSTR sz); +/* +* Hash the uppercase version of a string with the ROT13 algorithm. +* -- wsz +* -- return +*/ +DWORD Util_HashStringUpperW(_In_opt_ LPCWSTR wsz); + /* * Print a maximum of 8192 bytes of binary data as hexascii on the screen. * -- pb @@ -145,6 +152,24 @@ LPWSTR Util_PathFileSplitW(_In_ LPWSTR wsz, _Out_writes_(MAX_PATH) LPWSTR wszPat */ int Util_wcsstrncmp(_In_ LPSTR sz, _In_ LPWSTR wsz, _In_opt_ DWORD cMax); +/* +* snprintf a line with fixed line length in a fairly error safe way. +* -- szBuffer +* -- cszBuffer +* -- cszLineLength = line length in characters excluding terminating NULL. +* -- szFormat = printf format string. +* -- ... = printf varargs. +* -- return +*/ +_Success_(return >= 0) +DWORD Util_snprintf_ln( + _Out_writes_(min(cszBuffer, cszLineLength + 1)) LPSTR szBuffer, + _In_ QWORD cszBuffer, + _In_ QWORD cszLineLength, + _In_z_ _Printf_format_string_ LPSTR szFormat, + ... +); + /* * Return the path of the specified hModule (DLL) - ending with a backslash, or current Executable. * -- szPath @@ -165,7 +190,18 @@ LPSTR Util_StrDupA(_In_opt_ LPSTR sz); * -- pFileTime * -- szTime */ -VOID Util_FileTime2String(_In_ PFILETIME pFileTime, _Out_writes_(MAX_PATH) LPSTR szTime); +VOID Util_FileTime2String(_In_ PFILETIME pFileTime, _Out_writes_(32) LPSTR szTime); + +/* +* Find an entry in a sorted array in an efficient way - O(log2(n)). +* -- pvFind +* -- cMap +* -- pvMap +* -- cbEntry +* -- pfnCmp +* -- return = the entry found or NULL on failure. +*/ +PVOID Util_qfind(_In_ PVOID pvFind, _In_ DWORD cMap, _In_ PVOID pvMap, _In_ DWORD cbEntry, _In_ int(*pfnCmp)(_In_ PVOID pvFind, _In_ PVOID pvEntry)); /* * Utility functions for read/write towards different underlying data representations. diff --git a/vmm/version.h b/vmm/version.h index 8f5f1652..f193a066 100644 --- a/vmm/version.h +++ b/vmm/version.h @@ -1,12 +1,12 @@ #define STRINGIZE2(s) #s #define STRINGIZE(s) STRINGIZE2(s) -#define VERSION_MAJOR 2 -#define VERSION_MINOR 10 -#define VERSION_REVISION 2 -#define VERSION_BUILD 2 +#define VERSION_MAJOR 3 +#define VERSION_MINOR 0 +#define VERSION_REVISION 0 +#define VERSION_BUILD 3 -#define VER_FILE_DESCRIPTION_STR "The Memory Process File System : Core" +#define VER_FILE_DESCRIPTION_STR "MemProcFS : Core" #define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD #define VER_FILE_VERSION_STR STRINGIZE(VERSION_MAJOR) \ "." STRINGIZE(VERSION_MINOR) \ diff --git a/vmm/vmm.c b/vmm/vmm.c index 153958ec..f17e0680 100644 --- a/vmm/vmm.c +++ b/vmm/vmm.c @@ -5,13 +5,11 @@ // #include "vmm.h" -#include "mm_x86.h" -#include "mm_x86pae.h" -#include "mm_x64.h" -#include "mm_x64_page_win.h" +#include "mm.h" #include "ob.h" #include "pdb.h" #include "vmmproc.h" +#include "vmmwin.h" #include "vmmwinreg.h" #include "pluginmanager.h" #include "util.h" @@ -24,7 +22,7 @@ /* * Retrieve cache table from ctxVmm given a specific tag. */ -PVMM_CACHE_TABLE VmmCacheTableGet(_In_ WORD wTblTag) +PVMM_CACHE_TABLE VmmCacheTableGet(_In_ DWORD wTblTag) { switch(wTblTag) { case VMM_CACHE_TAG_PHYS: @@ -44,12 +42,12 @@ PVMM_CACHE_TABLE VmmCacheTableGet(_In_ WORD wTblTag) /* * Invalidate a cache entry (if exists) */ -VOID VmmCacheInvalidate_2(_In_ WORD wTblTag, _In_ QWORD qwA) +VOID VmmCacheInvalidate_2(_In_ DWORD dwTblTag, _In_ QWORD qwA) { DWORD iR, iB; PVMM_CACHE_TABLE t; PVMMOB_MEM pOb, pObNext; - t = VmmCacheTableGet(wTblTag); + t = VmmCacheTableGet(dwTblTag); if(!t || !t->fActive) { return; } iR = VMM_CACHE2_GET_REGION(qwA); iB = VMM_CACHE2_GET_BUCKET(qwA); @@ -131,18 +129,18 @@ VOID VmmCacheReclaim(_In_ PVMM_CACHE_TABLE t, _In_ DWORD iR, _In_ BOOL fTotal) * Clear the specified cache from all entries. * -- wTblTag */ -VOID VmmCacheClear(_In_ WORD wTblTag) +VOID VmmCacheClear(_In_ DWORD dwTblTag) { DWORD i; PVMM_CACHE_TABLE t; PVMM_PROCESS pObProcess = NULL; // 1: clear cache - t = VmmCacheTableGet(wTblTag); + t = VmmCacheTableGet(dwTblTag); for(i = 0; i < VMM_CACHE2_REGIONS; i++) { VmmCacheReclaim(t, i, TRUE); } // 2: if tlb cache clear -> update process 'is spider done' flag - if(wTblTag == VMM_CACHE_TAG_TLB) { + if(dwTblTag == VMM_CACHE_TAG_TLB) { while((pObProcess = VmmProcessGetNext(pObProcess, 0))) { if(pObProcess->fTlbSpiderDone) { EnterCriticalSection(&pObProcess->LockUpdate); @@ -208,13 +206,13 @@ VOID VmmCacheReserveReturn(_In_opt_ PVMMOB_MEM pOb) LeaveCriticalSection(&t->R[iR].Lock); } -PVMMOB_MEM VmmCacheReserve(_In_ WORD wTblTag) +PVMMOB_MEM VmmCacheReserve(_In_ DWORD dwTblTag) { PVMM_CACHE_TABLE t; PVMMOB_MEM pOb; PSLIST_ENTRY e; WORD iReclaimLast, cLoopProtect = 0; - t = VmmCacheTableGet(wTblTag); + t = VmmCacheTableGet(dwTblTag); if(!t || !t->fActive) { return NULL; } while(!(e = InterlockedPopEntrySList(&t->ListHeadEmpty))) { if(t->cTotal < VMM_CACHE2_MAX_ENTRIES) { @@ -235,7 +233,7 @@ PVMMOB_MEM VmmCacheReserve(_In_ WORD wTblTag) iReclaimLast = InterlockedIncrement16(&t->iReclaimLast); VmmCacheReclaim(t, iReclaimLast % VMM_CACHE2_REGIONS, FALSE); if(++cLoopProtect == VMM_CACHE2_REGIONS) { - vmmprintf_fn("ERROR - SHOULD NOT HAPPEN - CACHE %02X DRAINED OF ENTRIES\n", wTblTag); + vmmprintf_fn("ERROR - SHOULD NOT HAPPEN - CACHE %04X DRAINED OF ENTRIES\n", dwTblTag); Sleep(10); } } @@ -246,12 +244,12 @@ PVMMOB_MEM VmmCacheReserve(_In_ WORD wTblTag) return pOb; // reference overtaken by callee (from EmptyList) } -PVMMOB_MEM VmmCacheGet(_In_ WORD wTblTag, _In_ QWORD qwA) +PVMMOB_MEM VmmCacheGet(_In_ DWORD dwTblTag, _In_ QWORD qwA) { PVMM_CACHE_TABLE t; DWORD iR; PVMMOB_MEM pOb; - t = VmmCacheTableGet(wTblTag); + t = VmmCacheTableGet(dwTblTag); if(!t || !t->fActive) { return NULL; } iR = VMM_CACHE2_GET_REGION(qwA); EnterCriticalSection(&t->R[iR].Lock); @@ -264,17 +262,16 @@ PVMMOB_MEM VmmCacheGet(_In_ WORD wTblTag, _In_ QWORD qwA) return pOb; } -PVMMOB_MEM VmmCacheGet_FromDeviceOnMiss(_In_ WORD wTblTag, _In_ WORD wTblTagSecondaryOpt, _In_ QWORD qwA) +PVMMOB_MEM VmmCacheGet_FromDeviceOnMiss(_In_ DWORD dwTblTag, _In_ DWORD dwTblTagSecondaryOpt, _In_ QWORD qwA) { PVMMOB_MEM pObMEM, pObReservedMEM; PMEM_IO_SCATTER_HEADER pMEM; - pObMEM = VmmCacheGet(wTblTag, qwA); + pObMEM = VmmCacheGet(dwTblTag, qwA); if(pObMEM) { return pObMEM; } - pObReservedMEM = VmmCacheReserve(wTblTag); - if(pObReservedMEM) { + if((pObReservedMEM = VmmCacheReserve(dwTblTag))) { pMEM = &pObReservedMEM->h; pMEM->qwA = qwA; - if(wTblTagSecondaryOpt && (pObMEM = VmmCacheGet(wTblTagSecondaryOpt, qwA))) { + if(dwTblTagSecondaryOpt && (pObMEM = VmmCacheGet(dwTblTagSecondaryOpt, qwA))) { pMEM->cb = 0x1000; memcpy(pMEM->pb, pObMEM->pb, 0x1000); Ob_DECREF(pObMEM); @@ -288,16 +285,16 @@ PVMMOB_MEM VmmCacheGet_FromDeviceOnMiss(_In_ WORD wTblTag, _In_ WORD wTblTagSeco VmmCacheReserveReturn(pObReservedMEM); return pObReservedMEM; } + VmmCacheReserveReturn(pObReservedMEM); } - VmmCacheReserveReturn(pObReservedMEM); return NULL; } -BOOL VmmCacheExists(_In_ WORD wTblTag, _In_ QWORD qwA) +BOOL VmmCacheExists(_In_ DWORD dwTblTag, _In_ QWORD qwA) { BOOL result; PVMMOB_MEM pOb; - pOb = VmmCacheGet(wTblTag, qwA); + pOb = VmmCacheGet(dwTblTag, qwA); result = pOb != NULL; Ob_DECREF(pOb); return result; @@ -333,13 +330,13 @@ PVMMOB_MEM VmmTlbGetPageTable(_In_ QWORD pa, _In_ BOOL fCacheOnly) return NULL; } -VOID VmmCache2Close(_In_ WORD wTblTag) +VOID VmmCache2Close(_In_ DWORD dwTblTag) { PVMM_CACHE_TABLE t; PVMMOB_MEM pOb; PSLIST_ENTRY e; DWORD i; - t = VmmCacheTableGet(wTblTag); + t = VmmCacheTableGet(dwTblTag); if(!t || !t->fActive) { return; } t->fActive = FALSE; // remove from "regions" @@ -361,11 +358,11 @@ VOID VmmCache2Close(_In_ WORD wTblTag) } } -VOID VmmCache2Initialize(_In_ WORD wTblTag) +VOID VmmCache2Initialize(_In_ DWORD dwTblTag) { DWORD i; PVMM_CACHE_TABLE t; - t = VmmCacheTableGet(wTblTag); + t = VmmCacheTableGet(dwTblTag); if(!t || t->fActive) { return; } for(i = 0; i < VMM_CACHE2_REGIONS; i++) { InitializeCriticalSection(&t->R[i].Lock); @@ -373,7 +370,7 @@ VOID VmmCache2Initialize(_In_ WORD wTblTag) InitializeSListHead(&t->ListHeadEmpty); InitializeSListHead(&t->ListHeadTotal); t->fActive = TRUE; - t->tag = wTblTag; + t->tag = dwTblTag; } /* @@ -417,8 +414,9 @@ VOID VmmTlbPrefetch(_In_ POB_VSET pTlbPrefetch) * NB! pPrefetchPages must not be updated/altered during the function call. * -- pProcess * -- pPrefetchPages +* -- flags */ -VOID VmmCachePrefetchPages(_In_opt_ PVMM_PROCESS pProcess, _In_opt_ POB_VSET pPrefetchPages) +VOID VmmCachePrefetchPages(_In_opt_ PVMM_PROCESS pProcess, _In_opt_ POB_VSET pPrefetchPages, _In_ QWORD flags) { QWORD qwA = 0; DWORD cPages, iMEM = 0; @@ -430,11 +428,11 @@ VOID VmmCachePrefetchPages(_In_opt_ PVMM_PROCESS pProcess, _In_opt_ POB_VSET pPr ppMEMs[iMEM++]->qwA = qwA & ~0xfff; } if(pProcess) { - VmmReadScatterVirtual(pProcess, ppMEMs, iMEM, 0); + VmmReadScatterVirtual(pProcess, ppMEMs, iMEM, flags); } else { - VmmReadScatterPhysical(ppMEMs, iMEM, 0); + VmmReadScatterPhysical(ppMEMs, iMEM, flags); } - LocalFree(ppMEMs); + LeechCore_MemFree(ppMEMs); } /* @@ -455,7 +453,7 @@ VOID VmmCachePrefetchPages2(_In_opt_ PVMM_PROCESS pProcess, _In_ DWORD cAddresse cAddresses--; } va_end(arguments); - VmmCachePrefetchPages(pProcess, pObVSet); + VmmCachePrefetchPages(pProcess, pObVSet, 0); Ob_DECREF(pObVSet); } @@ -467,8 +465,9 @@ VOID VmmCachePrefetchPages2(_In_opt_ PVMM_PROCESS pProcess, _In_ DWORD cAddresse * -- pProcess * -- pPrefetchPagesNonPageAligned * -- cb +* -- flags */ -VOID VmmCachePrefetchPages3(_In_opt_ PVMM_PROCESS pProcess, _In_opt_ POB_VSET pPrefetchPagesNonPageAligned, _In_ DWORD cb) +VOID VmmCachePrefetchPages3(_In_opt_ PVMM_PROCESS pProcess, _In_opt_ POB_VSET pPrefetchPagesNonPageAligned, _In_ DWORD cb, _In_ QWORD flags) { QWORD qwA = 0; POB_VSET pObSetAlign; @@ -477,10 +476,240 @@ VOID VmmCachePrefetchPages3(_In_opt_ PVMM_PROCESS pProcess, _In_opt_ POB_VSET pP while((qwA = ObVSet_GetNext(pPrefetchPagesNonPageAligned, qwA))) { ObVSet_Push_PageAlign(pObSetAlign, qwA, cb); } - VmmCachePrefetchPages(pProcess, pObSetAlign); + VmmCachePrefetchPages(pProcess, pObSetAlign, flags); Ob_DECREF(pObSetAlign); } +/* +* Prefetch an array of optionally non-page aligned addresses. This is useful +* when reading data from somewhat known addresses over higher latency connections. +* -- pProcess +* -- cAddresses +* -- pqwAddresses = array of addresses to fetch +* -- cb +* -- flags +*/ +VOID VmmCachePrefetchPages4(_In_opt_ PVMM_PROCESS pProcess, _In_ DWORD cAddresses, _In_ PQWORD pqwAddresses, _In_ DWORD cb, _In_ QWORD flags) +{ + POB_VSET pObVSet = NULL; + if(!cAddresses || !(pObVSet = ObVSet_New())) { return; } + while(cAddresses) { + cAddresses--; + if(pqwAddresses[cAddresses]) { + ObVSet_Push_PageAlign(pObVSet, pqwAddresses[cAddresses], cb); + } + } + VmmCachePrefetchPages(pProcess, pObVSet, 0); + Ob_DECREF(pObVSet); +} + +// ---------------------------------------------------------------------------- +// MAP FUNCTIONALITY BELOW: +// SUPPORTED MAPS: PTE, VAD, MODULE, HEAP +// ---------------------------------------------------------------------------- + +/* +* Retrieve the PTE hardware page table memory map. +* CALLER DECREF: ppObPteMap +* -- pProcess +* -- ppObPteMap +* -- fExtendedText +* -- return +*/ +_Success_(return) +BOOL VmmMap_GetPte(_In_ PVMM_PROCESS pProcess, _Out_ PVMMOB_MAP_PTE *ppObPteMap, _In_ BOOL fExtendedText) +{ + return + (ctxVmm->tpMemoryModel != VMM_MEMORYMODEL_NA) && + ctxVmm->fnMemoryModel.pfnPteMapInitialize(pProcess) && + (!fExtendedText || VmmWin_InitializePteMapText(pProcess)) && + (*ppObPteMap = Ob_INCREF(pProcess->Map.pObPte)); +} + +int VmmMap_GetPteEntry_CmpFind(_In_ QWORD vaFind, _In_ PVMM_MAP_PTEENTRY pEntry) +{ + if(pEntry->vaBase > vaFind) { return -1; } + if(pEntry->vaBase + (pEntry->cPages << 12) - 1 < vaFind) { return 1; } + return 0; +} + +/* +* Retrieve a single PVMM_MAP_PTEENTRY from the PTE hardware page table memory map. +* -- pProcess +* -- ppObPteMap +* -- fExtendedText +* -- return = PTR to PTEENTRY or NULL on fail. Must not be used out of pPteMap scope. +*/ +PVMM_MAP_PTEENTRY VmmMap_GetPteEntry(_In_ PVMMOB_MAP_PTE pPteMap, _In_ QWORD va) +{ + if(!pPteMap) { return NULL; } + return Util_qfind((PVOID)va, pPteMap->cMap, pPteMap->pMap, sizeof(VMM_MAP_PTEENTRY), (int(*)(PVOID, PVOID))VmmMap_GetPteEntry_CmpFind); +} + +/* +* Retrieve the VAD memory map. +* CALLER DECREF: ppObVadMap +* -- pProcess +* -- ppObVadMap +* -- fExtendedText +* -- return +*/ +_Success_(return) +BOOL VmmMap_GetVad(_In_ PVMM_PROCESS pProcess, _Out_ PVMMOB_MAP_VAD *ppObVadMap, _In_ BOOL fExtendedText) +{ + if(!MmVad_MapInitialize(pProcess, fExtendedText, 0)) { return FALSE; } + *ppObVadMap = Ob_INCREF(pProcess->Map.pObVad); + return TRUE; +} + +int VmmMap_GetVadEntry_CmpFind(_In_ QWORD vaFind, _In_ PVMM_MAP_VADENTRY pEntry) +{ + if(pEntry->vaStart > vaFind) { return -1; } + if(pEntry->vaEnd < vaFind) { return 1; } + return 0; +} + +/* +* Retrieve a single PVMM_MAP_VADENTRY for a given VadMap and address inside it. +* -- pVadMap +* -- va +* -- return = PTR to VADENTRY or NULL on fail. Must not be used out of pVadMap scope. +*/ +PVMM_MAP_VADENTRY VmmMap_GetVadEntry(_In_opt_ PVMMOB_MAP_VAD pVadMap, _In_ QWORD va) +{ + if(!pVadMap) { return NULL; } + return Util_qfind((PVOID)va, pVadMap->cMap, pVadMap->pMap, sizeof(VMM_MAP_VADENTRY), (int(*)(PVOID, PVOID))VmmMap_GetVadEntry_CmpFind); +} + +/* +* Retrieve the process module map. +* CALLER DECREF: ppObModuleMap +* -- pProcess +* -- ppObModuleMap +* -- return +*/ +_Success_(return) +BOOL VmmMap_GetModule(_In_ PVMM_PROCESS pProcess, _Out_ PVMMOB_MAP_MODULE *ppObModuleMap) +{ + if(!pProcess->Map.pObModule && !VmmWin_InitializeLdrModules(pProcess)) { return FALSE; } + *ppObModuleMap = Ob_INCREF(pProcess->Map.pObModule); + return TRUE; +} + +int VmmMap_GetModuleEntry_CmpFind(_In_ DWORD qwHash, _In_ PDWORD pdwEntry) +{ + if(*pdwEntry > qwHash) { return -1; } + if(*pdwEntry < qwHash) { return 1; } + return 0; +} + +/* +* Retrieve a single PVMM_MAP_MODULEENTRY for a given ModuleMap and module name inside it. +* -- pModuleMap +* -- wszModuleName +* -- return = PTR to VMM_MAP_MODULEENTRY or NULL on fail. Must not be used out of pModuleMap scope. +*/ +PVMM_MAP_MODULEENTRY VmmMap_GetModuleEntry(_In_ PVMMOB_MAP_MODULE pModuleMap, _In_ LPWSTR wszModuleName) +{ + QWORD qwHash, *pqwHashIndex; + WCHAR wsz[MAX_PATH]; + Util_PathFileNameFixW(wsz, wszModuleName, 0); + qwHash = Util_HashStringUpperW(wsz); + pqwHashIndex = (PQWORD)Util_qfind((PVOID)qwHash, pModuleMap->cMap, pModuleMap->pHashTableLookup, sizeof(QWORD), (int(*)(PVOID, PVOID))VmmMap_GetModuleEntry_CmpFind); + return pqwHashIndex ? &pModuleMap->pMap[*pqwHashIndex >> 32] : NULL; +} + +/* +* Retrieve the heap map. +* CALLER DECREF: ppObHeapMap +* -- pProcess +* -- ppObHeapMap +* -- return +*/ +_Success_(return) +BOOL VmmMap_GetHeap(_In_ PVMM_PROCESS pProcess, _Out_ PVMMOB_MAP_HEAP *ppObHeapMap) +{ + if(!pProcess->Map.pObHeap && !VmmWinHeap_Initialize(pProcess)) { return FALSE; } + *ppObHeapMap = Ob_INCREF(pProcess->Map.pObHeap); + return TRUE; +} + +/* +* LPTHREAD_START_ROUTINE for VmmMap_GetThreadAsync. +*/ +DWORD VmmMap_GetThreadAsync_Thread(_In_ PVMM_PROCESS pProcess) +{ + if(ctxVmm->ThreadWorkers.fEnabled) { + InterlockedIncrement(&ctxVmm->ThreadWorkers.c); + VmmWinThread_Initialize(pProcess, TRUE); + InterlockedDecrement(&ctxVmm->ThreadWorkers.c); + } + return 1; +} + +/* +* Start async initialization of the thread map. This may be done to speed up +* retrieval of the thread map in the future since processing to retrieve it +* has already been progressing for a while. This may be useful for processes +* with large amount of threads - such as the system process. +* -- pProcess +*/ +VOID VmmMap_GetThreadAsync(_In_ PVMM_PROCESS pProcess) +{ + HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)VmmMap_GetThreadAsync_Thread, pProcess, 0, NULL); + if(hThread) { CloseHandle(hThread); } +} + +/* +* Retrieve the thread map. +* CALLER DECREF: ppObThreadMap +* -- pProcess +* -- ppObThreadMap +* -- return +*/ +_Success_(return) +BOOL VmmMap_GetThread(_In_ PVMM_PROCESS pProcess, _Out_ PVMMOB_MAP_THREAD *ppObThreadMap) +{ + if(!pProcess->Map.pObThread && !VmmWinThread_Initialize(pProcess, FALSE)) { return FALSE; } + *ppObThreadMap = Ob_INCREF(pProcess->Map.pObThread); + return TRUE; +} + +int VmmMap_GetThreadEntry_CmpFind(_In_ DWORD dwTID, _In_ PVMM_MAP_THREADENTRY pEntry) +{ + if(pEntry->dwTID > dwTID) { return -1; } + if(pEntry->dwTID < dwTID) { return 1; } + return 0; +} + +/* +* Retrieve a single PVMM_MAP_THREADENTRY for a given ThreadMap and ThreadID. +* -- pThreadMap +* -- dwTID +* -- return = PTR to VMM_MAP_THREADENTRY or NULL on fail. Must not be used out of pThreadMap scope. +*/ +PVMM_MAP_THREADENTRY VmmMap_GetThreadEntry(_In_ PVMMOB_MAP_THREAD pThreadMap, _In_ DWORD dwTID) +{ + QWORD qwTID = dwTID; + return Util_qfind((PVOID)qwTID, pThreadMap->cMap, pThreadMap->pMap, sizeof(VMM_MAP_THREADENTRY), (int(*)(PVOID, PVOID))VmmMap_GetThreadEntry_CmpFind); +} + +/* +* Retrieve the HANDLE map +* CALLER DECREF: ppObHandleMap +* -- pProcess +* -- ppObHandleMap +* -- fExtendedText +* -- return +*/ +_Success_(return) +BOOL VmmMap_GetHandle(_In_ PVMM_PROCESS pProcess, _Out_ PVMMOB_MAP_HANDLE *ppObHandleMap, _In_ BOOL fExtendedText) +{ + if(!VmmWinHandle_Initialize(pProcess, fExtendedText)) { return FALSE; } + *ppObHandleMap = Ob_INCREF(pProcess->Map.pObHandle); + return TRUE; +} + // ---------------------------------------------------------------------------- // PROCESS MANAGEMENT FUNCTIONALITY: // @@ -607,8 +836,10 @@ PVMM_PROCESS VmmProcessGetNextEx(_In_opt_ PVMMOB_PROCESS_TABLE pt, _In_opt_ PVMM VOID VmmProcessStatic_CloseObCallback(_In_ PVOID pVmmOb) { PVMMOB_PROCESS_PERSISTENT pProcessStatic = (PVMMOB_PROCESS_PERSISTENT)pVmmOb; - Ob_DECREF_NULL(&pProcessStatic->pObCLdrModulesCachePrefetch32); - Ob_DECREF_NULL(&pProcessStatic->pObCLdrModulesCachePrefetch64); + Ob_DECREF_NULL(&pProcessStatic->pObCMapVadPrefetch); + Ob_DECREF_NULL(&pProcessStatic->pObCLdrModulesPrefetch32); + Ob_DECREF_NULL(&pProcessStatic->pObCLdrModulesPrefetch64); + Ob_DECREF_NULL(&pProcessStatic->pObCMapThreadPrefetch); LocalFree(pProcessStatic->UserProcessParams.szCommandLine); LocalFree(pProcessStatic->UserProcessParams.szImagePathName); } @@ -620,11 +851,13 @@ VOID VmmProcessStatic_CloseObCallback(_In_ PVOID pVmmOb) VOID VmmProcessStatic_Initialize(_In_ PVMM_PROCESS pProcess) { EnterCriticalSection(&pProcess->LockUpdate); - Ob_DECREF_NULL(&pProcess->pObProcessPersistent); - pProcess->pObProcessPersistent = Ob_Alloc('PS', LMEM_ZEROINIT, sizeof(VMMOB_PROCESS_PERSISTENT), VmmProcessStatic_CloseObCallback, NULL); - if(pProcess->pObProcessPersistent) { - pProcess->pObProcessPersistent->pObCLdrModulesCachePrefetch32 = ObContainer_New(NULL); - pProcess->pObProcessPersistent->pObCLdrModulesCachePrefetch64 = ObContainer_New(NULL); + Ob_DECREF_NULL(&pProcess->pObPersistent); + pProcess->pObPersistent = Ob_Alloc(OB_TAG_VMM_PROCESS_PERSISTENT, LMEM_ZEROINIT, sizeof(VMMOB_PROCESS_PERSISTENT), VmmProcessStatic_CloseObCallback, NULL); + if(pProcess->pObPersistent) { + pProcess->pObPersistent->pObCMapVadPrefetch = ObContainer_New(NULL); + pProcess->pObPersistent->pObCLdrModulesPrefetch32 = ObContainer_New(NULL); + pProcess->pObPersistent->pObCLdrModulesPrefetch64 = ObContainer_New(NULL); + pProcess->pObPersistent->pObCMapThreadPrefetch = ObContainer_New(NULL); } LeaveCriticalSection(&pProcess->LockUpdate); } @@ -637,15 +870,21 @@ VOID VmmProcess_CloseObCallback(_In_ PVOID pVmmOb) { PVMM_PROCESS pProcess = (PVMM_PROCESS)pVmmOb; // general cleanup below - Ob_DECREF(pProcess->pObMemMap); - Ob_DECREF(pProcess->pObModuleMap); - Ob_DECREF(pProcess->pObProcessPersistent); + Ob_DECREF(pProcess->Map.pObPte); + Ob_DECREF(pProcess->Map.pObVad); + Ob_DECREF(pProcess->Map.pObModule); + Ob_DECREF(pProcess->Map.pObHeap); + Ob_DECREF(pProcess->Map.pObThread); + Ob_DECREF(pProcess->Map.pObHandle); + Ob_DECREF(pProcess->pObPersistent); // plugin cleanup below Ob_DECREF(pProcess->Plugin.pObCLdrModulesDisplayCache); Ob_DECREF(pProcess->Plugin.pObCPeDumpDirCache); Ob_DECREF(pProcess->Plugin.pObCPhys2Virt); // delete lock DeleteCriticalSection(&pProcess->LockUpdate); + DeleteCriticalSection(&pProcess->Map.LockUpdateThreadMap); + DeleteCriticalSection(&pProcess->Map.LockUpdateExtendedInfo); } /* @@ -724,6 +963,8 @@ PVMM_PROCESS VmmProcessCreateEntry(_In_ BOOL fTotalRefresh, _In_ DWORD dwPID, _I pProcess = (PVMM_PROCESS)Ob_Alloc(OB_TAG_VMM_PROCESS, LMEM_ZEROINIT, sizeof(VMM_PROCESS), VmmProcess_CloseObCallback, NULL); if(!pProcess) { goto fail; } InitializeCriticalSectionAndSpinCount(&pProcess->LockUpdate, 4096); + InitializeCriticalSection(&pProcess->Map.LockUpdateThreadMap); + InitializeCriticalSection(&pProcess->Map.LockUpdateExtendedInfo); memcpy(pProcess->szName, szName, 16); pProcess->szName[15] = 0; pProcess->dwPID = dwPID; @@ -743,7 +984,7 @@ PVMM_PROCESS VmmProcessCreateEntry(_In_ BOOL fTotalRefresh, _In_ DWORD dwPID, _I // attach pre-existing static process info entry or create new pProcessOld = VmmProcessGet(dwPID); if(pProcessOld) { - pProcess->pObProcessPersistent = (PVMMOB_PROCESS_PERSISTENT)Ob_INCREF(pProcessOld->pObProcessPersistent); + pProcess->pObPersistent = (PVMMOB_PROCESS_PERSISTENT)Ob_INCREF(pProcessOld->pObPersistent); } else { VmmProcessStatic_Initialize(pProcess); } @@ -932,7 +1173,7 @@ VOID VmmProcessActionForeachParallel(_In_opt_ PVOID ctx, _In_opt_ DWORD dwThread pObProcess = VmmProcessGet(dwPID); if(pObProcess) { if(!ctxObForeach) { - ctxObForeach = Ob_Alloc('ea', 0, sizeof(VMMOB_PROCESS_ACTION_FOREACH) + dwThreadLoadFactor * sizeof(PVMM_PROCESS), VmmProcessActionForeachParallel_CloseObCallback, NULL); + ctxObForeach = Ob_Alloc('ea__', 0, sizeof(VMMOB_PROCESS_ACTION_FOREACH) + dwThreadLoadFactor * sizeof(PVMM_PROCESS), VmmProcessActionForeachParallel_CloseObCallback, NULL); if(!ctxObForeach) { goto fail; } ctxObForeach->ctx = ctx; ctxObForeach->hSemaphore = hSemaphore; @@ -1060,12 +1301,13 @@ VOID VmmReadScatterPhysical(_Inout_ PPMEM_IO_SCATTER_HEADER ppMEMsPhys, _In_ DWO // 2: speculative future read if negligible performance loss if(fCache && cSpeculative && (cSpeculative < 0x18)) { while(cSpeculative < 0x18) { - ppObCacheSpeculative[cSpeculative] = VmmCacheReserve(VMM_CACHE_TAG_PHYS); - ppMEMsSpeculative[cSpeculative] = &ppObCacheSpeculative[cSpeculative]->h; - ppMEMsSpeculative[cSpeculative]->cb = 0; - ppMEMsSpeculative[cSpeculative]->qwA = ((QWORD)ppMEMsSpeculative[cSpeculative - 1]->qwA & ~0xfff) + 0x1000; - ppMEMsSpeculative[cSpeculative]->pvReserved2 = (PVOID)3; // 3 == speculative & backed by cache reserved - cSpeculative++; + if((ppObCacheSpeculative[cSpeculative] = VmmCacheReserve(VMM_CACHE_TAG_PHYS))) { + ppMEMsSpeculative[cSpeculative] = &ppObCacheSpeculative[cSpeculative]->h; + ppMEMsSpeculative[cSpeculative]->cb = 0; + ppMEMsSpeculative[cSpeculative]->qwA = ((QWORD)ppMEMsSpeculative[cSpeculative - 1]->qwA & ~0xfff) + 0x1000; + ppMEMsSpeculative[cSpeculative]->pvReserved2 = (PVOID)3; // 3 == speculative & backed by cache reserved + cSpeculative++; + } } ppMEMsPhys = ppMEMsSpeculative; cpMEMsPhys = cSpeculative; @@ -1095,95 +1337,12 @@ VOID VmmReadScatterPhysical(_Inout_ PPMEM_IO_SCATTER_HEADER ppMEMsPhys, _In_ DWO VmmCacheReserveReturn(ppObCacheSpeculative[i]); } if((0 == (QWORD)pMEM->pvReserved2) && (pMEM->cb == 0x1000)) { // 0 = default - pObReservedMEM = VmmCacheReserve(VMM_CACHE_TAG_PHYS); - pObReservedMEM->h.qwA = pMEM->qwA; - pObReservedMEM->h.cb = 0x1000; - memcpy(pObReservedMEM->h.pb, pMEM->pb, 0x1000); - VmmCacheReserveReturn(pObReservedMEM); - } - } - } -} - -/* -* Scatter read paged virtual memory if possible and supported. Non contiguous 4096-byte pages. -* -- pProcess -* -- ppMEMsPaged -* -- cpMEMsPaged -*/ -VOID VmmReadScatterPaged(_In_ PVMM_PROCESS pProcess, _Inout_ PPMEM_IO_SCATTER_HEADER ppMEMsPaged, _In_ DWORD cpMEMsPaged, _In_ QWORD flags) -{ - BOOL fCache, fPaged; - DWORD c, i; - PVMMOB_MEM pObCacheEntry, pObReservedMEM; - PMEM_IO_SCATTER_HEADER pMEM; - fCache = !(VMM_FLAG_NOCACHE & (flags | ctxVmm->flags)); - fPaged = !(VMM_FLAG_NOPAGING & (flags | ctxVmm->flags)); - // 1: cache read - if(fCache && fPaged) { - c = 0; - for(i = 0; i < cpMEMsPaged; i++) { - pMEM = ppMEMsPaged[i]; - pMEM->pvReserved2 = (PVOID)0; - if(pMEM->cb == pMEM->cbMax) { - // already valid -> skip - pMEM->pvReserved2 = (PVOID)1; // 1 == already read - c++; - continue; - } - // retrieve from cache (if found) - if((pMEM->cbMax == 0x1000) && (pObCacheEntry = VmmCacheGet(VMM_CACHE_TAG_PAGING, pMEM->qwA))) { - // in cache - copy data into requester and set as completed! - pMEM->pvReserved2 = (PVOID)2; // 2 == cache hit - pMEM->cb = 0x1000; - memcpy(pMEM->pb, pObCacheEntry->pb, 0x1000); - Ob_DECREF(pObCacheEntry); - - pObCacheEntry = VmmCacheGet(VMM_CACHE_TAG_PAGING, pMEM->qwA); - Ob_DECREF(pObCacheEntry); - - InterlockedIncrement64(&ctxVmm->stat.cPageReadSuccessCacheHit); - c++; - continue; - } - // known failed page (with large overhead) - if(ObVSet_Exists(ctxVmm->Cache.PAGING_FAILED, pMEM->qwA)) { - pMEM->pvReserved2 = (PVOID)3; // 2 == cache fail - InterlockedIncrement64(&ctxVmm->stat.cPageReadFailedCacheHit); - c++; - continue; - } - } - if(c == cpMEMsPaged) { return; } // all found in cache -> return! - if(VMM_FLAG_FORCECACHE_READ & flags) { return; } // only cached reads allowed -> return! - } - // 2: read! - if(fCache && fPaged) { - if(ctxVmm->tpSystem == VMM_SYSTEM_WINDOWS_X64) { - MmX64PageWin_ReadScatterPaged(pProcess, ppMEMsPaged, cpMEMsPaged); - } - } - // 3: cache put - if(fCache && fPaged) { - for(i = 0; i < cpMEMsPaged; i++) { - pMEM = ppMEMsPaged[i]; - if((0 == (QWORD)pMEM->pvReserved2) && (pMEM->cb == 0x1000)) { // 0 = default - pObReservedMEM = VmmCacheReserve(VMM_CACHE_TAG_PAGING); - pObReservedMEM->h.qwA = pMEM->qwA; - pObReservedMEM->h.cb = 0x1000; - memcpy(pObReservedMEM->h.pb, pMEM->pb, 0x1000); - VmmCacheReserveReturn(pObReservedMEM); - } - } - } - // 4: read fail zero fixups (if required) - if(flags & VMM_FLAG_ZEROPAD_ON_FAIL) { - for(i = 0; i < cpMEMsPaged; i++) { - pMEM = ppMEMsPaged[i]; - if(pMEM->cb != pMEM->cbMax) { - // fail - ZeroMemory(pMEM->pb, pMEM->cbMax); - pMEM->cb = pMEM->cbMax; + if((pObReservedMEM = VmmCacheReserve(VMM_CACHE_TAG_PHYS))) { + pObReservedMEM->h.qwA = pMEM->qwA; + pObReservedMEM->h.cb = 0x1000; + memcpy(pObReservedMEM->h.pb, pMEM->pb, 0x1000); + VmmCacheReserveReturn(pObReservedMEM); + } } } } @@ -1195,8 +1354,8 @@ VOID VmmReadScatterVirtual(_In_ PVMM_PROCESS pProcess, _Inout_updates_(cpMEMsVir // - physical memory (grows from 0 upwards) // - paged memory (grows from top downwards). BOOL fVirt2Phys; - DWORD i = 0, iVA, iPA, iPGA; - QWORD qwPA; + DWORD i = 0, iVA, iPA; + QWORD qwPA, qwPagedPA = 0; BYTE pbBufferSmall[0x20 * (sizeof(MEM_IO_SCATTER_HEADER) + sizeof(PMEM_IO_SCATTER_HEADER))]; PBYTE pbBufferMEMs, pbBufferLarge = NULL; PMEM_IO_SCATTER_HEADER pIoPA, pIoVA; @@ -1211,17 +1370,29 @@ VOID VmmReadScatterVirtual(_In_ PVMM_PROCESS pProcess, _Inout_updates_(cpMEMsVir pbBufferMEMs = pbBufferLarge + cpMEMsVirt * sizeof(PMEM_IO_SCATTER_HEADER); } // 2: translate virt2phys - for(iVA = 0, iPA = 0, iPGA = 0; iVA < cpMEMsVirt; iVA++) { + for(iVA = 0, iPA = 0; iVA < cpMEMsVirt; iVA++) { pIoVA = ppMEMsVirt[iVA]; + qwPA = 0; fVirt2Phys = VmmVirt2Phys(pProcess, pIoVA->qwA, &qwPA); - if(fVirt2Phys) { // PHYS transation = OK + // PAGED MEMORY + if(!fVirt2Phys && !(VMM_FLAG_NOPAGING & (flags | ctxVmm->flags)) && (pIoVA->cbMax == 0x1000) && ctxVmm->fnMemoryModel.pfnPagedRead) { + if(ctxVmm->fnMemoryModel.pfnPagedRead(pProcess, pIoVA->qwA, qwPA, pIoVA->pb, &qwPagedPA, flags)) { + pIoVA->cb = 0x1000; + continue; + } + if(qwPagedPA) { + qwPA = qwPagedPA; + fVirt2Phys = TRUE; + } + } + if(fVirt2Phys) { // PHYS MEMORY pIoPA = ppMEMsPhys[iPA] = (PMEM_IO_SCATTER_HEADER)pbBufferMEMs + iPA; iPA++; - } else if(qwPA) { // PAGED MEMORY (POTENTIALLY PAGED MEMORY) - iPGA++; - pIoPA = ppMEMsPhys[cpMEMsVirt - iPGA] = (PMEM_IO_SCATTER_HEADER)pbBufferMEMs + cpMEMsVirt - iPGA; - } else { // NO TRANSLATION MEMORY + } else { // NO TRANSLATION MEMORY / FAILED PAGED MEMORY pIoVA->cb = 0; + if(VMM_FLAG_ZEROPAD_ON_FAIL & (flags | ctxVmm->flags)) { + ZeroMemory(pIoVA->pb, pIoVA->cbMax); + } continue; } pIoPA->magic = MEM_IO_SCATTER_HEADER_MAGIC; @@ -1240,13 +1411,6 @@ VOID VmmReadScatterVirtual(_In_ PVMM_PROCESS pProcess, _Inout_updates_(cpMEMsVir ((PMEM_IO_SCATTER_HEADER)ppMEMsPhys[iPA]->pvReserved1)->cb = ppMEMsPhys[iPA]->cb; } } - if(iPGA) { - VmmReadScatterPaged(pProcess, ppMEMsPhys + cpMEMsVirt - iPGA, iPGA, flags); - while(iPGA > 0) { - ((PMEM_IO_SCATTER_HEADER)ppMEMsPhys[cpMEMsVirt - iPGA]->pvReserved1)->cb = ppMEMsPhys[cpMEMsVirt - iPGA]->cb; - iPGA--; - } - } LocalFree(pbBufferLarge); } @@ -1267,9 +1431,9 @@ PVMMOB_PHYS2VIRT_INFORMATION VmmPhys2VirtGetInformation(_In_ PVMM_PROCESS pProce { PVMMOB_PHYS2VIRT_INFORMATION pObP2V = NULL; if(paTarget) { - pProcess->pObProcessPersistent->Plugin.paPhys2Virt = paTarget; + pProcess->pObPersistent->Plugin.paPhys2Virt = paTarget; } else { - paTarget = pProcess->pObProcessPersistent->Plugin.paPhys2Virt; + paTarget = pProcess->pObPersistent->Plugin.paPhys2Virt; } pObP2V = ObContainer_GetOb(pProcess->Plugin.pObCPhys2Virt); if(paTarget && (!pObP2V || (pObP2V->paTarget != paTarget))) { @@ -1278,7 +1442,7 @@ PVMMOB_PHYS2VIRT_INFORMATION VmmPhys2VirtGetInformation(_In_ PVMM_PROCESS pProce pObP2V = ObContainer_GetOb(pProcess->Plugin.pObCPhys2Virt); if(paTarget && (!pObP2V || (pObP2V->paTarget != paTarget))) { Ob_DECREF_NULL(&pObP2V); - pObP2V = Ob_Alloc('PV', LMEM_ZEROINIT, sizeof(VMMOB_PHYS2VIRT_INFORMATION), NULL, NULL); + pObP2V = Ob_Alloc('PAVA', LMEM_ZEROINIT, sizeof(VMMOB_PHYS2VIRT_INFORMATION), NULL, NULL); pObP2V->paTarget = paTarget; pObP2V->dwPID = pProcess->dwPID; if(ctxVmm->fnMemoryModel.pfnPhys2VirtGetInformation) { @@ -1292,7 +1456,7 @@ PVMMOB_PHYS2VIRT_INFORMATION VmmPhys2VirtGetInformation(_In_ PVMM_PROCESS pProce EnterCriticalSection(&pProcess->LockUpdate); pObP2V = ObContainer_GetOb(pProcess->Plugin.pObCPhys2Virt); if(!pObP2V) { - pObP2V = Ob_Alloc('PV', LMEM_ZEROINIT, sizeof(VMMOB_PHYS2VIRT_INFORMATION), NULL, NULL); + pObP2V = Ob_Alloc('PAVA', LMEM_ZEROINIT, sizeof(VMMOB_PHYS2VIRT_INFORMATION), NULL, NULL); pObP2V->dwPID = pProcess->dwPID; ObContainer_SetOb(pProcess->Plugin.pObCPhys2Virt, pObP2V); } @@ -1326,14 +1490,17 @@ VOID VmmClose() if(ctxVmm->fnMemoryModel.pfnClose) { ctxVmm->fnMemoryModel.pfnClose(); } + MmWin_PagingClose(); VmmCache2Close(VMM_CACHE_TAG_PHYS); VmmCache2Close(VMM_CACHE_TAG_TLB); VmmCache2Close(VMM_CACHE_TAG_PAGING); Ob_DECREF_NULL(&ctxVmm->Cache.PAGING_FAILED); + Ob_DECREF_NULL(&ctxVmm->Cache.pmPrototypePte); Ob_DECREF_NULL(&ctxVmm->pObCCachePrefetchEPROCESS); Ob_DECREF_NULL(&ctxVmm->pObCCachePrefetchRegistry); DeleteCriticalSection(&ctxVmm->TcpIp.LockUpdate); DeleteCriticalSection(&ctxVmm->MasterLock); + LocalFree(ctxVmm->ObjectTypeTable.wszMultiText); LocalFree(ctxVmm); ctxVmm = NULL; } @@ -1452,6 +1619,41 @@ VOID VmmReadEx(_In_opt_ PVMM_PROCESS pProcess, _In_ QWORD qwA, _Out_writes_(cb) LocalFree(pbBuffer); } +#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) +#define STATUS_END_OF_FILE ((NTSTATUS)0xC0000011L) + +NTSTATUS VmmReadAsFile(_In_opt_ PVMM_PROCESS pProcess, _In_ QWORD qwMemoryAddress, _In_ QWORD cbMemorySize, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset) +{ + QWORD cbMax; + if(cbMemorySize <= cbOffset) { + *pcbRead = 0; + return STATUS_END_OF_FILE; + } + cbMax = min(qwMemoryAddress + cbMemorySize, (qwMemoryAddress + cb + cbOffset)) - (qwMemoryAddress + cbOffset); // min(entry_top_addr, request_top_addr) - request_start_addr + *pcbRead = (DWORD)min(cb, cbMax); + if(!*pcbRead) { + return STATUS_END_OF_FILE; + } + VmmReadEx(pProcess, qwMemoryAddress + cbOffset, pb, *pcbRead, NULL, VMM_FLAG_ZEROPAD_ON_FAIL); + return STATUS_SUCCESS; +} + +NTSTATUS VmmWriteAsFile(_In_opt_ PVMM_PROCESS pProcess, _In_ QWORD qwMemoryAddress, _In_ QWORD cbMemorySize, _In_reads_(cb) PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbWrite, _In_ QWORD cbOffset) +{ + QWORD cbMax; + if(cbMemorySize <= cbOffset) { + *pcbWrite = 0; + return STATUS_END_OF_FILE; + } + cbMax = min(qwMemoryAddress + cbMemorySize, (qwMemoryAddress + cb + cbOffset)) - (qwMemoryAddress + cbOffset); // min(entry_top_addr, request_top_addr) - request_start_addr + *pcbWrite = (DWORD)min(cb, cbMax); + if(!*pcbWrite) { + return STATUS_END_OF_FILE; + } + VmmWriteEx(pProcess, qwMemoryAddress + cbOffset, pb, *pcbWrite, NULL); + return STATUS_SUCCESS; +} + _Success_(return) BOOL VmmRead_U2A_Size(_In_ PVMM_PROCESS pProcess, _In_ BOOL f32, _In_ QWORD flags, _In_ QWORD vaUS, _Out_ PQWORD pvaStr, _Out_ PWORD pcbStr) { @@ -1537,7 +1739,15 @@ BOOL VmmRead(_In_opt_ PVMM_PROCESS pProcess, _In_ QWORD qwA, _Out_writes_(cb) PB } _Success_(return) -BOOL VmmReadPage(_In_opt_ PVMM_PROCESS pProcess, _In_ QWORD qwA, _Inout_bytecount_(4096) PBYTE pbPage) +BOOL VmmRead2(_In_opt_ PVMM_PROCESS pProcess, _In_ QWORD qwA, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb, _In_ QWORD flags) +{ + DWORD cbRead; + VmmReadEx(pProcess, qwA, pb, cb, &cbRead, flags); + return (cbRead == cb); +} + +_Success_(return) +BOOL VmmReadPage(_In_opt_ PVMM_PROCESS pProcess, _In_ QWORD qwA, _Out_writes_(4096) PBYTE pbPage) { DWORD cb; VmmReadEx(pProcess, qwA, pbPage, 0x1000, &cb, 0); @@ -1591,7 +1801,9 @@ BOOL VmmInitialize() VmmCache2Initialize(VMM_CACHE_TAG_PAGING); if(!ctxVmm->Cache.PAGING.fActive) { goto fail; } if(!(ctxVmm->Cache.PAGING_FAILED = ObVSet_New())) { goto fail; } - // 6: OTHER INIT: + // 6: CACHE INIT: Prototype PTE Cache Map + if(!(ctxVmm->Cache.pmPrototypePte = ObMap_New(OB_MAP_FLAGS_OBJECT_OB))) { goto fail; } + // 7: OTHER INIT: ctxVmm->pObCCachePrefetchEPROCESS = ObContainer_New(NULL); ctxVmm->pObCCachePrefetchRegistry = ObContainer_New(NULL); InitializeCriticalSection(&ctxVmm->MasterLock); diff --git a/vmm/vmm.h b/vmm/vmm.h index c896b665..3e856860 100644 --- a/vmm/vmm.h +++ b/vmm/vmm.h @@ -32,26 +32,45 @@ typedef unsigned __int64 QWORD, *PQWORD; #define VMM_MEMMAP_PAGE_MASK 0x8000000000000006 #define VMM_MEMMAP_FLAG_MODULES 0x0001 -#define VMM_MEMMAP_FLAG_SCAN 0x0002 -#define VMM_MEMMAP_FLAG_ALL (VMM_MEMMAP_FLAG_MODULES | VMM_MEMMAP_FLAG_SCAN) +#define VMM_MEMMAP_FLAG_SCAN_PE 0x0002 +#define VMM_MEMMAP_FLAG_ALL (VMM_MEMMAP_FLAG_MODULES | VMM_MEMMAP_FLAG_SCAN_PE) #define VMM_CACHE_TABLESIZE 0x4011 // (not even # to prevent clogging at specific table 'hash' buckets) #define VMM_CACHE_TLB_ENTRIES 0x4000 // -> 64MB of cached data #define VMM_CACHE_PHYS_ENTRIES 0x4000 // -> 64MB of cached data -#define VMM_FLAG_NOCACHE 0x0001 // do not use the data cache (force reading from memory acquisition device) -#define VMM_FLAG_ZEROPAD_ON_FAIL 0x0002 // zero pad failed physical memory reads and report success if read within range of physical memory. -#define VMM_FLAG_PROCESS_SHOW_TERMINATED 0x0004 // show terminated processes in the process list (if they can be found). -#define VMM_FLAG_FORCECACHE_READ 0x0008 // force use of cache - fail non-cached pages - only valid for reads, invalid with VMM_FLAG_NOCACHE/VMM_FLAG_ZEROPAD_ON_FAIL. -#define VMM_FLAG_NOPAGING 0x0010 // do not try to retrieve memory from paged out memory from pagefile/compressed (even if possible). +#define VMM_FLAG_NOCACHE 0x00000001 // do not use the data cache (force reading from memory acquisition device). +#define VMM_FLAG_ZEROPAD_ON_FAIL 0x00000002 // zero pad failed physical memory reads and report success if read within range of physical memory. +#define VMM_FLAG_PROCESS_SHOW_TERMINATED 0x00000004 // show terminated processes in the process list (if they can be found). +#define VMM_FLAG_FORCECACHE_READ 0x00000008 // force use of cache - fail non-cached pages - only valid for reads, invalid with VMM_FLAG_NOCACHE/VMM_FLAG_ZEROPAD_ON_FAIL. +#define VMM_FLAG_NOPAGING 0x00000010 // do not try to retrieve memory from paged out memory (even if possible). +#define VMM_FLAG_NOPAGING_IO 0x00000020 // do not try to retrieve memory from paged out memory if read would incur additional I/O (even if possible). +#define VMM_FLAG_PAGING_LOOP_PROTECT_BITS 0x00ff0000 // placeholder bits for paging loop protect counter. +#define VMM_FLAG_NOVAD 0x01000000 // do not try to retrieve memory from backing VAD even if otherwise possible. #define PAGE_SIZE 0x1000 - -#define VMM_KADDR32_8(va) ((va & 0xf0000007) == 0x80000000) +#define VMM_POOLTAG(v, tag) (v == _byteswap_ulong(tag)) +#define VMM_POOLTAG_PREPENDED(pb, o, tag) (VMM_POOLTAG(*(PDWORD)(pb + o - (ctxVmm->f32 ? 4 : 12)), tag)) +#define VMM_PTR_OFFSET(f32, pb, o) (f32 ? *(PDWORD)(o + (PBYTE)pb) : *(PQWORD)(o + (PBYTE)pb)) + +#define VMM_KADDR32(va) ((va & 0x80000000) == 0x80000000) +#define VMM_KADDR32_4(va) ((va & 0x80000003) == 0x80000000) +#define VMM_KADDR32_8(va) ((va & 0x80000007) == 0x80000000) +#define VMM_KADDR32_PAGE(va) ((va & 0x80000fff) == 0x80000000) +#define VMM_UADDR32(va) ((va & 0x80000000) == 0) +#define VMM_UADDR32_4(va) ((va & 0x80000003) == 0) +#define VMM_UADDR32_8(va) ((va & 0x80000007) == 0) +#define VMM_UADDR32_PAGE(va) ((va & 0x80000fff) == 0) #define VMM_KADDR64(va) ((va & 0xffff8000'00000000) == 0xffff8000'00000000) #define VMM_KADDR64_8(va) ((va & 0xffff8000'00000007) == 0xffff8000'00000000) #define VMM_KADDR64_16(va) ((va & 0xffff8000'0000000f) == 0xffff8000'00000000) #define VMM_KADDR64_PAGE(va) ((va & 0xffff8000'00000fff) == 0xffff8000'00000000) +#define VMM_UADDR64(va) ((va & 0xffff8000'00000000) == 0) +#define VMM_UADDR64_8(va) ((va & 0xffff8000'00000007) == 0) +#define VMM_UADDR64_16(va) ((va & 0xffff8000'0000000f) == 0) +#define VMM_UADDR64_PAGE(va) ((va & 0xffff8000'00000fff) == 0) +#define VMM_KADDR(va) (ctxVmm->f32 ? VMM_KADDR32(va) : VMM_KADDR64(va)) +#define VMM_KADDR_PAGE(va) (ctxVmm->f32 ? VMM_KADDR32_PAGE(va) : VMM_KADDR64_PAGE(va)) static const LPSTR VMM_MEMORYMODEL_TOSTRING[4] = { "N/A", "X86", "X86PAE", "X64" }; @@ -69,65 +88,175 @@ typedef enum tdVMM_SYSTEM_TP { VMM_SYSTEM_WINDOWS_X86 = 4 } VMM_SYSTEM_TP; -typedef struct tdVMM_MEMMAP_ENTRY { - QWORD AddrBase; +typedef struct tdVMM_MAP_PTEENTRY { + QWORD vaBase; QWORD cPages; QWORD fPage; BOOL fWoW64; - CHAR szTag[32]; -} VMM_MEMMAP_ENTRY, *PVMM_MEMMAP_ENTRY; - -typedef struct tdVMM_MODULEMAP_ENTRY { - QWORD BaseAddress; - QWORD EntryPoint; - DWORD SizeOfImage; + DWORD cwszText; + LPWSTR wszText; + DWORD _Reserved1[2]; +} VMM_MAP_PTEENTRY, *PVMM_MAP_PTEENTRY; + +typedef struct tdVMM_MAP_VADENTRY { + QWORD vaStart; + QWORD vaEnd; + QWORD vaVad; + union { + struct { + // DWORD 0 + DWORD VadType : 3; // Pos 0 + DWORD Protection : 5; // Pos 3 + DWORD fImage : 1; // Pos 8 + DWORD fFile : 1; // Pos 9 + DWORD fPageFile : 1; // Pos 10 + DWORD fPrivateMemory : 1; // Pos 11 + DWORD fTeb : 1; // Pos 12 + DWORD fStack : 1; // Pos 13 + DWORD fSpare : 10; // Pos 14 + DWORD HeapNum : 7; // Pos 24 + DWORD fHeap : 1; // Pos 31 + // DWORD 1 + DWORD CommitCharge : 31; // Pos 0 + DWORD MemCommit : 1; // Pos 31 + // DWORD 2 + DWORD FileOffset : 24; // Pos 0 + DWORD Large : 1; // Pos 24 + DWORD TrimBehind : 1; // Pos 25 + DWORD Inherit : 1; // Pos 26 + DWORD CopyOnWrite : 1; // Pos 27 + DWORD NoValidationNeeded : 1; // Pos 28 + DWORD _Spare2 : 3; // Pos 29 + }; + DWORD flags[3]; + }; + DWORD cbPrototypePte; + QWORD vaPrototypePte; + QWORD vaSubsection; + LPWSTR wszText; // optional LPWSTR pointed into VMMOB_MAP_VAD.wszMultiText + DWORD cwszText; // WCHAR count not including terminating null + DWORD _Reserved1; +} VMM_MAP_VADENTRY, *PVMM_MAP_VADENTRY; + +typedef struct tdVMM_MAP_MODULEENTRY { + QWORD vaBase; + QWORD vaEntry; + DWORD cbImageSize; BOOL fWoW64; - CHAR szName[32]; - // # of entries in EAT / IAT (lazy loaded due to performance reasons) + LPWSTR wszText; // LPWSTR pointed into VMM_MAP_MODULE.wszMultiText + DWORD cwszText; // WCHAR count not including terminating null + // optional internal fields lazy loaded due to perfomance reasons BOOL fLoadedEAT; - DWORD cbDisplayBufferEAT; BOOL fLoadedIAT; - DWORD cbDisplayBufferIAT; DWORD cbDisplayBufferSections; + union { + struct { + DWORD cbDisplayBufferIAT; + DWORD cbDisplayBufferEAT; + }; + QWORD _Reserved1; + }; DWORD cbFileSizeRaw; - QWORD BaseDllName_Buffer; - WORD BaseDllName_Length; -} VMM_MODULEMAP_ENTRY, *PVMM_MODULEMAP_ENTRY; - -typedef struct tdVMMDATALIST { - DWORD iNext; - QWORD Value; -} VMMDATALIST, *PVMMDATALIST; + DWORD _Reserved2; +} VMM_MAP_MODULEENTRY, *PVMM_MAP_MODULEENTRY; -typedef struct tdVMMOB_DATA { - OB ObHdr; +typedef struct tdVMM_MAP_HEAPENTRY { + QWORD vaHeapSegment; union { - BYTE pbData[]; - DWORD pdwData[]; - QWORD pqwData[]; - VMMDATALIST pList[]; + struct { + DWORD cPages; + DWORD cPagesUnCommitted : 24; + DWORD HeapId : 7; + DWORD fPrimary : 1; + }; + QWORD qwHeapData; }; -} VMMOB_DATA, *PVMMOB_DATA; +} VMM_MAP_HEAPENTRY, *PVMM_MAP_HEAPENTRY; + +typedef struct tdVMM_MAP_THREADENTRY { + DWORD dwTID; + DWORD dwPID; + DWORD dwExitStatus; + UCHAR bState; + UCHAR bRunning; + UCHAR bPriority; + UCHAR bBasePriority; + QWORD vaETHREAD; + QWORD vaTeb; + QWORD ftCreateTime; + QWORD ftExitTime; + QWORD vaStartAddress; + QWORD vaStackBaseUser; // value from _NT_TIB / _TEB + QWORD vaStackLimitUser; // value from _NT_TIB / _TEB + QWORD vaStackBaseKernel; + QWORD vaStackLimitKernel; + DWORD _FutureUse[10]; +} VMM_MAP_THREADENTRY, *PVMM_MAP_THREADENTRY; + +typedef struct tdVMM_MAP_HANDLEENTRY { + QWORD vaObject; + DWORD dwHandle; + DWORD dwGrantedAccess : 24; + DWORD iType : 8; + QWORD qwHandleCount; + QWORD qwPointerCount; + QWORD vaObjectCreateInfo; + QWORD vaSecurityDescriptor; + LPWSTR wszText; // optional LPWSTR pointed into VMMOB_MAP_HANDLE.wszMultiText + DWORD cwszText; // WCHAR count not including terminating null + DWORD dwPID; + DWORD dwPoolTag; + DWORD _FutureUse[4]; + DWORD _Reserved1; + QWORD _Reserved2; +} VMM_MAP_HANDLEENTRY, *PVMM_MAP_HANDLEENTRY; + +typedef struct tdVMMOB_MAP_PTE { + OB ObHdr; + LPWSTR wszMultiText; // NULL or multi-wstr pointed into by VMM_MAP_PTEENTRY.wszText + DWORD cbMultiText; + BOOL fTagScan; // map contains tags from modules and scan. + DWORD cMap; // # map entries. + VMM_MAP_PTEENTRY pMap[]; // map entries. +} VMMOB_MAP_PTE, *PVMMOB_MAP_PTE; + +typedef struct tdVMMOB_MAP_VAD { + OB ObHdr; + BOOL fSpiderPrototypePte; + LPWSTR wszMultiText; // NULL or multi-wstr pointed into by VMM_MAP_VADENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMM_MAP_VADENTRY pMap[]; // map entries. +} VMMOB_MAP_VAD, *PVMMOB_MAP_VAD; + +typedef struct tdVMMOB_MAP_MODULE { + OB ObHdr; + PQWORD pHashTableLookup; + LPWSTR wszMultiText; // multi-wstr pointed into by VMM_MAP_MODULEENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMM_MAP_MODULEENTRY pMap[]; // map entries. +} VMMOB_MAP_MODULE, *PVMMOB_MAP_MODULE; + +typedef struct tdVMMOB_MAP_HEAP { + OB ObHdr; + DWORD cMap; // # map entries. + VMM_MAP_HEAPENTRY pMap[]; // map entries. +} VMMOB_MAP_HEAP, *PVMMOB_MAP_HEAP; -typedef struct tdVMMOB_MEMMAP { +typedef struct tdVMMOB_MAP_THREAD { OB ObHdr; - BOOL fValid; // map is valid (did not fail initialization) - BOOL fTagModules; // map contains tags from modules. - BOOL fTagScan; // map contains tags from scan. - DWORD cMap; // # map entries. - DWORD cbDisplay; // byte count of display map (even if not existing yet). - PVMMOB_DATA pObDisplay; // human readable memory map. - VMM_MEMMAP_ENTRY pMap[]; // map entries -} VMMOB_MEMMAP, *PVMMOB_MEMMAP; - -typedef struct tdVMMOB_MODULEMAP { + DWORD cMap; // # map entries. + VMM_MAP_THREADENTRY pMap[]; // map entries. +} VMMOB_MAP_THREAD, *PVMMOB_MAP_THREAD; + +typedef struct tdVMMOB_MAP_HANDLE { OB ObHdr; - BOOL fValid; // map is valid (did not fail initialization). - DWORD cMap; // # map entries. - DWORD cbDisplay; // size of 'text' module map. - PBYTE pbDisplay; // 'text' module map stored in-object after pMap). - VMM_MODULEMAP_ENTRY pMap[]; // map entries -} VMMOB_MODULEMAP, *PVMMOB_MODULEMAP; + LPWSTR wszMultiText; // multi-wstr pointed into by VMM_MAP_HANDLEENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMM_MAP_HANDLEENTRY pMap[]; // map entries. +} VMMOB_MAP_HANDLE, *PVMMOB_MAP_HANDLE; typedef struct tdVMMWIN_USER_PROCESS_PARAMETERS { BOOL fProcessed; @@ -157,8 +286,10 @@ typedef struct tdVMMOB_PHYS2VIRT_INFORMATION { typedef struct tdVMMOB_PROCESS_PERSISTENT { OB ObHdr; BOOL fIsPostProcessingComplete; - POB_CONTAINER pObCLdrModulesCachePrefetch32; - POB_CONTAINER pObCLdrModulesCachePrefetch64; + POB_CONTAINER pObCMapVadPrefetch; + POB_CONTAINER pObCLdrModulesPrefetch32; + POB_CONTAINER pObCLdrModulesPrefetch64; + POB_CONTAINER pObCMapThreadPrefetch; VMMWIN_USER_PROCESS_PARAMETERS UserProcessParams; // kernel path and long name (from EPROCESS.SeAuditProcessCreationInfo) WORD cchNameLong; @@ -184,15 +315,23 @@ typedef struct tdVMM_PROCESS { BOOL fUserOnly; BOOL fTlbSpiderDone; BOOL fFileCacheDisabled; - PVMMOB_MEMMAP pObMemMap; - PVMMOB_MODULEMAP pObModuleMap; - PVMMOB_PROCESS_PERSISTENT pObProcessPersistent; // Always exists + struct { + PVMMOB_MAP_PTE pObPte; + PVMMOB_MAP_VAD pObVad; + PVMMOB_MAP_MODULE pObModule; + PVMMOB_MAP_HEAP pObHeap; + PVMMOB_MAP_THREAD pObThread; + PVMMOB_MAP_HANDLE pObHandle; + // separate locks from main process lock to avoid deadlocks + // but also for increased parallelization for slow tasks. + CRITICAL_SECTION LockUpdateExtendedInfo; + CRITICAL_SECTION LockUpdateThreadMap; + } Map; + PVMMOB_PROCESS_PERSISTENT pObPersistent; // Always exists struct { QWORD vaPEB; DWORD vaPEB32; // WoW64 only - QWORD vaENTRY; BOOL fWow64; - QWORD vaSeAuditProcessCreationInfo; struct { QWORD va; DWORD cb; @@ -220,9 +359,9 @@ typedef struct tdVMMOB_PROCESS_TABLE { #define VMM_CACHE2_BUCKETS 2039 #define VMM_CACHE2_MAX_ENTRIES 0x8000 -#define VMM_CACHE_TAG_PHYS 'Ph' -#define VMM_CACHE_TAG_PAGING 'Pg' -#define VMM_CACHE_TAG_TLB 'Tb' +#define VMM_CACHE_TAG_PHYS 'CaPh' +#define VMM_CACHE_TAG_PAGING 'CaPg' +#define VMM_CACHE_TAG_TLB 'CaTb' typedef struct tdVMMOB_MEM { OB Ob; @@ -241,12 +380,12 @@ typedef struct tdVMMOB_MEM { } VMMOB_MEM, *PVMMOB_MEM, **PPVMMOB_MEM; typedef struct tdVMM_CACHE_TABLE { + BOOL fActive; + DWORD tag; SLIST_HEADER ListHeadEmpty; SLIST_HEADER ListHeadTotal; DWORD cEmpty; DWORD cTotal; - BOOL fActive; - WORD tag; WORD iReclaimLast; struct { DWORD c; @@ -271,12 +410,10 @@ typedef struct tdVMM_MEMORYMODEL_FUNCTIONS { BOOL(*pfnVirt2Phys)(_In_ QWORD paDTB, _In_ BOOL fUserOnly, _In_ BYTE iPML, _In_ QWORD va, _Out_ PQWORD ppa); VOID(*pfnVirt2PhysGetInformation)(_Inout_ PVMM_PROCESS pProcess, _Inout_ PVMM_VIRT2PHYS_INFORMATION pVirt2PhysInfo); VOID(*pfnPhys2VirtGetInformation)(_In_ PVMM_PROCESS pProcess, _Inout_ PVMMOB_PHYS2VIRT_INFORMATION pP2V); - VOID(*pfnMapInitialize)(_In_ PVMM_PROCESS pProcess); - VOID(*pfnMapTag)(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaBase, _In_ QWORD vaLimit, _In_opt_ LPSTR szTag, _In_opt_ LPWSTR wszTag, _In_ BOOL fWoW64, _In_ BOOL fOverwrite); - BOOL(*pfnMapGetEntries)(_In_ PVMM_PROCESS pProcess, _In_ DWORD flags, _Out_ PVMMOB_MEMMAP *ppObMemMap); - BOOL(*pfnMapGetDisplay)(_In_ PVMM_PROCESS pProcess, _In_ DWORD flags, _Out_ PVMMOB_DATA *ppObDisplay); + BOOL(*pfnPteMapInitialize)(_In_ PVMM_PROCESS pProcess); VOID(*pfnTlbSpider)(_In_ PVMM_PROCESS pProcess); BOOL(*pfnTlbPageTableVerify)(_Inout_ PBYTE pb, _In_ QWORD pa, _In_ BOOL fSelfRefReq); + BOOL(*pfnPagedRead)(_In_ PVMM_PROCESS pProcess, _In_ QWORD va, _In_ QWORD pte, _Out_writes_(4096) PBYTE pbPage, _Out_ PQWORD ppa, _In_ QWORD flags); } VMM_MEMORYMODEL_FUNCTIONS; // ---------------------------------------------------------------------------- @@ -285,7 +422,6 @@ typedef struct tdVMM_MEMORYMODEL_FUNCTIONS { typedef struct tdVmmConfig { CHAR szMountPoint[1]; - CHAR szPythonPath[MAX_PATH]; QWORD paCR3; // flags below BOOL fVerboseDll; @@ -295,6 +431,10 @@ typedef struct tdVmmConfig { BOOL fDisableBackgroundRefresh; BOOL fDisableLeechCoreClose; // when device 'existing' BOOL fDisableSymbolServerOnStartup; + BOOL fWaitInitialize; + // strings below + CHAR szPythonPath[MAX_PATH]; + CHAR szPageFile[10][MAX_PATH]; } VMMCONFIG, *PVMMCONFIG; typedef struct tdVMM_STATISTICS { @@ -303,12 +443,20 @@ typedef struct tdVMM_STATISTICS { QWORD cPhysReadFail; QWORD cPhysWrite; QWORD cPhysRefreshCache; - QWORD cPageReadSuccessCacheHit; - QWORD cPageReadSuccessCompressed; - QWORD cPageReadSuccessDemandZero; - QWORD cPageReadFailedCacheHit; - QWORD cPageReadFailedCompressed; - QWORD cPageReadFailed; + struct { + QWORD cPrototype; + QWORD cTransition; + QWORD cDemandZero; + QWORD cVAD; + QWORD cPageFile; + QWORD cCacheHit; + QWORD cCompressed; + QWORD cFailCacheHit; + QWORD cFailVAD; + QWORD cFailPageFile; + QWORD cFailCompressed; + QWORD cFail; + } page; QWORD cPageRefreshCache; QWORD cTlbCacheHit; QWORD cTlbReadSuccess; @@ -320,17 +468,21 @@ typedef struct tdVMM_STATISTICS { typedef struct tdVMM_WIN_EPROCESS_OFFSET { BOOL fValid; + BOOL f64VistaOr7; WORD cbMaxOffset; WORD State; WORD DTB; + WORD DTB_User; WORD Name; WORD PID; WORD PPID; WORD FLink; WORD BLink; WORD PEB; - WORD DTB_User; WORD SeAuditProcessCreationInfo; + WORD VadRoot; + WORD ObjectTable; + WORD Wow64Process; // only valid for 64-bit windows struct { // values may not exist - indicated by zero offset BOOL fFailInitialize; @@ -339,6 +491,30 @@ typedef struct tdVMM_WIN_EPROCESS_OFFSET { } opt; } VMM_WIN_EPROCESS_OFFSET, *PVMM_WIN_EPROCESS_OFFSET; +typedef struct tdVMM_WIN_THREADINFO { + WORD oThreadListHeadKP; + // _KTHREAD offsets + WORD oStackBase; + WORD oStackLimit; + WORD oState; + WORD oRunningOpt; + WORD oPriority; + WORD oBasePriority; + WORD oTeb; + WORD oProcessOpt; + // _ETHREAD offsets + WORD oCreateTime; + WORD oExitTime; + WORD oExitStatus; + WORD oStartAddress; + WORD oThreadListEntry; + WORD oCid; + WORD oMax; + // other + WORD oTebStackBase; + WORD oTebStackLimit; +} VMM_WIN_THREADINFO, *PVMM_WIN_THEADINFO; + typedef struct tdVMMWIN_REGISTRY_CONTEXT *PVMMWIN_REGISTRY_CONTEXT; typedef QWORD VMMWIN_PDB_HANDLE; @@ -364,33 +540,6 @@ typedef struct tdVMMWIN_TCPIP_CONTEXT { VMMWIN_TCPIP_OFFSET_TcpE OTcpE; } VMMWIN_TCPIP_CONTEXT, *PVMMWIN_TCPIP_CONTEXT; -typedef struct tdVMMWIN_MEMCOMPRESS_OFFSET { - BOOL _fValid; - BOOL _fProcessedTry; - WORD _Size; - struct { - WORD PagesTree; - WORD SmkmStore; - WORD ChunkMetaData; - WORD RegionSizeMask; - WORD RegionIndexMask; - WORD CompressionAlgorithm; - WORD CompressedRegionPtrArray; - WORD OwnerProcess; - } SMKM_STORE; -} VMMWIN_MEMCOMPRESS_OFFSET, *PVMMWIN_MEMCOMPRESS_OFFSET; - -typedef struct tdVMMWIN_MEMCOMPRESS_CONTEXT { - QWORD vaEPROCESS; - DWORD dwPid; - DWORD dwPageFileNumber; - BOOL fValid; - BOOL fInitialized; - QWORD vaSmGlobals; - QWORD vaKeyToStoreTree; - VMMWIN_MEMCOMPRESS_OFFSET O; -} VMMWIN_MEMCOMPRESS_CONTEXT, *PVMMWIN_MEMCOMPRESS_CONTEXT; - typedef struct tdVMMWIN_OPTIONAL_KERNEL_CONTEXT { BOOL fInitialized; DWORD cCPUs; @@ -417,8 +566,8 @@ typedef struct tdVMM_KERNELINFO { DWORD dwVersionBuild; QWORD vaPsLoadedModuleListPtr; VMM_WIN_EPROCESS_OFFSET OffsetEPROCESS; - VMMWIN_MEMCOMPRESS_CONTEXT MemCompress; VMMWIN_OPTIONAL_KERNEL_CONTEXT opt; + VMM_WIN_THREADINFO ThreadInfo; } VMM_KERNELINFO; typedef NTSTATUS VMMFN_RtlDecompressBuffer( @@ -436,6 +585,26 @@ typedef struct tdVMM_DYNAMIC_LOAD_FUNCTIONS { VMMFN_RtlDecompressBuffer *RtlDecompressBuffer; // ntdll.dll!RtlDecompressBuffer } VMM_DYNAMIC_LOAD_FUNCTIONS; +// OBJECT TYPE table exists on Win7+ It's initialized on first use and it will +// exist throughout the lifetime of vmm context. Call function: +// VmmWin_ObjectTypeGet() to retrieve the type for a specific object type. +// OBJECT TYPE description table is dependant on PDB symbol functionality. +typedef struct tdVMMWIN_OBJECT_TYPE { + WORD cwsz; + WORD owsz; + LPWSTR wsz; +} VMMWIN_OBJECT_TYPE, *PVMMWIN_OBJECT_TYPE; + +typedef struct tdVMMWIN_OBJECT_TYPE_TABLE { + BOOL fInitialized; + BOOL fInitializedFailed; + BYTE bObjectHeaderCookie; + DWORD cbMultiText; + LPWSTR wszMultiText; + DWORD c; + VMMWIN_OBJECT_TYPE h[256]; +} VMMWIN_OBJECT_TYPE_TABLE, *PVMMWIN_OBJECT_TYPE_TABLE; + typedef struct tdVMM_CONTEXT { HMODULE hModuleVmm; // do not call FreeLibrary on hModuleVmm CRITICAL_SECTION MasterLock; @@ -443,8 +612,9 @@ typedef struct tdVMM_CONTEXT { VMM_MEMORYMODEL_FUNCTIONS fnMemoryModel; VMM_MEMORYMODEL_TP tpMemoryModel; BOOL f32; + BOOL fThreadMapEnabled; // Thread Map subsystem is enabled / available VMM_SYSTEM_TP tpSystem; - DWORD flags; // VMM_FLAG_* + DWORD flags; // VMM_FLAG_* struct { BOOL fEnabled; HANDLE hThread; @@ -459,6 +629,7 @@ typedef struct tdVMM_CONTEXT { VMM_KERNELINFO kernel; POB pObVfsDumpContext; PVOID pPdbContext; + PVOID pMmContext; PVMMWIN_REGISTRY_CONTEXT pRegistry; VMMWIN_TCPIP_CONTEXT TcpIp; QWORD paPluginPhys2VirtRoot; @@ -472,12 +643,15 @@ typedef struct tdVMM_CONTEXT { VMM_CACHE_TABLE TLB; VMM_CACHE_TABLE PAGING; POB_VSET PAGING_FAILED; + POB_MAP pmPrototypePte; // map with mm_vad.c managed data } Cache; // thread worker count struct { BOOL fEnabled; DWORD c; } ThreadWorkers; + WCHAR _EmptyWCHAR; + VMMWIN_OBJECT_TYPE_TABLE ObjectTypeTable; } VMM_CONTEXT, *PVMM_CONTEXT; typedef struct tdVMM_MAIN_CONTEXT { @@ -510,6 +684,11 @@ PVMM_MAIN_CONTEXT ctxMain; #define vmmprintfvv_fn(format, ...) vmmprintfvv("%s: "format, __func__, ##__VA_ARGS__); #define vmmprintfvvv_fn(format, ...) vmmprintfvvv("%s: "format, __func__, ##__VA_ARGS__); +#define VMM_EPROCESS_DWORD(pProcess, offset) (*(PDWORD)(pProcess->win.EPROCESS.pb + offset)) +#define VMM_EPROCESS_QWORD(pProcess, offset) (*(PQWORD)(pProcess->win.EPROCESS.pb + offset)) +#define VMM_EPROCESS_PTR(pProcess, offset) (ctxVmm->f32 ? VMM_EPROCESS_DWORD(pProcess, offset) : VMM_EPROCESS_QWORD(pProcess, offset)) + + // ---------------------------------------------------------------------------- // CACHE AND TLB FUNCTIONALITY BELOW: // ---------------------------------------------------------------------------- @@ -517,11 +696,11 @@ PVMM_MAIN_CONTEXT ctxMain; /* * Retrieve an item from the cache. * CALLER DECREF: return -* -- wTblTag +* -- dwTblTag * -- qwA * -- return */ -PVMMOB_MEM VmmCacheGet(_In_ WORD wTblTag, _In_ QWORD qwA); +PVMMOB_MEM VmmCacheGet(_In_ DWORD dwTblTag, _In_ QWORD qwA); /* * Retrieve a page table (0x1000 bytes) via the TLB cache. @@ -534,21 +713,21 @@ PVMMOB_MEM VmmTlbGetPageTable(_In_ QWORD pa, _In_ BOOL fCacheOnly); /* * Check if an address page exists in the indicated cache. -* -- wTblTag +* -- dwTblTag * -- qwA * -- return */ -BOOL VmmCacheExists(_In_ WORD wTblTag, _In_ QWORD qwA); +BOOL VmmCacheExists(_In_ DWORD dwTblTag, _In_ QWORD qwA); /* * Check out an empty memory cache item from the cache. NB! once the item is * filled (successfully or unsuccessfully) it must be returned to the cache with * VmmCacheReserveReturn and must _NOT_ otherwise be DEFREF'ed. * CALLER DECREF SPECIAL: return -* -- wTblTag +* -- dwTblTag * -- return */ -PVMMOB_MEM VmmCacheReserve(_In_ WORD wTblTag); +PVMMOB_MEM VmmCacheReserve(_In_ DWORD wTblTag); /* * Return an entry retrieved with VmmCacheReserve to the cache. @@ -649,6 +828,21 @@ BOOL VmmRead_U2A_RawStr(_In_ PVMM_PROCESS pProcess, _In_ QWORD flags, _In_ QWORD _Success_(return) BOOL VmmRead(_In_opt_ PVMM_PROCESS pProcess, _In_ QWORD qwA, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb); +/* +* Identical functionality as provided by 'VmmRead' - but with flags parameter. +* Read a contigious arbitrary amount of memory, virtual or physical. +* Virtual memory is read if a process is specified in pProcess parameter. +* Physical memory is read if NULL is specified in pProcess parameter. +* -- pProcess +* -- qwA +* -- pb +* -- cb +* -- flags = flags as in VMM_FLAG_* +* -- return +*/ +_Success_(return) +BOOL VmmRead2(_In_opt_ PVMM_PROCESS pProcess, _In_ QWORD qwA, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb, _In_ QWORD flags); + /* * Read a contigious arbitrary amount of memory, physical or virtual, and report * the number of bytes read in pcbRead. @@ -673,7 +867,7 @@ VOID VmmReadEx(_In_opt_ PVMM_PROCESS pProcess, _In_ QWORD qwA, _Out_writes_(cb) * -- return */ _Success_(return) -BOOL VmmReadPage(_In_opt_ PVMM_PROCESS pProcess, _In_ QWORD qwA, _Inout_bytecount_(4096) PBYTE pbPage); +BOOL VmmReadPage(_In_opt_ PVMM_PROCESS pProcess, _In_ QWORD qwA, _Out_writes_(4096) PBYTE pbPage); /* * Scatter read virtual memory. Non contiguous 4096-byte pages. @@ -692,6 +886,34 @@ VOID VmmReadScatterVirtual(_In_ PVMM_PROCESS pProcess, _Inout_updates_(cpMEMsVir */ VOID VmmReadScatterPhysical(_Inout_ PPMEM_IO_SCATTER_HEADER ppMEMsPhys, _In_ DWORD cpMEMsPhys, _In_ QWORD flags); +/* +* Read a memory segment as a file. This function is mainly a helper function +* for various file system functionality. +* -- pProcess = NULL=='physical memory read', PTR=='virtual memory read' +* -- qwMemoryAddress +* -- cbMemorySize +* -- pb +* -- cb +* -- pcbRead +* -- cbOffset +* -- return = NTSTATUS value +*/ +NTSTATUS VmmReadAsFile(_In_opt_ PVMM_PROCESS pProcess, _In_ QWORD qwMemoryAddress, _In_ QWORD cbMemorySize, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset); + +/* +* Write to a memory segment as a file. This function is mainly a helper +* function for virtual file system functionality. +* -- pProcess = NULL=='physical memory read', PTR=='virtual memory read' +* -- qwMemoryAddress +* -- cbMemorySize +* -- pb +* -- cb +* -- pcbWrite +* -- cbOffset +* -- return = NTSTATUS value +*/ +NTSTATUS VmmWriteAsFile(_In_opt_ PVMM_PROCESS pProcess, _In_ QWORD qwMemoryAddress, _In_ QWORD cbMemorySize, _In_reads_(cb) PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbWrite, _In_ QWORD cbOffset); + /* * Translate a virtual address to a physical address by walking the page tables. * The successfully translated Physical Address (PA) is returned in ppa. @@ -781,7 +1003,7 @@ inline VOID VmmVirt2PhysGetInformation(_Inout_ PVMM_PROCESS pProcess, _Inout_ PV * a previously stored address will be used. * It's not possible to use this function to retrieve multiple targeted * addresses in parallell. -* -- CALLER DECREF: return +* CALLER DECREF: return * -- pProcess * -- paTarget = targeted physical address (or 0 if use previously saved). * -- return @@ -789,51 +1011,109 @@ inline VOID VmmVirt2PhysGetInformation(_Inout_ PVMM_PROCESS pProcess, _Inout_ PV PVMMOB_PHYS2VIRT_INFORMATION VmmPhys2VirtGetInformation(_In_ PVMM_PROCESS pProcess, _In_ QWORD paTarget); /* -* Map a tag into the sorted memory map in O(log2) operations. Supply only one -* of szTag or wszTag. Tags are usually module/dll name. +* Retrieve the PTE hardware page table memory map. +* CALLER DECREF: ppObPteMap * -- pProcess -* -- vaBase -* -- vaLimit = limit == vaBase + size (== top address in range +1) -* -- szTag -* -- wszTag -* -- fWoW64 -* -- fOverwrite +* -- ppObPteMap +* -- fExtendedText +* -- return */ -inline VOID VmmMemMapTag(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaBase, _In_ QWORD vaLimit, _In_opt_ LPSTR szTag, _In_opt_ LPWSTR wszTag, _In_opt_ BOOL fWoW64, _In_ BOOL fOverwrite) -{ - if(ctxVmm->tpMemoryModel == VMM_MEMORYMODEL_NA) { return; } - ctxVmm->fnMemoryModel.pfnMapTag(pProcess, vaBase, vaLimit, szTag, wszTag, fWoW64, fOverwrite); -} +_Success_(return) +BOOL VmmMap_GetPte(_In_ PVMM_PROCESS pProcess, _Out_ PVMMOB_MAP_PTE *ppObPteMap, _In_ BOOL fExtendedText); /* -* Retrieve the memory map. -* CALLER DECREF: ppObMemMap +* Retrieve a single PVMM_MAP_PTEENTRY from the PTE hardware page table memory map. * -- pProcess -* -- flags -* -- ppObMemMap +* -- ppObPteMap +* -- fExtendedText +* -- return = PTR to PTEENTRY or NULL on fail. Must not be used out of pPteMap scope. +*/ +PVMM_MAP_PTEENTRY VmmMap_GetPteEntry(_In_ PVMMOB_MAP_PTE pPteMap, _In_ QWORD va); + +/* +* Retrieve the VAD memory map. +* CALLER DECREF: ppObVadMap +* -- pProcess +* -- ppObVadMap +* -- fExtendedText * -- return */ _Success_(return) -inline BOOL VmmMemMapGetEntries(_In_ PVMM_PROCESS pProcess, _In_ DWORD flags, _Out_ PVMMOB_MEMMAP *ppObMemMap) -{ - if(ctxVmm->tpMemoryModel == VMM_MEMORYMODEL_NA) { return FALSE; } - return ctxVmm->fnMemoryModel.pfnMapGetEntries(pProcess, flags, ppObMemMap); -} +BOOL VmmMap_GetVad(_In_ PVMM_PROCESS pProcess, _Out_ PVMMOB_MAP_VAD *ppObVadMap, _In_ BOOL fExtendedText); + +/* +* Retrieve a single PVMM_MAP_VADENTRY for a given VadMap and address inside it. +* -- pVadMap +* -- va +* -- return = PTR to VADENTRY or NULL on fail. Must not be used out of pVadMap scope. +*/ +PVMM_MAP_VADENTRY VmmMap_GetVadEntry(_In_opt_ PVMMOB_MAP_VAD pVadMap, _In_ QWORD va); /* -* Retrieve a human-readable memory map as text in buffer. -* CALLER DECREF: ppObDisplay +* Retrieve the process module map. +* CALLER DECREF: ppObModuleMap * -- pProcess -* -- flags = flags as specified by VMM_MAP_FLAGS* -* -- ppObDisplay +* -- ppObModuleMap * -- return */ _Success_(return) -inline BOOL VmmMemMapGetDisplay(_In_ PVMM_PROCESS pProcess, _In_ DWORD flags, _Out_ PVMMOB_DATA *ppObDisplay) -{ - if(ctxVmm->tpMemoryModel == VMM_MEMORYMODEL_NA) { return FALSE; } - return ctxVmm->fnMemoryModel.pfnMapGetDisplay(pProcess, flags, ppObDisplay); -} +BOOL VmmMap_GetModule(_In_ PVMM_PROCESS pProcess, _Out_ PVMMOB_MAP_MODULE *ppObModuleMap); + +/* +* Retrieve a single VMM_MAP_MODULEENTRY for a given ModuleMap and module name inside it. +* -- pModuleMap +* -- wszModuleName +* -- return = PTR to VMM_MAP_MODULEENTRY or NULL on fail. Must not be used out of pModuleMap scope. +*/ +PVMM_MAP_MODULEENTRY VmmMap_GetModuleEntry(_In_ PVMMOB_MAP_MODULE pModuleMap, _In_ LPWSTR wszModuleName); + +/* +* Retrieve the heap map. +* CALLER DECREF: ppObHeapMap +* -- pProcess +* -- ppObHeapMap +* -- return +*/ +_Success_(return) +BOOL VmmMap_GetHeap(_In_ PVMM_PROCESS pProcess, _Out_ PVMMOB_MAP_HEAP *ppObHeapMap); + +/* +* Retrieve the thread map. +* CALLER DECREF: ppObThreadMap +* -- pProcess +* -- ppObThreadMap +* -- return +*/ +_Success_(return) +BOOL VmmMap_GetThread(_In_ PVMM_PROCESS pProcess, _Out_ PVMMOB_MAP_THREAD *ppObThreadMap); + +/* +* Start async initialization of the thread map. This may be done to speed up +* retrieval of the thread map in the future since processing to retrieve it +* has already been progressing for a while. This may be useful for processes +* with large amount of threads - such as the system process. +* -- pProcess +*/ +VOID VmmMap_GetThreadAsync(_In_ PVMM_PROCESS pProcess); + +/* +* Retrieve a single PVMM_MAP_THREADENTRY for a given ThreadMap and ThreadID. +* -- pThreadMap +* -- dwTID +* -- return = PTR to VMM_MAP_THREADENTRY or NULL on fail. Must not be used out of pThreadMap scope. +*/ +PVMM_MAP_THREADENTRY VmmMap_GetThreadEntry(_In_ PVMMOB_MAP_THREAD pThreadMap, _In_ DWORD dwTID); + +/* +* Retrieve the HANDLE map +* CALLER DECREF: ppObHandleMap +* -- pProcess +* -- ppObHandleMap +* -- fExtendedText +* -- return +*/ +_Success_(return) +BOOL VmmMap_GetHandle(_In_ PVMM_PROCESS pProcess, _Out_ PVMMOB_MAP_HANDLE *ppObHandleMap, _In_ BOOL fExtendedText); /* * Retrieve an existing process given a process id (PID). @@ -963,9 +1243,9 @@ BOOL VmmProcessActionForeachParallel_CriteriaActiveOnly(_In_ PVMM_PROCESS pProce /* * Clear the specified cache from all entries. -* -- wTblTag +* -- dwTblTag */ -VOID VmmCacheClear(_In_ WORD wTblTag); +VOID VmmCacheClear(_In_ DWORD dwTblTag); /* * Invalidate cache entries belonging to a specific physical address. @@ -980,8 +1260,9 @@ VOID VmmCacheInvalidate(_In_ QWORD pa); * NB! pPrefetchPages must not be updated/altered during the function call. * -- pProcess * -- pPrefetchPages +* -- flags */ -VOID VmmCachePrefetchPages(_In_opt_ PVMM_PROCESS pProcess, _In_opt_ POB_VSET pPrefetchPages); +VOID VmmCachePrefetchPages(_In_opt_ PVMM_PROCESS pProcess, _In_opt_ POB_VSET pPrefetchPages, _In_ QWORD flags); /* * Prefetch a set of addresses. This is useful when reading data from somewhat @@ -1000,8 +1281,20 @@ VOID VmmCachePrefetchPages2(_In_opt_ PVMM_PROCESS pProcess, _In_ DWORD cAddresse * -- pProcess * -- pPrefetchPagesNonPageAligned * -- cb +* -- flags +*/ +VOID VmmCachePrefetchPages3(_In_opt_ PVMM_PROCESS pProcess, _In_opt_ POB_VSET pPrefetchPagesNonPageAligned, _In_ DWORD cb, _In_ QWORD flags); + +/* +* Prefetch an array of optionally non-page aligned addresses. This is useful +* when reading data from somewhat known addresses over higher latency connections. +* -- pProcess +* -- cAddresses +* -- pqwAddresses = array of addresses to fetch +* -- cb +* -- flags */ -VOID VmmCachePrefetchPages3(_In_opt_ PVMM_PROCESS pProcess, _In_opt_ POB_VSET pPrefetchPagesNonPageAligned, _In_ DWORD cb); +VOID VmmCachePrefetchPages4(_In_opt_ PVMM_PROCESS pProcess, _In_ DWORD cAddresses, _In_ PQWORD pqwAddresses, _In_ DWORD cb, _In_ QWORD flags); /* * Initialize the memory model specified and discard any previous memory models diff --git a/vmm/vmm.vcxproj b/vmm/vmm.vcxproj index 03387dfd..cc0986bf 100644 --- a/vmm/vmm.vcxproj +++ b/vmm/vmm.vcxproj @@ -115,16 +115,9 @@ copy $(ProjectDir)\vmmdll.h $(SolutionDir)\files\ /y - - - - - - - - + + - @@ -136,24 +129,26 @@ copy $(ProjectDir)\vmmdll.h $(SolutionDir)\files\ /y - - + + - + + + diff --git a/vmm/vmm.vcxproj.filters b/vmm/vmm.vcxproj.filters index 38a09514..0bea093b 100644 --- a/vmm/vmm.vcxproj.filters +++ b/vmm/vmm.vcxproj.filters @@ -16,18 +16,15 @@ {aedb4365-6f4c-4a1b-b130-a1d3adc27bf6} - - {9f5e224d-58ed-4c7b-874f-0b6ff71a4ef5} - - - {482b0bfd-832c-4ce4-bf9f-883a6aa96d1c} - {00c5c579-6475-46c5-8997-359b6c6649b3} {4e91bd71-3b0c-4f6d-9e2c-306e6f275360} + + {338e412d-8a2d-4a56-9f79-32aa6091c271} + @@ -51,15 +48,6 @@ Header Files - - Header Files - - - Header Files - - - Header Files - Header Files @@ -78,46 +66,28 @@ Header Files - - Header Files - Header Files - - Header Files\modules - - - Header Files\modules - - - Header Files\modules + + Header Files - - Header Files\modules + + Header Files - - Header Files\modules + + Header Files - Header Files\ob - - - Header Files\modules - - - Header Files\modules + Header Files - + Header Files - Header Files\modules - - Header Files - + Header Files @@ -143,15 +113,6 @@ Source Files - - Source Files - - - Source Files - - - Source Files - Source Files @@ -161,9 +122,6 @@ Source Files - - Source Files - Source Files @@ -212,6 +170,30 @@ Source Files + + Source Files\mm + + + Source Files\mm + + + Source Files\mm + + + Source Files\mm + + + Source Files\mm + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + diff --git a/vmm/vmmdll.c b/vmm/vmmdll.c index 5bf41b06..1532f69f 100644 --- a/vmm/vmmdll.c +++ b/vmm/vmmdll.c @@ -18,7 +18,6 @@ #include "vmmwinreg.h" #include "vmmwintcpip.h" #include "vmmvfs.h" -#include "mm_x64_page_win.h" // ---------------------------------------------------------------------------- // Synchronization macro below. The VMM isn't thread safe so it's important to @@ -55,7 +54,7 @@ BOOL VmmDll_ConfigIntialize(_In_ DWORD argc, _In_ char* argv[]) { char* argv2[3]; CHAR chMountMount = '\0'; - DWORD i = 0; + DWORD i = 0, iPageFile; if((argc == 2) && argv[1][0] && (argv[1][0] != '-')) { // click to open -> only 1 argument ... argv2[0] = argv[0]; @@ -91,32 +90,43 @@ BOOL VmmDll_ConfigIntialize(_In_ DWORD argc, _In_ char* argv[]) ctxMain->cfg.fDisableBackgroundRefresh = TRUE; i++; continue; + } else if(0 == _stricmp(argv[i], "-waitinitialize")) { + ctxMain->cfg.fWaitInitialize = TRUE; + i++; + continue; } else if(i + 1 >= argc) { return FALSE; - } else if(0 == strcmp(argv[i], "-cr3")) { + } else if(0 == _stricmp(argv[i], "-cr3")) { ctxMain->cfg.paCR3 = Util_GetNumericA(argv[i + 1]); i += 2; continue; - } else if(0 == strcmp(argv[i], "-max")) { + } else if(0 == _stricmp(argv[i], "-max")) { ctxMain->dev.paMax = Util_GetNumericA(argv[i + 1]); i += 2; continue; - } else if((0 == strcmp(argv[i], "-device")) || (0 == strcmp(argv[i], "-z"))) { + } else if((0 == _stricmp(argv[i], "-device")) || (0 == strcmp(argv[i], "-z"))) { strcpy_s(ctxMain->dev.szDevice, MAX_PATH, argv[i + 1]); i += 2; continue; - } else if(0 == strcmp(argv[i], "-remote")) { + } else if(0 == _stricmp(argv[i], "-remote")) { strcpy_s(ctxMain->dev.szRemote, MAX_PATH, argv[i + 1]); i += 2; continue; - } else if(0 == strcmp(argv[i], "-pythonpath")) { + } else if(0 == _stricmp(argv[i], "-pythonpath")) { strcpy_s(ctxMain->cfg.szPythonPath, MAX_PATH, argv[i + 1]); i += 2; continue; - } else if(0 == strcmp(argv[i], "-mount")) { + } else if(0 == _stricmp(argv[i], "-mount")) { chMountMount = argv[i + 1][0]; i += 2; continue; + } else if(0 == _strnicmp(argv[i], "-pagefile", 9)) { + iPageFile = argv[i][9] - '0'; + if(iPageFile < 10) { + strcpy_s(ctxMain->cfg.szPageFile[iPageFile], MAX_PATH, argv[i + 1]); + } + i += 2; + continue; } else { return FALSE; } @@ -189,6 +199,9 @@ VOID VmmDll_PrintHelp() " -cr3 : base address of kernel/process page table (PML4) / CR3 CPU register. \n" \ " -max : memory max address, valid range: 0x0 .. 0xffffffffffffffff \n" \ " default: auto-detect (max supported by device / target system). \n" \ + " -pagefile0..9 : specify specify page file / swap file. By default pagefile \n" \ + " have index 0 - example: -pagefile0 pagefile.sys while swapfile have \n" \ + " have index 1 - example: -pagefile1 swapfile.sys \n" \ " -pythonpath : specify the path to a python 3 installation for Windows. \n" \ " The path given should be to the directory that contain: python.dll \n" \ " Example: -pythonpath \"C:\\Program Files\\Python37\" \n" \ @@ -200,6 +213,8 @@ VOID VmmDll_PrintHelp() " -symbolserverdisable : disable any integrations with the Microsoft Symbol \n" \ " Server used by the debugging .pdb symbol subsystem. Functionality \n" \ " will be limited if this is activated. Example: -symbolserverdisable \n" \ + " -waitinitialize : wait debugging .pdb symbol subsystem to fully start before\n" \ + " mounting file system and fully starting MemProcFS. \n" \ " \n", VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION ); @@ -588,7 +603,7 @@ DWORD VMMDLL_MemReadScatter(_In_ DWORD dwPID, _Inout_ PPMEM_IO_SCATTER_HEADER pp } _Success_(return) -BOOL VMMDLL_MemReadEx_Impl(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWORD cb, _Out_opt_ PDWORD pcbReadOpt, _In_ ULONG64 flags) +BOOL VMMDLL_MemReadEx_Impl(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb, _Out_opt_ PDWORD pcbReadOpt, _In_ ULONG64 flags) { PVMM_PROCESS pObProcess = NULL; if(dwPID != -1) { @@ -601,7 +616,7 @@ BOOL VMMDLL_MemReadEx_Impl(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _ } _Success_(return) -BOOL VMMDLL_MemReadEx(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWORD cb, _Out_opt_ PDWORD pcbReadOpt, _In_ ULONG64 flags) +BOOL VMMDLL_MemReadEx(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb, _Out_opt_ PDWORD pcbReadOpt, _In_ ULONG64 flags) { CALL_IMPLEMENTATION_VMM( STATISTICS_ID_VMMDLL_MemReadEx, @@ -609,7 +624,7 @@ BOOL VMMDLL_MemReadEx(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ D } _Success_(return) -BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWORD cb) +BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb) { DWORD dwRead; return VMMDLL_MemReadEx(dwPID, qwA, pb, cb, &dwRead, 0) && (dwRead == cb); @@ -637,7 +652,7 @@ BOOL VMMDLL_MemPrefetchPages_Impl(_In_ DWORD dwPID, _In_reads_(cPrefetchAddresse for(i = 0; i < cPrefetchAddresses; i++) { ObVSet_Push(pObVSet_PrefetchAddresses, pPrefetchAddresses[i] & ~0xfff); } - VmmCachePrefetchPages(pObProcess, pObVSet_PrefetchAddresses); + VmmCachePrefetchPages(pObProcess, pObVSet_PrefetchAddresses, 0); result = TRUE; fail: Ob_DECREF(pObVSet_PrefetchAddresses); @@ -654,7 +669,7 @@ BOOL VMMDLL_MemPrefetchPages(_In_ DWORD dwPID, _In_reads_(cPrefetchAddresses) PU } _Success_(return) -BOOL VMMDLL_MemWrite_Impl(_In_ DWORD dwPID, _In_ ULONG64 qwA, _In_ PBYTE pb, _In_ DWORD cb) +BOOL VMMDLL_MemWrite_Impl(_In_ DWORD dwPID, _In_ ULONG64 qwA, _In_reads_(cb) PBYTE pb, _In_ DWORD cb) { BOOL result; PVMM_PROCESS pObProcess = NULL; @@ -668,7 +683,7 @@ BOOL VMMDLL_MemWrite_Impl(_In_ DWORD dwPID, _In_ ULONG64 qwA, _In_ PBYTE pb, _In } _Success_(return) -BOOL VMMDLL_MemWrite(_In_ DWORD dwPID, _In_ ULONG64 qwA, _In_ PBYTE pb, _In_ DWORD cb) +BOOL VMMDLL_MemWrite(_In_ DWORD dwPID, _In_ ULONG64 qwA, _In_reads_(cb) PBYTE pb, _In_ DWORD cb) { CALL_IMPLEMENTATION_VMM( STATISTICS_ID_VMMDLL_MemWrite, @@ -699,150 +714,314 @@ BOOL VMMDLL_MemVirt2Phys(_In_ DWORD dwPID, _In_ ULONG64 qwVA, _Out_ PULONG64 pqw //----------------------------------------------------------------------------- _Success_(return) -BOOL VMMDLL_ProcessGetMemoryMap_Impl(_In_ DWORD dwPID, _Out_opt_ PVMMDLL_MEMMAP_ENTRY pMemMapEntries, _Inout_ PULONG64 pcMemMapEntries, _In_ BOOL fIdentifyModules) +BOOL VMMDLL_ProcessMap_GetPte_Impl(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbPteMap) PVMMDLL_MAP_PTE pPteMap, _Inout_ PDWORD pcbPteMap, _In_ BOOL fIdentifyModules) { + BOOL fResult = FALSE; + QWORD i, cbData = 0, cbDataMap, cbMultiTextDiff; + PVMMOB_MAP_PTE pObMap = NULL; PVMM_PROCESS pObProcess = NULL; - PVMMOB_MEMMAP pObMap = NULL; - pObProcess = VmmProcessGet(dwPID); - if(!pObProcess) { return FALSE; } - if(!VmmMemMapGetEntries(pObProcess, fIdentifyModules ? VMM_MEMMAP_FLAG_ALL : 0, &pObMap)) { goto fail; } - if(!pMemMapEntries) { - *pcMemMapEntries = pObMap->cMap; - } else { - if(!pObMap->cMap || (*pcMemMapEntries < pObMap->cMap)) { goto fail; } - memcpy(pMemMapEntries, pObMap->pMap, sizeof(VMMDLL_MEMMAP_ENTRY) * pObMap->cMap); - *pcMemMapEntries = pObMap->cMap; + if(!(pObProcess = VmmProcessGet(dwPID))) { goto fail; } + if(!VmmMap_GetPte(pObProcess, &pObMap, fIdentifyModules)) { goto fail; } + cbDataMap = pObMap->cMap * sizeof(VMMDLL_MAP_PTEENTRY); + cbData = sizeof(VMMDLL_MAP_PTE) + cbDataMap + max(2, pObMap->cbMultiText); + if(pPteMap) { + if(*pcbPteMap < cbData) { goto fail; } + ZeroMemory(pPteMap, sizeof(VMMDLL_MAP_PTE)); + pPteMap->dwVersion = VMMDLL_MAP_PTE_VERSION; + pPteMap->cMap = pObMap->cMap; + memcpy(pPteMap->pMap, pObMap->pMap, cbDataMap); + pPteMap->cbMultiText = pObMap->cbMultiText; + pPteMap->wszMultiText = (LPWSTR)(pPteMap->pMap + pPteMap->cMap); + if(fIdentifyModules) { + memcpy(pPteMap->wszMultiText, pObMap->wszMultiText, pPteMap->cbMultiText); + cbMultiTextDiff = (QWORD)pPteMap->wszMultiText - (QWORD)pObMap->wszMultiText; + for(i = 0; i < pPteMap->cMap; i++) { + if(pPteMap->pMap[i].cwszText) { + pPteMap->pMap[i].wszText = (LPWSTR)((QWORD)pObMap->pMap[i].wszText - (QWORD)pObMap->wszMultiText + (QWORD)pPteMap->wszMultiText); + } else { + pPteMap->pMap[i].wszText = pPteMap->wszMultiText; + } + } + } else { + pPteMap->wszMultiText[0] = 0; + for(i = 0; i < pPteMap->cMap; i++) { + pPteMap->pMap[i].wszText = pPteMap->wszMultiText; + } + } } - Ob_DECREF(pObMap); - Ob_DECREF(pObProcess); - return TRUE; + fResult = TRUE; fail: + *pcbPteMap = (DWORD)cbData; + Ob_DECREF(pObProcess); Ob_DECREF(pObMap); + return fResult; +} + +_Success_(return) +BOOL VMMDLL_ProcessMap_GetPte(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbPteMap) PVMMDLL_MAP_PTE pPteMap, _Inout_ PDWORD pcbPteMap, _In_ BOOL fIdentifyModules) +{ + CALL_IMPLEMENTATION_VMM( + STATISTICS_ID_VMMDLL_ProcessMap_GetPte, + VMMDLL_ProcessMap_GetPte_Impl(dwPID, pPteMap, pcbPteMap, fIdentifyModules)) +} + +_Success_(return) +BOOL VMMDLL_ProcessMap_GetVad_Impl(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbVadMap) PVMMDLL_MAP_VAD pVadMap, _Inout_ PDWORD pcbVadMap, _In_ BOOL fIdentifyModules) +{ + BOOL fResult = FALSE; + DWORD i, cbData = 0, cbDataMap; + QWORD cbMultiTextDiff; + PVMMOB_MAP_VAD pObMap = NULL; + PVMM_PROCESS pObProcess = NULL; + if(!(pObProcess = VmmProcessGet(dwPID))) { goto fail; } + if(!VmmMap_GetVad(pObProcess, &pObMap, fIdentifyModules)) { goto fail; } + cbDataMap = pObMap->cMap * sizeof(VMMDLL_MAP_VADENTRY); + cbData = sizeof(VMMDLL_MAP_VAD) + cbDataMap + max(2, pObMap->cbMultiText); + if(pVadMap) { + if(*pcbVadMap < cbData) { goto fail; } + ZeroMemory(pVadMap, sizeof(VMMDLL_MAP_VAD)); + pVadMap->dwVersion = VMMDLL_MAP_VAD_VERSION; + pVadMap->cMap = pObMap->cMap; + memcpy(pVadMap->pMap, pObMap->pMap, cbDataMap); + pVadMap->cbMultiText = pObMap->cbMultiText; + pVadMap->wszMultiText = (LPWSTR)(pVadMap->pMap + pVadMap->cMap); + if(fIdentifyModules) { + memcpy(pVadMap->wszMultiText, pObMap->wszMultiText, pVadMap->cbMultiText); + cbMultiTextDiff = (QWORD)pVadMap->wszMultiText - (QWORD)pObMap->wszMultiText; + for(i = 0; i < pVadMap->cMap; i++) { + if(pVadMap->pMap[i].cwszText) { + pVadMap->pMap[i].wszText = (LPWSTR)(cbMultiTextDiff + (QWORD)pObMap->pMap[i].wszText); + } else { + pVadMap->pMap[i].wszText = pVadMap->wszMultiText; + } + } + } else { + pVadMap->wszMultiText[0] = 0; + for(i = 0; i < pVadMap->cMap; i++) { + pVadMap->pMap[i].wszText = pVadMap->wszMultiText; + } + } + } + fResult = TRUE; +fail: + *pcbVadMap = cbData; Ob_DECREF(pObProcess); - return FALSE; + Ob_DECREF(pObMap); + return fResult; } _Success_(return) -BOOL VMMDLL_ProcessGetMemoryMap(_In_ DWORD dwPID, _Out_opt_ PVMMDLL_MEMMAP_ENTRY pMemMapEntries, _Inout_ PULONG64 pcMemMapEntries, _In_ BOOL fIdentifyModules) +BOOL VMMDLL_ProcessMap_GetVad(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbVadMap) PVMMDLL_MAP_VAD pVadMap, _Inout_ PDWORD pcbVadMap, _In_ BOOL fIdentifyModules) { CALL_IMPLEMENTATION_VMM( - STATISTICS_ID_VMMDLL_ProcessGetMemoryMap, - VMMDLL_ProcessGetMemoryMap_Impl(dwPID, pMemMapEntries, pcMemMapEntries, fIdentifyModules)) + STATISTICS_ID_VMMDLL_ProcessMap_GetVad, + VMMDLL_ProcessMap_GetVad_Impl(dwPID, pVadMap, pcbVadMap, fIdentifyModules)) } _Success_(return) -BOOL VMMDLL_ProcessGetMemoryMapEntry_Impl(_In_ DWORD dwPID, _Out_ PVMMDLL_MEMMAP_ENTRY pMemMapEntry, _In_ ULONG64 va, _In_ BOOL fIdentifyModules) +BOOL VMMDLL_ProcessMap_GetModule_Impl(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbModuleMap) PVMMDLL_MAP_MODULE pModuleMap, _Inout_ PDWORD pcbModuleMap) { + BOOL fResult = FALSE; + QWORD i, cbData = 0, cbDataMap, cbMultiTextDiff; + PVMMOB_MAP_MODULE pObMap = NULL; PVMM_PROCESS pObProcess = NULL; - DWORD i; - PVMMOB_MEMMAP pObMap = NULL; - PVMM_MEMMAP_ENTRY pe; - pObProcess = VmmProcessGet(dwPID); - if(!pObProcess) { return FALSE; } - if(!VmmMemMapGetEntries(pObProcess, fIdentifyModules ? VMM_MEMMAP_FLAG_ALL : 0, &pObMap)) {goto fail; } - for(i = 0; i < pObMap->cMap; i++) { - pe = pObMap->pMap + i; - if((pe->AddrBase >= va) && (va <= pe->AddrBase + (pe->cPages << 12))) { - memcpy(pMemMapEntry, pe, sizeof(VMM_MEMMAP_ENTRY)); - Ob_DECREF(pObMap); - Ob_DECREF(pObProcess); - return TRUE; + if(!(pObProcess = VmmProcessGet(dwPID))) { goto fail; } + if(!VmmMap_GetModule(pObProcess, &pObMap)) { goto fail; } + cbDataMap = pObMap->cMap * sizeof(VMMDLL_MAP_MODULEENTRY); + cbData = sizeof(VMMDLL_MAP_MODULE) + cbDataMap + pObMap->cbMultiText; + if(pModuleMap) { + if(*pcbModuleMap < cbData) { goto fail; } + ZeroMemory(pModuleMap, sizeof(VMMDLL_MAP_MODULE)); + pModuleMap->dwVersion = VMMDLL_MAP_MODULE_VERSION; + pModuleMap->wszMultiText = (LPWSTR)(((PBYTE)pModuleMap->pMap) + cbDataMap); + pModuleMap->cbMultiText = pObMap->cbMultiText; + pModuleMap->cMap = pObMap->cMap; + memcpy(pModuleMap->pMap, pObMap->pMap, cbDataMap); + memcpy(pModuleMap->wszMultiText, pObMap->wszMultiText, pObMap->cbMultiText); + cbMultiTextDiff = (QWORD)pModuleMap->wszMultiText - (QWORD)pObMap->wszMultiText; + for(i = 0; i < pModuleMap->cMap; i++) { + pModuleMap->pMap[i].wszText = (LPWSTR)(cbMultiTextDiff + (QWORD)pObMap->pMap[i].wszText); } } + fResult = TRUE; fail: + *pcbModuleMap = (DWORD)cbData; + Ob_DECREF(pObProcess); Ob_DECREF(pObMap); + return fResult; +} + +_Success_(return) +BOOL VMMDLL_ProcessMap_GetModule(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbModuleMap) PVMMDLL_MAP_MODULE pModuleMap, _Inout_ PDWORD pcbModuleMap) +{ + CALL_IMPLEMENTATION_VMM( + STATISTICS_ID_VMMDLL_ProcessMap_GetModule, + VMMDLL_ProcessMap_GetModule_Impl(dwPID, pModuleMap, pcbModuleMap)) +} + +_Success_(return) +BOOL VMMDLL_ProcessMap_GetModuleFromName_Impl(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _Out_ PVMMDLL_MAP_MODULEENTRY pModuleMapEntry) +{ + BOOL fResult; + PVMM_PROCESS pObProcess = NULL; + PVMMOB_MAP_MODULE pObMap = NULL; + PVMM_MAP_MODULEENTRY pVmmModuleMapEntry = NULL; + fResult = + (pObProcess = VmmProcessGet(dwPID)) && + VmmMap_GetModule(pObProcess, &pObMap) && + (pVmmModuleMapEntry = VmmMap_GetModuleEntry(pObMap, wszModuleName)); + if(fResult) { + memcpy(pModuleMapEntry, pVmmModuleMapEntry, sizeof(VMMDLL_MAP_MODULEENTRY)); + // no mem allocation for module name in single item. + pModuleMapEntry->_Reserved1[0] = 0; + pModuleMapEntry->wszText = (LPWSTR)pModuleMapEntry->_Reserved1; + } Ob_DECREF(pObProcess); - return FALSE; + Ob_DECREF(pObMap); + return fResult; } _Success_(return) -BOOL VMMDLL_ProcessGetMemoryMapEntry(_In_ DWORD dwPID, _Out_ PVMMDLL_MEMMAP_ENTRY pMemMapEntry, _In_ ULONG64 va, _In_ BOOL fIdentifyModules) +BOOL VMMDLL_ProcessMap_GetModuleFromName(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _Out_ PVMMDLL_MAP_MODULEENTRY pModuleMapEntry) { CALL_IMPLEMENTATION_VMM( - STATISTICS_ID_VMMDLL_ProcessGetMemoryMapEntry, - VMMDLL_ProcessGetMemoryMapEntry_Impl(dwPID, pMemMapEntry, va, fIdentifyModules)) + STATISTICS_ID_VMMDLL_ProcessMap_GetModuleFromName, + VMMDLL_ProcessMap_GetModuleFromName_Impl(dwPID, wszModuleName, pModuleMapEntry)) } _Success_(return) -BOOL VMMDLL_ProcessGetModuleMap_Impl(_In_ DWORD dwPID, _Out_writes_opt_(*pcModuleEntries) PVMMDLL_MODULEMAP_ENTRY pModuleEntries, _Inout_ PULONG64 pcModuleEntries) +BOOL VMMDLL_ProcessMap_GetHeap_Impl(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbHeapMap) PVMMDLL_MAP_HEAP pHeapMap, _Inout_ PDWORD pcbHeapMap) { - ULONG64 i; + BOOL fResult = FALSE; + DWORD cbData = 0, cbDataMap; + PVMMOB_MAP_HEAP pObMap = NULL; PVMM_PROCESS pObProcess = NULL; - PVMMOB_MODULEMAP pObModuleMap; - if(!pcModuleEntries) { return FALSE; } - pObProcess = VmmProcessGet(dwPID); - if(!pObProcess) { return FALSE; } - if(VmmProc_ModuleMapGet(pObProcess, &pObModuleMap)) { - if(!pModuleEntries) { - *pcModuleEntries = pObModuleMap->cMap; - } else { - if(!pObModuleMap->pMap || (*pcModuleEntries < pObModuleMap->cMap)) { - Ob_DECREF(pObModuleMap); - Ob_DECREF(pObProcess); - return FALSE; - } - for(i = 0; i < pObModuleMap->cMap; i++) { - memcpy(pModuleEntries + i, pObModuleMap->pMap + i, sizeof(VMMDLL_MODULEMAP_ENTRY)); - } - *pcModuleEntries = pObModuleMap->cMap; - } - Ob_DECREF(pObModuleMap); - } else { - *pcModuleEntries = 0; + if(!(pObProcess = VmmProcessGet(dwPID))) { goto fail; } + if(!VmmMap_GetHeap(pObProcess, &pObMap)) { goto fail; } + cbDataMap = pObMap->cMap * sizeof(VMMDLL_MAP_HEAPENTRY); + cbData = sizeof(VMMDLL_MAP_HEAP) + cbDataMap; + if(pHeapMap) { + if(*pcbHeapMap < cbData) { goto fail; } + ZeroMemory(pHeapMap, sizeof(VMMDLL_MAP_HEAP)); + pHeapMap->dwVersion = VMMDLL_MAP_HEAP_VERSION; + pHeapMap->cMap = pObMap->cMap; + memcpy(pHeapMap->pMap, pObMap->pMap, cbDataMap); } + fResult = TRUE; +fail: + *pcbHeapMap = cbData; Ob_DECREF(pObProcess); - return TRUE; + Ob_DECREF(pObMap); + return fResult; } _Success_(return) -BOOL VMMDLL_ProcessGetModuleMap(_In_ DWORD dwPID, _Out_opt_ PVMMDLL_MODULEMAP_ENTRY pModuleEntries, _Inout_ PULONG64 pcModuleEntries) +BOOL VMMDLL_ProcessMap_GetHeap(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbHeapMap) PVMMDLL_MAP_HEAP pHeapMap, _Inout_ PDWORD pcbHeapMap) { CALL_IMPLEMENTATION_VMM( - STATISTICS_ID_VMMDLL_ProcessGetModuleMap, - VMMDLL_ProcessGetModuleMap_Impl(dwPID, pModuleEntries, pcModuleEntries)) + STATISTICS_ID_VMMDLL_ProcessMap_GetHeap, + VMMDLL_ProcessMap_GetHeap_Impl(dwPID, pHeapMap, pcbHeapMap)) } _Success_(return) -BOOL VMMDLL_ProcessGetModuleFromName_Impl(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _Out_ PVMMDLL_MODULEMAP_ENTRY pModuleEntry) +BOOL VMMDLL_ProcessMap_GetThread_Impl(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbThreadMap) PVMMDLL_MAP_THREAD pThreadMap, _Inout_ PDWORD pcbThreadMap) { - BOOL result; - ULONG64 i, cModuleEntries = 0; - PVMMDLL_MODULEMAP_ENTRY pModuleEntries = NULL; - result = VMMDLL_ProcessGetModuleMap_Impl(dwPID, NULL, &cModuleEntries); - if(!result || !cModuleEntries) { return FALSE; } - pModuleEntries = (PVMMDLL_MODULEMAP_ENTRY)LocalAlloc(0, sizeof(VMMDLL_MODULEMAP_ENTRY) * cModuleEntries); - if(!pModuleEntries) { return FALSE; } - result = VMMDLL_ProcessGetModuleMap_Impl(dwPID, pModuleEntries, &cModuleEntries); - if(result && cModuleEntries) { - for(i = 0; i < cModuleEntries; i++) { - if(!_strnicmp(szModuleName, pModuleEntries[i].szName, 31)) { - memcpy(pModuleEntry, pModuleEntries + i, sizeof(VMMDLL_MODULEMAP_ENTRY)); - LocalFree(pModuleEntries); - return TRUE; + BOOL fResult = FALSE; + DWORD cbData = 0, cbDataMap; + PVMMOB_MAP_THREAD pObMap = NULL; + PVMM_PROCESS pObProcess = NULL; + if(!(pObProcess = VmmProcessGet(dwPID))) { goto fail; } + if(!VmmMap_GetThread(pObProcess, &pObMap)) { goto fail; } + cbDataMap = pObMap->cMap * sizeof(VMMDLL_MAP_THREADENTRY); + cbData = sizeof(VMMDLL_MAP_THREAD) + cbDataMap; + if(pThreadMap) { + if(*pcbThreadMap < cbData) { goto fail; } + ZeroMemory(pThreadMap, sizeof(VMMDLL_MAP_HEAP)); + pThreadMap->dwVersion = VMMDLL_MAP_VAD_VERSION; + pThreadMap->cMap = pObMap->cMap; + memcpy(pThreadMap->pMap, pObMap->pMap, cbDataMap); + } + fResult = TRUE; +fail: + *pcbThreadMap = cbData; + Ob_DECREF(pObProcess); + Ob_DECREF(pObMap); + return fResult; +} + +_Success_(return) +BOOL VMMDLL_ProcessMap_GetThread(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbThreadMap) PVMMDLL_MAP_THREAD pThreadMap, _Inout_ PDWORD pcbThreadMap) +{ + CALL_IMPLEMENTATION_VMM( + STATISTICS_ID_VMMDLL_ProcessMap_GetThread, + VMMDLL_ProcessMap_GetThread_Impl(dwPID, pThreadMap, pcbThreadMap)) +} + +_Success_(return) +BOOL VMMDLL_ProcessMap_GetHandle_Impl(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbHandleMap) PVMMDLL_MAP_HANDLE pHandleMap, _Inout_ PDWORD pcbHandleMap) +{ + BOOL fResult = FALSE; + QWORD i, cbData = 0, cbDataMap, cbMultiTextDiff; + PVMMDLL_MAP_HANDLEENTRY pe; + PVMMWIN_OBJECT_TYPE pOT; + PVMMOB_MAP_HANDLE pObMap = NULL; + PVMM_PROCESS pObProcess = NULL; + LPWSTR wszTypeMultiText; + if(!(pObProcess = VmmProcessGet(dwPID))) { goto fail; } + if(!VmmMap_GetHandle(pObProcess, &pObMap, TRUE)) { goto fail; } + VmmWin_ObjectTypeGet(2); + cbDataMap = pObMap->cMap * sizeof(VMMDLL_MAP_HANDLEENTRY); + cbData = sizeof(VMMDLL_MAP_HANDLE) + cbDataMap + pObMap->cbMultiText + ctxVmm->ObjectTypeTable.cbMultiText; + if(pHandleMap) { + if(*pcbHandleMap < cbData) { goto fail; } + ZeroMemory(pHandleMap, sizeof(VMMDLL_MAP_HANDLE)); + pHandleMap->dwVersion = VMMDLL_MAP_HANDLE_VERSION; + + pHandleMap->wszMultiText = (LPWSTR)(((PBYTE)pHandleMap->pMap) + cbDataMap); + pHandleMap->cbMultiText = pObMap->cbMultiText; + wszTypeMultiText = (LPWSTR)(((PBYTE)pHandleMap->wszMultiText) + pObMap->cbMultiText); + pHandleMap->cMap = pObMap->cMap; + memcpy(pHandleMap->pMap, pObMap->pMap, cbDataMap); + memcpy(pHandleMap->wszMultiText, pObMap->wszMultiText, pObMap->cbMultiText); + memcpy(wszTypeMultiText, ctxVmm->ObjectTypeTable.wszMultiText, ctxVmm->ObjectTypeTable.cbMultiText); + cbMultiTextDiff = (QWORD)pHandleMap->wszMultiText - (QWORD)pObMap->wszMultiText; + for(i = 0; i < pHandleMap->cMap; i++) { + pe = pHandleMap->pMap + i; + pe->wszText = (LPWSTR)(cbMultiTextDiff + (QWORD)pObMap->pMap[i].wszText); + if((pOT = VmmWin_ObjectTypeGet((BYTE)pe->iType))) { + pe->cwszType = pOT->cwsz; + pe->wszType = wszTypeMultiText + pOT->owsz; + } else { + pe->cwszType = 0; + pe->wszType = pHandleMap->wszMultiText; } } } - LocalFree(pModuleEntries); - return FALSE; + fResult = TRUE; +fail: + *pcbHandleMap = (DWORD)cbData; + Ob_DECREF(pObProcess); + Ob_DECREF(pObMap); + return fResult; } _Success_(return) -BOOL VMMDLL_ProcessGetModuleFromName(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _Out_ PVMMDLL_MODULEMAP_ENTRY pModuleEntry) +BOOL VMMDLL_ProcessMap_GetHandle(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbHandleMap) PVMMDLL_MAP_HANDLE pHandleMap, _Inout_ PDWORD pcbHandleMap) { CALL_IMPLEMENTATION_VMM( - STATISTICS_ID_VMMDLL_ProcessGetModuleFromName, - VMMDLL_ProcessGetModuleFromName_Impl(dwPID, szModuleName, pModuleEntry)) + STATISTICS_ID_VMMDLL_ProcessMap_GetHandle, + VMMDLL_ProcessMap_GetHandle_Impl(dwPID, pHandleMap, pcbHandleMap)) } _Success_(return) -BOOL VMMDLL_PidList_Impl(_Out_opt_ PDWORD pPIDs, _Inout_ PULONG64 pcPIDs) +BOOL VMMDLL_PidList_Impl(_Out_writes_opt_(*pcPIDs) PDWORD pPIDs, _Inout_ PULONG64 pcPIDs) { VmmProcessListPIDs(pPIDs, pcPIDs, 0); return TRUE; } _Success_(return) -BOOL VMMDLL_PidList(_Out_opt_ PDWORD pPIDs, _Inout_ PULONG64 pcPIDs) +BOOL VMMDLL_PidList(_Out_writes_opt_(*pcPIDs) PDWORD pPIDs, _Inout_ PULONG64 pcPIDs) { CALL_IMPLEMENTATION_VMM( STATISTICS_ID_VMMDLL_PidList, @@ -856,7 +1035,7 @@ BOOL VMMDLL_PidGetFromName_Impl(_In_ LPSTR szProcName, _Out_ PDWORD pdwPID) // 1: try locate process using long (full) name while((pObProcess = VmmProcessGetNext(pObProcess, 0))) { if(pObProcess->dwState) { continue; } - if(!pObProcess->pObProcessPersistent->szNameLong || _stricmp(szProcName, pObProcess->pObProcessPersistent->szNameLong)) { continue; } + if(!pObProcess->pObPersistent->szNameLong || _stricmp(szProcName, pObProcess->pObPersistent->szNameLong)) { continue; } *pdwPID = pObProcess->dwPID; Ob_DECREF(pObProcess); return TRUE; @@ -906,18 +1085,16 @@ BOOL VMMDLL_ProcessGetInformation_Impl(_In_ DWORD dwPID, _Inout_opt_ PVMMDLL_PRO pInfo->paDTB = pObProcess->paDTB; pInfo->paDTB_UserOpt = pObProcess->paDTB_UserOpt; memcpy(pInfo->szName, pObProcess->szName, sizeof(pInfo->szName)); - strncpy_s(pInfo->szNameLong, sizeof(pInfo->szNameLong), pObProcess->pObProcessPersistent->szNameLong, _TRUNCATE); + strncpy_s(pInfo->szNameLong, sizeof(pInfo->szNameLong), pObProcess->pObPersistent->szNameLong, _TRUNCATE); // set operating system specific parameters switch(ctxVmm->tpSystem) { case VMM_SYSTEM_WINDOWS_X64: pInfo->os.win.fWow64 = pObProcess->win.fWow64; - pInfo->os.win.vaENTRY = pObProcess->win.vaENTRY; pInfo->os.win.vaEPROCESS = pObProcess->win.EPROCESS.va; pInfo->os.win.vaPEB = pObProcess->win.vaPEB; pInfo->os.win.vaPEB32 = pObProcess->win.vaPEB32; break; case VMM_SYSTEM_WINDOWS_X86: - pInfo->os.win.vaENTRY = pObProcess->win.vaENTRY; pInfo->os.win.vaEPROCESS = pObProcess->win.EPROCESS.va; pInfo->os.win.vaPEB = pObProcess->win.vaPEB; break; @@ -936,7 +1113,7 @@ BOOL VMMDLL_ProcessGetInformation(_In_ DWORD dwPID, _Inout_opt_ PVMMDLL_PROCESS_ BOOL VMMDLL_ProcessGetInformationString_Impl_CallbackCriteria(_In_ PVMM_PROCESS pProcess, _In_ PVOID ctx) { - return !pProcess->pObProcessPersistent->UserProcessParams.fProcessed; + return !pProcess->pObPersistent->UserProcessParams.fProcessed; } VOID VMMDLL_ProcessGetInformationString_Impl_CallbackAction(_In_ PVMM_PROCESS pProcess, _In_ PVOID ctx) @@ -952,19 +1129,19 @@ LPSTR VMMDLL_ProcessGetInformationString_Impl(_In_ DWORD dwPID, _In_ DWORD fOpti switch(fOptionString) { case VMMDLL_PROCESS_INFORMATION_OPT_STRING_PATH_USER_IMAGE: case VMMDLL_PROCESS_INFORMATION_OPT_STRING_CMDLINE: - if(!pObProcess->pObProcessPersistent->UserProcessParams.fProcessed) { + if(!pObProcess->pObPersistent->UserProcessParams.fProcessed) { VmmProcessActionForeachParallel(NULL, 5, VMMDLL_ProcessGetInformationString_Impl_CallbackCriteria, VMMDLL_ProcessGetInformationString_Impl_CallbackAction); } } switch(fOptionString) { case VMMDLL_PROCESS_INFORMATION_OPT_STRING_PATH_KERNEL: - sz = pObProcess->pObProcessPersistent->szPathKernel; + sz = pObProcess->pObPersistent->szPathKernel; break; case VMMDLL_PROCESS_INFORMATION_OPT_STRING_PATH_USER_IMAGE: - sz = pObProcess->pObProcessPersistent->UserProcessParams.szImagePathName; + sz = pObProcess->pObPersistent->UserProcessParams.szImagePathName; break; case VMMDLL_PROCESS_INFORMATION_OPT_STRING_CMDLINE: - sz = pObProcess->pObProcessPersistent->UserProcessParams.szCommandLine; + sz = pObProcess->pObPersistent->UserProcessParams.szCommandLine; break; } szStrDup = Util_StrDupA(sz); @@ -984,7 +1161,7 @@ LPSTR VMMDLL_ProcessGetInformationString(_In_ DWORD dwPID, _In_ DWORD fOptionStr _Success_(return) BOOL VMMDLL_ProcessGet_Directories_Sections_IAT_EAT_Impl( _In_ DWORD dwPID, - _In_ LPSTR szModule, + _In_ LPWSTR wszModule, _In_ DWORD cData, _Out_ PDWORD pcData, _Out_writes_opt_(16) PIMAGE_DATA_DIRECTORY pDataDirectory, @@ -998,19 +1175,14 @@ BOOL VMMDLL_ProcessGet_Directories_Sections_IAT_EAT_Impl( ) { DWORD i; - PVMMOB_MODULEMAP pObModuleMap = NULL; - PVMM_MODULEMAP_ENTRY pModule = NULL; + PVMMOB_MAP_MODULE pObModuleMap = NULL; + PVMM_MAP_MODULEENTRY pModule = NULL; PVMM_PROCESS pObProcess = NULL; pObProcess = VmmProcessGet(dwPID); if(!pObProcess) { goto fail; } // fetch requested module - if(!VmmProc_ModuleMapGet(pObProcess, &pObModuleMap)) { goto fail; } - for(i = 0; i < pObModuleMap->cMap; i++) { - if(!_stricmp(pObModuleMap->pMap[i].szName, szModule)) { - pModule = pObModuleMap->pMap + i; - } - } - if(!pModule) { goto fail; } + if(!VmmMap_GetModule(pObProcess, &pObModuleMap)) { goto fail; } + if(!(pModule = VmmMap_GetModuleEntry(pObModuleMap, wszModule))) { goto fail; } // data directories if(fDataDirectory) { if(!pDataDirectory) { *pcData = 16; goto success; } @@ -1021,7 +1193,7 @@ BOOL VMMDLL_ProcessGet_Directories_Sections_IAT_EAT_Impl( } // sections if(fSections) { - i = PE_SectionGetNumberOf(pObProcess, pModule->BaseAddress); + i = PE_SectionGetNumberOf(pObProcess, pModule->vaBase); if(!pSections) { *pcData = i; goto success; } if(cData < i) { goto fail; } VmmWin_PE_SECTION_DisplayBuffer(pObProcess, pModule, NULL, 0, NULL, &cData, pSections); @@ -1030,7 +1202,7 @@ BOOL VMMDLL_ProcessGet_Directories_Sections_IAT_EAT_Impl( } // export address table (EAT) if(fEAT) { - i = PE_EatGetNumberOf(pObProcess, pModule->BaseAddress); + i = PE_EatGetNumberOf(pObProcess, pModule->vaBase); if(!pEAT) { *pcData = i; goto success; } if(cData < i) { goto fail; } VmmWin_PE_LoadEAT_DisplayBuffer(pObProcess, pModule, (PVMMPROC_WINDOWS_EAT_ENTRY)pEAT, cData, &cData); @@ -1039,7 +1211,7 @@ BOOL VMMDLL_ProcessGet_Directories_Sections_IAT_EAT_Impl( } // import address table (IAT) if(fIAT) { - i = PE_IatGetNumberOf(pObProcess, pModule->BaseAddress); + i = PE_IatGetNumberOf(pObProcess, pModule->vaBase); if(!pIAT) { *pcData = i; goto success; } if(cData < i) { goto fail; } VmmWin_PE_LoadIAT_DisplayBuffer(pObProcess, pModule, (PVMMWIN_IAT_ENTRY)pIAT, cData, &cData); @@ -1057,81 +1229,80 @@ BOOL VMMDLL_ProcessGet_Directories_Sections_IAT_EAT_Impl( } _Success_(return) -BOOL VMMDLL_ProcessGetDirectories(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_writes_(16) PIMAGE_DATA_DIRECTORY pData, _In_ DWORD cData, _Out_ PDWORD pcData) +BOOL VMMDLL_ProcessGetDirectories(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_writes_(16) PIMAGE_DATA_DIRECTORY pData, _In_ DWORD cData, _Out_ PDWORD pcData) { CALL_IMPLEMENTATION_VMM( STATISTICS_ID_VMMDLL_ProcessGetDirectories, - VMMDLL_ProcessGet_Directories_Sections_IAT_EAT_Impl(dwPID, szModule, cData, pcData, pData, NULL, NULL, NULL, TRUE, FALSE, FALSE, FALSE)) + VMMDLL_ProcessGet_Directories_Sections_IAT_EAT_Impl(dwPID, wszModule, cData, pcData, pData, NULL, NULL, NULL, TRUE, FALSE, FALSE, FALSE)) } _Success_(return) -BOOL VMMDLL_ProcessGetSections(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PIMAGE_SECTION_HEADER pData, _In_ DWORD cData, _Out_ PDWORD pcData) +BOOL VMMDLL_ProcessGetSections(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PIMAGE_SECTION_HEADER pData, _In_ DWORD cData, _Out_ PDWORD pcData) { CALL_IMPLEMENTATION_VMM( STATISTICS_ID_VMMDLL_ProcessGetSections, - VMMDLL_ProcessGet_Directories_Sections_IAT_EAT_Impl(dwPID, szModule, cData, pcData, NULL, pData, NULL, NULL, FALSE, TRUE, FALSE, FALSE)) + VMMDLL_ProcessGet_Directories_Sections_IAT_EAT_Impl(dwPID, wszModule, cData, pcData, NULL, pData, NULL, NULL, FALSE, TRUE, FALSE, FALSE)) } _Success_(return) -BOOL VMMDLL_ProcessGetEAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PVMMDLL_EAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData) +BOOL VMMDLL_ProcessGetEAT(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PVMMDLL_EAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData) { CALL_IMPLEMENTATION_VMM( STATISTICS_ID_VMMDLL_ProcessGetEAT, - VMMDLL_ProcessGet_Directories_Sections_IAT_EAT_Impl(dwPID, szModule, cData, pcData, NULL, NULL, pData, NULL, FALSE, FALSE, TRUE, FALSE)) + VMMDLL_ProcessGet_Directories_Sections_IAT_EAT_Impl(dwPID, wszModule, cData, pcData, NULL, NULL, pData, NULL, FALSE, FALSE, TRUE, FALSE)) } _Success_(return) -BOOL VMMDLL_ProcessGetIAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PVMMDLL_IAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData) +BOOL VMMDLL_ProcessGetIAT(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PVMMDLL_IAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData) { CALL_IMPLEMENTATION_VMM( STATISTICS_ID_VMMDLL_ProcessGetIAT, - VMMDLL_ProcessGet_Directories_Sections_IAT_EAT_Impl(dwPID, szModule, cData, pcData, NULL, NULL, NULL, pData, FALSE, FALSE, FALSE, TRUE)) + VMMDLL_ProcessGet_Directories_Sections_IAT_EAT_Impl(dwPID, wszModule, cData, pcData, NULL, NULL, NULL, pData, FALSE, FALSE, FALSE, TRUE)) } -ULONG64 VMMDLL_ProcessGetProcAddress_Impl(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szFunctionName) +ULONG64 VMMDLL_ProcessGetProcAddress_Impl(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szFunctionName) { QWORD vaFn = 0; - VMMDLL_MODULEMAP_ENTRY oModuleEntry = { 0 }; + VMMDLL_MAP_MODULEENTRY oModuleEntry = { 0 }; PVMM_PROCESS pObProcess = NULL; pObProcess = VmmProcessGet(dwPID); if(!pObProcess) { return 0; } - if(VMMDLL_ProcessGetModuleFromName_Impl(dwPID, szModuleName, &oModuleEntry)) { - vaFn = PE_GetProcAddress(pObProcess, oModuleEntry.BaseAddress, szFunctionName); + if(VMMDLL_ProcessMap_GetModuleFromName_Impl(dwPID, wszModuleName, &oModuleEntry)) { + vaFn = PE_GetProcAddress(pObProcess, oModuleEntry.vaBase, szFunctionName); } Ob_DECREF(pObProcess); return vaFn; } -ULONG64 VMMDLL_ProcessGetProcAddress(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szFunctionName) +ULONG64 VMMDLL_ProcessGetProcAddress(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szFunctionName) { CALL_IMPLEMENTATION_VMM_RETURN( STATISTICS_ID_VMMDLL_ProcessGetIAT, ULONG64, 0, - VMMDLL_ProcessGetProcAddress_Impl(dwPID, szModuleName, szFunctionName)) + VMMDLL_ProcessGetProcAddress_Impl(dwPID, wszModuleName, szFunctionName)) } -ULONG64 VMMDLL_ProcessGetModuleBase_Impl(_In_ DWORD dwPID, _In_ LPSTR szModuleName) +ULONG64 VMMDLL_ProcessGetModuleBase_Impl(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName) { QWORD vaModuleBase = 0; - VMMDLL_MODULEMAP_ENTRY oModuleEntry = { 0 }; PVMM_PROCESS pObProcess = NULL; - pObProcess = VmmProcessGet(dwPID); - if(!pObProcess) { return 0; } - if(VMMDLL_ProcessGetModuleFromName_Impl(dwPID, szModuleName, &oModuleEntry)) { - vaModuleBase = oModuleEntry.BaseAddress; + VMMDLL_MAP_MODULEENTRY oModuleEntry = { 0 }; + if(!(pObProcess = VmmProcessGet(dwPID))) { return 0; } + if(VMMDLL_ProcessMap_GetModuleFromName_Impl(dwPID, wszModuleName, &oModuleEntry)) { + vaModuleBase = oModuleEntry.vaBase; } Ob_DECREF(pObProcess); return vaModuleBase; } -ULONG64 VMMDLL_ProcessGetModuleBase(_In_ DWORD dwPID, _In_ LPSTR szModuleName) +ULONG64 VMMDLL_ProcessGetModuleBase(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName) { CALL_IMPLEMENTATION_VMM_RETURN( STATISTICS_ID_VMMDLL_ProcessGetModuleBase, ULONG64, 0, - VMMDLL_ProcessGetModuleBase_Impl(dwPID, szModuleName)) + VMMDLL_ProcessGetModuleBase_Impl(dwPID, wszModuleName)) } @@ -1375,49 +1546,46 @@ PVMMDLL_WIN_TCPIP VMMDLL_WinNet_Get() //----------------------------------------------------------------------------- _Success_(return) -BOOL VMMDLL_WinGetThunkInfoEAT_Impl(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT) +BOOL VMMDLL_WinGetThunkInfoEAT_Impl(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT) { BOOL f; - VMMDLL_MODULEMAP_ENTRY oModuleEntry = { 0 }; PVMM_PROCESS pObProcess = NULL; - pObProcess = VmmProcessGet(dwPID); - if(!pObProcess) { return FALSE; } - f = VMMDLL_ProcessGetModuleFromName_Impl(dwPID, szModuleName, &oModuleEntry) && - PE_GetThunkInfoEAT(pObProcess, oModuleEntry.BaseAddress, szExportFunctionName, (PPE_THUNKINFO_EAT)pThunkInfoEAT); + VMMDLL_MAP_MODULEENTRY oModuleEntry = { 0 }; + f = (pObProcess = VmmProcessGet(dwPID)) && + VMMDLL_ProcessMap_GetModuleFromName_Impl(dwPID, wszModuleName, &oModuleEntry) && + PE_GetThunkInfoEAT(pObProcess, oModuleEntry.vaBase, szExportFunctionName, (PPE_THUNKINFO_EAT)pThunkInfoEAT); Ob_DECREF(pObProcess); return f; } _Success_(return) -BOOL VMMDLL_WinGetThunkInfoEAT(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT) +BOOL VMMDLL_WinGetThunkInfoEAT(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT) { CALL_IMPLEMENTATION_VMM( STATISTICS_ID_VMMDLL_WinGetThunkEAT, - VMMDLL_WinGetThunkInfoEAT_Impl(dwPID, szModuleName, szExportFunctionName, pThunkInfoEAT)) + VMMDLL_WinGetThunkInfoEAT_Impl(dwPID, wszModuleName, szExportFunctionName, pThunkInfoEAT)) } _Success_(return) -BOOL VMMDLL_WinGetThunkInfoIAT_Impl(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szImportModuleName, _In_ LPSTR szImportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_IAT pThunkInfoIAT) +BOOL VMMDLL_WinGetThunkInfoIAT_Impl(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szImportModuleName, _In_ LPSTR szImportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_IAT pThunkInfoIAT) { - BOOL result = FALSE; - VMMDLL_MODULEMAP_ENTRY oModuleEntry = { 0 }; + BOOL f = FALSE; PVMM_PROCESS pObProcess = NULL; - if(sizeof(VMMDLL_WIN_THUNKINFO_IAT) != sizeof(PE_THUNKINFO_IAT)) { return FALSE; } - pObProcess = VmmProcessGet(dwPID); - if(!pObProcess) { return 0; } - if(VMMDLL_ProcessGetModuleFromName_Impl(dwPID, szModuleName, &oModuleEntry)) { - result = PE_GetThunkInfoIAT(pObProcess, oModuleEntry.BaseAddress, szImportModuleName, szImportFunctionName, (PPE_THUNKINFO_IAT)pThunkInfoIAT); - } + VMMDLL_MAP_MODULEENTRY oModuleEntry = { 0 }; + f = (sizeof(VMMDLL_WIN_THUNKINFO_IAT) == sizeof(PE_THUNKINFO_IAT)) && + (pObProcess = VmmProcessGet(dwPID)) && + VMMDLL_ProcessMap_GetModuleFromName_Impl(dwPID, wszModuleName, &oModuleEntry) && + PE_GetThunkInfoIAT(pObProcess, oModuleEntry.vaBase, szImportModuleName, szImportFunctionName, (PPE_THUNKINFO_IAT)pThunkInfoIAT); Ob_DECREF(pObProcess); - return result; + return f; } _Success_(return) -BOOL VMMDLL_WinGetThunkInfoIAT(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szImportModuleName, _In_ LPSTR szImportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_IAT pThunkInfoIAT) +BOOL VMMDLL_WinGetThunkInfoIAT(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szImportModuleName, _In_ LPSTR szImportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_IAT pThunkInfoIAT) { CALL_IMPLEMENTATION_VMM( STATISTICS_ID_VMMDLL_WinGetThunkIAT, - VMMDLL_WinGetThunkInfoIAT_Impl(dwPID, szModuleName, szImportModuleName, szImportFunctionName, pThunkInfoIAT)) + VMMDLL_WinGetThunkInfoIAT_Impl(dwPID, wszModuleName, szImportModuleName, szImportFunctionName, pThunkInfoIAT)) } diff --git a/vmm/vmmdll.def b/vmm/vmmdll.def index 6d0aa7d5..e51f4c5f 100644 --- a/vmm/vmmdll.def +++ b/vmm/vmmdll.def @@ -31,10 +31,13 @@ EXPORTS VMMDLL_PidList VMMDLL_PidGetFromName - VMMDLL_ProcessGetMemoryMap - VMMDLL_ProcessGetMemoryMapEntry - VMMDLL_ProcessGetModuleMap - VMMDLL_ProcessGetModuleFromName + VMMDLL_ProcessMap_GetPte + VMMDLL_ProcessMap_GetVad + VMMDLL_ProcessMap_GetModule + VMMDLL_ProcessMap_GetModuleFromName + VMMDLL_ProcessMap_GetHeap + VMMDLL_ProcessMap_GetThread + VMMDLL_ProcessMap_GetHandle VMMDLL_ProcessGetInformation VMMDLL_ProcessGetInformationString diff --git a/vmm/vmmdll.h b/vmm/vmmdll.h index 8a6b6a38..87f3f191 100644 --- a/vmm/vmmdll.h +++ b/vmm/vmmdll.h @@ -4,7 +4,7 @@ // (c) Ulf Frisk, 2018-2019 // Author: Ulf Frisk, pcileech@frizk.net // -// Header Version: 2.10.2 +// Header Version: 3.0 // #include @@ -23,11 +23,11 @@ extern "C" { /* * Initialize VMM.DLL with command line parameters. For a more detailed info -* about the parameters please see github wiki for Memory Process File System -* and LeechCore. THIS IS THE PREFERED WAY OF INITIALIZING VMM.DLL +* about the parameters please see github wiki for MemProcFS and LeechCore. +* NB! LeechCore initialization parameters are _also_ valid to this function. * Important parameters are: -* -printf = show printf style outputs) -* -v -vv -vvv = extra verbosity levels) +* -printf = show printf style outputs. +* -v -vv -vvv = extra verbosity levels. * -device = device as on format for LeechCore - please see leechcore.h or * Github documentation for additional information. Some values * are: , fpga, usb3380, hvsavedstate, totalmeltdown, pmem @@ -35,8 +35,19 @@ extern "C" { * documentation for additional information. * -norefresh = disable background refreshes (even if backing memory is * volatile memory). -* -symbolserverdisable = disable symbol server until user change. This -* parameter will take precedence over registry settings. +* -symbolserverdisable = disable symbol server until user change. +* This parameter will take precedence over registry settings. +* -pagefile[0-9] = page file(s) to use in addition to physical memory. +* Normally pagefile.sys have index 0 and swapfile.sys index 1. +* Page files are in constant flux - do not use if time diff +* between memory dump and page files are more than few minutes. +* Example: 'pagefile0 swapfile.sys' +* -waitinitialize = Wait for initialization to complete before returning. +* Normal use is that some initialization is done asynchronously +* and may not be completed when initialization call is completed. +* This includes virtual memory compression, registry and more. +* Example: '-waitinitialize' +* * -- argc * -- argv * -- return = success/fail @@ -78,9 +89,9 @@ VOID VMMDLL_MemFree(_Frees_ptr_opt_ PVOID pvMem); //----------------------------------------------------------------------------- /* -* Options used together with the functions: VMMDLL_GetOption & VMMDLL_SetOption +* Options used together with the functions: VMMDLL_ConfigGet & VMMDLL_ConfigSet * Options are defined with either: VMMDLL_OPT_* in this header file or as -* MEMDEVICE_OPT_* in memdevice.h +* LEECHCORE_OPT_* in leechcore.h * For more detailed information check the sources for individual device types. */ #define VMMDLL_OPT_CORE_PRINTF_ENABLE 0x80000001 // RW @@ -349,17 +360,17 @@ typedef struct tdVMMDLL_PLUGIN_REGINFO { #define VMMDLL_FLAG_ZEROPAD_ON_FAIL 0x0002 // zero pad failed physical memory reads and report success if read within range of physical memory. #define VMMDLL_FLAG_FORCECACHE_READ 0x0008 // force use of cache - fail non-cached pages - only valid for reads, invalid with VMM_FLAG_NOCACHE/VMM_FLAG_ZEROPAD_ON_FAIL. #define VMMDLL_FLAG_NOPAGING 0x0010 // do not try to retrieve memory from paged out memory from pagefile/compressed (even if possible) +#define VMMDLL_FLAG_NOPAGING_IO 0x0020 // do not try to retrieve memory from paged out memory if read would incur additional I/O (even if possible). /* * Read memory in various non-contigious locations specified by the pointers to -* the items in the ppDMAs array. Result for each unit of work will be given +* the items in the ppMEMs array. Result for each unit of work will be given * individually. No upper limit of number of items to read, but no performance * boost will be given if above hardware limit. Max size of each unit of work is * one 4k page (4096 bytes). * -- dwPID - PID of target process, (DWORD)-1 to read physical memory. * -- ppMEMs = array of scatter read headers. -* -- cpMEMs = count of ppDMAs. -* -- pcpDMAsRead = optional count of number of successfully read ppDMAs. +* -- cpMEMs = count of ppMEMs. * -- flags = optional flags as given by VMMDLL_FLAG_* * -- return = the number of successfully read items. */ @@ -384,7 +395,7 @@ BOOL VMMDLL_MemReadPage(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Inout_bytecount_(40 * -- return = success/fail (depending if all requested bytes are read or not). */ _Success_(return) -BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWORD cb); +BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb); /* * Read a contigious amount of memory and report the number of bytes read in pcbRead. @@ -398,7 +409,7 @@ BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWO * read - it's recommended to verify pcbReadOpt parameter. */ _Success_(return) -BOOL VMMDLL_MemReadEx(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWORD cb, _Out_opt_ PDWORD pcbReadOpt, _In_ ULONG64 flags); +BOOL VMMDLL_MemReadEx(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb, _Out_opt_ PDWORD pcbReadOpt, _In_ ULONG64 flags); /* * Prefetch a number of addresses (specified in the pA array) into the memory @@ -426,7 +437,7 @@ BOOL VMMDLL_MemPrefetchPages(_In_ DWORD dwPID, _In_reads_(cPrefetchAddresses) PU * -- return = TRUE on success, FALSE on partial or zero write. */ _Success_(return) -BOOL VMMDLL_MemWrite(_In_ DWORD dwPID, _In_ ULONG64 qwA, _In_ PBYTE pb, _In_ DWORD cb); +BOOL VMMDLL_MemWrite(_In_ DWORD dwPID, _In_ ULONG64 qwA, _In_reads_(cb) PBYTE pb, _In_ DWORD cb); /* * Translate a virtual address to a physical address by walking the page tables @@ -442,106 +453,286 @@ BOOL VMMDLL_MemVirt2Phys(_In_ DWORD dwPID, _In_ ULONG64 qwVA, _Out_ PULONG64 pqw //----------------------------------------------------------------------------- -// VMM PROCESS FUNCTIONALITY BELOW: -// Functionality below is mostly relating to Windows processes. +// VMM PROCESS MAP FUNCTIONALITY BELOW: +// Functionality for retrieving process related collections of items such as +// page table map (PTE), virtual address descriptor map (VAD), loaded modules, +// heaps and threads. //----------------------------------------------------------------------------- -/* -* Retrieve an active process given it's name. Please note that if multiple -* processes with the same name exists only one will be returned. If required to -* parse all processes with the same name please iterate over the PID list by -* calling VMMDLL_PidList together with VMMDLL_ProcessGetInformation. -* -- szProcName = process name case insensitive. -* -- pdwPID = pointer that will receive PID on success. -* -- return -*/ -_Success_(return) -BOOL VMMDLL_PidGetFromName(_In_ LPSTR szProcName, _Out_ PDWORD pdwPID); - -/* -* List the PIDs in the system. -* -- pPIDs = DWORD array of at least number of PIDs in system, or NULL. -* -- pcPIDs = size of (in number of DWORDs) pPIDs array on entry, number of PIDs in system on exit. -* -- return = success/fail. -*/ -_Success_(return) -BOOL VMMDLL_PidList(_Out_opt_ PDWORD pPIDs, _Inout_ PULONG64 pcPIDs); +#define VMMDLL_MAP_PTE_VERSION 1 +#define VMMDLL_MAP_VAD_VERSION 1 +#define VMMDLL_MAP_MODULE_VERSION 1 +#define VMMDLL_MAP_HEAP_VERSION 1 +#define VMMDLL_MAP_THREAD_VERSION 1 +#define VMMDLL_MAP_HANDLE_VERSION 1 -// flags to check for existence in the fPage field of PCILEECH_VMM_MEMMAP_ENTRY +// flags to check for existence in the fPage field of VMMDLL_MAP_PTEENTRY #define VMMDLL_MEMMAP_FLAG_PAGE_W 0x0000000000000002 #define VMMDLL_MEMMAP_FLAG_PAGE_NS 0x0000000000000004 #define VMMDLL_MEMMAP_FLAG_PAGE_NX 0x8000000000000000 #define VMMDLL_MEMMAP_FLAG_PAGE_MASK 0x8000000000000006 -typedef struct tdVMMDLL_MEMMAP_ENTRY { - ULONG64 AddrBase; - ULONG64 cPages; - ULONG64 fPage; +typedef struct tdVMMDLL_MAP_PTEENTRY { + QWORD vaBase; + QWORD cPages; + QWORD fPage; BOOL fWoW64; - CHAR szTag[32]; -} VMMDLL_MEMMAP_ENTRY, *PVMMDLL_MEMMAP_ENTRY; + DWORD cwszText; + LPWSTR wszText; + DWORD _Reserved1[2]; +} VMMDLL_MAP_PTEENTRY, *PVMMDLL_MAP_PTEENTRY; + +typedef struct tdVMMDLL_MAP_VADENTRY { + QWORD vaStart; + QWORD vaEnd; + QWORD vaVad; + // DWORD 0 + DWORD VadType : 3; // Pos 0 + DWORD Protection : 5; // Pos 3 + DWORD fImage : 1; // Pos 8 + DWORD fFile : 1; // Pos 9 + DWORD fPageFile : 1; // Pos 10 + DWORD fPrivateMemory : 1; // Pos 11 + DWORD fTeb : 1; // Pos 12 + DWORD fStack : 1; // Pos 13 + DWORD fSpare : 2; // Pos 14 + DWORD HeapNum : 7; // Pos 16 + DWORD fHeap : 1; // Pos 23 + DWORD cwszDescription : 8; // Pos 24 + // DWORD 1 + DWORD CommitCharge : 31; // Pos 0 + DWORD MemCommit : 1; // Pos 31 + DWORD u2; + DWORD cbPrototypePte; + QWORD vaPrototypePte; + QWORD vaSubsection; + LPWSTR wszText; // optional LPWSTR pointed into VMMDLL_MAP_VAD.wszMultiText + DWORD cwszText; // WCHAR count not including terminating null + DWORD _Reserved1; +} VMMDLL_MAP_VADENTRY, *PVMMDLL_MAP_VADENTRY; + +typedef struct tdVMMDLL_MAP_MODULEENTRY { + QWORD vaBase; + QWORD vaEntry; + DWORD cbImageSize; + BOOL fWoW64; + LPWSTR wszText; + DWORD cwszText; // wchar count not including terminating null + DWORD _Reserved1[7]; +} VMMDLL_MAP_MODULEENTRY, *PVMMDLL_MAP_MODULEENTRY; + +typedef struct tdVMMDLL_MAP_HEAPENTRY { + QWORD vaHeapSegment; + DWORD cPages; + DWORD cPagesUnCommitted : 24; + DWORD HeapId : 7; + DWORD fPrimary : 1; +} VMMDLL_MAP_HEAPENTRY, *PVMMDLL_MAP_HEAPENTRY; + +typedef struct tdVMMDLL_MAP_THREADENTRY { + DWORD dwTID; + DWORD dwPID; + DWORD dwExitStatus; + UCHAR bState; + UCHAR bRunning; + UCHAR bPriority; + UCHAR bBasePriority; + QWORD vaETHREAD; + QWORD vaTeb; + QWORD ftCreateTime; + QWORD ftExitTime; + QWORD vaStartAddress; + QWORD vaStackBaseUser; + QWORD vaStackLimitUser; + QWORD vaStackBaseKernel; + QWORD vaStackLimitKernel; + DWORD _FutureUse[10]; +} VMMDLL_MAP_THREADENTRY, *PVMMDLL_MAP_THREADENTRY; + +typedef struct tdVMMDLL_MAP_HANDLEENTRY { + QWORD vaObject; + DWORD dwHandle; + DWORD dwGrantedAccess : 24; + DWORD iType : 8; + QWORD qwHandleCount; + QWORD qwPointerCount; + QWORD vaObjectCreateInfo; + QWORD vaSecurityDescriptor; + LPWSTR wszText; + DWORD cwszText; + DWORD dwPID; + DWORD dwPoolTag; + DWORD _FutureUse[4]; + DWORD cwszType; + LPWSTR wszType; +} VMMDLL_MAP_HANDLEENTRY, *PVMMDLL_MAP_HANDLEENTRY; + +typedef struct tdVMMDLL_MAP_PTE { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // NULL or multi-wstr pointed into by VMMDLL_MAP_VADENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_PTEENTRY pMap[]; // map entries. +} VMMDLL_MAP_PTE, *PVMMDLL_MAP_PTE; + +typedef struct tdVMMDLL_MAP_VAD { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // NULL or multi-wstr pointed into by VMMDLL_MAP_VADENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_VADENTRY pMap[]; // map entries. +} VMMDLL_MAP_VAD, *PVMMDLL_MAP_VAD; + +typedef struct tdVMMDLL_MAP_MODULE { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // multi-wstr pointed into by VMMDLL_MAP_MODULEENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_MODULEENTRY pMap[]; // map entries. +} VMMDLL_MAP_MODULE, *PVMMDLL_MAP_MODULE; + +typedef struct tdVMMDLL_MAP_HEAP { + DWORD dwVersion; + DWORD _Reserved1[8]; + DWORD cMap; // # map entries. + VMMDLL_MAP_HEAPENTRY pMap[]; // map entries. +} VMMDLL_MAP_HEAP, *PVMMDLL_MAP_HEAP; + +typedef struct tdVMMDLL_MAP_THREAD { + DWORD dwVersion; + DWORD _Reserved[8]; + DWORD cMap; // # map entries. + VMMDLL_MAP_THREADENTRY pMap[]; // map entries. +} VMMDLL_MAP_THREAD, *PVMMDLL_MAP_THREAD; + +typedef struct tdVMMDLL_MAP_HANDLE { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // multi-wstr pointed into by VMMDLL_MAP_HANDLEENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_HANDLEENTRY pMap[]; // map entries. +} VMMDLL_MAP_HANDLE, *PVMMDLL_MAP_HANDLE; /* -* Retrieve memory map entries from the specified process. Memory map entries -* are copied into the user supplied buffer that must be at least of size: -* sizeof(VMMDLL_MEMMAP_ENTRY)*pcMemMapEntries bytes. -* If the pMemMapEntries is set to NULL the number of memory map entries will be -* given in the pcMemMapEntries parameter. +* Retrieve the memory map entries based on hardware page tables (PTE) for the +* process. If pPteMap is set to NULL the number of bytes required will be +* returned in parameter pcbPteMap. +* Entries returned are sorted on VMMDLL_MAP_PTEENTRY.vaBase * -- dwPID -* -- pMemMapEntries = buffer of minimum length sizeof(VMMDLL_MEMMAP_ENTRY)*pcMemMapEntries, or NULL. -* -- pcMemMapEntries = pointer to number of memory map entries. +* -- pPteMap = buffer of minimum byte length *pcbPteMap or NULL. +* -- pcbPteMap = pointer to byte count of pPteMap buffer. * -- fIdentifyModules = try identify modules as well (= slower) * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetMemoryMap(_In_ DWORD dwPID, _Out_opt_ PVMMDLL_MEMMAP_ENTRY pMemMapEntries, _Inout_ PULONG64 pcMemMapEntries, _In_ BOOL fIdentifyModules); +BOOL VMMDLL_ProcessMap_GetPte(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbPteMap) PVMMDLL_MAP_PTE pPteMap, _Inout_ PDWORD pcbPteMap, _In_ BOOL fIdentifyModules); /* -* Retrieve a single memory map entry given a virtual address within that entrys -* range. +* Retrieve memory map entries based on virtual address descriptor (VAD) for +* the process. If pVadMap is set to NULL the number of bytes required +* will be returned in parameter pcbVadMap. +* Entries returned are sorted on VMMDLL_MAP_VADENTRY.vaStart * -- dwPID -* -- pMemMapEntry -* -- va = virtual address in the memory map entry to retrieve. +* -- pVadMap = buffer of minimum byte length *pcbVadMap or NULL. +* -- pcbVadMap = pointer to byte count of pVadMap buffer. * -- fIdentifyModules = try identify modules as well (= slower) * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetMemoryMapEntry(_In_ DWORD dwPID, _Out_ PVMMDLL_MEMMAP_ENTRY pMemMapEntry, _In_ ULONG64 va, _In_ BOOL fIdentifyModules); - -typedef struct tdVMMDLL_MODULEMAP_ENTRY { - ULONG64 BaseAddress; - ULONG64 EntryPoint; - DWORD SizeOfImage; - BOOL fWoW64; - CHAR szName[32]; -} VMMDLL_MODULEMAP_ENTRY, *PVMMDLL_MODULEMAP_ENTRY; +BOOL VMMDLL_ProcessMap_GetVad(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbVadMap) PVMMDLL_MAP_VAD pVadMap, _Inout_ PDWORD pcbVadMap, _In_ BOOL fIdentifyModules); /* -* Retrieve the module entries from the specified process. The module entries -* are copied into the user supplied buffer that must be at least of size: -* sizeof(VMMDLL_MODULEMAP_ENTRY)*pcModuleEntries bytes long. If the -* pcModuleEntries is set to NULL the number of module entries will be given -* in the pcModuleEntries parameter. +* Retrieve the modules (.dlls) for the specified process. If pModuleMap is set +* to NULL the number of bytes required will be returned in parameter pcbModuleMap. * -- dwPID -* -- pModuleEntries = buffer of minimum length sizeof(VMMDLL_MODULEMAP_ENTRY)*pcModuleEntries, or NULL. -* -- pcModuleEntries = pointer to number of memory map entries. +* -- pModuleMap = buffer of minimum byte length *pcbModuleMap or NULL. +* -- pcbModuleMap = pointer to byte count of pModuleMap buffer. * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetModuleMap(_In_ DWORD dwPID, _Out_opt_ PVMMDLL_MODULEMAP_ENTRY pModuleEntries, _Inout_ PULONG64 pcModuleEntries); +BOOL VMMDLL_ProcessMap_GetModule(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbModuleMap) PVMMDLL_MAP_MODULE pModuleMap, _Inout_ PDWORD pcbModuleMap); /* -* Retrieve a module (.exe or .dll or similar) given a module name. +* Retrieve a module map entry (.exe / .dll) given a process and module name. +* NB! PVMMDLL_MAP_MODULEENTRY->wszText will not be set by this function. +* If module name is required use VMMDLL_ProcessMap_GetModule(). * -- dwPID * -- szModuleName -* -- pModuleEntry +* -- pModuleMapEntry * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetModuleFromName(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _Out_ PVMMDLL_MODULEMAP_ENTRY pModuleEntry); +BOOL VMMDLL_ProcessMap_GetModuleFromName(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _Out_ PVMMDLL_MAP_MODULEENTRY pModuleMapEntry); + +/* +* Retrieve the heaps for the specified process. If pHeapMap is set to NULL +* the number of bytes required will be returned in parameter pcbHeapMap. +* -- dwPID +* -- pHeapMap = buffer of minimum byte length *pcbHeapMap or NULL. +* -- pcbHeapMap = pointer to byte count of pHeapMap buffer. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_ProcessMap_GetHeap(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbHeapMap) PVMMDLL_MAP_HEAP pHeapMap, _Inout_ PDWORD pcbHeapMap); + +/* +* Retrieve the threads for the specified process. If pThreadMap is set to NULL +* the number of bytes required will be returned in parameter pcbThreadMap. +* Entries returned are sorted on VMMDLL_MAP_THREADENTRY.dwTID +* -- dwPID +* -- pThreadMap = buffer of minimum byte length *pcbThreadMap or NULL. +* -- pcbThreadMap = pointer to byte count of pThreadMap buffer. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_ProcessMap_GetThread(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbThreadMap) PVMMDLL_MAP_THREAD pThreadMap, _Inout_ PDWORD pcbThreadMap); + +/* +* Retrieve the handles for the specified process. If pHandleMap is set to NULL +* the number of bytes required will be returned in parameter pcbHandleMap. +* Entries returned are sorted on VMMDLL_MAP_HANDLEENTRY.dwHandle +* -- dwPID +* -- pHandleMap = buffer of minimum byte length *pcbHandleMap or NULL. +* -- pcbHandleMap = pointer to byte count of pHandleMap buffer. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_ProcessMap_GetHandle(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbHandleMap) PVMMDLL_MAP_HANDLE pHandleMap, _Inout_ PDWORD pcbHandleMap); + + + +//----------------------------------------------------------------------------- +// VMM PROCESS FUNCTIONALITY BELOW: +// Functionality below is mostly relating to Windows processes. +//----------------------------------------------------------------------------- + +/* +* Retrieve an active process given it's name. Please note that if multiple +* processes with the same name exists only one will be returned. If required to +* parse all processes with the same name please iterate over the PID list by +* calling VMMDLL_PidList together with VMMDLL_ProcessGetInformation. +* -- szProcName = process name case insensitive. +* -- pdwPID = pointer that will receive PID on success. +* -- return +*/ +_Success_(return) +BOOL VMMDLL_PidGetFromName(_In_ LPSTR szProcName, _Out_ PDWORD pdwPID); + +/* +* List the PIDs in the system. +* -- pPIDs = DWORD array of at least number of PIDs in system, or NULL. +* -- pcPIDs = size of (in number of DWORDs) pPIDs array on entry, number of PIDs in system on exit. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_PidList(_Out_writes_opt_(*pcPIDs) PDWORD pPIDs, _Inout_ PULONG64 pcPIDs); #define VMMDLL_PROCESS_INFORMATION_MAGIC 0xc0ffee663df9301e -#define VMMDLL_PROCESS_INFORMATION_VERSION 4 +#define VMMDLL_PROCESS_INFORMATION_VERSION 5 typedef struct tdVMMDLL_PROCESS_INFORMATION { ULONG64 magic; @@ -561,7 +752,7 @@ typedef struct tdVMMDLL_PROCESS_INFORMATION { struct { ULONG64 vaEPROCESS; ULONG64 vaPEB; - ULONG64 vaENTRY; + ULONG64 _Reserved1; BOOL fWow64; DWORD vaPEB32; // WoW64 only } win; @@ -613,37 +804,37 @@ typedef struct tdVMMDLL_IAT_ENTRY { * If the pData == NULL upon entry the number of entries of the pData array must * have in order to be able to hold the data is returned. * -- dwPID -* -- szModule +* -- wszModule * -- pData * -- cData * -- pcData * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetDirectories(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_writes_(16) PIMAGE_DATA_DIRECTORY pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetDirectories(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_writes_(16) PIMAGE_DATA_DIRECTORY pData, _In_ DWORD cData, _Out_ PDWORD pcData); _Success_(return) -BOOL VMMDLL_ProcessGetSections(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PIMAGE_SECTION_HEADER pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetSections(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PIMAGE_SECTION_HEADER pData, _In_ DWORD cData, _Out_ PDWORD pcData); _Success_(return) -BOOL VMMDLL_ProcessGetEAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PVMMDLL_EAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetEAT(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PVMMDLL_EAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); _Success_(return) -BOOL VMMDLL_ProcessGetIAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PVMMDLL_IAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetIAT(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PVMMDLL_IAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); /* * Retrieve the virtual address of a given function inside a process/module. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- szFunctionName * -- return = virtual address of function, zero on fail. */ -ULONG64 VMMDLL_ProcessGetProcAddress(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szFunctionName); +ULONG64 VMMDLL_ProcessGetProcAddress(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szFunctionName); /* * Retrieve the base address of a given module. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- return = virtual address of module base, zero on fail. */ -ULONG64 VMMDLL_ProcessGetModuleBase(_In_ DWORD dwPID, _In_ LPSTR szModuleName); +ULONG64 VMMDLL_ProcessGetModuleBase(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName); @@ -729,7 +920,7 @@ BOOL VMMDLL_WinReg_HiveList(_Out_writes_(cHives) PVMMDLL_REGISTRY_HIVE_INFORMATI * -- ra * -- pb * -- cb -* -- pcbRead +* -- pcbReadOpt * -- flags = flags as in VMMDLL_FLAG_* * -- return = success/fail. NB! reads may report as success even if 0 bytes are * read - it's recommended to verify pcbReadOpt parameter. @@ -901,26 +1092,26 @@ typedef struct tdVMMDLL_WIN_THUNKINFO_EAT { * function. This includes the virtual address of the IAT thunk which is useful * for hooking. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- szImportModuleName * -- szImportFunctionName * -- pThunkIAT * -- return */ _Success_(return) -BOOL VMMDLL_WinGetThunkInfoIAT(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szImportModuleName, _In_ LPSTR szImportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_IAT pThunkInfoIAT); +BOOL VMMDLL_WinGetThunkInfoIAT(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szImportModuleName, _In_ LPSTR szImportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_IAT pThunkInfoIAT); /* * Retrieve information about the export address table EAT thunk for an exported * function. This includes the virtual address of the EAT thunk which is useful * for hooking. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- pThunkEAT * -- return */ _Success_(return) -BOOL VMMDLL_WinGetThunkInfoEAT(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT); +BOOL VMMDLL_WinGetThunkInfoEAT(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT); diff --git a/vmm/vmmproc.c b/vmm/vmmproc.c index 5c5abc91..08a92601 100644 --- a/vmm/vmmproc.c +++ b/vmm/vmmproc.c @@ -156,51 +156,6 @@ DWORD VmmProcCacheUpdaterThread() return 0; } -VOID VmmProc_ModuleMapInitialize(_In_ PVMM_PROCESS pProcess) -{ - if((ctxVmm->tpSystem == VMM_SYSTEM_WINDOWS_X64) || (ctxVmm->tpSystem == VMM_SYSTEM_WINDOWS_X86)) { - VmmTlbSpider(pProcess); - VmmWin_ModuleMapInitialize(pProcess); - } -} - -_Success_(return) -BOOL VmmProc_ModuleMapGet(_In_ PVMM_PROCESS pProcess, _Out_ PVMMOB_MODULEMAP *ppObModuleMap) -{ - if(!pProcess->pObModuleMap) { - VmmProc_ModuleMapInitialize(pProcess); - } - if(pProcess->pObModuleMap && pProcess->pObModuleMap->fValid) { - *ppObModuleMap = Ob_INCREF(pProcess->pObModuleMap); - return TRUE; - } - return FALSE; -} - -_Success_(return) -BOOL VmmProc_ModuleMapGetSingleEntry(_In_ PVMM_PROCESS pProcess, _In_ LPWSTR wszModuleName, _Out_ PVMMOB_MODULEMAP *ppObModuleMap, _Out_ PVMM_MODULEMAP_ENTRY *ppModuleMapEntry) -{ - DWORD iModule; - PVMMOB_MODULEMAP pObModuleMap = NULL; - if(!VmmProc_ModuleMapGet(pProcess, &pObModuleMap)) { return FALSE; } - for(iModule = 0; iModule < pObModuleMap->cMap; iModule++) { - if(0 == Util_wcsstrncmp(pObModuleMap->pMap[iModule].szName, wszModuleName, 0)) { - *ppObModuleMap = pObModuleMap; - *ppModuleMapEntry = pObModuleMap->pMap + iModule; - return TRUE; - } - } - Ob_DECREF(pObModuleMap); - return FALSE; -} - -VOID VmmProc_ScanTagsMemMap(_In_ PVMM_PROCESS pProcess) -{ - if((ctxVmm->tpSystem == VMM_SYSTEM_WINDOWS_X64) || (ctxVmm->tpSystem == VMM_SYSTEM_WINDOWS_X86)) { - VmmWin_ScanTagsMemMap(pProcess); - } -} - BOOL VmmProcInitialize() { BOOL result = FALSE; @@ -216,7 +171,7 @@ BOOL VmmProcInitialize() } } // set up cache maintenance in the form of a separate worker thread in case - // the backend is a writeable device (FPGA). If the underlying device isn't + // the backend is a volatile device (FPGA). If the underlying device isn't // volatile then there is no need to update! NB! Files are not considered // to be volatile. if(result && ctxMain->dev.fVolatile && !ctxMain->cfg.fDisableBackgroundRefresh) { diff --git a/vmm/vmmproc.h b/vmm/vmmproc.h index 2f3285d3..184e6456 100644 --- a/vmm/vmmproc.h +++ b/vmm/vmmproc.h @@ -14,40 +14,6 @@ */ BOOL VmmProc_RefreshProcesses(_In_ BOOL fRefreshTotal); -/* -* Load operating system dependant module names, such as parsed from PE or ELF -* into the modules map. -*/ -VOID VmmProc_ModuleMapInitialize(_In_ PVMM_PROCESS pProcess); - -/* -* Retrieve the module map. Map is generated on-demand if not already existing. -* CALLER DECREF: ppObModuleMap -* -- pProcess -* -- ppObModuleMap -* -- return -*/ -_Success_(return) -BOOL VmmProc_ModuleMapGet(_In_ PVMM_PROCESS pProcess, _Out_ PVMMOB_MODULEMAP *ppObModuleMap); - -/* -* Retrieve a single module map entry and its backing module map (if found). -* CALLER DECREF: ppObModuleMap -* -- pProcess -* -- wszModuleName -* -- ppObModuleMap -* -- pModuleMapEntry -* -- return -*/ -_Success_(return) -BOOL VmmProc_ModuleMapGetSingleEntry(_In_ PVMM_PROCESS pProcess, _In_ LPWSTR wszModuleName, _Out_ PVMMOB_MODULEMAP *ppObModuleMap, _Out_ PVMM_MODULEMAP_ENTRY *ppModuleMapEntry); - -/* -* Scan additional process information (not already in the initialized modulemap) -* and put the result into the memory map. -*/ -VOID VmmProc_ScanTagsMemMap(_In_ PVMM_PROCESS pProcess); - /* * Tries to automatically identify the operating system given by the supplied * memory device (fpga hardware or file). If an operating system is successfully diff --git a/vmm/vmmvfs.c b/vmm/vmmvfs.c index 8ba28d9c..d6e4a03d 100644 --- a/vmm/vmmvfs.c +++ b/vmm/vmmvfs.c @@ -7,8 +7,6 @@ #include "vmmdll.h" #include "m_vmmvfs_dump.h" #include "pluginmanager.h" -#include "vmmproc.h" -#include "vmmwin.h" #include "util.h" typedef struct tdVMMVFS_PATH { @@ -79,28 +77,14 @@ VOID VmmVfs_UtilTimeStampFile(_In_opt_ PVMM_PROCESS pProcess, _Out_ PVMMDLL_VFS_ NTSTATUS VmmVfsReadFileProcess(_In_ PVMM_PROCESS pProcess, _In_ PVMMVFS_PATH pPath, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset) { - NTSTATUS nt; - BYTE pbBuffer[0x800]; + QWORD cbMemSize; DWORD cbBuffer; - PVMMOB_DATA pObMemMapDisplay = NULL; - PVMMOB_MODULEMAP pObModuleMap = NULL; + BYTE pbBuffer[0x800]; ZeroMemory(pbBuffer, 48); // read memory from "vmem" file if(!_wcsicmp(pPath->wszPath1, L"vmem")) { - if((ctxVmm->tpMemoryModel != VMM_MEMORYMODEL_X64) && (cbOffset + cb >= 0x100000000)) { - if(cbOffset >= 0x100000000) { return VMM_STATUS_END_OF_FILE; } - cb = (DWORD)(0x100000000 - cbOffset); - } - VmmReadEx(pProcess, cbOffset, pb, cb, NULL, 0); - *pcbRead = cb; - return VMM_STATUS_SUCCESS; - } - // read the memory map - if(!_wcsicmp(pPath->wszPath1, L"map")) { - if(!VmmMemMapGetDisplay(pProcess, VMM_MEMMAP_FLAG_ALL, &pObMemMapDisplay)) { return VMMDLL_STATUS_FILE_INVALID; } - nt = Util_VfsReadFile_FromPBYTE(pObMemMapDisplay->pbData, pObMemMapDisplay->ObHdr.cbData, pb, cb, pcbRead, cbOffset); - Ob_DECREF(pObMemMapDisplay); - return nt; + cbMemSize = (ctxVmm->tpMemoryModel == VMM_MEMORYMODEL_X64) ? 1ULL << 48 : 1ULL << 32; + return VmmReadAsFile(pProcess, 0, cbMemSize, pb, cb, pcbRead, cbOffset); } // read genereal numeric values from files, pml4, pid, name, virt if(!_wcsicmp(pPath->wszPath1, L"dtb")) { @@ -136,28 +120,19 @@ NTSTATUS VmmVfsReadFileProcess(_In_ PVMM_PROCESS pProcess, _In_ PVMMVFS_PATH pPa // windows specific reads below: if((ctxVmm->tpSystem == VMM_SYSTEM_WINDOWS_X64) || (ctxVmm->tpSystem == VMM_SYSTEM_WINDOWS_X86)) { if(!_wcsicmp(pPath->wszPath1, L"name-long")) { - return Util_VfsReadFile_FromPBYTE(pProcess->pObProcessPersistent->szNameLong, pProcess->pObProcessPersistent->cchNameLong, pb, cb, pcbRead, cbOffset); + return Util_VfsReadFile_FromPBYTE(pProcess->pObPersistent->szNameLong, pProcess->pObPersistent->cchNameLong, pb, cb, pcbRead, cbOffset); } if(!_wcsicmp(pPath->wszPath1, L"win-cmdline")) { - return Util_VfsReadFile_FromPBYTE(pProcess->pObProcessPersistent->UserProcessParams.szCommandLine, pProcess->pObProcessPersistent->UserProcessParams.cchCommandLine, pb, cb, pcbRead, cbOffset); + return Util_VfsReadFile_FromPBYTE(pProcess->pObPersistent->UserProcessParams.szCommandLine, pProcess->pObPersistent->UserProcessParams.cchCommandLine, pb, cb, pcbRead, cbOffset); } if(!_wcsicmp(pPath->wszPath1, L"win-path")) { - return Util_VfsReadFile_FromPBYTE(pProcess->pObProcessPersistent->szPathKernel, pProcess->pObProcessPersistent->cchPathKernel, pb, cb, pcbRead, cbOffset); + return Util_VfsReadFile_FromPBYTE(pProcess->pObPersistent->szPathKernel, pProcess->pObPersistent->cchPathKernel, pb, cb, pcbRead, cbOffset); } - if(!_wcsicmp(pPath->wszPath1, L"win-modules") && VmmProc_ModuleMapGet(pProcess, &pObModuleMap)) { - nt = Util_VfsReadFile_FromPBYTE(pObModuleMap->pbDisplay, pObModuleMap->cbDisplay, pb, cb, pcbRead, cbOffset); - Ob_DECREF_NULL(&pObModuleMap); - return nt; - } - } if(ctxVmm->tpSystem == VMM_SYSTEM_WINDOWS_X64) { if(!_wcsicmp(pPath->wszPath1, L"win-eprocess")) { return Util_VfsReadFile_FromQWORD(pProcess->win.EPROCESS.va, pb, cb, pcbRead, cbOffset, FALSE); } - if(!_wcsicmp(pPath->wszPath1, L"win-entry")) { - return Util_VfsReadFile_FromQWORD(pProcess->win.vaENTRY, pb, cb, pcbRead, cbOffset, FALSE); - } if(!_wcsicmp(pPath->wszPath1, L"win-peb")) { return Util_VfsReadFile_FromQWORD(pProcess->win.vaPEB, pb, cb, pcbRead, cbOffset, FALSE); } @@ -169,9 +144,6 @@ NTSTATUS VmmVfsReadFileProcess(_In_ PVMM_PROCESS pProcess, _In_ PVMMVFS_PATH pPa if(!_wcsicmp(pPath->wszPath1, L"win-eprocess")) { return Util_VfsReadFile_FromDWORD((DWORD)pProcess->win.EPROCESS.va, pb, cb, pcbRead, cbOffset, FALSE); } - if(!_wcsicmp(pPath->wszPath1, L"win-entry")) { - return Util_VfsReadFile_FromDWORD((DWORD)pProcess->win.vaENTRY, pb, cb, pcbRead, cbOffset, FALSE); - } if(!_wcsicmp(pPath->wszPath1, L"win-peb")) { return Util_VfsReadFile_FromDWORD((DWORD)pProcess->win.vaPEB, pb, cb, pcbRead, cbOffset, FALSE); } @@ -212,11 +184,11 @@ NTSTATUS VmmVfs_Read(LPCWSTR wcsFileName, _Out_writes_(cb) PBYTE pb, _In_ DWORD NTSTATUS VmmVfsWriteFileProcess(_In_ PVMM_PROCESS pProcess, _In_ PVMMVFS_PATH pPath, _In_ LPVOID pb, _In_ DWORD cb, _Out_ PDWORD pcbWrite, _In_ QWORD cbOffset) { - BOOL fFound, result; + BOOL fFound; + QWORD cbMemSize; // read only files - report zero bytes written fFound = !_wcsicmp(pPath->wszPath1, L"dtb") || - !_wcsicmp(pPath->wszPath1, L"map") || !_wcsicmp(pPath->wszPath1, L"name") || !_wcsicmp(pPath->wszPath1, L"pid") || !_wcsicmp(pPath->wszPath1, L"ppid") || @@ -230,10 +202,8 @@ NTSTATUS VmmVfsWriteFileProcess(_In_ PVMM_PROCESS pProcess, _In_ PVMMVFS_PATH pP fFound = !_wcsicmp(pPath->wszPath1, L"name-long") || !_wcsicmp(pPath->wszPath1, L"win-cmdline") || - !_wcsicmp(pPath->wszPath1, L"win-entry") || !_wcsicmp(pPath->wszPath1, L"win-eprocess") || !_wcsicmp(pPath->wszPath1, L"win-kpath") || - !_wcsicmp(pPath->wszPath1, L"win-modules") || !_wcsicmp(pPath->wszPath1, L"win-peb"); if(fFound) { *pcbWrite = 0; @@ -242,9 +212,8 @@ NTSTATUS VmmVfsWriteFileProcess(_In_ PVMM_PROCESS pProcess, _In_ PVMMVFS_PATH pP } // write memory to "vmem" file if(!_wcsicmp(pPath->wszPath1, L"vmem")) { - result = VmmWrite(pProcess, cbOffset, pb, cb); - *pcbWrite = cb; - return VMM_STATUS_SUCCESS; + cbMemSize = (ctxVmm->tpMemoryModel == VMM_MEMORYMODEL_X64) ? 1ULL << 48 : 1ULL << 32; + return VmmWriteAsFile(pProcess, 0, cbMemSize, pb, cb, pcbWrite, cbOffset); } // no hit - call down the loadable modules chain for potential hits return PluginManager_Write(pProcess, pPath->wszPath1, pPath->wszPath2, pb, cb, pcbWrite, cbOffset); @@ -281,28 +250,17 @@ NTSTATUS VmmVfs_Write(_In_ LPCWSTR wcsFileName, _In_reads_(cb) PBYTE pb, _In_ DW VOID VmmVfsListFiles_OsSpecific(_In_ PVMM_PROCESS pProcess, _In_ PVMMDLL_VFS_FILELIST_EXINFO pExInfo, _Inout_ PHANDLE pFileList) { - PVMMOB_MODULEMAP pObModuleMap; // WINDOWS - 32 & 64-bit if((ctxVmm->tpSystem == VMM_SYSTEM_WINDOWS_X64) || (ctxVmm->tpSystem == VMM_SYSTEM_WINDOWS_X86)) { - VMMDLL_VfsList_AddFileEx(pFileList, "name-long", NULL, pProcess->pObProcessPersistent->cchNameLong, pExInfo); - VMMDLL_VfsList_AddFileEx(pFileList, "win-path", NULL, pProcess->pObProcessPersistent->cchPathKernel, pExInfo); - if(pProcess->pObProcessPersistent->UserProcessParams.cchCommandLine) { - VMMDLL_VfsList_AddFileEx(pFileList, "win-cmdline", NULL, pProcess->pObProcessPersistent->UserProcessParams.cchCommandLine, pExInfo); - } - // modules - if(VmmProc_ModuleMapGet(pProcess, &pObModuleMap)) { - if(pObModuleMap->cbDisplay) { - VMMDLL_VfsList_AddFileEx(pFileList, "win-modules", NULL, pObModuleMap->cbDisplay, pExInfo); - } - Ob_DECREF(pObModuleMap); + VMMDLL_VfsList_AddFileEx(pFileList, "name-long", NULL, pProcess->pObPersistent->cchNameLong, pExInfo); + VMMDLL_VfsList_AddFileEx(pFileList, "win-path", NULL, pProcess->pObPersistent->cchPathKernel, pExInfo); + if(pProcess->pObPersistent->UserProcessParams.cchCommandLine) { + VMMDLL_VfsList_AddFileEx(pFileList, "win-cmdline", NULL, pProcess->pObPersistent->UserProcessParams.cchCommandLine, pExInfo); } } // WINDOWS - 64-bit specific if(ctxVmm->tpSystem == VMM_SYSTEM_WINDOWS_X64) { VMMDLL_VfsList_AddFileEx(pFileList, "win-eprocess", NULL, 16, pExInfo); - if(pProcess->win.vaENTRY) { - VMMDLL_VfsList_AddFileEx(pFileList, "win-entry", NULL, 16, pExInfo); - } // 64-bit PEB and modules if(pProcess->win.vaPEB) { VMMDLL_VfsList_AddFileEx(pFileList, "win-peb", NULL, 16, pExInfo); @@ -315,9 +273,6 @@ VOID VmmVfsListFiles_OsSpecific(_In_ PVMM_PROCESS pProcess, _In_ PVMMDLL_VFS_FIL // WINDOWS 32-bit specific if(ctxVmm->tpSystem == VMM_SYSTEM_WINDOWS_X86) { VMMDLL_VfsList_AddFileEx(pFileList, "win-eprocess", NULL, 8, pExInfo); - if(pProcess->win.vaENTRY) { - VMMDLL_VfsList_AddFileEx(pFileList, "win-entry", NULL, 8, pExInfo); - } // PEB if(pProcess->win.vaPEB) { VMMDLL_VfsList_AddFileEx(pFileList, "win-peb", NULL, 8, pExInfo); @@ -350,14 +305,10 @@ BOOL VmmVfsListFilesProcessRoot(_In_ PVMMVFS_PATH pPath, _Inout_ PHANDLE pFileLi _Success_(return) BOOL VmmVfsListFilesProcess(_In_ PVMM_PROCESS pProcess, _In_ PVMMVFS_PATH pPath, _Inout_ PHANDLE pFileList) { - PVMMOB_MEMMAP pObMemMap = NULL; VMMDLL_VFS_FILELIST_EXINFO ExInfo = { 0 }; VmmVfs_UtilTimeStampFile(pProcess, &ExInfo); // populate process directory - list standard files and subdirectories if(!pPath->wszPath1) { - VmmMemMapGetEntries(pProcess, 0, &pObMemMap); - VMMDLL_VfsList_AddFileEx(pFileList, "map", NULL, (pObMemMap ? pObMemMap->cbDisplay : 0), &ExInfo); - Ob_DECREF(pObMemMap); VMMDLL_VfsList_AddFileEx(pFileList, "name", NULL, 16, &ExInfo); VMMDLL_VfsList_AddFileEx(pFileList, "pid", NULL, 10, &ExInfo); VMMDLL_VfsList_AddFileEx(pFileList, "ppid", NULL, 10, &ExInfo); diff --git a/vmm/vmmvfs.h b/vmm/vmmvfs.h index 10de6e98..3cc73aa8 100644 --- a/vmm/vmmvfs.h +++ b/vmm/vmmvfs.h @@ -7,6 +7,13 @@ #define __VMMVFS_H__ #include "vmm.h" +/* +* Set file timestamp into the ExInfo struct if possible. +* -- pProcess +* -- pExInfo +*/ +VOID VmmVfs_UtilTimeStampFile(_In_opt_ PVMM_PROCESS pProcess, _Out_ PVMMDLL_VFS_FILELIST_EXINFO pExInfo); + /* * List files in the virtual file system directory specified by the path name. * -- wcsPath diff --git a/vmm/vmmwin.c b/vmm/vmmwin.c index 7ed2a8db..a2c562c3 100644 --- a/vmm/vmmwin.c +++ b/vmm/vmmwin.c @@ -8,6 +8,7 @@ #include "vmmwin.h" #include "vmmproc.h" #include "util.h" +#include "pdb.h" #include "pe.h" #include @@ -41,7 +42,7 @@ PIMAGE_NT_HEADERS VmmWin_GetVerifyHeaderPE(_In_ PVMM_PROCESS pProcess, _In_opt_ VOID VmmWin_PE_SECTION_DisplayBuffer( _In_ PVMM_PROCESS pProcess, - _In_ PVMM_MODULEMAP_ENTRY pModule, + _In_ PVMM_MAP_MODULEENTRY pModule, _Out_writes_bytes_opt_(*pcbDisplayBuffer) PBYTE pbDisplayBufferOpt, _In_ DWORD cbDisplayBufferMax, _Out_opt_ PDWORD pcbDisplayBuffer, @@ -58,7 +59,7 @@ VOID VmmWin_PE_SECTION_DisplayBuffer( cSectionsOpt = *pcSectionsOpt; *pcSectionsOpt = 0; } - if(!(ntHeader64 = VmmWin_GetVerifyHeaderPE(pProcess, pModule->BaseAddress, pbModuleHeader, &fHdr32))) { return; } + if(!(ntHeader64 = VmmWin_GetVerifyHeaderPE(pProcess, pModule->vaBase, pbModuleHeader, &fHdr32))) { return; } pSectionBase = fHdr32 ? (PIMAGE_SECTION_HEADER)((QWORD)ntHeader64 + sizeof(IMAGE_NT_HEADERS32)) : (PIMAGE_SECTION_HEADER)((QWORD)ntHeader64 + sizeof(IMAGE_NT_HEADERS64)); @@ -73,7 +74,7 @@ VOID VmmWin_PE_SECTION_DisplayBuffer( "%02x %-8.8s %016llx %08x %08x %c%c%c %08x %08x\n", i, pSectionBase[i].Name, - pModule->BaseAddress + pSectionBase[i].VirtualAddress, + pModule->vaBase + pSectionBase[i].VirtualAddress, pSectionBase[i].VirtualAddress, pSectionBase[i].Misc.VirtualSize, (pSectionBase[i].Characteristics & IMAGE_SCN_MEM_READ) ? 'r' : '-', @@ -92,7 +93,7 @@ VOID VmmWin_PE_SECTION_DisplayBuffer( VOID VmmWin_PE_DIRECTORY_DisplayBuffer( _In_ PVMM_PROCESS pProcess, - _In_ PVMM_MODULEMAP_ENTRY pModule, + _In_ PVMM_MAP_MODULEENTRY pModule, _Out_writes_bytes_opt_(*pcbDisplayBuffer) PBYTE pbDisplayBufferOpt, _In_ DWORD cbDisplayBufferMax, _Out_opt_ PDWORD pcbDisplayBuffer, @@ -104,7 +105,7 @@ VOID VmmWin_PE_DIRECTORY_DisplayBuffer( PIMAGE_DATA_DIRECTORY pDataDirectoryBase; BOOL fHdr32; if(pcbDisplayBuffer) { *pcbDisplayBuffer = 0; } - if(!(ntHeader64 = VmmWin_GetVerifyHeaderPE(pProcess, pModule->BaseAddress, pbModuleHeader, &fHdr32))) { return; } + if(!(ntHeader64 = VmmWin_GetVerifyHeaderPE(pProcess, pModule->vaBase, pbModuleHeader, &fHdr32))) { return; } ntHeader32 = (PIMAGE_NT_HEADERS32)ntHeader64; pDataDirectoryBase = fHdr32 ? ntHeader32->OptionalHeader.DataDirectory : ntHeader64->OptionalHeader.DataDirectory; if(pbDisplayBufferOpt) { @@ -116,7 +117,7 @@ VOID VmmWin_PE_DIRECTORY_DisplayBuffer( "%x %-16.16s %016llx %08x %08x\n", i, PE_DATA_DIRECTORIES[i], - pModule->BaseAddress + pDataDirectoryBase[i].VirtualAddress, + pModule->vaBase + pDataDirectoryBase[i].VirtualAddress, pDataDirectoryBase[i].VirtualAddress, pDataDirectoryBase[i].Size ); @@ -129,7 +130,7 @@ VOID VmmWin_PE_DIRECTORY_DisplayBuffer( } _Success_(return) -BOOL VmmWin_PE_LoadEAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MODULEMAP_ENTRY pModule, _Out_writes_opt_(cEATs) PVMMPROC_WINDOWS_EAT_ENTRY pEATs, _In_ DWORD cEATs, _Out_ PDWORD pcEATs) +BOOL VmmWin_PE_LoadEAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MAP_MODULEENTRY pModule, _Out_writes_opt_(cEATs) PVMMPROC_WINDOWS_EAT_ENTRY pEATs, _In_ DWORD cEATs, _Out_ PDWORD pcEATs) { BYTE pbModuleHeader[0x1000] = { 0 }; PIMAGE_NT_HEADERS64 ntHeader64; @@ -142,7 +143,7 @@ BOOL VmmWin_PE_LoadEAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MODUL BOOL fHdr32; *pcEATs = 0; // load both 32/64 bit ntHeader (only one will be valid) - if(!(ntHeader64 = VmmWin_GetVerifyHeaderPE(pProcess, pModule->BaseAddress, pbModuleHeader, &fHdr32))) { goto fail; } + if(!(ntHeader64 = VmmWin_GetVerifyHeaderPE(pProcess, pModule->vaBase, pbModuleHeader, &fHdr32))) { goto fail; } ntHeader32 = (PIMAGE_NT_HEADERS32)ntHeader64; // Load Export Address Table (EAT) oExportDirectory = fHdr32 ? @@ -153,7 +154,7 @@ BOOL VmmWin_PE_LoadEAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MODUL ntHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size; if(!oExportDirectory || !cbExportDirectory || cbExportDirectory > 0x01000000) { goto fail; } if(!(pbExportDirectory = LocalAlloc(0, cbExportDirectory))) { goto fail; } - if(!VmmRead(pProcess, pModule->BaseAddress + oExportDirectory, pbExportDirectory, (DWORD)cbExportDirectory)) { goto fail; } + if(!VmmRead(pProcess, pModule->vaBase + oExportDirectory, pbExportDirectory, (DWORD)cbExportDirectory)) { goto fail; } // Walk exported functions pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)pbExportDirectory; for(i = 0; i < pExportDirectory->NumberOfNames && i < cEATs; i++) { @@ -172,7 +173,7 @@ BOOL VmmWin_PE_LoadEAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MODUL vaFunctionOffset = *(PDWORD)(pbExportDirectory - oExportDirectory + oFunction); // store into caller supplied info struct pEATs[i].vaFunctionOffset = vaFunctionOffset; - pEATs[i].vaFunction = pModule->BaseAddress + vaFunctionOffset; + pEATs[i].vaFunction = pModule->vaBase + vaFunctionOffset; strncpy_s(pEATs[i].szFunction, 40, (LPSTR)(pbExportDirectory - oExportDirectory + oName), _TRUNCATE); } *pcEATs = (DWORD)i; @@ -183,7 +184,7 @@ BOOL VmmWin_PE_LoadEAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MODUL return FALSE; } -VOID VmmWin_PE_LoadIAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MODULEMAP_ENTRY pModule, _Out_writes_(*pcIATs) PVMMWIN_IAT_ENTRY pIATs, _In_ DWORD cIATs, _Out_ PDWORD pcIATs) +VOID VmmWin_PE_LoadIAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MAP_MODULEENTRY pModule, _Out_writes_(*pcIATs) PVMMWIN_IAT_ENTRY pIATs, _In_ DWORD cIATs, _Out_ PDWORD pcIATs) { BYTE pbModuleHeader[0x1000] = { 0 }; PIMAGE_NT_HEADERS64 ntHeader64; @@ -198,13 +199,13 @@ VOID VmmWin_PE_LoadIAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MODUL DWORD c, j; *pcIATs = 0; // Load the module - if(pModule->SizeOfImage > 0x02000000) { return; } - cbModule = pModule->SizeOfImage; + if(pModule->cbImageSize > 0x02000000) { return; } + cbModule = pModule->cbImageSize; if(!(pbModule = LocalAlloc(LMEM_ZEROINIT, cbModule))) { return; } - VmmReadEx(pProcess, pModule->BaseAddress, pbModule, cbModule, &cbRead, 0); + VmmReadEx(pProcess, pModule->vaBase, pbModule, cbModule, &cbRead, 0); if(cbRead <= 0x2000) { goto cleanup; } // load both 32/64 bit ntHeader (only one will be valid) - if(!(ntHeader64 = VmmWin_GetVerifyHeaderPE(pProcess, pModule->BaseAddress, pbModuleHeader, &fHdr32))) { goto cleanup; } + if(!(ntHeader64 = VmmWin_GetVerifyHeaderPE(pProcess, pModule->vaBase, pbModuleHeader, &fHdr32))) { goto cleanup; } ntHeader32 = (PIMAGE_NT_HEADERS32)ntHeader64; oImportDirectory = fHdr32 ? ntHeader32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress : @@ -262,7 +263,7 @@ VOID VmmWin_PE_LoadIAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MODUL LocalFree(pbModule); } -VOID VmmWin_PE_SetSizeSectionIATEAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _Inout_ PVMM_MODULEMAP_ENTRY pModule) +VOID VmmWin_PE_SetSizeSectionIATEAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MAP_MODULEENTRY pModule) { BYTE pbModuleHeader[0x1000] = { 0 }; PIMAGE_NT_HEADERS64 pNtHeaders64; @@ -275,19 +276,19 @@ VOID VmmWin_PE_SetSizeSectionIATEAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _I return; } // load both 32/64 bit ntHeader (only one will be valid) - if(!(pNtHeaders64 = VmmWin_GetVerifyHeaderPE(pProcess, pModule->BaseAddress, pbModuleHeader, &fHdr32))) { + if(!(pNtHeaders64 = VmmWin_GetVerifyHeaderPE(pProcess, pModule->vaBase, pbModuleHeader, &fHdr32))) { LeaveCriticalSection(&pProcess->LockUpdate); return; } // calculate display buffer size of: SECTIONS, EAT, IAT, RawFileSize - pModule->cbFileSizeRaw = PE_FileRaw_Size(pProcess, pModule->BaseAddress, pbModuleHeader); - pModule->cbDisplayBufferSections = PE_SectionGetNumberOfEx(pProcess, pModule->BaseAddress, pbModuleHeader) * 70; // each display buffer human readable line == 70 bytes. + pModule->cbFileSizeRaw = PE_FileRaw_Size(pProcess, pModule->vaBase, pbModuleHeader); + pModule->cbDisplayBufferSections = PE_SectionGetNumberOfEx(pProcess, pModule->vaBase, pbModuleHeader) * 70; // each display buffer human readable line == 70 bytes. if(!pModule->fLoadedEAT) { - pModule->cbDisplayBufferEAT = PE_EatGetNumberOfEx(pProcess, pModule->BaseAddress, pbModuleHeader) * 64; // each display buffer human readable line == 64 bytes. + pModule->cbDisplayBufferEAT = PE_EatGetNumberOfEx(pProcess, pModule->vaBase, pbModuleHeader) * 64; // each display buffer human readable line == 64 bytes. pModule->fLoadedEAT = TRUE; } if(!pModule->fLoadedIAT) { - pModule->cbDisplayBufferIAT = PE_IatGetNumberOfEx(pProcess, pModule->BaseAddress, pbModuleHeader) * 128; // each display buffer human readable line == 128 bytes. + pModule->cbDisplayBufferIAT = PE_IatGetNumberOfEx(pProcess, pModule->vaBase, pbModuleHeader) * 128; // each display buffer human readable line == 128 bytes. pModule->fLoadedIAT = TRUE; } LeaveCriticalSection(&pProcess->LockUpdate); @@ -298,6 +299,8 @@ VOID VmmWin_PE_SetSizeSectionIATEAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _I // PEB/LDR USER MODE PARSING CODE (64-bit and 32-bit) // ---------------------------------------------------------------------------- +#define VMMPROCWINDOWS_MAX_MODULES 512 + // more extensive definition of the Windows LDR_DATA_TABLE_ENTRY struct. typedef struct _VMMPROC_LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderModuleList; @@ -315,9 +318,66 @@ typedef struct _VMMPROC_LDR_DATA_TABLE_ENTRY { ULONG TimeDateStamp; } VMMPROC_LDR_DATA_TABLE_ENTRY, *PVMMPROC_LDR_DATA_TABLE_ENTRY; -#define VMMWIN_SCANLDRMODULES_PREFETCH_MAX 0x100 +typedef struct _UNICODE_STRING32 { + USHORT Length; + USHORT MaximumLength; + DWORD Buffer; +} UNICODE_STRING32, *PUNICODE_STRING32; + +typedef struct _UNICODE_STRING64 { + USHORT Length; + USHORT MaximumLength; + DWORD Filler; + QWORD Buffer; +} UNICODE_STRING64, *PUNICODE_STRING64; + +typedef struct _LDR_MODULE32 { + LIST_ENTRY32 InLoadOrderModuleList; + LIST_ENTRY32 InMemoryOrderModuleList; + LIST_ENTRY32 InInitializationOrderModuleList; + DWORD BaseAddress; + DWORD EntryPoint; + ULONG SizeOfImage; + UNICODE_STRING32 FullDllName; + UNICODE_STRING32 BaseDllName; + ULONG Flags; + SHORT LoadCount; + SHORT TlsIndex; + LIST_ENTRY32 HashTableEntry; + ULONG TimeDateStamp; +} LDR_MODULE32, *PLDR_MODULE32; + +typedef struct _PEB_LDR_DATA32 { + BYTE Reserved1[8]; + DWORD Reserved2[3]; + LIST_ENTRY32 InMemoryOrderModuleList; +} PEB_LDR_DATA32, *PPEB_LDR_DATA32; -VOID VmmWin_ScanLdrModules_VSetPutVA(_In_ POB_VSET pObVSet_vaAll, _In_ POB_VSET pObVSet_vaTry1, _In_ QWORD va) +typedef struct _PEB32 { + BYTE Reserved1[2]; + BYTE BeingDebugged; + BYTE Reserved2[1]; + DWORD Reserved3[2]; + DWORD Ldr; + DWORD ProcessParameters; + DWORD SubSystemData; + DWORD ProcessHeap; + DWORD Unknown1[27]; + DWORD NumberOfHeaps; + DWORD MaximumNumberOfHeaps; + DWORD ProcessHeaps; + // ... +} PEB32, *PPEB32; + +typedef struct tdVMMWIN_LDRMODULES_CONTEXT { + DWORD cchNameTotal; + DWORD cModules; + DWORD cModulesMax; + PVMM_MAP_MODULEENTRY pModules; + POB_VSET psVaName; +} VMMWIN_LDRMODULES_CONTEXT, *PVMMWIN_LDRMODULES_CONTEXT; + +VOID VmmWin_InitializeLdrModules_VSetPutVA(_In_ POB_VSET pObVSet_vaAll, _In_ POB_VSET pObVSet_vaTry1, _In_ QWORD va) { if(!ObVSet_Exists(pObVSet_vaAll, va)) { ObVSet_Push(pObVSet_vaAll, va); @@ -325,27 +385,25 @@ VOID VmmWin_ScanLdrModules_VSetPutVA(_In_ POB_VSET pObVSet_vaAll, _In_ POB_VSET } } -VOID VmmWin_ScanLdrModules64(_In_ PVMM_PROCESS pProcess, _Inout_ PVMM_MODULEMAP_ENTRY pModules, _Inout_ PDWORD pcModules, _In_ DWORD cModulesMax, _Out_ PBOOL fWow64) +VOID VmmWin_InitializeLdrModules64(_In_ PVMM_PROCESS pProcess, _In_ PVMMWIN_LDRMODULES_CONTEXT ctx) { QWORD vaModuleLdrFirst, vaModuleLdr = 0; BYTE pbPEB[sizeof(PEB)], pbPEBLdrData[sizeof(PEB_LDR_DATA)], pbLdrModule[sizeof(VMMPROC_LDR_DATA_TABLE_ENTRY)]; PPEB pPEB = (PPEB)pbPEB; PPEB_LDR_DATA pPEBLdrData = (PPEB_LDR_DATA)pbPEBLdrData; PVMMPROC_LDR_DATA_TABLE_ENTRY pLdrModule = (PVMMPROC_LDR_DATA_TABLE_ENTRY)pbLdrModule; - PVMM_MODULEMAP_ENTRY pModule; - POB_VSET pObVSet_vaAll = NULL, pObVSet_vaTry1 = NULL, pObVSet_vaTry2 = NULL, pObVSet_vaName = NULL; - BOOL fNameRead, fNameDefaultChar, fTry1; - DWORD i, cbReadData; + PVMM_MAP_MODULEENTRY pModule; + POB_VSET pObVSet_vaAll = NULL, pObVSet_vaTry1 = NULL, pObVSet_vaTry2 = NULL; + BOOL fTry1; + DWORD cbReadData; // prefetch existing addresses (if any) & allocate new vaModuleLdr VSet - pObVSet_vaAll = ObContainer_GetOb(pProcess->pObProcessPersistent->pObCLdrModulesCachePrefetch64); - VmmCachePrefetchPages3(pProcess, pObVSet_vaAll, sizeof(VMMPROC_LDR_DATA_TABLE_ENTRY)); + pObVSet_vaAll = ObContainer_GetOb(pProcess->pObPersistent->pObCLdrModulesPrefetch64); + VmmCachePrefetchPages3(pProcess, pObVSet_vaAll, sizeof(VMMPROC_LDR_DATA_TABLE_ENTRY), 0); Ob_DECREF_NULL(&pObVSet_vaAll); if(!(pObVSet_vaAll = ObVSet_New())) { goto fail; } if(!(pObVSet_vaTry1 = ObVSet_New())) { goto fail; } if(!(pObVSet_vaTry2 = ObVSet_New())) { goto fail; } - if(!(pObVSet_vaName = ObVSet_New())) { goto fail; } // set up initial entry in vaModuleLdr DataSet - *fWow64 = FALSE; if(pProcess->fUserOnly) { // User mode process -> walk PEB LDR list to enumerate modules / .dlls. if(!pProcess->win.vaPEB) { goto fail; } @@ -363,12 +421,12 @@ VOID VmmWin_ScanLdrModules64(_In_ PVMM_PROCESS pProcess, _Inout_ PVMM_MODULEMAP_ // iterate over modules using all available linked lists in an efficient way. fTry1 = TRUE; vaModuleLdr = 0; - while(TRUE) { + while(ctx->cModules < ctx->cModulesMax) { if(fTry1) { vaModuleLdr = ObVSet_Pop(pObVSet_vaTry1); if(!vaModuleLdr && (0 == ObVSet_Size(pObVSet_vaTry2))) { break; } if(!vaModuleLdr) { - VmmCachePrefetchPages3(pProcess, pObVSet_vaAll, sizeof(PEB_LDR_DATA)); + VmmCachePrefetchPages3(pProcess, pObVSet_vaAll, sizeof(PEB_LDR_DATA), 0); fTry1 = FALSE; continue; } @@ -384,134 +442,70 @@ VOID VmmWin_ScanLdrModules64(_In_ PVMM_PROCESS pProcess, _Inout_ PVMM_MODULEMAP_ if(!VmmRead(pProcess, vaModuleLdr, pbLdrModule, sizeof(VMMPROC_LDR_DATA_TABLE_ENTRY))) { continue; } } if(!pLdrModule->BaseAddress || !pLdrModule->SizeOfImage) { continue; } - pModule = pModules + *pcModules; - pModule->BaseAddress = (QWORD)pLdrModule->BaseAddress; - pModule->EntryPoint = (QWORD)pLdrModule->EntryPoint; - pModule->SizeOfImage = (DWORD)pLdrModule->SizeOfImage; - pModule->fWoW64 = FALSE; + pModule = ctx->pModules + ctx->cModules; if(!pLdrModule->BaseDllName.Length) { continue; } - pModule->BaseDllName_Buffer = (QWORD)pLdrModule->BaseDllName.Buffer; - pModule->BaseDllName_Length = pLdrModule->BaseDllName.Length; - *pcModules = *pcModules + 1; + pModule->vaBase = (QWORD)pLdrModule->BaseAddress; + pModule->vaEntry = (QWORD)pLdrModule->EntryPoint; + pModule->cbImageSize = (DWORD)pLdrModule->SizeOfImage; + pModule->fWoW64 = FALSE; + pModule->cwszText = min(MAX_PATH - 1, pLdrModule->BaseDllName.Length); + ctx->cchNameTotal += max(12, 1 + pModule->cwszText); + pModule->_Reserved1 = ((QWORD)pLdrModule->BaseDllName.Buffer) + pLdrModule->BaseDllName.Length - pModule->cwszText; + ObVSet_Push(ctx->psVaName, pModule->_Reserved1); + ctx->cModules = ctx->cModules + 1; // add FLink/BLink lists if(pLdrModule->InLoadOrderModuleList.Flink && !((QWORD)pLdrModule->InLoadOrderModuleList.Flink & 0x7)) { - VmmWin_ScanLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD(pLdrModule->InLoadOrderModuleList.Flink, VMMPROC_LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList)); + VmmWin_InitializeLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD(pLdrModule->InLoadOrderModuleList.Flink, VMMPROC_LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList)); } if(pLdrModule->InLoadOrderModuleList.Blink && !((QWORD)pLdrModule->InLoadOrderModuleList.Blink & 0x7)) { - VmmWin_ScanLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD(pLdrModule->InLoadOrderModuleList.Blink, VMMPROC_LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList)); + VmmWin_InitializeLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD(pLdrModule->InLoadOrderModuleList.Blink, VMMPROC_LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList)); } if(pProcess->fUserOnly) { if(pLdrModule->InInitializationOrderModuleList.Flink && !((QWORD)pLdrModule->InInitializationOrderModuleList.Flink & 0x7)) { - VmmWin_ScanLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD(pLdrModule->InInitializationOrderModuleList.Flink, VMMPROC_LDR_DATA_TABLE_ENTRY, InInitializationOrderModuleList)); + VmmWin_InitializeLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD(pLdrModule->InInitializationOrderModuleList.Flink, VMMPROC_LDR_DATA_TABLE_ENTRY, InInitializationOrderModuleList)); } if(pLdrModule->InInitializationOrderModuleList.Blink && !((QWORD)pLdrModule->InInitializationOrderModuleList.Blink & 0x7)) { - VmmWin_ScanLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD(pLdrModule->InInitializationOrderModuleList.Blink, VMMPROC_LDR_DATA_TABLE_ENTRY, InInitializationOrderModuleList)); + VmmWin_InitializeLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD(pLdrModule->InInitializationOrderModuleList.Blink, VMMPROC_LDR_DATA_TABLE_ENTRY, InInitializationOrderModuleList)); } if(pLdrModule->InMemoryOrderModuleList.Flink && !((QWORD)pLdrModule->InMemoryOrderModuleList.Flink & 0x7)) { - VmmWin_ScanLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD(pLdrModule->InMemoryOrderModuleList.Flink, VMMPROC_LDR_DATA_TABLE_ENTRY, InMemoryOrderModuleList)); + VmmWin_InitializeLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD(pLdrModule->InMemoryOrderModuleList.Flink, VMMPROC_LDR_DATA_TABLE_ENTRY, InMemoryOrderModuleList)); } if(pLdrModule->InMemoryOrderModuleList.Blink && !((QWORD)pLdrModule->InMemoryOrderModuleList.Blink & 0x7)) { - VmmWin_ScanLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD(pLdrModule->InMemoryOrderModuleList.Blink, VMMPROC_LDR_DATA_TABLE_ENTRY, InMemoryOrderModuleList)); + VmmWin_InitializeLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD(pLdrModule->InMemoryOrderModuleList.Blink, VMMPROC_LDR_DATA_TABLE_ENTRY, InMemoryOrderModuleList)); } } - if(*pcModules >= cModulesMax) { break; } } - // fetch module names in an efficient way. - for(i = 0; i < *pcModules; i++) { - ObVSet_Push_PageAlign(pObVSet_vaName, pModules[i].BaseDllName_Buffer, 32); - } - VmmCachePrefetchPages(pProcess, pObVSet_vaName); - for(i = 0; i < *pcModules; i++) { - pModule = pModules + i; - fNameRead = VmmRead_U2A_RawStr(pProcess, 0, pModule->BaseDllName_Buffer, pModule->BaseDllName_Length, pModule->szName, _countof(pModule->szName), NULL, &fNameDefaultChar) && !fNameDefaultChar; - fNameRead = fNameRead || PE_GetModuleName(pProcess, pModule->BaseAddress, pModule->szName, 32); - if(fNameRead) { - *fWow64 = pProcess->fUserOnly && (*fWow64 || !memcmp(pModule->szName, "wow64.dll", 10)); - vmmprintfvv_fn("%016llx %016llx %08x %i %s\n", pModule->BaseAddress, pModule->EntryPoint, pModule->SizeOfImage, (pModule->fWoW64 ? 1 : 0), pModule->szName); - } else { - snprintf(pModule->szName, 31, "_UNKNOWN-%llx.dll", pModule->BaseAddress); - vmmprintfvv_fn("INFO: Unable to get name - paged out? PID=%04i BASE=0x%016llx REPLACE='%s'\n", pProcess->dwPID, pModule->BaseAddress, pModule->szName); - } - } - // save prefetch addresses (if required) - if(ctxMain->dev.fRemote && ctxVmm->ThreadProcCache.fEnabled) { - ObContainer_SetOb(pProcess->pObProcessPersistent->pObCLdrModulesCachePrefetch64, pObVSet_vaAll); + // save prefetch addresses (if desirable) + if(ctxMain->dev.fVolatile && ctxVmm->ThreadProcCache.fEnabled) { + ObContainer_SetOb(pProcess->pObPersistent->pObCLdrModulesPrefetch64, pObVSet_vaAll); } fail: Ob_DECREF(pObVSet_vaAll); Ob_DECREF(pObVSet_vaTry1); Ob_DECREF(pObVSet_vaTry2); - Ob_DECREF(pObVSet_vaName); } -typedef struct _UNICODE_STRING32 { - USHORT Length; - USHORT MaximumLength; - DWORD Buffer; -} UNICODE_STRING32; - -typedef struct _LDR_MODULE32 { - LIST_ENTRY32 InLoadOrderModuleList; - LIST_ENTRY32 InMemoryOrderModuleList; - LIST_ENTRY32 InInitializationOrderModuleList; - DWORD BaseAddress; - DWORD EntryPoint; - ULONG SizeOfImage; - UNICODE_STRING32 FullDllName; - UNICODE_STRING32 BaseDllName; - ULONG Flags; - SHORT LoadCount; - SHORT TlsIndex; - LIST_ENTRY32 HashTableEntry; - ULONG TimeDateStamp; -} LDR_MODULE32, *PLDR_MODULE32; - -typedef struct _PEB_LDR_DATA32 { - BYTE Reserved1[8]; - DWORD Reserved2[3]; - LIST_ENTRY32 InMemoryOrderModuleList; -} PEB_LDR_DATA32, *PPEB_LDR_DATA32; - -typedef struct _PEB32 { - BYTE Reserved1[2]; - BYTE BeingDebugged; - BYTE Reserved2[1]; - DWORD Reserved3[2]; - DWORD Ldr; - DWORD ProcessParameters; - DWORD SubSystemData; - DWORD ProcessHeap; - DWORD Unknown1[27]; - DWORD NumberOfHeaps; - DWORD MaximumNumberOfHeaps; - DWORD ProcessHeaps; - // ... -} PEB32, *PPEB32; - -_Success_(return) -BOOL VmmWin_ScanLdrModules32(_In_ PVMM_PROCESS pProcess, _Inout_ PVMM_MODULEMAP_ENTRY pModules, _Inout_ PDWORD pcModules, _In_ DWORD cModulesMax) +VOID VmmWin_InitializeLdrModules32(_In_ PVMM_PROCESS pProcess, _In_ PVMMWIN_LDRMODULES_CONTEXT ctx) { - BOOL fResult = FALSE; DWORD vaModuleLdrFirst32, vaModuleLdr32 = 0; BYTE pbPEB32[sizeof(PEB32)], pbPEBLdrData32[sizeof(PEB_LDR_DATA32)], pbLdrModule32[sizeof(LDR_MODULE32)]; PPEB32 pPEB32 = (PPEB32)pbPEB32; PPEB_LDR_DATA32 pPEBLdrData32 = (PPEB_LDR_DATA32)pbPEBLdrData32; PLDR_MODULE32 pLdrModule32 = (PLDR_MODULE32)pbLdrModule32; - PVMM_MODULEMAP_ENTRY pModule; - POB_VSET pObVSet_vaAll = NULL, pObVSet_vaTry1 = NULL, pObVSet_vaTry2 = NULL, pObVSet_vaName = NULL; - BOOL fNameRead, fNameDefaultChar, fTry1; - DWORD i, cbReadData; + PVMM_MAP_MODULEENTRY pModule; + POB_VSET pObVSet_vaAll = NULL, pObVSet_vaTry1 = NULL, pObVSet_vaTry2 = NULL; + BOOL fTry1; + DWORD cbReadData; // prefetch existing addresses (if any) & allocate new vaModuleLdr VSet - pObVSet_vaAll = ObContainer_GetOb(pProcess->pObProcessPersistent->pObCLdrModulesCachePrefetch32); - VmmCachePrefetchPages3(pProcess, pObVSet_vaAll, sizeof(LDR_MODULE32)); + pObVSet_vaAll = ObContainer_GetOb(pProcess->pObPersistent->pObCLdrModulesPrefetch32); + VmmCachePrefetchPages3(pProcess, pObVSet_vaAll, sizeof(LDR_MODULE32), 0); Ob_DECREF(pObVSet_vaAll); if(!(pObVSet_vaAll = ObVSet_New())) { goto fail; } if(!(pObVSet_vaTry1 = ObVSet_New())) { goto fail; } if(!(pObVSet_vaTry2 = ObVSet_New())) { goto fail; } - if(!(pObVSet_vaName = ObVSet_New())) { goto fail; } // set up initial entry in vaModuleLdr DataSet if(pProcess->fUserOnly) { - if(!pProcess->win.vaPEB) { goto fail; } + if(!pProcess->win.vaPEB32) { goto fail; } if(!VmmRead(pProcess, pProcess->win.vaPEB32, pbPEB32, sizeof(PEB32))) { goto fail; } if(!VmmRead(pProcess, (DWORD)pPEB32->Ldr, pbPEBLdrData32, sizeof(PEB_LDR_DATA32))) { goto fail; } vaModuleLdrFirst32 = (DWORD)pPEBLdrData32->InMemoryOrderModuleList.Flink - 0x08; // InLoadOrderModuleList == InMemoryOrderModuleList - 0x08 @@ -528,12 +522,12 @@ BOOL VmmWin_ScanLdrModules32(_In_ PVMM_PROCESS pProcess, _Inout_ PVMM_MODULEMAP_ // iterate over modules using all available linked lists in an efficient way. fTry1 = TRUE; vaModuleLdr32 = 0; - while(TRUE) { + while(ctx->cModules < ctx->cModulesMax) { if(fTry1) { vaModuleLdr32 = (DWORD)ObVSet_Pop(pObVSet_vaTry1); if(!vaModuleLdr32 && (0 == ObVSet_Size(pObVSet_vaTry2))) { break; } if(!vaModuleLdr32) { - VmmCachePrefetchPages3(pProcess, pObVSet_vaAll, sizeof(PEB_LDR_DATA)); + VmmCachePrefetchPages3(pProcess, pObVSet_vaAll, sizeof(PEB_LDR_DATA), 0); fTry1 = FALSE; continue; } @@ -548,528 +542,1420 @@ BOOL VmmWin_ScanLdrModules32(_In_ PVMM_PROCESS pProcess, _Inout_ PVMM_MODULEMAP_ if(!vaModuleLdr32) { fTry1 = TRUE; continue; } if(!VmmRead(pProcess, vaModuleLdr32, pbLdrModule32, sizeof(LDR_MODULE32))) { continue; } } - if(!pLdrModule32->BaseAddress || !pLdrModule32->SizeOfImage) { continue; } - if(*pcModules && (pLdrModule32->BaseAddress == pModules->BaseAddress)) { + if(pProcess->win.fWow64 && (pLdrModule32->BaseAddress == ctx->pModules[0].vaBase)) { // WOW64 only: 32-bit main exeutable (.exe) shows up in both 32-bit and // 64-bit views in WOW64-processes. // -> convert the 1st entry to a correct WoW64 (32-bit) entry. - pModules->fWoW64 = TRUE; + ctx->pModules[0].fWoW64 = TRUE; } else { - pModule = pModules + *pcModules; - pModule->BaseAddress = (QWORD)pLdrModule32->BaseAddress; - pModule->EntryPoint = (QWORD)pLdrModule32->EntryPoint; - pModule->SizeOfImage = (DWORD)pLdrModule32->SizeOfImage; - pModule->fWoW64 = TRUE; + pModule = ctx->pModules + ctx->cModules; if(!pLdrModule32->BaseDllName.Length) { continue; } - pModule->BaseDllName_Buffer = (QWORD)pLdrModule32->BaseDllName.Buffer; - pModule->BaseDllName_Length = pLdrModule32->BaseDllName.Length; - *pcModules = *pcModules + 1; + pModule->vaBase = (QWORD)pLdrModule32->BaseAddress; + pModule->vaEntry = (QWORD)pLdrModule32->EntryPoint; + pModule->cbImageSize = (DWORD)pLdrModule32->SizeOfImage; + pModule->fWoW64 = pProcess->win.fWow64; + pModule->cwszText = min(MAX_PATH - 1, pLdrModule32->BaseDllName.Length); + ctx->cchNameTotal += max(12, 1 + pModule->cwszText); + pModule->_Reserved1 = ((QWORD)pLdrModule32->BaseDllName.Buffer) + pLdrModule32->BaseDllName.Length - pModule->cwszText; + ObVSet_Push(ctx->psVaName, pModule->_Reserved1); + ctx->cModules = ctx->cModules + 1; } // add FLink/BLink lists if(pLdrModule32->InLoadOrderModuleList.Flink && !((DWORD)pLdrModule32->InLoadOrderModuleList.Flink & 0x3)) { - VmmWin_ScanLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD32(pLdrModule32->InLoadOrderModuleList.Flink, LDR_MODULE32, InLoadOrderModuleList)); + VmmWin_InitializeLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD32(pLdrModule32->InLoadOrderModuleList.Flink, LDR_MODULE32, InLoadOrderModuleList)); } if(pLdrModule32->InLoadOrderModuleList.Blink && !((DWORD)pLdrModule32->InLoadOrderModuleList.Blink & 0x3)) { - VmmWin_ScanLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD32(pLdrModule32->InLoadOrderModuleList.Blink, LDR_MODULE32, InLoadOrderModuleList)); + VmmWin_InitializeLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD32(pLdrModule32->InLoadOrderModuleList.Blink, LDR_MODULE32, InLoadOrderModuleList)); } if(pProcess->fUserOnly) { if(pLdrModule32->InInitializationOrderModuleList.Flink && !((DWORD)pLdrModule32->InInitializationOrderModuleList.Flink & 0x3)) { - VmmWin_ScanLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD32(pLdrModule32->InInitializationOrderModuleList.Flink, LDR_MODULE32, InInitializationOrderModuleList)); + VmmWin_InitializeLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD32(pLdrModule32->InInitializationOrderModuleList.Flink, LDR_MODULE32, InInitializationOrderModuleList)); } if(pLdrModule32->InInitializationOrderModuleList.Blink && !((DWORD)pLdrModule32->InInitializationOrderModuleList.Blink & 0x3)) { - VmmWin_ScanLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD32(pLdrModule32->InInitializationOrderModuleList.Blink, LDR_MODULE32, InInitializationOrderModuleList)); + VmmWin_InitializeLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD32(pLdrModule32->InInitializationOrderModuleList.Blink, LDR_MODULE32, InInitializationOrderModuleList)); } if(pLdrModule32->InMemoryOrderModuleList.Flink && !((DWORD)pLdrModule32->InMemoryOrderModuleList.Flink & 0x3)) { - VmmWin_ScanLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD32(pLdrModule32->InMemoryOrderModuleList.Flink, LDR_MODULE32, InMemoryOrderModuleList)); + VmmWin_InitializeLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD32(pLdrModule32->InMemoryOrderModuleList.Flink, LDR_MODULE32, InMemoryOrderModuleList)); } if(pLdrModule32->InMemoryOrderModuleList.Blink && !((DWORD)pLdrModule32->InMemoryOrderModuleList.Blink & 0x3)) { - VmmWin_ScanLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD32(pLdrModule32->InMemoryOrderModuleList.Blink, LDR_MODULE32, InMemoryOrderModuleList)); + VmmWin_InitializeLdrModules_VSetPutVA(pObVSet_vaAll, pObVSet_vaTry1, (QWORD)CONTAINING_RECORD32(pLdrModule32->InMemoryOrderModuleList.Blink, LDR_MODULE32, InMemoryOrderModuleList)); } } - if(*pcModules >= cModulesMax) { break; } } - // fetch module names in an efficient way. - for(i = 0; i < *pcModules; i++) { - ObVSet_Push_PageAlign(pObVSet_vaName, pModules[i].BaseDllName_Buffer, 32); + // save prefetch addresses (if desirable) + if(ctxMain->dev.fVolatile && ctxVmm->ThreadProcCache.fEnabled) { + ObContainer_SetOb(pProcess->pObPersistent->pObCLdrModulesPrefetch64, pObVSet_vaAll); } - VmmCachePrefetchPages(pProcess, pObVSet_vaName); - for(i = 0; i < *pcModules; i++) { - pModule = pModules + i; - fNameRead = VmmRead_U2A_RawStr(pProcess, 0, pModule->BaseDllName_Buffer, pModule->BaseDllName_Length, pModule->szName, _countof(pModule->szName), NULL, &fNameDefaultChar) && !fNameDefaultChar; - fNameRead = fNameRead || PE_GetModuleName(pProcess, pModule->BaseAddress, pModule->szName, 32); - if(fNameRead) { - vmmprintfvv_fn("%08x %08x %08x %s\n", (DWORD)pModule->BaseAddress, (DWORD)pModule->EntryPoint, pModule->SizeOfImage, pModule->szName); - } else { - snprintf(pModule->szName, 31, "_UNKNOWN-%llx.dll", pModule->BaseAddress); - vmmprintfvv_fn("INFO: Unable to get name - paged out? PID=%04i BASE=0x%08x REPLACE='%s'\n", pProcess->dwPID, (DWORD)pModule->BaseAddress, pModule->szName); - } - } - // save prefetch addresses (if required) - if(ctxMain->dev.fRemote && ctxVmm->ThreadProcCache.fEnabled) { - ObContainer_SetOb(pProcess->pObProcessPersistent->pObCLdrModulesCachePrefetch64, pObVSet_vaAll); - } - fResult = TRUE; fail: Ob_DECREF(pObVSet_vaAll); Ob_DECREF(pObVSet_vaTry1); Ob_DECREF(pObVSet_vaTry2); - Ob_DECREF(pObVSet_vaName); - return fResult; } -#define VMMPROCWINDOWS_MAX_MODULES 512 +VOID VmmWin_InitializeLdrModules_Name(_In_ PVMM_PROCESS pProcess, _In_ PVMMOB_MAP_MODULE pModuleMap) +{ + QWORD i; + DWORD cUnknown = 0, oText = 1; + PVMM_MAP_MODULEENTRY pe; + CHAR szBuffer[MAX_PATH] = { 0 }; + WCHAR wszBuffer[MAX_PATH] = { 0 }; + for(i = 0; i < pModuleMap->cMap; i++) { + pe = pModuleMap->pMap + i; + if(VmmRead2(pProcess, pe->_Reserved1, (PBYTE)wszBuffer, pe->cwszText << 1, VMM_FLAG_FORCECACHE_READ)) { + pe->wszText = pModuleMap->wszMultiText + oText; + pe->cwszText = Util_PathFileNameFixW(pModuleMap->wszMultiText + oText, wszBuffer, pe->cwszText); + oText += pe->cwszText + 1; + } + } + for(i = 0; i < pModuleMap->cMap; i++) { + pe = pModuleMap->pMap + i; + if(!pe->wszText) { + if(PE_GetModuleName(pProcess, pe->vaBase, szBuffer, MAX_PATH - 1)) { + pe->wszText = pModuleMap->wszMultiText + oText; + pe->cwszText = Util_PathFileNameFixA(pModuleMap->wszMultiText + oText, szBuffer, pe->cwszText); + oText += pe->cwszText + 1; + } else { + pe->wszText = pModuleMap->wszMultiText + oText; + pe->cwszText = swprintf_s(pModuleMap->wszMultiText + oText, 12, L"_NA-%x.dll", ++cUnknown); + oText += pe->cwszText + 1; + } + } + pModuleMap->pHashTableLookup[i] = (i << 32) | Util_HashStringUpperW(pe->wszText); + + } + if(ctxMain->cfg.fVerboseExtra) { + for(i = 0; i < pModuleMap->cMap; i++) { + pe = pModuleMap->pMap + i; + vmmprintfvv_fn("%016llx %016llx %08x %i %S\n", pe->vaBase, pe->vaEntry, pe->cbImageSize, (pe->fWoW64 ? 1 : 0), pe->wszText); + } + } +} + +int VmmWin_InitializeLdrModules_CmpSort(PDWORD pdw1, PDWORD pdw2) +{ + return + (*pdw1 < *pdw2) ? -1 : + (*pdw1 > *pdw2) ? 1 : 0; +} -VOID VmmWin_InitializeLdrModules(_In_ PVMM_PROCESS pProcess) +/* +* Initialize the module map containing information about loaded modules in the +* system. This is performed by a PEB/Ldr walk/scan of in-process memory +* structures. This may be unreliable if a process is obfuscated or tampered. +* -- pProcess +* -- return +*/ +_Success_(return) +BOOL VmmWin_InitializeLdrModules(_In_ PVMM_PROCESS pProcess) { - PVMM_MODULEMAP_ENTRY pModules, pModule; - PVMMOB_MODULEMAP pOb = NULL; - DWORD i, o, cModules; - BOOL result, fWow64 = FALSE; - if(pProcess->pObModuleMap) { return; } + DWORD cbObMap; + PVMMOB_MAP_MODULE pObMap = NULL; + VMMWIN_LDRMODULES_CONTEXT ctx = { 0 }; + if(pProcess->Map.pObModule) { return TRUE; } + VmmTlbSpider(pProcess); EnterCriticalSection(&pProcess->LockUpdate); - if(pProcess->pObModuleMap) { + if(pProcess->Map.pObModule) { LeaveCriticalSection(&pProcess->LockUpdate); - return; + return TRUE; } - // allocate and enumerate - pModules = (PVMM_MODULEMAP_ENTRY)LocalAlloc(LMEM_ZEROINIT, VMMPROCWINDOWS_MAX_MODULES * sizeof(VMM_MODULEMAP_ENTRY)); - if(!pModules) { goto fail; } - cModules = 0; + // set up ctx + ctx.cchNameTotal = 1; + ctx.cModulesMax = VMMPROCWINDOWS_MAX_MODULES; + if(!(ctx.psVaName = ObVSet_New())) { goto fail; } + if(!(ctx.pModules = (PVMM_MAP_MODULEENTRY)LocalAlloc(LMEM_ZEROINIT, VMMPROCWINDOWS_MAX_MODULES * sizeof(VMM_MAP_MODULEENTRY)))) { goto fail; } + // fetch modules if(ctxVmm->tpSystem == VMM_SYSTEM_WINDOWS_X64) { - VmmWin_ScanLdrModules64(pProcess, pModules, &cModules, VMMPROCWINDOWS_MAX_MODULES, &fWow64); - if((cModules > 0) && (!pModules[cModules - 1].BaseAddress)) { cModules--; } - if(fWow64) { - pProcess->win.vaPEB32 = (DWORD)pProcess->win.vaPEB - 0x1000; - result = VmmWin_ScanLdrModules32(pProcess, pModules, &cModules, VMMPROCWINDOWS_MAX_MODULES); - if(!result) { - pProcess->win.vaPEB32 = (DWORD)pProcess->win.vaPEB + 0x1000; - result = VmmWin_ScanLdrModules32(pProcess, pModules, &cModules, VMMPROCWINDOWS_MAX_MODULES); - } - if(!result) { - pProcess->win.vaPEB32 = 0; - } + VmmWin_InitializeLdrModules64(pProcess, &ctx); + } + if((ctxVmm->tpSystem == VMM_SYSTEM_WINDOWS_X86) || ((ctxVmm->tpSystem == VMM_SYSTEM_WINDOWS_X64) && pProcess->win.fWow64)) { + VmmWin_InitializeLdrModules32(pProcess, &ctx); + } + // set up module map object + cbObMap = sizeof(VMMOB_MAP_MODULE) + ctx.cModules * (sizeof(VMM_MAP_MODULEENTRY) + sizeof(QWORD)) + ctx.cchNameTotal * sizeof(WCHAR); + if(!(pObMap = Ob_Alloc(OB_TAG_MAP_MODULE, LMEM_ZEROINIT, cbObMap, NULL, NULL))) { goto fail; } + pObMap->pHashTableLookup = (PQWORD)(((PBYTE)pObMap) + sizeof(VMMOB_MAP_MODULE) + ctx.cModules * sizeof(VMM_MAP_MODULEENTRY)); + pObMap->wszMultiText = (LPWSTR)(((PBYTE)pObMap) + sizeof(VMMOB_MAP_MODULE) + ctx.cModules * (sizeof(VMM_MAP_MODULEENTRY) + sizeof(QWORD))); + pObMap->cbMultiText = ctx.cchNameTotal * sizeof(WCHAR); + pObMap->cMap = ctx.cModules; + memcpy(pObMap->pMap, ctx.pModules, ctx.cModules * sizeof(VMM_MAP_MODULEENTRY)); + // fetch module names + VmmCachePrefetchPages3(pProcess, ctx.psVaName, MAX_PATH << 1, 0); + VmmWin_InitializeLdrModules_Name(pProcess, pObMap); + // finish set-up + qsort(pObMap->pHashTableLookup, pObMap->cMap, sizeof(QWORD), (int(*)(const void*, const void*))VmmWin_InitializeLdrModules_CmpSort); + pProcess->Map.pObModule = pObMap; +fail: + if(!pProcess->Map.pObModule) { + // try set up zero-sized module map on fail + pObMap = Ob_Alloc(OB_TAG_MAP_MODULE, LMEM_ZEROINIT, sizeof(VMMOB_MAP_MODULE) + 2, NULL, NULL); + pObMap->wszMultiText = (LPWSTR)pObMap->pMap; + pObMap->pHashTableLookup = (PQWORD)pObMap->pMap; + pProcess->Map.pObModule = pObMap; + } + LeaveCriticalSection(&pProcess->LockUpdate); + Ob_DECREF(ctx.psVaName); + LocalFree(ctx.pModules); + return pProcess->Map.pObModule ? TRUE : FALSE; +} + +// ---------------------------------------------------------------------------- +// USER PROCESS PARAMETERS FUNCTIONALITY BELOW: +// ---------------------------------------------------------------------------- + +PVMMWIN_USER_PROCESS_PARAMETERS VmmWin_UserProcessParameters_Get(_In_ PVMM_PROCESS pProcess) +{ + BOOL f; + QWORD vaUserProcessParameters = 0; + PVMMWIN_USER_PROCESS_PARAMETERS pu = &pProcess->pObPersistent->UserProcessParams; + if(pu->fProcessed || pProcess->dwState) { return pu; } + EnterCriticalSection(&pProcess->LockUpdate); + if(ctxVmm->f32) { + f = pProcess->win.vaPEB && + VmmRead(pProcess, pProcess->win.vaPEB + 0x10, (PBYTE)&vaUserProcessParameters, sizeof(DWORD)) && + !(vaUserProcessParameters & 0x80000003); + } else { + f = pProcess->win.vaPEB && + VmmRead(pProcess, pProcess->win.vaPEB + 0x20, (PBYTE)&vaUserProcessParameters, sizeof(QWORD)) && + !(vaUserProcessParameters & 0xffff8000'00000007); + } + if(f) { + if(!VmmRead_U2A_Alloc(pProcess, ctxVmm->f32, 0, vaUserProcessParameters + (ctxVmm->f32 ? 0x038 : 0x060), &pu->szImagePathName, &pu->cchImagePathName, NULL)) { // ImagePathName + VmmRead_U2A_Alloc(pProcess, ctxVmm->f32, 0, vaUserProcessParameters + (ctxVmm->f32 ? 0x030 : 0x050), &pu->szImagePathName, &pu->cchImagePathName, NULL); // DllPath (mutually exclusive with ImagePathName?) } - if((cModules > 0) && (!pModules[cModules - 1].BaseAddress)) { cModules--; } - if(!cModules) { goto fail; } - pProcess->win.vaENTRY = pModules[0].EntryPoint; - // allocate / set up VmmOb - pOb = Ob_Alloc('MO', 0, sizeof(VMMOB_MODULEMAP) + cModules * (89ULL + sizeof(VMM_MODULEMAP_ENTRY)), NULL, NULL); - if(!pOb) { goto fail; } - pOb->pbDisplay = ((PBYTE)pOb->pMap) + cModules * sizeof(VMM_MODULEMAP_ENTRY); - // create 'text' module map - for(i = 0, o = 0; i < cModules; i++) { - pModule = pModules + i; - if(!pModule->BaseAddress) { continue; } - o += snprintf( - pOb->pbDisplay + o, - 89, - "%04x %8x %016llx-%016llx %s %s\n", - i, - pModule->SizeOfImage >> 12, - pModule->BaseAddress, - pModule->BaseAddress + pModule->SizeOfImage - 1, - pModule->fWoW64 ? "32" : " ", - pModule->szName - ); + VmmRead_U2A_Alloc(pProcess, ctxVmm->f32, 0, vaUserProcessParameters + (ctxVmm->f32 ? 0x040 : 0x070), &pu->szCommandLine, &pu->cchCommandLine, NULL); // CommandLine + } + pu->fProcessed = TRUE; + LeaveCriticalSection(&pProcess->LockUpdate); + return pu; +} + +// ---------------------------------------------------------------------------- +// PTE MAP FUNCTIONALITY BELOW: +// +// Memory Maps based on hardware page tables (PTE MAP) is generated by the +// virtual memory sub-system be waking the hardware page tables. The generated +// pte map does initially not contain information about loaded modules but may +// be enriched with this information by calling VmmWin_InitializePteMapText(). +// Module names will be inserted from: +// 1) the module map +// 2) if not found in (1) and suitable pte signature by PE header peek. +// ---------------------------------------------------------------------------- + +typedef struct tdVMMWIN_INITIALIZEPTEMAP_CONTEXT { + LPWSTR wsz; + QWORD cwsz; + QWORD cwszMax; +} VMMWIN_INITIALIZEPTEMAP_CONTEXT, *PVMMWIN_INITIALIZEPTEMAP_CONTEXT; + +/* +* Map a tag into the sorted memory map in O(log2) operations. Supply only one of szTag or wszTag. +* -- pProcess +* -- ctx +* -- vaBase +* -- vaLimit = limit == vaBase + size (== top address in range +1) +* -- szTag +* -- wszTag +* -- fWoW64 +*/ +VOID VmmWin_InitializePteMapText_MapTag(_In_ PVMM_PROCESS pProcess, _In_ PVMMWIN_INITIALIZEPTEMAP_CONTEXT ctx, _In_ QWORD vaBase, _In_ QWORD vaLimit, _In_opt_z_ LPSTR szTag, _In_opt_z_ LPWSTR wszTag, _In_ BOOL fWoW64) +{ + PVMM_MAP_PTEENTRY pMap; + QWORD i, lvl, cMap, cwszTag, cwszBufCount; + BOOL fTagWrite = FALSE; + pMap = pProcess->Map.pObPte->pMap; + cMap = pProcess->Map.pObPte->cMap; + if(!pMap || !cMap) { return; } + // 1: locate base + lvl = 1; + i = cMap >> lvl; + while(TRUE) { + lvl++; + if((cMap >> lvl) == 0) { + break; } - pProcess->win.fWow64 = fWow64; - pOb->cbDisplay = o; - } else if(ctxVmm->tpSystem == VMM_SYSTEM_WINDOWS_X86) { - pProcess->win.vaPEB32 = (DWORD)pProcess->win.vaPEB; - VmmWin_ScanLdrModules32(pProcess, pModules, &cModules, VMMPROCWINDOWS_MAX_MODULES); - if((cModules > 0) && (!pModules[cModules - 1].BaseAddress)) { cModules--; } - pProcess->win.vaENTRY = pModules[0].EntryPoint; - // allocate / set up VmmOb - pOb = Ob_Alloc('MO', 0, sizeof(VMMOB_MODULEMAP) + cModules * (89ULL + sizeof(VMM_MODULEMAP_ENTRY)), NULL, NULL); - if(!pOb) { goto fail; } - pOb->pbDisplay = ((PBYTE)pOb->pMap) + cModules * sizeof(VMM_MODULEMAP_ENTRY); - // create 'text' module map - for(i = 0, o = 0; i < cModules; i++) { - pModule = pModules + i; - if(!pModule->BaseAddress) { continue; } - o += snprintf( - pOb->pbDisplay + o, - 70, - "%04x %8x %08x-%08x %s\n", - i, - pModule->SizeOfImage >> 12, - (DWORD)pModule->BaseAddress, - (DWORD)(pModule->BaseAddress + pModule->SizeOfImage - 1), - pModule->szName - ); + if(pMap[i].vaBase > vaBase) { + i -= (cMap >> lvl); + } else { + i += (cMap >> lvl); } - pOb->cbDisplay = o; + } + // 2: scan back if needed + while(i && (pMap[i].vaBase > vaBase)) { + i--; + } + // 3.1: fill in tag + cwszBufCount = ctx->cwszMax - ctx->cwsz; + if(szTag) { + cwszTag = _snwprintf_s(ctx->wsz + ctx->cwsz, cwszBufCount, cwszBufCount, L"%S", szTag); } else { - LeaveCriticalSection(&pProcess->LockUpdate); + cwszTag = _snwprintf_s(ctx->wsz + ctx->cwsz, cwszBufCount, cwszBufCount, L"%s", wszTag); + } + for(; i < cMap; i++) { + if(pMap[i].vaBase >= vaLimit) { break; } // outside scope + if(pMap[i].vaBase + (pMap[i].cPages << 12) <= vaBase) { continue; } // outside scope + if(pMap[i].cwszText) { continue; } + pMap[i].fWoW64 = fWoW64; + pMap[i].cwszText = (DWORD)cwszTag; + pMap[i]._Reserved1[0] = (DWORD)ctx->cwsz; + fTagWrite = TRUE; + } + if(fTagWrite) { + ctx->cwsz += cwszTag + 1; + } +} + +/* +* Identify module names by scanning for PE headers and tag them into the memory map. +*/ +VOID VmmWin_InitializePteMapText_ScanHeaderPE(_In_ PVMM_PROCESS pProcess, _In_ PVMMWIN_INITIALIZEPTEMAP_CONTEXT ctx) +{ + DWORD cMap; + PVMMOB_MAP_PTE pObMemMap = NULL; + PVMM_MAP_PTEENTRY pMap; + PVMM_MAP_PTEENTRY ppMAPs[0x400]; + PPMEM_IO_SCATTER_HEADER ppMEMs = NULL; + DWORD i, cMEMs = 0, cbImageSize; + BOOL result; + CHAR szBuffer[MAX_PATH]; + // 1: checks and allocate buffers for parallel read of MZ header candidates + if(!LeechCore_AllocScatterEmpty(0x400, &ppMEMs)) { return; } + if(!VmmMap_GetPte(pProcess, &pObMemMap, FALSE)) { return; } + cMap = pObMemMap->cMap; + pMap = pObMemMap->pMap; + // 2: scan memory map for MZ header candidates and put them on list for read + for(i = 0; i < cMap - 1; i++) { + if(ctxVmm->tpMemoryModel == VMM_MEMORYMODEL_X86) { + result = + !(pMap[i].vaBase & 0xffff) && // starts at even 0x10000 offset + !pMap[i].cwszText; // tag not already set + } else { + result = + (pMap[i].cPages == 1) && // PE header is only 1 page + !(pMap[i].vaBase & 0xffff) && // starts at even 0x10000 offset + !pMap[i].cwszText && // tag not already set + (pMap[i].fPage & VMM_MEMMAP_PAGE_NX) && // no-execute + !(pMap[i + 1].fPage & VMM_MEMMAP_PAGE_NX); // next page is executable + } + if(result) { + ppMEMs[cMEMs]->qwA = pMap[i].vaBase; + ppMAPs[cMEMs] = pMap + i; + cMEMs++; + if(cMEMs == 0x400) { break; } + } + } + // 3: read all MZ header candicates previously selected and try load name from them (after read is successful) + if(cMEMs) { + VmmReadScatterVirtual(pProcess, ppMEMs, cMEMs, 0); + for(i = 0; i < cMEMs; i++) { + if(ppMEMs[i]->cb == 0x1000) { + result = PE_GetModuleNameEx(pProcess, ppMAPs[i]->vaBase, TRUE, ppMEMs[i]->pb, szBuffer, _countof(szBuffer), &cbImageSize); + if(result && (cbImageSize < 0x01000000)) { + VmmWin_InitializePteMapText_MapTag(pProcess, ctx, ppMAPs[i]->vaBase, ppMAPs[i]->vaBase + cbImageSize - 1, szBuffer, NULL, FALSE); + } + } + } + } + LocalFree(ppMEMs); + Ob_DECREF(pObMemMap); +} + +VOID VmmWin_InitializePteMapText_Modules(_In_ PVMM_PROCESS pProcess, _In_ PVMMWIN_INITIALIZEPTEMAP_CONTEXT ctx) +{ + DWORD i; + PVMM_MAP_MODULEENTRY pModule; + PVMMOB_MAP_MODULE pObModuleMap = NULL; + if(VmmMap_GetModule(pProcess, &pObModuleMap)) { + // update memory map with names + for(i = 0; i < pObModuleMap->cMap; i++) { + pModule = pObModuleMap->pMap + i; + VmmWin_InitializePteMapText_MapTag(pProcess, ctx, pModule->vaBase, pModule->vaBase + pModule->cbImageSize - 1, NULL, pModule->wszText, pModule->fWoW64); + } + Ob_DECREF(pObModuleMap); + } +} + +VOID VmmWin_InitializePteMapText_DoWork(_In_ PVMM_PROCESS pProcess) +{ + DWORD i, cbMultiText; + PVMM_MAP_PTEENTRY pe; + VMMWIN_INITIALIZEPTEMAP_CONTEXT ctx = { 0 }; + PVMMOB_MAP_PTE pObMap = pProcess->Map.pObPte; + ctx.cwsz = 1; + ctx.cwszMax = 0x00080000; + ctx.wsz = LocalAlloc(0, ctx.cwszMax << 1); + if(!ctx.wsz) { return; } + ctx.wsz[0] = 0; + VmmWin_InitializePteMapText_Modules(pProcess, &ctx); + VmmWin_InitializePteMapText_ScanHeaderPE(pProcess, &ctx); + cbMultiText = (DWORD)(ctx.cwsz << 1); + if(!(pObMap->wszMultiText = LocalAlloc(0, cbMultiText))) { + LocalFree(ctx.wsz); return; } - // copy modules map into Process struct - pOb->fValid = TRUE; - pOb->cMap = cModules; - memcpy(pOb->pMap, pModules, cModules * sizeof(VMM_MODULEMAP_ENTRY)); - pProcess->pObModuleMap = pOb; // reference taken by pProcess -> no need for DECREF. - LeaveCriticalSection(&pProcess->LockUpdate); - LocalFree(pModules); - return; -fail: - pProcess->pObModuleMap = Ob_Alloc('MO', LMEM_ZEROINIT, sizeof(VMMOB_MODULEMAP), NULL, NULL); // fValid set to false by default == failed initialization! + memcpy(pObMap->wszMultiText, ctx.wsz, cbMultiText); + pObMap->cbMultiText = cbMultiText; + for(i = 0; i < pObMap->cMap; i++) { + pe = pObMap->pMap + i; + pe->wszText = pObMap->wszMultiText + pe->_Reserved1[0]; + } + pObMap->fTagScan = TRUE; +} + +/* +* Try initialize PteMap text descriptions. This function will first try to pop- +* ulate the pre-existing VMMOB_MAP_PTE object in pProcess with module names and +* then, if failed or partially failed, try to initialize from PE file headers. +* -- pProcess +*/ +BOOL VmmWin_InitializePteMapText(_In_ PVMM_PROCESS pProcess) +{ + if(pProcess->Map.pObPte->fTagScan) { return TRUE; } + VmmTlbSpider(pProcess); + EnterCriticalSection(&pProcess->LockUpdate); + VmmWin_InitializePteMapText_DoWork(pProcess); LeaveCriticalSection(&pProcess->LockUpdate); - LocalFree(pModules); + return pProcess->Map.pObPte->fTagScan; } // ---------------------------------------------------------------------------- -// USER PROCESS PARAMETERS FUNCTIONALITY BELOW: +// HEAP FUNCTIONALITY BELOW: // ---------------------------------------------------------------------------- -PVMMWIN_USER_PROCESS_PARAMETERS VmmWin_UserProcessParameters_Get(_In_ PVMM_PROCESS pProcess) +typedef struct tdVMMWIN_HEAP_SEGMENT64 { + QWORD HeapEntry[2]; + DWORD SegmentSignature; + DWORD SegmentFlags; + LIST_ENTRY64 _ListEntry; + QWORD Heap; + QWORD BaseAddress; + QWORD NumberOfPages; + QWORD FirstEntry; + QWORD LastValidEntry; + DWORD NumberOfUnCommittedPages; + DWORD NumberOfUnCommittedRanges; + DWORD SegmentAllocatorBackTraceIndex; + DWORD Reserved; + LIST_ENTRY64 UCRSegmentList; +} VMMWIN_HEAP_SEGMENT64, *PVMMWIN_HEAP_SEGMENT64; + +typedef struct tdVMMWIN_HEAP_SEGMENT32 { + DWORD HeapEntry[2]; + DWORD SegmentSignature; + DWORD SegmentFlags; + LIST_ENTRY32 _ListEntry; + DWORD Heap; + DWORD BaseAddress; + DWORD NumberOfPages; + DWORD FirstEntry; + DWORD LastValidEntry; + DWORD NumberOfUnCommittedPages; + DWORD NumberOfUnCommittedRanges; + DWORD SegmentAllocatorBackTraceIndex; + DWORD Reserved; + LIST_ENTRY32 UCRSegmentList; +} VMMWIN_HEAP_SEGMENT32, *PVMMWIN_HEAP_SEGMENT32; + +VOID VmmWinHeap_Initialize32_Pre(_In_ PVMM_PROCESS pProcess, _In_opt_ POB_MAP ctx, _In_ QWORD va, _In_ PBYTE pb, _In_ DWORD cb, _In_ QWORD vaFLink, _In_ QWORD vaBLink, _In_ POB_VSET pVSetAddress, _Inout_ PBOOL pfValidEntry, _Inout_ PBOOL pfValidFLink, _Inout_ PBOOL pfValidBLink) +{ + QWORD v; + VMM_MAP_HEAPENTRY e = { 0 }; + PVMMWIN_HEAP_SEGMENT32 h = (PVMMWIN_HEAP_SEGMENT32)pb; + if((h->SegmentSignature != 0xffeeffee) || (h->NumberOfPages >= 0x00f00000)) { return; } + *pfValidFLink = VMM_UADDR32_4(vaFLink); + *pfValidBLink = VMM_UADDR32_4(vaBLink); + *pfValidEntry = *pfValidFLink || *pfValidBLink; + if((v = (QWORD)ObMap_GetByKey(ctx, h->Heap))) { + e.HeapId = v >> 57; + } else { + e.HeapId = ObMap_Size(ctx); + e.fPrimary = 1; + } + e.cPages = (DWORD)h->NumberOfPages; + e.cPagesUnCommitted = h->NumberOfUnCommittedPages; + ObMap_Push(ctx, va, (PVOID)e.qwHeapData); +} + +VOID VmmWinHeap_Initialize64_Pre(_In_ PVMM_PROCESS pProcess, _In_opt_ POB_MAP ctx, _In_ QWORD va, _In_ PBYTE pb, _In_ DWORD cb, _In_ QWORD vaFLink, _In_ QWORD vaBLink, _In_ POB_VSET pVSetAddress, _Inout_ PBOOL pfValidEntry, _Inout_ PBOOL pfValidFLink, _Inout_ PBOOL pfValidBLink) +{ + QWORD v; + VMM_MAP_HEAPENTRY e = { 0 }; + PVMMWIN_HEAP_SEGMENT64 h = (PVMMWIN_HEAP_SEGMENT64)pb; + if((h->SegmentSignature != 0xffeeffee) || (h->NumberOfPages >= 0x00f00000)) { return; } + *pfValidFLink = VMM_UADDR64_8(vaFLink); + *pfValidBLink = VMM_UADDR64_8(vaBLink); + *pfValidEntry = *pfValidFLink || *pfValidBLink; + if((v = (QWORD)ObMap_GetByKey(ctx, h->Heap))) { + e.HeapId = v >> 57; + } else { + e.HeapId = ObMap_Size(ctx); + e.fPrimary = 1; + } + e.cPages = (DWORD)h->NumberOfPages; + e.cPagesUnCommitted = h->NumberOfUnCommittedPages; + ObMap_Push(ctx, va, (PVOID)e.qwHeapData); +} + +/* +* Identify and scan for 64-bit heaps in a process memory space and commit the +* result to the pProcess memory map. +* NB! The 32-bit variant below is NOT robust. It will fail a lot of times +* especially on older versions - but it will fail silently without causing +* harm except a few extra reads. Probably due to bad hardcoded values. It's +* primarily heap-header analysis that is failing. But it seems to mostly work +* on newer windows versions. +* NB! WINXP is not supported. +* NB! Must be called in thread-safe way. +*/ +VOID VmmWinHeap_Initialize32(_In_ PVMM_PROCESS pProcess, _In_ BOOL fWow64) { BOOL f; - QWORD vaUserProcessParameters = 0; - PVMMWIN_USER_PROCESS_PARAMETERS pu = &pProcess->pObProcessPersistent->UserProcessParams; - if(pu->fProcessed) { return pu; } + BYTE pbPEB[sizeof(PEB32)]; + PPEB32 pPEB = (PPEB32)pbPEB; + DWORD cHeaps; + QWORD vaHeapPrimary, vaHeaps[0x80]; + POB_MAP pmObHeap; + PVMMOB_MAP_HEAP pObHeapMap; + // 1: Read PEB + if(!fWow64 && !pProcess->win.vaPEB) { return; } + if(fWow64 && !pProcess->win.vaPEB32) { return; } + if(!VmmRead(pProcess, (fWow64 ? pProcess->win.vaPEB32 : pProcess->win.vaPEB), pbPEB, sizeof(PEB32))) { return; } + vaHeapPrimary = pPEB->ProcessHeap; + cHeaps = pPEB->NumberOfHeaps; + if(cHeaps > 0x80) { return; } // probably not valid + // 2: Read heap array + f = (cHeaps <= 0x80) && + VmmRead(pProcess, pPEB->ProcessHeaps, (PBYTE)vaHeaps, sizeof(DWORD) * cHeaps) && + (vaHeaps[0] == vaHeapPrimary); + if(!f) { return; } + // 3: Traverse heap linked list + if(!(pmObHeap = ObMap_New(0))) { return; } + VmmWin_ListTraversePrefetch( + pProcess, + FALSE, + pmObHeap, + cHeaps, + vaHeaps, + 0x18, + sizeof(VMMWIN_HEAP_SEGMENT32), + VmmWinHeap_Initialize32_Pre, + NULL, + NULL + ); + // 4: allocate and set result + cHeaps = ObMap_Size(pmObHeap); + if((pObHeapMap = Ob_Alloc('HeaM', 0, sizeof(VMMOB_MAP_HEAP) + cHeaps * sizeof(VMM_MAP_HEAPENTRY), NULL, NULL))) { + pObHeapMap->cMap = cHeaps; + while(cHeaps) { + cHeaps--; + pObHeapMap->pMap[cHeaps].qwHeapData = (QWORD)ObMap_PopWithKey(pmObHeap, &pObHeapMap->pMap[cHeaps].vaHeapSegment); + } + pProcess->Map.pObHeap = pObHeapMap; + } + Ob_DECREF(pmObHeap); +} + +/* +* Identify and scan for 64-bit heaps in a process memory space and commit the +* result to the pProcess memory map. +* NB! WINXP is not supported. +* NB! Must be called in thread-safe way. +*/ +VOID VmmWinHeap_Initialize64(_In_ PVMM_PROCESS pProcess) +{ + BOOL f; + BYTE pbPEB[sizeof(PEB)]; + PPEB pPEB = (PPEB)pbPEB; + DWORD cHeaps; + QWORD vaHeapPrimary, vaHeaps[0x80]; + POB_MAP pmObHeap; + PVMMOB_MAP_HEAP pObHeapMap; + // 1: Read PEB + f = pProcess->win.vaPEB && VmmRead(pProcess, pProcess->win.vaPEB, pbPEB, sizeof(PEB)); + if(!f) { return; } + vaHeapPrimary = (QWORD)pPEB->Reserved4[1]; + cHeaps = (DWORD)(QWORD)pPEB->Reserved9[16]; + // 2: Read heap array + f = (cHeaps <= 0x80) && + VmmRead(pProcess, (QWORD)pPEB->Reserved9[17], (PBYTE)vaHeaps, sizeof(QWORD) * cHeaps) && + (vaHeaps[0] == vaHeapPrimary); + if(!f) { return; } + // 3: Traverse heap linked list + if(!(pmObHeap = ObMap_New(0))) { return; } + VmmWin_ListTraversePrefetch( + pProcess, + FALSE, + pmObHeap, + cHeaps, + vaHeaps, + 0x18, + sizeof(VMMWIN_HEAP_SEGMENT64), + VmmWinHeap_Initialize64_Pre, + NULL, + NULL + ); + // 4: allocate and set result + cHeaps = ObMap_Size(pmObHeap); + if((pObHeapMap = Ob_Alloc('HeaM', 0, sizeof(VMMOB_MAP_HEAP) + cHeaps * sizeof(VMM_MAP_HEAPENTRY), NULL, NULL))) { + pObHeapMap->cMap = cHeaps; + while(cHeaps) { + cHeaps--; + pObHeapMap->pMap[cHeaps].qwHeapData = (QWORD)ObMap_PopWithKey(pmObHeap, &pObHeapMap->pMap[cHeaps].vaHeapSegment); + } + pProcess->Map.pObHeap = pObHeapMap; // pProcess take reference responsibility + } + Ob_DECREF(pmObHeap); +} + +/* +* Initialize the meap map containing information about the process heaps in the +* specific process. This is performed by a PEB walk/scan of in-process memory +* structures. This may be unreliable if a process is obfuscated or tampered. +* -- pProcess +* -- return +*/ +BOOL VmmWinHeap_Initialize(_In_ PVMM_PROCESS pProcess) +{ + if(pProcess->Map.pObHeap) { return TRUE; } + VmmTlbSpider(pProcess); EnterCriticalSection(&pProcess->LockUpdate); + if(!pProcess->Map.pObHeap) { + if((ctxVmm->tpSystem == VMM_SYSTEM_WINDOWS_X86) || ((ctxVmm->tpSystem == VMM_SYSTEM_WINDOWS_X64) && pProcess->win.fWow64)) { + VmmWinHeap_Initialize32(pProcess, pProcess->win.fWow64); + } else if(ctxVmm->tpSystem == VMM_SYSTEM_WINDOWS_X64) { + VmmWinHeap_Initialize64(pProcess); + } + } + LeaveCriticalSection(&pProcess->LockUpdate); + return pProcess->Map.pObHeap ? TRUE : FALSE; +} + +// ---------------------------------------------------------------------------- +// THREADING FUNCTIONALITY BELOW: +// +// The threading subsystem is dependent on loaded kernel pdb symbols and being +// initialized asynchronously at startup. i.e. it may not be immediately avail- +// able at startup time or not available at all. Loading threads may be slow +// the first time if many threads exist in a process since a list have to be +// traversed - hence functionality exists to start a load asynchronously. +// ---------------------------------------------------------------------------- + +typedef struct tdVMMWIN_INITIALIZETHREAD_CONTEXT { + POB_MAP pmThread; + POB_VSET psTeb; + PVMM_PROCESS pProcess; +} VMMWIN_INITIALIZETHREAD_CONTEXT, *PVMMWIN_INITIALIZETHREAD_CONTEXT; + +int VmmWinThread_Initialize_CmpThreadEntry(PVMM_MAP_THREADENTRY v1, PVMM_MAP_THREADENTRY v2) +{ + return + (v1->dwTID < v2->dwTID) ? -1 : + (v1->dwTID > v2->dwTID) ? 1 : 0; +} + +VOID VmmWinThread_Initialize_DoWork_Pre(_In_ PVMM_PROCESS pSystemProcess, _In_opt_ PVMMWIN_INITIALIZETHREAD_CONTEXT ctx, _In_ QWORD va, _In_ PBYTE pb, _In_ DWORD cb, _In_ QWORD vaFLink, _In_ QWORD vaBLink, _In_ POB_VSET pVSetAddress, _Inout_ PBOOL pfValidEntry, _Inout_ PBOOL pfValidFLink, _Inout_ PBOOL pfValidBLink) +{ + BOOL f, f32 = ctxVmm->f32; + PVMM_MAP_THREADENTRY e; + PVMM_WIN_THEADINFO pti = &ctxVmm->kernel.ThreadInfo; + // 1: sanity check + f = ctx && + (f32 ? VMM_KADDR32_4(vaFLink) : VMM_KADDR64_8(vaFLink)) && + (f32 ? VMM_KADDR32_4(vaBLink) : VMM_KADDR64_8(vaBLink)) && + (!pti->oProcessOpt || (VMM_PTR_OFFSET(f32, pb, pti->oProcessOpt) == ctx->pProcess->win.EPROCESS.va)); + if(!f) { return; } + *pfValidEntry = *pfValidFLink = *pfValidBLink = TRUE; + // 2: allocate and populate thread entry with info. + if(!(e = LocalAlloc(LMEM_ZEROINIT, sizeof(VMM_MAP_THREADENTRY)))) { return; } + e->vaETHREAD = va; + e->dwTID = (DWORD)VMM_PTR_OFFSET(f32, pb, pti->oCid + (f32 ? 4ULL : 8ULL)); + e->dwPID = (DWORD)VMM_PTR_OFFSET(f32, pb, pti->oCid); + e->dwExitStatus = *(PDWORD)(pb + pti->oExitStatus); + e->bState = *(PUCHAR)(pb + pti->oState); + if(pti->oRunningOpt) { e->bRunning = *(PUCHAR)(pb + pti->oRunningOpt); } + e->bPriority = *(PUCHAR)(pb + pti->oPriority); + e->bBasePriority = *(PUCHAR)(pb + pti->oBasePriority); + e->vaTeb = VMM_PTR_OFFSET(f32, pb, pti->oTeb); + e->ftCreateTime = *(PQWORD)(pb + pti->oCreateTime); + e->ftExitTime = *(PQWORD)(pb + pti->oExitTime); + e->vaStartAddress = VMM_PTR_OFFSET(f32, pb, pti->oStartAddress); + e->vaStackBaseKernel = VMM_PTR_OFFSET(f32, pb, pti->oStackBase); + e->vaStackLimitKernel = VMM_PTR_OFFSET(f32, pb, pti->oStackLimit); + if(e->ftExitTime > 0x02000000'00000000) { e->ftExitTime = 0; } + ObVSet_Push(ctx->psTeb, e->vaTeb); + ObMap_Push(ctx->pmThread, e->dwTID, e); // map will free allocation when cleared +} + +VOID VmmWinThread_Initialize_DoWork(_In_ PVMM_PROCESS pProcess) +{ + BOOL f32 = ctxVmm->f32; + BYTE pbTeb[0x20]; + DWORD i, cMap; + QWORD va, vaThreadListEntry; + POB_VSET psObTeb = NULL; + POB_MAP pmObThreads = NULL; + PVMMOB_MAP_THREAD pObThreadMap = NULL; + PVMM_MAP_THREADENTRY pThreadEntry; + PVMM_PROCESS pObSystemProcess = NULL; + VMMWIN_INITIALIZETHREAD_CONTEXT ctx = { 0 }; + // 1: set up and perform list traversal call. + vaThreadListEntry = VMM_PTR_OFFSET(f32, pProcess->win.EPROCESS.pb, ctxVmm->kernel.ThreadInfo.oThreadListHeadKP); + if(f32 ? !VMM_KADDR32_4(vaThreadListEntry) : !VMM_KADDR64_8(vaThreadListEntry)) { goto fail; } + if(!(pObSystemProcess = VmmProcessGet(4))) { goto fail; } + if(!(psObTeb = ObVSet_New())) { goto fail; } + if(!(pmObThreads = ObMap_New(OB_MAP_FLAGS_OBJECT_LOCALFREE))) { goto fail; } + ctx.pmThread = pmObThreads; + ctx.psTeb = psObTeb; + ctx.pProcess = pProcess; + va = vaThreadListEntry - ctxVmm->kernel.ThreadInfo.oThreadListEntry; + VmmWin_ListTraversePrefetch( + pObSystemProcess, + f32, + &ctx, + 1, + &va, + ctxVmm->kernel.ThreadInfo.oThreadListEntry, + ctxVmm->kernel.ThreadInfo.oMax, + VmmWinThread_Initialize_DoWork_Pre, + NULL, + pProcess->pObPersistent->pObCMapThreadPrefetch); + // 2: transfer result from generic map into PVMMOB_MAP_THREAD + if(!(cMap = ObMap_Size(pmObThreads))) { goto fail; } + if(!(pObThreadMap = Ob_Alloc(OB_TAG_MAP_THREAD, 0, sizeof(VMMOB_MAP_THREAD) + cMap * sizeof(VMM_MAP_THREADENTRY), NULL, NULL))) { goto fail; } + pObThreadMap->cMap = cMap; + VmmCachePrefetchPages3(pProcess, psObTeb, 0x20, 0); + for(i = 0; i < cMap; i++) { + pThreadEntry = (PVMM_MAP_THREADENTRY)ObMap_GetByIndex(pmObThreads, i); + if(VmmRead2(pProcess, pThreadEntry->vaTeb, pbTeb, 0x20, VMM_FLAG_FORCECACHE_READ)) { + pThreadEntry->vaStackBaseUser = f32 ? *(PDWORD)(pbTeb + 4) : *(PQWORD)(pbTeb + 8); + pThreadEntry->vaStackLimitUser = f32 ? *(PDWORD)(pbTeb + 8) : *(PQWORD)(pbTeb + 16); + } + memcpy(pObThreadMap->pMap + i, pThreadEntry, sizeof(VMM_MAP_THREADENTRY)); + } + // 3: sort on thread id (TID) and assign result to process object. + qsort(pObThreadMap->pMap, cMap, sizeof(VMM_MAP_THREADENTRY), (int(*)(const void*, const void*))VmmWinThread_Initialize_CmpThreadEntry); + pProcess->Map.pObThread = pObThreadMap; // pProcess take reference responsibility +fail: + Ob_DECREF(psObTeb); + Ob_DECREF(pmObThreads); + Ob_DECREF(pObSystemProcess); +} + +/* +* Initialize the thread map for a specific process. +* NB! The threading sub-system is dependent on pdb symbols and may take a small +* amount of time before it's available after system startup. +* -- pProcess +* -- fNonBlocking +* -- return +*/ +BOOL VmmWinThread_Initialize(_In_ PVMM_PROCESS pProcess, _In_ BOOL fNonBlocking) +{ + if(pProcess->Map.pObThread) { return TRUE; } + if(!ctxVmm->fThreadMapEnabled) { return FALSE; } + VmmTlbSpider(pProcess); + if(fNonBlocking) { + if(!TryEnterCriticalSection(&pProcess->Map.LockUpdateThreadMap)) { return FALSE; } + } else { + EnterCriticalSection(&pProcess->Map.LockUpdateThreadMap); + } + if(!pProcess->Map.pObThread) { + VmmWinThread_Initialize_DoWork(pProcess); + if(!pProcess->Map.pObThread) { + pProcess->Map.pObThread = Ob_Alloc(OB_TAG_MAP_THREAD, LMEM_ZEROINIT, sizeof(VMMOB_MAP_THREAD), NULL, NULL); + } + } + LeaveCriticalSection(&pProcess->Map.LockUpdateThreadMap); + return pProcess->Map.pObThread ? TRUE : FALSE; +} + +// ---------------------------------------------------------------------------- +// HANDLE FUNCTIONALITY BELOW: +// +// The code below is responsible for parsing the HANDLE table into a map. The +// function will read the handle table and then also peek into each handle to +// determine its type. Even though parsing is generally efficient in number of +// calls quite a few memory pages may be retrieved - worst case ~1 per handle! +// ---------------------------------------------------------------------------- + +/* +* Retrieve a pointer to a VMMWIN_OBJECT_TYPE if possible. Initialization of the +* table takes place on first use. The table only exists in Win7+ and is is +* dependant on PDB symbol functionality for initialization. +* -- iObjectType +* -- return +*/ +_Success_(return != NULL) +PVMMWIN_OBJECT_TYPE VmmWin_ObjectTypeGet(_In_ BYTE iObjectType) +{ + BOOL f, fResult = FALSE; + QWORD vaTypeTable = 0; + PVMM_PROCESS pObSystemProcess = NULL; + DWORD i, cType = 2; + QWORD ava[256]; + WORD acbwsz[256]; + BYTE pb[256 * 8]; + PQWORD pva64; + PDWORD pva32; + DWORD owszMultiText = 1; + LPWSTR wszMultiText = NULL; + if(ctxVmm->ObjectTypeTable.fInitialized) { + return ctxVmm->ObjectTypeTable.h[iObjectType].wsz ? &ctxVmm->ObjectTypeTable.h[iObjectType] : NULL; + } + EnterCriticalSection(&ctxVmm->MasterLock); + if(ctxVmm->ObjectTypeTable.fInitialized) { + LeaveCriticalSection(&ctxVmm->MasterLock); + return ctxVmm->ObjectTypeTable.h[iObjectType].wsz ? &ctxVmm->ObjectTypeTable.h[iObjectType] : NULL; + } + if(!(pObSystemProcess = VmmProcessGet(4))) { goto fail; } + PDB_Initialize_WaitComplete(); + if(!PDB_GetSymbolAddress(VMMWIN_PDB_HANDLE_KERNEL, "ObTypeIndexTable", &vaTypeTable)) { goto fail; } + if(ctxVmm->kernel.dwVersionMajor == 10) { + if(!PDB_GetSymbolDWORD(VMMWIN_PDB_HANDLE_KERNEL, "ObHeaderCookie", pObSystemProcess, &i)) { goto fail; } + ctxVmm->ObjectTypeTable.bObjectHeaderCookie = (BYTE)i; + } + // fetch and count object type addresses + ZeroMemory(ava, sizeof(ava)); + ZeroMemory(acbwsz, sizeof(acbwsz)); + VmmReadEx(pObSystemProcess, vaTypeTable, pb, 256 * 8, NULL, VMM_FLAG_ZEROPAD_ON_FAIL); if(ctxVmm->f32) { - f = pProcess->win.vaPEB && - VmmRead(pProcess, pProcess->win.vaPEB + 0x10, (PBYTE)&vaUserProcessParameters, sizeof(DWORD)) && - !(vaUserProcessParameters & 0x80000003); + pva32 = (PDWORD)pb; + while(VMM_KADDR32_8(pva32[cType]) && (cType < 256)) { + ava[cType] = pva32[cType]; + cType++; + } } else { - f = pProcess->win.vaPEB && - VmmRead(pProcess, pProcess->win.vaPEB + 0x20, (PBYTE)&vaUserProcessParameters, sizeof(QWORD)) && - !(vaUserProcessParameters & 0xffff8000'00000007); + pva64 = (PQWORD)pb; + while(VMM_KADDR64_16(pva64[cType]) && (cType < 256)) { + ava[cType] = pva64[cType]; + cType++; + } } - if(f) { - if(!VmmRead_U2A_Alloc(pProcess, ctxVmm->f32, 0, vaUserProcessParameters + (ctxVmm->f32 ? 0x038 : 0x060), &pu->szImagePathName, &pu->cchImagePathName, NULL)) { // ImagePathName - VmmRead_U2A_Alloc(pProcess, ctxVmm->f32, 0, vaUserProcessParameters + (ctxVmm->f32 ? 0x030 : 0x050), &pu->szImagePathName, &pu->cchImagePathName, NULL); // DllPath (mutually exclusive with ImagePathName?) + if(cType == 2) { goto fail; } // none found + // fetch unicode length and addresses of text + VmmCachePrefetchPages4(pObSystemProcess, cType, ava, 0x10, 0); + for(i = 2; i < cType; i++) { + f = VmmRead2(pObSystemProcess, ava[i] + (ctxVmm->f32 ? 8 : 16), pb, 0x10, VMM_FLAG_FORCECACHE_READ); + f = f && (*(PWORD)(pb) < MAX_PATH); + f = f && (*(PWORD)(pb) <= *(PQWORD)(pb + 2)); + f = f && (acbwsz[i] = *(PWORD)(pb)); + f = f && (ava[i] = ctxVmm->f32 ? *(PDWORD)(pb + 4) : *(PQWORD)(pb + 8)); + f = f && (ctxVmm->f32 ? VMM_KADDR32_8(ava[i]) : VMM_KADDR64_16(ava[i])); + if(!f) { + ava[i] = 0; } - VmmRead_U2A_Alloc(pProcess, ctxVmm->f32, 0, vaUserProcessParameters + (ctxVmm->f32 ? 0x040 : 0x070), &pu->szCommandLine, &pu->cchCommandLine, NULL); // CommandLine } - pu->fProcessed = TRUE; - LeaveCriticalSection(&pProcess->LockUpdate); - return pu; + // fetch text + if(!(wszMultiText = LocalAlloc(0, 2 + 2ULL * MAX_PATH * cType))) { goto fail; } + wszMultiText[0] = 0; + VmmCachePrefetchPages4(pObSystemProcess, cType, ava, 2 * MAX_PATH, 0); + for(i = 2; i < cType; i++) { + if(ava[i] && VmmRead2(pObSystemProcess, ava[i] - 16, pb, 16 + acbwsz[i], VMM_FLAG_FORCECACHE_READ) && VMM_POOLTAG_PREPENDED(pb, 16, 'ObNm')) { + memcpy((PBYTE)(wszMultiText + owszMultiText), pb + 16, acbwsz[i]); + ava[i] = owszMultiText; + owszMultiText += acbwsz[i] >> 1; + wszMultiText[owszMultiText] = 0; + owszMultiText++; + } + } + // finish! + ctxVmm->ObjectTypeTable.cbMultiText = owszMultiText << 1; + if(!(ctxVmm->ObjectTypeTable.wszMultiText = LocalAlloc(0, ctxVmm->ObjectTypeTable.cbMultiText))) { goto fail; } + memcpy(ctxVmm->ObjectTypeTable.wszMultiText, wszMultiText, ctxVmm->ObjectTypeTable.cbMultiText); + for(i = 2; i < cType; i++) { + if(ava[i]) { + ctxVmm->ObjectTypeTable.h[i].cwsz = acbwsz[i] >> 1; + ctxVmm->ObjectTypeTable.h[i].owsz = (WORD)ava[i]; + ctxVmm->ObjectTypeTable.h[i].wsz = ctxVmm->ObjectTypeTable.wszMultiText + ava[i]; + } + } + ctxVmm->ObjectTypeTable.c = cType; + fResult = TRUE; + // fall-trough to cleanup / "fail" +fail: + ctxVmm->ObjectTypeTable.fInitialized = TRUE; + if(!fResult) { ctxVmm->ObjectTypeTable.fInitializedFailed = TRUE; } + LeaveCriticalSection(&ctxVmm->MasterLock); + Ob_DECREF(pObSystemProcess); + LocalFree(wszMultiText); + return ctxVmm->ObjectTypeTable.h[iObjectType].wsz ? &ctxVmm->ObjectTypeTable.h[iObjectType] : NULL; } -// ---------------------------------------------------------------------------- -// HEAP FUNCTIONALITY BELOW: -// ---------------------------------------------------------------------------- +/* +* _OBJECT_HEADER.TypeIndex is encoded on Windows 10 - this function decodes it. +* https://medium.com/@ashabdalhalim/e8f907e7073a +* -- vaObjectHeader +* -- iTypeIndexTableEncoded +* -- return +*/ +BYTE VmmWin_ObjectTypeGetIndexFromEncoded(_In_ QWORD vaObjectHeader, _In_ BYTE iTypeIndexTableEncoded) +{ + if(ctxVmm->kernel.dwVersionMajor != 10) { return iTypeIndexTableEncoded; } + if(!ctxVmm->ObjectTypeTable.fInitialized) { VmmWin_ObjectTypeGet(0); } // DUMMY call to initialize ctxVmm->ObjectTypeTable + if(ctxVmm->ObjectTypeTable.fInitializedFailed) { return 0; } + return iTypeIndexTableEncoded ^ (BYTE)(vaObjectHeader >> 8) ^ ctxVmm->ObjectTypeTable.bObjectHeaderCookie; +} -typedef struct tdVMMWIN_HEAP_SEGMENT64 { - QWORD HeapEntry[2]; - DWORD SegmentSignature; - DWORD SegmentFlags; - LIST_ENTRY64 _ListEntry; - QWORD Heap; - QWORD BaseAddress; - QWORD NumberOfPages; - QWORD FirstEntry; - QWORD LastValidEntry; - DWORD NumberOfUnCommittedPages; - DWORD NumberOfUnCommittedRanges; - DWORD SegmentAllocatorBackTraceIndex; - DWORD Reserved; - LIST_ENTRY64 UCRSegmentList; -} VMMWIN_HEAP_SEGMENT64, *PVMMWIN_HEAP_SEGMENT64; +typedef struct tdVMMWIN_INITIALIZE_HANDLE_CONTEXT { + PVMM_PROCESS pSystemProcess; + PVMM_PROCESS pProcess; + DWORD cTables; + DWORD cTablesMax; + PQWORD pvaTables; + PVMMOB_MAP_HANDLE pHandleMap; + DWORD iMap; +} VMMWIN_INITIALIZE_HANDLE_CONTEXT, *PVMMWIN_INITIALIZE_HANDLE_CONTEXT; -typedef struct tdVMMWIN_HEAP_SEGMENT32 { - DWORD HeapEntry[2]; - DWORD SegmentSignature; - DWORD SegmentFlags; - LIST_ENTRY32 _ListEntry; - DWORD Heap; - DWORD BaseAddress; - DWORD NumberOfPages; - DWORD FirstEntry; - DWORD LastValidEntry; - DWORD NumberOfUnCommittedPages; - DWORD NumberOfUnCommittedRanges; - DWORD SegmentAllocatorBackTraceIndex; - DWORD Reserved; - LIST_ENTRY32 UCRSegmentList; -} VMMWIN_HEAP_SEGMENT32, *PVMMWIN_HEAP_SEGMENT32; +/* +* Object manager callback function for object cleanup tasks. +* -- pVmmHandle +*/ +VOID VmmWinHandle_CloseObCallback(_In_ PVOID pVmmHandle) +{ + PVMMOB_MAP_HANDLE pOb = (PVMMOB_MAP_HANDLE)pVmmHandle; + LocalFree(pOb->wszMultiText); +} /* -* Identify 64-bit HEAPs in a process and tag them into the memory map. -* WINXP is not supported. +* Spider the handle table hierarchy if there is one. +* -- ctx +* -- vaTable +* -- fLevel2 */ -VOID VmmWin_ScanPebHeap64(_In_ PVMM_PROCESS pProcess) +VOID VmmWinHandle_InitializeCore_SpiderTables(_In_ PVMMWIN_INITIALIZE_HANDLE_CONTEXT ctx, _In_ QWORD vaTable, _In_ BOOL fLevel2) { - BOOL fReadHasMore, fFirst = TRUE; - CHAR szBuffer[MAX_PATH]; - BYTE pbPEB[sizeof(PEB)]; - PPEB pPEB = (PPEB)pbPEB; - DWORD i, j, cHeaps, cHeapsMax, cLoopProtect = 0; - QWORD vaHeapPrimary, vaHeaps[0x80]; - PMEM_IO_SCATTER_HEADER pMEM, *ppMEMs = NULL; - PVMMWIN_HEAP_SEGMENT64 pH, PH2; - // 1: Read PEB - if(!pProcess->win.vaPEB) { return; } - if(!VmmRead(pProcess, pProcess->win.vaPEB, pbPEB, sizeof(PEB))) { return; } - vaHeapPrimary = (QWORD)pPEB->Reserved4[1]; - cHeaps = (DWORD)((QWORD)pPEB->Reserved9[16]); - cHeapsMax = (DWORD)((QWORD)pPEB->Reserved9[16] >> 32); - if(cHeaps > 0x80) { return; } // probably not valid - // 2: Read heap array - if(!VmmRead(pProcess, (QWORD)pPEB->Reserved9[17], (PBYTE)vaHeaps, sizeof(QWORD) * cHeaps)) { return; } - if(vaHeaps[0] != vaHeapPrimary) { return; } - // 3: Read heap headers in one go (scatter read) - if(!LeechCore_AllocScatterEmpty(cHeaps << 1, &ppMEMs)) { return; } - for(i = 0; i < cHeaps; i++) { - if(vaHeaps[i] & 0xffff) { - LocalFree(ppMEMs); - return; - } - ppMEMs[i]->qwA = vaHeaps[i]; - } - // 4: Analyze result - do { - VmmReadScatterVirtual(pProcess, ppMEMs, cHeaps << 1, 0); - fReadHasMore = FALSE; - for(i = 0; i < (cHeaps << 1); i++) { - pMEM = ppMEMs[i]; - if(pMEM->cb != 0x1000) { continue; } - pH = PH2 = (PVMMWIN_HEAP_SEGMENT64)ppMEMs[i]->pb; - if(pH->SegmentSignature != 0xffeeffee) { continue; } - if(pH->Heap != vaHeaps[i % cHeaps]) { continue; } // heap address mis-match - if(pH->NumberOfPages >= 0x40000) { continue; } // heap size > 1GB == unrealistic. - // set tag - sprintf_s(szBuffer, MAX_PATH, "HEAP%02X", i % cHeaps); - VmmMemMapTag(pProcess, pH->BaseAddress, pH->BaseAddress + (pH->NumberOfPages << 12), szBuffer, NULL, FALSE, FALSE); - // prepare next read - pMEM[i].cb = 0; - if((i >= cHeaps) || fFirst) { // BLink - j = i + ((i < cHeaps) ? cHeaps : 0); - // BLink inside same page -> jump forward - if((PH2->_ListEntry.Blink - pMEM[i].qwA < 0x800) && ((PH2->_ListEntry.Blink & 0xfff) >= sizeof(VMMWIN_HEAP_SEGMENT64))) { - PH2 = (PVMMWIN_HEAP_SEGMENT64)(ppMEMs[i]->pb + (PH2->_ListEntry.Blink & 0xfff) - 0x18); - } - pMEM[j].qwA = (QWORD)-1; - if((((PH2->_ListEntry.Blink - 0x18) & 0xffff) == 0) && ((PH2->_ListEntry.Blink - 0x18) != vaHeaps[i])) { - pMEM[j].qwA = PH2->_ListEntry.Blink - 0x18; - fReadHasMore = TRUE; - } + QWORD i, va = 0; + union { + BYTE pb[0x1000]; + DWORD pdw[0x400]; + QWORD pqw[0x200]; + } u; + if(!VmmRead(ctx->pSystemProcess, vaTable, u.pb, 0x1000)) { return; } + if(ctxVmm->f32) { + for(i = 0; i < 0x400; i++) { + va = u.pdw[i]; + if(!VMM_KADDR32_PAGE(va)) { return; } + if(fLevel2) { + VmmWinHandle_InitializeCore_SpiderTables(ctx, va, FALSE); + if(ctx->cTables == ctx->cTablesMax) { return; } + } else { + ctx->pvaTables[ctx->cTables] = va; + ctx->cTables++; + if(ctx->cTables == ctx->cTablesMax) { return; } } - if(i < cHeaps) { // FLink - pMEM[i].qwA = (QWORD)-1; - if((((pH->_ListEntry.Flink - 0x18) & 0xffff) == 0) && ((pH->_ListEntry.Flink - 0x18) != vaHeaps[i])) { - pMEM[i].qwA = pH->_ListEntry.Flink - 0x18; - fReadHasMore = TRUE; - } + } + } else { + for(i = 0; i < 0x200; i++) { + va = u.pqw[i]; + if(!VMM_KADDR64_PAGE(va)) { return; } + if(fLevel2) { + VmmWinHandle_InitializeCore_SpiderTables(ctx, va, FALSE); + if(ctx->cTables == ctx->cTablesMax) { return; } + } else { + ctx->pvaTables[ctx->cTables] = va; + ctx->cTables++; + if(ctx->cTables == ctx->cTablesMax) { return; } } } - fFirst = FALSE; - } while(fReadHasMore && (++cLoopProtect < 0x40)); - LocalFree(ppMEMs); + } } /* -* Identify 32-bit HEAPs in a process and tag them into the memory map. -* NB! The 32-bit variant below is NOT robust. It will fail a lot of times -* especially on older versions - but it will fail silently without causing -* harm except a few extra reads. Probably due to bad hardcoded values. It's -* primarily heap-header analysis that is failing. But it seems to mostly work -* on newer windows versions. -* WINXP is not supported. +* Count the number of valid handles. +* -- ctx +* -- return = the number of valid handles. */ -VOID VmmWin_ScanPebHeap32(_In_ PVMM_PROCESS pProcess, _In_ BOOL fWow64) +DWORD VmmWinHandle_InitializeCore_CountHandles(_In_ PVMMWIN_INITIALIZE_HANDLE_CONTEXT ctx) { - BOOL fReadHasMore, fFirst = TRUE; - CHAR szBuffer[MAX_PATH]; - BYTE pbPEB[sizeof(PEB32)]; - PPEB32 pPEB = (PPEB32)pbPEB; - DWORD i, j, cHeaps, cHeapsMax, cLoopProtect = 0; - DWORD vaHeapPrimary, vaHeaps[0x80]; - PMEM_IO_SCATTER_HEADER pMEM, *ppMEMs = NULL; - PVMMWIN_HEAP_SEGMENT32 pH, PH2; - // 1: Read PEB - if(!fWow64 && !pProcess->win.vaPEB) { return; } - if(fWow64 && !pProcess->win.vaPEB32) { return; } - if(!VmmRead(pProcess, (fWow64 ? pProcess->win.vaPEB32 : pProcess->win.vaPEB), pbPEB, sizeof(PEB32))) { return; } - vaHeapPrimary = pPEB->ProcessHeap; - cHeaps = pPEB->NumberOfHeaps; - cHeapsMax = pPEB->MaximumNumberOfHeaps; - if(cHeaps > 0x80) { return; } // probably not valid - // 2: Read heap array - if(!VmmRead(pProcess, pPEB->ProcessHeaps, (PBYTE)vaHeaps, sizeof(DWORD) * cHeaps)) { return; } - if(vaHeaps[0] != vaHeapPrimary) { return; } - // 3: Read heap headers in one go (scatter read) - if(!LeechCore_AllocScatterEmpty(cHeaps << 1, &ppMEMs)) { return; } - for(i = 0; i < cHeaps; i++) { - if(vaHeaps[i] & 0xffff) { - LocalFree(ppMEMs); - return; - } - ppMEMs[i]->qwA = vaHeaps[i]; - } - VmmReadScatterVirtual(pProcess, ppMEMs, cHeaps, 0); - // 4: Analyze result - do { - VmmReadScatterVirtual(pProcess, ppMEMs, cHeaps << 1, 0); - fReadHasMore = FALSE; - for(i = 0; i < (cHeaps << 1); i++) { - pMEM = ppMEMs[i]; - if(pMEM->cb != 0x1000) { continue; } - pH = PH2 = (PVMMWIN_HEAP_SEGMENT32)ppMEMs[i]->pb; - if(pH->SegmentSignature != 0xffeeffee) { continue; } - if(pH->Heap != vaHeaps[i % cHeaps]) { continue; } // heap address mis-match - if(pH->NumberOfPages >= 0x40000) { continue; } // heap size > 1GB == unrealistic. - // set tag - sprintf_s(szBuffer, MAX_PATH, "HEAP%02X", i % cHeaps); - VmmMemMapTag(pProcess, pH->BaseAddress, (QWORD)pH->BaseAddress + ((QWORD)pH->NumberOfPages << 12), szBuffer, NULL, fWow64, FALSE); - // prepare next read - pMEM[i].cb = 0; - if((i >= cHeaps) || fFirst) { // BLink - j = i + ((i < cHeaps) ? cHeaps : 0); - // BLink inside same page -> jump forward - if((PH2->_ListEntry.Blink - pMEM[i].qwA < 0x800) && ((PH2->_ListEntry.Blink & 0xfff) >= sizeof(VMMWIN_HEAP_SEGMENT64))) { - PH2 = (PVMMWIN_HEAP_SEGMENT32)(ppMEMs[i]->pb + (PH2->_ListEntry.Blink & 0xfff) - 0x18); - } - pMEM[j].qwA = (QWORD)-1; - if((((PH2->_ListEntry.Blink - 0x18) & 0xffff) == 0) && ((PH2->_ListEntry.Blink - 0x18) != vaHeaps[i])) { - pMEM[j].qwA = PH2->_ListEntry.Blink - 0x18; - fReadHasMore = TRUE; - } + QWORD va; + DWORD iTable, i, cHandles = 0; + union { + BYTE pb[0x1000]; + DWORD pdw[0x400]; + QWORD pqw[0x200]; + } u; + VmmCachePrefetchPages4(ctx->pSystemProcess, ctx->cTables, ctx->pvaTables, 0x1000, 0); + for(iTable = 0; iTable < ctx->cTables; iTable++) { + if(!VmmRead(ctx->pSystemProcess, ctx->pvaTables[iTable], u.pb, 0x1000)) { continue; } + if(ctxVmm->f32) { + for(i = 1; i < 512; i++) { + if(!VMM_KADDR32(u.pdw[i << 1])) { continue; } + cHandles++; } - if(i < cHeaps) { // FLink - pMEM[i].qwA = (QWORD)-1; - if((((pH->_ListEntry.Flink - 0x18) & 0xffff) == 0) && ((pH->_ListEntry.Flink - 0x18) != vaHeaps[i])) { - pMEM[i].qwA = pH->_ListEntry.Flink - 0x18; - fReadHasMore = TRUE; + } else { + for(i = 1; i < 256; i++) { + va = u.pqw[i << 1]; + if(ctxVmm->kernel.dwVersionBuild >= 9200) { // Win8 or later + va = 0xffff0000'00000000 | (va >> 16); } + if(!VMM_KADDR64(va)) { continue; } + cHandles++; } } - fFirst = FALSE; - } while(fReadHasMore && (++cLoopProtect < 0x40)); - LocalFree(ppMEMs); + } + return cHandles; } /* -* Identify module names by scanning for PE headers and tag them into the memory map. +* Read the handle tables and populate only basic information into the HandleMap +* i.e. data that don't require reading of the actual objects pointed to. +* -- ctx +* -- vaHandleTable +* -- dwBaseHandleId */ -VOID VmmWin_ScanHeaderPE(_In_ PVMM_PROCESS pProcess) +VOID VmmWinHandle_InitializeCore_ReadHandleTable(_In_ PVMMWIN_INITIALIZE_HANDLE_CONTEXT ctx, _In_ QWORD vaHandleTable, _In_ DWORD dwBaseHandleId) { - DWORD cMap; - PVMMOB_MEMMAP pObMemMap = NULL; - PVMM_MEMMAP_ENTRY pMap; - PVMM_MEMMAP_ENTRY ppMAPs[0x400]; - PPMEM_IO_SCATTER_HEADER ppMEMs = NULL; - DWORD i, cMEMs = 0, cbImageSize; - BOOL result; - CHAR szBuffer[MAX_PATH]; - // 1: checks and allocate buffers for parallel read of MZ header candidates - if(!LeechCore_AllocScatterEmpty(0x400, &ppMEMs)) { return; } - if(!VmmMemMapGetEntries(pProcess, 0, &pObMemMap)) { return; } - cMap = pObMemMap->cMap; - pMap = pObMemMap->pMap; - // 2: scan memory map for MZ header candidates and put them on list for read - for(i = 0; i < cMap - 1; i++) { - if(ctxVmm->tpMemoryModel == VMM_MEMORYMODEL_X86) { - result = - !(pMap[i].AddrBase & 0xffff) && // starts at even 0x10000 offset - !pMap[i].szTag[0]; // tag not already set - } else { - result = - (pMap[i].cPages == 1) && // PE header is only 1 page - !(pMap[i].AddrBase & 0xffff) && // starts at even 0x10000 offset - !pMap[i].szTag[0] && // tag not already set - (pMap[i].fPage & VMM_MEMMAP_PAGE_NX) && // no-execute - !(pMap[i + 1].fPage & VMM_MEMMAP_PAGE_NX); // next page is executable + DWORD i; + QWORD va; + PVMM_MAP_HANDLEENTRY pe; + union { + BYTE pb[0x1000]; + DWORD pdw[0x400]; + QWORD pqw[0x200]; + } u; + if(!VmmRead(ctx->pSystemProcess, vaHandleTable, u.pb, 0x1000)) { return; } + if(ctxVmm->f32) { + for(i = 1; i < 512; i++) { + va = u.pdw[i << 1] & ~3; + if(!VMM_KADDR32(va)) { continue; } + pe = ctx->pHandleMap->pMap + ctx->iMap; + pe->vaObject = (va & ~7) + 0x18ULL; + pe->dwGrantedAccess = u.pdw[(i << 1) + 1] & 0x00ffffff; + pe->dwHandle = dwBaseHandleId + (i << 2); + pe->dwPID = ctx->pProcess->dwPID; + ctx->iMap++; + if(ctx->iMap == ctx->pHandleMap->cMap) { break; } } - if(result) { - ppMEMs[cMEMs]->qwA = pMap[i].AddrBase; - ppMAPs[cMEMs] = pMap + i; - cMEMs++; - if(cMEMs == 0x400) { break; } + } else { + for(i = 1; i < 256; i++) { + va = u.pqw[i << 1]; + if(ctxVmm->kernel.dwVersionBuild >= 9600) { // Win8.1 or later + va = 0xffff0000'00000000 | (va >> 16); + } else if(ctxVmm->kernel.dwVersionBuild >= 9200) { // Win8 or later + va = 0xfffff800'00000000 | (va >> 19); + } + if(!VMM_KADDR64(va)) { continue; } + pe = ctx->pHandleMap->pMap + ctx->iMap; + pe->vaObject = (va & ~7) + 0x30; + pe->dwGrantedAccess = (DWORD)u.pqw[(i << 1) + 1] & 0x00ffffff; + pe->dwHandle = dwBaseHandleId + (i << 2); + pe->dwPID = ctx->pProcess->dwPID; + ctx->iMap++; + if(ctx->iMap == ctx->pHandleMap->cMap) { break; } } } - // 3: read all MZ header candicates previously selected and try load name from them (after read is successful) - if(cMEMs) { - VmmReadScatterVirtual(pProcess, ppMEMs, cMEMs, 0); - for(i = 0; i < cMEMs; i++) { - if(ppMEMs[i]->cb == 0x1000) { - result = PE_GetModuleNameEx(pProcess, ppMAPs[i]->AddrBase, TRUE, ppMEMs[i]->pb, szBuffer, _countof(szBuffer), &cbImageSize); - if(result && (cbImageSize < 0x01000000)) { - VmmMemMapTag(pProcess, ppMAPs[i]->AddrBase, ppMAPs[i]->AddrBase + cbImageSize, szBuffer, NULL, FALSE, FALSE); +} + +typedef struct tdVMMWIN_OBJECT_HEADER32 { + DWORD PointerCount; + DWORD HandleCount; + DWORD Lock; + BYTE TypeIndex; + BYTE TraceFlags; + BYTE _Flags[2]; + DWORD ObjectCreateInfo; + DWORD SecurityDescriptor; +} VMMWIN_OBJECT_HEADER32, *PVMMWIN_OBJECT_HEADER32; + +typedef struct tdVMMWIN_OBJECT_HEADER64 { + QWORD PointerCount; + QWORD HandleCount; + QWORD Lock; + BYTE TypeIndex; + BYTE TraceFlags; + BYTE _Flags[2]; + DWORD _Reserved; + QWORD ObjectCreateInfo; + QWORD SecurityDescriptor; +} VMMWIN_OBJECT_HEADER64, *PVMMWIN_OBJECT_HEADER64; + +DWORD VmmWinHandle_InitializeText_GetPoolHeader2(DWORD dwPoolHeaderCandidate) +{ + CHAR i, ch; + for(i = 0; i < 32; i = i + 8) { + ch = (CHAR)(dwPoolHeaderCandidate >> i); + if(ch >= 'a' && ch <= 'z') { continue; } + if(ch >= 'A' && ch <= 'Z') { continue; } + if(ch == ' ') { continue; } + if((i == 24) && (ctxVmm->kernel.dwVersionBuild <= 9601)) { + return 0x20000000 | (dwPoolHeaderCandidate & 0x00ffffff); // last char usually A-Z in win7 + } + return 0; + } + return dwPoolHeaderCandidate; +} + +DWORD VmmWinHandle_InitializeText_GetPoolHeader32(_In_reads_(0x40) PBYTE pb, _Out_ PDWORD pdwOffset) +{ + DWORD dwPoolHeader, i = 0x38; + while(i) { + i -= 0x08; + if((dwPoolHeader = VmmWinHandle_InitializeText_GetPoolHeader2(*(PDWORD)(pb + i + 4)))) { + *pdwOffset = i + 4; + return dwPoolHeader; + } + } + *pdwOffset = 0; + return 0; +} + +DWORD VmmWinHandle_InitializeText_GetPoolHeader64(_In_reads_(0x60) PBYTE pb, _Out_ PDWORD pdwOffset) +{ + DWORD dwPoolHeader, i = 0x60; + while(i) { + i -= 0x10; + if((dwPoolHeader = VmmWinHandle_InitializeText_GetPoolHeader2(*(PDWORD)(pb + i + 4)))) { + *pdwOffset = i + 4; + return dwPoolHeader; + } + } + *pdwOffset = 0; + return 0; +} + +VOID VmmWinHandle_InitializeText_DoWork(_In_ PVMM_PROCESS pSystemProcess, _In_ PVMMOB_MAP_HANDLE pHandleMap) +{ + BOOL f, fThreadingEnabled; + PBYTE pbMultiText = NULL; + DWORD i, cbRead, oPoolHdr, cbObjectRead, cbMultiText = 4, ocbMultiText = 2; + POB_VSET psObPrefetch; + PUNICODE_STRING32 pus32; + PUNICODE_STRING64 pus64; + PVMM_MAP_HANDLEENTRY pe; + PVMM_PROCESS pObProcessHnd; + union { + BYTE pb[0x1000]; + struct { + BYTE _Filler1[0x60 - 0x18 - 0x0c]; + UNICODE_STRING32 String; + DWORD _Filler2; + VMMWIN_OBJECT_HEADER32 Header; + BYTE pb[]; + } O32; + struct { + BYTE _Filler1[0x90 - 0x30 - 0x18]; + UNICODE_STRING64 String; + QWORD _Filler2; + VMMWIN_OBJECT_HEADER64 Header; + BYTE pb[]; + } O64; + } u; + fThreadingEnabled = (ctxVmm->kernel.ThreadInfo.oCid > 0); + cbObjectRead = max(ctxVmm->kernel.OffsetEPROCESS.PID + 0x08, ctxVmm->kernel.ThreadInfo.oCid + 0x20); + cbObjectRead = 0x90 + max(0x70, cbObjectRead); + // 1: cache prefetch object data + if(!(psObPrefetch = ObVSet_New())) { goto fail; } + for(i = 0; i < pHandleMap->cMap; i++) { + ObVSet_Push(psObPrefetch, pHandleMap->pMap[i].vaObject - 0x90); + } + VmmCachePrefetchPages3(pSystemProcess, psObPrefetch, cbObjectRead, 0); + ObVSet_Clear(psObPrefetch); + // 2: read and interpret object data + if(ctxVmm->f32) { + for(i = 0; i < pHandleMap->cMap; i++) { + pe = pHandleMap->pMap + i; + VmmReadEx(pSystemProcess, pe->vaObject - 0x60, u.pb, cbObjectRead, &cbRead, VMM_FLAG_ZEROPAD_ON_FAIL | VMM_FLAG_FORCECACHE_READ); + if(cbRead < 0x60) { continue; } + // fetch and validate type index + pe->iType = VmmWin_ObjectTypeGetIndexFromEncoded(pe->vaObject - 0x18, u.O32.Header.TypeIndex); + if(!pe->iType || (ctxVmm->ObjectTypeTable.fInitialized && (pe->iType > ctxVmm->ObjectTypeTable.c))) { + continue; + } + // fetch pool tag (if found) + pe->dwPoolTag = VmmWinHandle_InitializeText_GetPoolHeader32(u.pb, &oPoolHdr); + // fetch remaining object header values + pe->qwHandleCount = u.O32.Header.HandleCount; + pe->qwPointerCount = u.O32.Header.PointerCount; + pe->vaObjectCreateInfo = u.O32.Header.ObjectCreateInfo; + pe->vaSecurityDescriptor = u.O32.Header.SecurityDescriptor; + // fetch text description length and address (if possible) + if(pe->dwPoolTag) { + pus32 = NULL; + if((pe->dwPoolTag & 0x00ffffff) == 'orP') { + pe->_Reserved1 = *(PDWORD)(u.O32.pb + ctxVmm->kernel.OffsetEPROCESS.PID); + cbMultiText += 31 * sizeof(WCHAR) + 2; + } else if(((pe->dwPoolTag & 0x00ffffff) == 'rhT') && fThreadingEnabled) { + if(ctxVmm->kernel.ThreadInfo.oCid && *(PDWORD)(u.O32.pb + ctxVmm->kernel.ThreadInfo.oCid + 4)) { + pe->_Reserved1 = *(PDWORD)(u.O32.pb + ctxVmm->kernel.ThreadInfo.oCid + 4); + cbMultiText += 11 * sizeof(WCHAR) + 2; + } + } else if((pe->dwPoolTag & 0x00ffffff) == 'liF') { + pus32 = (PUNICODE_STRING32)(u.O32.pb + 0x030); + } else if(pe->dwPoolTag && (oPoolHdr <= 0x34)) { + pus32 = &u.O32.String; + } + f = pus32 && (pus32->Length > 2) && + !(pus32->Length & 1) && (pus32->Length < (2 * MAX_PATH)) && (pus32->Length <= pus32->MaximumLength) && + VMM_KADDR32(pus32->Buffer); + if(f) { + cbMultiText += pus32->Length + 2; + pe->_Reserved1 = pus32->Length; + pe->_Reserved2 = pus32->Buffer; + ObVSet_Push(psObPrefetch, pus32->Buffer); + } + } + } + } else { + for(i = 0; i < pHandleMap->cMap; i++) { + pe = pHandleMap->pMap + i; + max(0x70, ctxVmm->kernel.ThreadInfo.oCid + 0x10); + VmmReadEx(pSystemProcess, pe->vaObject - 0x90, u.pb, cbObjectRead, &cbRead, VMM_FLAG_ZEROPAD_ON_FAIL | VMM_FLAG_FORCECACHE_READ); + if(cbRead < 0x90) { continue; } + // fetch and validate type index + pe->iType = VmmWin_ObjectTypeGetIndexFromEncoded(pe->vaObject - 0x30, u.O64.Header.TypeIndex); + if(!pe->iType || (ctxVmm->ObjectTypeTable.fInitialized && (pe->iType > ctxVmm->ObjectTypeTable.c))) { + continue; + } + // fetch pool tag (if found) + pe->dwPoolTag = VmmWinHandle_InitializeText_GetPoolHeader64(u.pb, &oPoolHdr); + // fetch remaining object header values + pe->qwHandleCount = u.O64.Header.HandleCount; + pe->qwPointerCount = u.O64.Header.PointerCount; + pe->vaObjectCreateInfo = u.O64.Header.ObjectCreateInfo; + pe->vaSecurityDescriptor = u.O64.Header.SecurityDescriptor; + // fetch text description length and address (if possible) + if(pe->dwPoolTag) { + pus64 = NULL; + if((pe->dwPoolTag & 0x00ffffff) == 'orP') { + pe->_Reserved1 = *(PDWORD)(u.O64.pb + ctxVmm->kernel.OffsetEPROCESS.PID); + cbMultiText += 31 * sizeof(WCHAR) + 2; + } else if(((pe->dwPoolTag & 0x00ffffff) == 'rhT') && fThreadingEnabled) { + if(ctxVmm->kernel.ThreadInfo.oCid && *(PDWORD)(u.O64.pb + ctxVmm->kernel.ThreadInfo.oCid + 8)) { + pe->_Reserved1 = *(PDWORD)(u.O64.pb + ctxVmm->kernel.ThreadInfo.oCid + 8); + cbMultiText += 11 * sizeof(WCHAR) + 2; + } + } else if((pe->dwPoolTag & 0x00ffffff) == 'liF') { + pus64 = (PUNICODE_STRING64)(u.O64.pb + 0x058); + } else if(pe->dwPoolTag && (oPoolHdr <= 0x38)) { + pus64 = &u.O64.String; + } + f = pus64 && (pus64->Length > 2) && + !(pus64->Length & 1) && (pus64->Length < (2 * MAX_PATH)) && (pus64->Length <= pus64->MaximumLength) && + VMM_KADDR64(pus64->Buffer); + if(f) { + cbMultiText += pus64->Length + 2; + pe->_Reserved1 = pus64->Length; + pe->_Reserved2 = pus64->Buffer; + ObVSet_Push(psObPrefetch, pus64->Buffer); } } } } - LocalFree(ppMEMs); - Ob_DECREF(pObMemMap); + // create and fill text descriptions + pHandleMap->wszMultiText = (LPWSTR)pbMultiText = LocalAlloc(LMEM_ZEROINIT, cbMultiText); + if(!pHandleMap->wszMultiText) { goto fail; } + pHandleMap->cbMultiText = cbMultiText; + VmmCachePrefetchPages3(pSystemProcess, psObPrefetch, MAX_PATH * 2, 0); + for(i = 0; i < pHandleMap->cMap; i++) { + pe = pHandleMap->pMap + i; + if((pe->dwPoolTag & 0x00ffffff) == 'orP') { // PROCESS + if((pe->_Reserved1 < 99999) && (pObProcessHnd = VmmProcessGet(pe->_Reserved1))) { + pe->cwszText = swprintf_s((LPWSTR)(pbMultiText + ocbMultiText), 32, L"PID %i - %S", pObProcessHnd->dwPID, pObProcessHnd->szName); + pe->wszText = (LPWSTR)(pbMultiText + ocbMultiText); + ocbMultiText += 31 * sizeof(WCHAR) + 2; + Ob_DECREF_NULL(&pObProcessHnd); + } else { + pe->wszText = (LPWSTR)pbMultiText; + } + } else if((pe->dwPoolTag & 0x00ffffff) == 'rhT') { // THREAD + if(pe->_Reserved1 && (pe->_Reserved1 < 99999)) { + pe->cwszText = swprintf_s((LPWSTR)(pbMultiText + ocbMultiText), 12, L"TID %i", pe->_Reserved1); + pe->wszText = (LPWSTR)(pbMultiText + ocbMultiText); + ocbMultiText += 11 * sizeof(WCHAR) + 2; + } else { + pe->wszText = (LPWSTR)pbMultiText; + } + } else if(pe->_Reserved2) { + if(VmmRead2(pSystemProcess, pe->_Reserved2, pbMultiText + ocbMultiText, pe->_Reserved1, VMM_FLAG_FORCECACHE_READ)) { + pe->cwszText = pe->_Reserved1 >> 1; + pe->wszText = (LPWSTR)(pbMultiText + ocbMultiText); + ocbMultiText += pe->_Reserved1 + 2; + } + } else { + pe->wszText = (LPWSTR)pbMultiText; + } + } + // fall-through +fail: + Ob_DECREF(psObPrefetch); +} + +VOID VmmWinHandle_InitializeCore_DoWork(_In_ PVMM_PROCESS pSystemProcess, _In_ PVMM_PROCESS pProcess) +{ + BOOL fResult = FALSE; + BOOL f32 = ctxVmm->f32; + BYTE pb[0x20], iLevel; + WORD oTableCode; + DWORD i, cHandles, iHandleMap = 0; + QWORD vaHandleTable = 0, vaTableCode = 0; + VMMWIN_INITIALIZE_HANDLE_CONTEXT ctx = { 0 }; + PVMMOB_MAP_HANDLE pObHandleMap = NULL; + ctx.pSystemProcess = pSystemProcess; + ctx.pProcess = pProcess; + vaHandleTable = VMM_PTR_OFFSET(f32, pProcess->win.EPROCESS.pb, ctxVmm->kernel.OffsetEPROCESS.ObjectTable); + if(!VMM_KADDR(vaHandleTable) || !VmmRead(pSystemProcess, vaHandleTable - 0x10, pb, 0x20)) { return; } + if(!VMM_POOLTAG_PREPENDED(pb, 0x10, 'Obtb') && !VMM_KADDR_PAGE(vaHandleTable)) { return; } + oTableCode = (ctxVmm->kernel.dwVersionBuild < 9200) ? 0 : 8; // WinXP::Win7 -> 0, otherwise 8. + vaTableCode = VMM_PTR_OFFSET(f32, pb + 0x10, oTableCode) & ~7; + iLevel = VMM_PTR_OFFSET(f32, pb + 0x10, oTableCode) & 7; + if((iLevel > 2) || !VMM_KADDR_PAGE(vaTableCode)) { return; } + ctx.cTablesMax = f32 ? 1024 : 512; + ctx.cTablesMax = iLevel ? ((iLevel == 1) ? (ctx.cTablesMax * ctx.cTablesMax) : ctx.cTablesMax) : 1; + if(!(ctx.pvaTables = LocalAlloc(0, ctx.cTablesMax * sizeof(QWORD)))) { return; } + if(iLevel) { + VmmWinHandle_InitializeCore_SpiderTables(&ctx, vaTableCode, (iLevel == 2)); + } else { + ctx.cTables = 1; + ctx.pvaTables[0] = vaTableCode; + } + // count handles and allocate map + if(!(cHandles = VmmWinHandle_InitializeCore_CountHandles(&ctx))) { goto fail; } + cHandles = min(cHandles, 256 * 1024); + ctx.pHandleMap = pObHandleMap = Ob_Alloc(OB_TAG_MAP_HANDLE, LMEM_ZEROINIT, sizeof(VMMOB_MAP_HANDLE) + cHandles * sizeof(VMM_MAP_HANDLEENTRY), VmmWinHandle_CloseObCallback, NULL); + if(!pObHandleMap) { goto fail; } + pObHandleMap->cMap = cHandles; + // walk handle tables to fill map with core handle information + for(i = 0; i < ctx.cTables; i++) { + VmmWinHandle_InitializeCore_ReadHandleTable(&ctx, ctx.pvaTables[i], i * (f32 ? 2048 : 1024)); + } + pProcess->Map.pObHandle = Ob_INCREF(pObHandleMap); +fail: + LocalFree(ctx.pvaTables); + Ob_DECREF(pObHandleMap); +} + +_Success_(return) +BOOL VmmWinHandle_InitializeCore(_In_ PVMM_PROCESS pProcess) +{ + PVMM_PROCESS pObSystemProcess; + if(pProcess->Map.pObHandle) { return TRUE; } + EnterCriticalSection(&pProcess->LockUpdate); + if(!pProcess->Map.pObHandle && (pObSystemProcess = VmmProcessGet(4))) { + VmmWinHandle_InitializeCore_DoWork(pObSystemProcess, pProcess); + if(!pProcess->Map.pObHandle) { + pProcess->Map.pObHandle = Ob_Alloc(OB_TAG_MAP_HANDLE, LMEM_ZEROINIT, sizeof(VMMOB_MAP_HANDLE), VmmWinHandle_CloseObCallback, NULL); + } + Ob_DECREF(pObSystemProcess); + } + LeaveCriticalSection(&pProcess->LockUpdate); + return pProcess->Map.pObHandle ? TRUE : FALSE; +} + +_Success_(return) +BOOL VmmWinHandle_InitializeText(_In_ PVMM_PROCESS pProcess) +{ + PVMM_PROCESS pObSystemProcess; + if(pProcess->Map.pObHandle->wszMultiText) { return TRUE; } + EnterCriticalSection(&pProcess->Map.LockUpdateExtendedInfo); + if(!pProcess->Map.pObHandle->wszMultiText && (pObSystemProcess = VmmProcessGet(4))) { + VmmWinHandle_InitializeText_DoWork(pObSystemProcess, pProcess->Map.pObHandle); + Ob_DECREF(pObSystemProcess); + } + LeaveCriticalSection(&pProcess->Map.LockUpdateExtendedInfo); + return pProcess->Map.pObHandle->wszMultiText ? TRUE : FALSE; +} + +/* +* Initialize Handles for a specific process. Extended information text may take +* extra time to initialize. +* -- pProcess +* -- fExtendedText = also fetch extended info such as handle paths/names. +* -- return +*/ +_Success_(return) +BOOL VmmWinHandle_Initialize(_In_ PVMM_PROCESS pProcess, _In_ BOOL fExtendedText) +{ + if(pProcess->Map.pObHandle && (!fExtendedText || pProcess->Map.pObHandle->wszMultiText)) { return TRUE; } + VmmTlbSpider(pProcess); + return VmmWinHandle_InitializeCore(pProcess) && (!fExtendedText || VmmWinHandle_InitializeText(pProcess)); } // ---------------------------------------------------------------------------- // WINDOWS EPROCESS WALKING FUNCTIONALITY FOR 64/32 BIT BELOW: // ---------------------------------------------------------------------------- -#define VMMPROC_EPROCESS_MAX_SIZE 0x500 -#define VMMWIN_EPROCESS_PREFETCH_MAX 0x200 +#define VMMPROC_EPROCESS64_MAX_SIZE 0x680 +#define VMMPROC_EPROCESS32_MAX_SIZE 0x480 VOID VmmWin_OffsetLocatorEPROCESS_Print() { PVMM_WIN_EPROCESS_OFFSET po = &ctxVmm->kernel.OffsetEPROCESS; vmmprintf_fn("OK: %s \n" \ - " PID: %03x PPID: %03x STAT: %03x DTB: %03x DTBU: %03x NAME: %03x PEB: %03x\n" \ - " FLnk: %03x BLnk: %03x oMax: %03x SeAu: %03x \n", + " PID: %03x PPID: %03x STAT: %03x DTB: %03x DTBU: %03x NAME: %03x PEB: %03x\n" \ + " FLnk: %03x BLnk: %03x oMax: %03x SeAu: %03x VadR: %03x ObjT: %03x WoW: %03x\n", po->fValid ? "TRUE" : "FALSE", po->PID, po->PPID, po->State, po->DTB, po->DTB_User, po->Name, po->PEB, - po->FLink, po->BLink, po->cbMaxOffset, po->SeAuditProcessCreationInfo + po->FLink, po->BLink, po->cbMaxOffset, po->SeAuditProcessCreationInfo, po->VadRoot, po->ObjectTable, po->Wow64Process ); } +VOID VmmWin_OffsetLocatorEPROCESS_SetMaxOffset() +{ + PVMM_WIN_EPROCESS_OFFSET po = &ctxVmm->kernel.OffsetEPROCESS; + WORD o; + o = max(po->opt.CreateTime, po->opt.ExitTime); + o = max(max(o, po->State), max(po->DTB, po->DTB_User)); + o = max(max(o, po->Name), max(po->PID, po->PPID)); + o = max(max(o, po->PEB), max(po->FLink, po->BLink)); + o = max(max(o, po->SeAuditProcessCreationInfo), max(po->VadRoot, po->ObjectTable)); + po->cbMaxOffset = o + 0x30; +} + /* * Very ugly hack that tries to locate some offsets required withn the EPROCESS struct. */ VOID VmmWin_OffsetLocatorEPROCESS64(_In_ PVMM_PROCESS pSystemProcess) { + PVMM_WIN_EPROCESS_OFFSET po = &ctxVmm->kernel.OffsetEPROCESS; BOOL f; WORD i, j, cLoopProtect; - QWORD va1, vaPEB, paPEB; - BYTE pbSYSTEM[VMMPROC_EPROCESS_MAX_SIZE], pbSMSS[VMMPROC_EPROCESS_MAX_SIZE], pb1[VMMPROC_EPROCESS_MAX_SIZE], pbPage[0x1000]; + QWORD va1, vaPEB, paPEB, vaP, oP; + BYTE pbSYSTEM[VMMPROC_EPROCESS64_MAX_SIZE], pbSMSS[VMMPROC_EPROCESS64_MAX_SIZE], pb1[VMMPROC_EPROCESS64_MAX_SIZE], pbPage[0x1000]; BYTE pbZero[0x800]; QWORD paMax, paDTB_0, paDTB_1; - PVMM_WIN_EPROCESS_OFFSET poEPROCESS = &ctxVmm->kernel.OffsetEPROCESS; - ZeroMemory(poEPROCESS, sizeof(VMM_WIN_EPROCESS_OFFSET)); - if(!VmmRead(pSystemProcess, pSystemProcess->win.EPROCESS.va, pbSYSTEM, VMMPROC_EPROCESS_MAX_SIZE)) { return; } + POB_VSET psObOff = NULL, psObVa = NULL; + ZeroMemory(po, sizeof(VMM_WIN_EPROCESS_OFFSET)); + if(!VmmRead(pSystemProcess, pSystemProcess->win.EPROCESS.va, pbSYSTEM, VMMPROC_EPROCESS64_MAX_SIZE)) { return; } if(ctxMain->cfg.fVerboseExtra) { vmmprintf_fn("SYSTEM DTB: %016llx EPROCESS: %016llx\n", pSystemProcess->paDTB, pSystemProcess->win.EPROCESS.va); - Util_PrintHexAscii(pbSYSTEM, VMMPROC_EPROCESS_MAX_SIZE, 0); + Util_PrintHexAscii(pbSYSTEM, VMMPROC_EPROCESS64_MAX_SIZE, 0); } // find offset State (static for now) if(*(PDWORD)(pbSYSTEM + 0x04)) { return; } - poEPROCESS->State = 0x04; + po->State = 0x04; // find offset PML4 (static for now) if(pSystemProcess->paDTB != (0xfffffffffffff000 & *(PQWORD)(pbSYSTEM + 0x28))) { return; } - poEPROCESS->DTB = 0x28; + po->DTB = 0x28; // find offset for Name - for(i = 0, f = FALSE; i < VMMPROC_EPROCESS_MAX_SIZE - 8; i += 8) { + for(i = 0, f = FALSE; i < VMMPROC_EPROCESS64_MAX_SIZE - 8; i += 8) { if(*(PQWORD)(pbSYSTEM + i) == 0x00006D6574737953) { - poEPROCESS->Name = i; + po->Name = i; f = TRUE; break; } } if(!f) { return; } // find offset for PID, FLink, BLink (assumed to be following eachother) - for(i = 0, f = FALSE; i < VMMPROC_EPROCESS_MAX_SIZE - 8; i += 8) { + for(i = 0, f = FALSE; i < VMMPROC_EPROCESS64_MAX_SIZE - 8; i += 8) { if(*(PQWORD)(pbSYSTEM + i) == 4) { // PID = correct, this is a candidate if(0xffff000000000000 != (0xffff000000000003 & *(PQWORD)(pbSYSTEM + i + 8))) { continue; } // FLink not valid kernel pointer va1 = *(PQWORD)(pbSYSTEM + i + 8) - i - 8; - f = VmmRead(pSystemProcess, va1, pb1, VMMPROC_EPROCESS_MAX_SIZE); + f = VmmRead(pSystemProcess, va1, pb1, VMMPROC_EPROCESS64_MAX_SIZE); if(!f) { continue; } f = FALSE; - if((*(PQWORD)(pb1 + poEPROCESS->Name) != 0x6578652e73736d73) && // smss.exe - (*(PQWORD)(pb1 + poEPROCESS->Name) != 0x7972747369676552) && // Registry - (*(PQWORD)(pb1 + poEPROCESS->Name) != 0x5320657275636553)) // Secure System + if((*(PQWORD)(pb1 + po->Name) != 0x6578652e73736d73) && // smss.exe + (*(PQWORD)(pb1 + po->Name) != 0x7972747369676552) && // Registry + (*(PQWORD)(pb1 + po->Name) != 0x5320657275636553)) // Secure System { continue; } if((*(PQWORD)(pb1 + i + 16) - i - 8) != pSystemProcess->win.EPROCESS.va) { continue; } - poEPROCESS->PID = i; - poEPROCESS->FLink = i + 8; - poEPROCESS->BLink = i + 16; + po->PID = i; + po->FLink = i + 8; + po->BLink = i + 16; f = TRUE; break; } @@ -1078,29 +1964,29 @@ VOID VmmWin_OffsetLocatorEPROCESS64(_In_ PVMM_PROCESS pSystemProcess) // find and read smss.exe { cLoopProtect = 0; - memcpy(pbSMSS, pbSYSTEM, VMMPROC_EPROCESS_MAX_SIZE); + memcpy(pbSMSS, pbSYSTEM, VMMPROC_EPROCESS64_MAX_SIZE); while(++cLoopProtect < 8) { - va1 = *(PQWORD)(pbSMSS + poEPROCESS->FLink) - poEPROCESS->FLink; - f = VmmRead(pSystemProcess, va1, pbSMSS, VMMPROC_EPROCESS_MAX_SIZE) && - (*(PQWORD)(pbSMSS + poEPROCESS->Name) == 0x6578652e73736d73); + va1 = *(PQWORD)(pbSMSS + po->FLink) - po->FLink; + f = VmmRead(pSystemProcess, va1, pbSMSS, VMMPROC_EPROCESS64_MAX_SIZE) && + (*(PQWORD)(pbSMSS + po->Name) == 0x6578652e73736d73); if(f) { break; } } if(!f) { return; } if(ctxMain->cfg.fVerboseExtra) { vmmprintf_fn("EPROCESS smss.exe BELOW:\n"); - Util_PrintHexAscii(pbSMSS, VMMPROC_EPROCESS_MAX_SIZE, 0); + Util_PrintHexAscii(pbSMSS, VMMPROC_EPROCESS64_MAX_SIZE, 0); } } // find offset for ParentPid (_EPROCESS!InheritedFromUniqueProcessId) // (parent pid is assumed to be located between BLink and Name { - for(i = poEPROCESS->BLink; i < poEPROCESS->Name; i += 8) { + for(i = po->BLink; i < po->Name; i += 8) { if((*(PQWORD)(pbSYSTEM + i) == 0) && (*(PQWORD)(pbSMSS + i) == 4)) { - poEPROCESS->PPID = i; + po->PPID = i; break; } } - if(!poEPROCESS->PPID) { return; } + if(!po->PPID) { return; } } // find offset for PEB (in EPROCESS) by comparing SYSTEM and SMSS [or other process on fail - max 4 tries] { @@ -1110,34 +1996,81 @@ VOID VmmWin_OffsetLocatorEPROCESS64(_In_ PVMM_PROCESS pSystemProcess) vaPEB = *(PQWORD)(pbSMSS + i); if(!vaPEB || (vaPEB & 0xffff800000000fff)) { continue; } // Verify potential PEB - if(!VmmVirt2PhysEx(*(PQWORD)(pbSMSS + poEPROCESS->DTB), TRUE, vaPEB, &paPEB)) { continue; } + if(!VmmVirt2PhysEx(*(PQWORD)(pbSMSS + po->DTB), TRUE, vaPEB, &paPEB)) { continue; } if(!VmmReadPage(NULL, paPEB, pbPage)) { continue; } if(*(PWORD)pbPage == 0x5a4d) { continue; } // MZ header -> likely entry point or something not PEB ... - poEPROCESS->PEB = i; + po->PEB = i; f = TRUE; break; } if(f) { break; } // failed locating PEB (paging?) -> try next process in EPROCESS list. - va1 = *(PQWORD)(pbSMSS + poEPROCESS->FLink) - poEPROCESS->FLink; - if(!VmmRead(pSystemProcess, va1, pbSMSS, VMMPROC_EPROCESS_MAX_SIZE)) { return; } + va1 = *(PQWORD)(pbSMSS + po->FLink) - po->FLink; + if(!VmmRead(pSystemProcess, va1, pbSMSS, VMMPROC_EPROCESS64_MAX_SIZE)) { return; } } if(!f) { return; } } - // find offset for SeAuditProcessCreationInfo by looking at SMSS. offset is - // located between PEB+0x058 and PEB+0x070 as observed so far. Look at some - // extra offsets just in case for the future. + // Wow64Process offset - "static" rule. + { + if(po->Name < po->PEB) { + po->f64VistaOr7 = TRUE; + po->Wow64Process = po->Name + 0x40; // Vista, Win7 + } else { + po->Wow64Process = po->PEB + 0x30; // Win8, Win10 + } + } + // locate various offsets primarily by reading pointers and checking pool + // headers in an efficient way (minimize number of reads). { - for(i = 0x058 + poEPROCESS->PEB; i < 0x090 + poEPROCESS->PEB; i += 8) { - va1 = *(PQWORD)(pbSMSS + i); - f = ((va1 & 0xffff8000'00000007) == 0xffff8000'00000000) && - VmmRead(pSystemProcess, va1, pbPage, 0x20) && + if(!(psObVa = ObVSet_New())) { goto fail; } + if(!(psObOff = ObVSet_New())) { goto fail; } + // ObjectTable candidate pointers + for(i = po->Name - 0x0e0; i < po->Name - 0x020; i += 8) { + if(VMM_KADDR64_16(*(PQWORD)(pbSYSTEM + i))) { + ObVSet_Push(psObOff, (i << 16) | 1); + ObVSet_Push(psObVa, *(PQWORD)(pbSYSTEM + i) - 0x10); + } + } + // SeAuditProcessCreationInfo candidate pointers by looking at SMSS. + // Offset is located between PEB+0x058 and PEB+0x070 as observed so far. + // Look at some extra offsets just in case for the future. + for(i = 0x058 + po->PEB; i < 0x090 + po->PEB; i += 8) { + if(VMM_KADDR64_8(*(PQWORD)(pbSMSS + i))) { + ObVSet_Push(psObOff, (i << 16) | 2); + ObVSet_Push(psObVa, *(PQWORD)(pbSMSS + i)); + } + } + // prefetch result into cache + VmmCachePrefetchPages3(pSystemProcess, psObVa, 0x40, 0); + // interpret result + while(ObVSet_Size(psObVa)) { + oP = ObVSet_Pop(psObOff); + vaP = ObVSet_Pop(psObVa); + if(!VmmRead2(pSystemProcess, vaP, pbPage, 0x40, VMM_FLAG_FORCECACHE_READ)) { continue; } + // ObjectTable + f = (1 == (oP & 0xff)) && (*(PDWORD)(pbPage + 4) == 0x6274624f); // Pool Header: Obtb + if(f) { po->ObjectTable = (WORD)(oP >> 16); } + f = (1 == (oP & 0xff)) && VMM_KADDR64_PAGE(vaP + 0x10) && !*(PQWORD)(pbPage + 0x10 + 0x10) && VMM_KADDR64_8(*(PQWORD)(pbPage + 0x10 + 0x18)) && VMM_KADDR64_8(*(PQWORD)(pbPage + 0x10 + 0x20)); // page-align (no pool hdr) + if(f) { po->ObjectTable = (WORD)(oP >> 16); } + // SeAuditProcessCreationInfo + f = (2 == (oP & 0xff)) && (*(PQWORD)(pbPage + 0x10) == 0x007600650044005C) && (*(PQWORD)(pbPage + 0x18) == 0x005C006500630069) && // L"\Device\" (*(PWORD)(pbPage + 0x00) < MAX_PATH) && (*(PWORD)(pbPage + 0x00) < *(PWORD)(pbPage + 0x02)); // _UNICODE_STRING length + if(f) { po->SeAuditProcessCreationInfo = (WORD)(oP >> 16); } + } + // check validity + if(!po->ObjectTable) { goto fail; } + if(!po->SeAuditProcessCreationInfo) { goto fail; } + } + // find offset for VadRoot by searching for ExitStatus value assumed to be + // set to: 0x00000103 and existing prior to VadRoot by -12(VISTA)/-4(Win7+) + { + for(i = 0x140 + po->Name; i < 0x680; i += 8) { + f = VMM_KADDR64(*(PQWORD)(pbSYSTEM + i)) && ((*(PDWORD)(pbSYSTEM + i - 4) == 0x00000103) || (*(PDWORD)(pbSYSTEM + i - 12) == 0x00000103)); if(f) { break; } } if(!f) { return; } - poEPROCESS->SeAuditProcessCreationInfo = i; + po->VadRoot = i; } // find "optional" offset for user cr3/pml4 (post meltdown only) // System have an entry pointing to a shadow PML4 which has empty user part @@ -1145,7 +2078,7 @@ VOID VmmWin_OffsetLocatorEPROCESS64(_In_ PVMM_PROCESS pSystemProcess) { ZeroMemory(pbZero, 0x800); paMax = ctxMain->dev.paMax; - for(i = poEPROCESS->DTB + 8; i < VMMPROC_EPROCESS_MAX_SIZE - 8; i += 8) { + for(i = po->DTB + 8; i < VMMPROC_EPROCESS64_MAX_SIZE - 8; i += 8) { paDTB_0 = *(PQWORD)(pbSYSTEM + i); paDTB_1 = *(PQWORD)(pbSMSS + i); f = !(paDTB_1 & ~1) && @@ -1156,13 +2089,16 @@ VOID VmmWin_OffsetLocatorEPROCESS64(_In_ PVMM_PROCESS pSystemProcess) !memcmp(pbPage, pbZero, 0x800) && VmmTlbPageTableVerify(pbPage, (paDTB_0 & ~0xfff), TRUE); if(f) { - poEPROCESS->DTB_User = i; + po->DTB_User = i; break; } } } - poEPROCESS->cbMaxOffset = min(VMMPROC_EPROCESS_MAX_SIZE, 16 + max(max(max(poEPROCESS->State, poEPROCESS->PID), max(poEPROCESS->Name, poEPROCESS->FLink)), max(max(poEPROCESS->DTB_User, poEPROCESS->DTB), max(poEPROCESS->PEB, poEPROCESS->SeAuditProcessCreationInfo)))); - poEPROCESS->fValid = TRUE; + VmmWin_OffsetLocatorEPROCESS_SetMaxOffset(); + po->fValid = TRUE; +fail: + Ob_DECREF(psObVa); + Ob_DECREF(psObOff); } /* @@ -1185,18 +2121,18 @@ VOID VmmWin_EnumerateEPROCESS_PostProcessing(_In_ PVMM_PROCESS pSystemProcess) if(!(ptObNew = (PVMMOB_PROCESS_TABLE)ObContainer_GetOb(ptObCurrent->pObCNewPROC))) { goto fail; } // 1: Iterate to gather memory locations of "SeAuditProcessCreationInfo" / "kernel path" for new processes while((pObProcess = VmmProcessGetNextEx(ptObNew, pObProcess, VMM_FLAG_PROCESS_SHOW_TERMINATED))) { - if(!pObProcess->pObProcessPersistent->fIsPostProcessingComplete) { - ObVSet_Push_PageAlign(pObPrefetchAddr, pObProcess->win.vaSeAuditProcessCreationInfo, 540); + if(!pObProcess->pObPersistent->fIsPostProcessingComplete) { + ObVSet_Push_PageAlign(pObPrefetchAddr, VMM_EPROCESS_PTR(pObProcess, ctxVmm->kernel.OffsetEPROCESS.SeAuditProcessCreationInfo), 540); } } if(0 == ObVSet_Size(pObPrefetchAddr)) { goto fail; } - VmmCachePrefetchPages(pSystemProcess, pObPrefetchAddr); + VmmCachePrefetchPages(pSystemProcess, pObPrefetchAddr, 0); // 2: Fetch "kernel path" and set "long name" for new processes. while((pObProcess = VmmProcessGetNextEx(ptObNew, pObProcess, VMM_FLAG_PROCESS_SHOW_TERMINATED))) { - pProcPers = pObProcess->pObProcessPersistent; + pProcPers = pObProcess->pObPersistent; if(!pProcPers->fIsPostProcessingComplete) { pProcPers->fIsPostProcessingComplete = TRUE; - f = VmmRead_U2A(pSystemProcess, ctxVmm->f32, VMM_FLAG_FORCECACHE_READ, pObProcess->win.vaSeAuditProcessCreationInfo, szBuffer, MAX_PATH, &cch, NULL) && + f = VmmRead_U2A(pSystemProcess, ctxVmm->f32, VMM_FLAG_FORCECACHE_READ, VMM_EPROCESS_PTR(pObProcess, ctxVmm->kernel.OffsetEPROCESS.SeAuditProcessCreationInfo), szBuffer, MAX_PATH, &cch, NULL) && !memcmp(szBuffer, "\\Device\\", 8); sz = szBuffer; if(f && (cch > _countof(pProcPers->szPathKernel) - 1)) { @@ -1241,22 +2177,22 @@ typedef struct tdVMMWIN_ENUMERATE_EPROCESS_CONTEXT { VOID VmmWin_EnumEPROCESS64_Pre(_In_ PVMM_PROCESS pProcess, _In_opt_ PVMMWIN_ENUMERATE_EPROCESS_CONTEXT ctx, _In_ QWORD va, _In_ PBYTE pb, _In_ DWORD cb, _In_ QWORD vaFLink, _In_ QWORD vaBLink, _In_ POB_VSET pVSetAddress, _Inout_ PBOOL pfValidEntry, _Inout_ PBOOL pfValidFLink, _Inout_ PBOOL pfValidBLink) { - if(!ctx || ((va & 0xffff8000'0000000f) != 0xffff8000'00000000)) { return; } + if(!ctx || !VMM_KADDR64_16(va)) { return; } ObVSet_Push(ctx->pObVSetPrefetchDTB, *(PQWORD)(pb + ctxVmm->kernel.OffsetEPROCESS.DTB) & ~0xfff); - *pfValidFLink = ((vaFLink & 0xffff8000'00000007) == 0xffff8000'00000000); - *pfValidBLink = ((vaBLink & 0xffff8000'00000007) == 0xffff8000'00000000); + *pfValidFLink = VMM_KADDR64_8(vaFLink); + *pfValidBLink = VMM_KADDR64_8(vaBLink); *pfValidEntry = *pfValidFLink || *pfValidBLink; } -BOOL VmmWin_EnumEPROCESS64_Post(_In_ PVMM_PROCESS pSystemProcess, _In_opt_ PVMMWIN_ENUMERATE_EPROCESS_CONTEXT ctx, _In_ QWORD va, _In_ PBYTE pb, _In_ DWORD cb) +VOID VmmWin_EnumEPROCESS64_Post(_In_ PVMM_PROCESS pSystemProcess, _In_opt_ PVMMWIN_ENUMERATE_EPROCESS_CONTEXT ctx, _In_ QWORD va, _In_ PBYTE pb, _In_ DWORD cb) { PVMM_WIN_EPROCESS_OFFSET po = &ctxVmm->kernel.OffsetEPROCESS; - PQWORD pqwDTB, pqwDTB_User, pqwPEB; + PQWORD pqwDTB, pqwDTB_User, pqwPEB, pqwWow64Process; PDWORD pdwState, pdwPID, pdwPPID; LPSTR szName; BOOL fUser; PVMM_PROCESS pObProcess = NULL; - if(!ctx || ((va & 0xffff8000'0000000f) != 0xffff8000'00000000)) { return FALSE; } + if(!ctx || !VMM_KADDR64_16(va)) { return; } pdwState = (PDWORD)(pb + po->State); pdwPID = (PDWORD)(pb + po->PID); pdwPPID = (PDWORD)(pb + po->PPID); @@ -1264,9 +2200,10 @@ BOOL VmmWin_EnumEPROCESS64_Post(_In_ PVMM_PROCESS pSystemProcess, _In_opt_ PVMMW pqwDTB_User = (PQWORD)(pb + po->DTB_User); szName = (LPSTR)(pb + po->Name); pqwPEB = (PQWORD)(pb + po->PEB); - if(*pqwDTB & 0xffffff00'00000000) { return TRUE; } // NB! Fail if target system have more than 1TB of memory (unlikely) + pqwWow64Process = (PQWORD)(pb + po->Wow64Process); + if(*pqwDTB & 0xffffff00'00000000) { return; } // NB! Fail if target system have more than 1TB of memory (unlikely) if(ctx->pObVSetPrefetchDTB) { // prefetch any physical pages in ctx->pObSetPrefetchDTB on 1st run only - VmmCachePrefetchPages(NULL, ctx->pObVSetPrefetchDTB); + VmmCachePrefetchPages(NULL, ctx->pObVSetPrefetchDTB, 0); Ob_DECREF_NULL(&ctx->pObVSetPrefetchDTB); } if(*pqwDTB && *(PQWORD)szName) { @@ -1287,18 +2224,27 @@ BOOL VmmWin_EnumEPROCESS64_Post(_In_ PVMM_PROCESS pSystemProcess, _In_opt_ PVMMW if(!pObProcess) { vmmprintfv("VMM: WARNING: PID '%i' already exists.\n", *pdwPID); if(++ctx->cNewProcessCollision >= 8) { - return TRUE; + return; } } } if(pObProcess) { pObProcess->win.EPROCESS.va = va; - pObProcess->win.vaSeAuditProcessCreationInfo = *(PQWORD)(pb + po->SeAuditProcessCreationInfo); + // PEB if(*pqwPEB % PAGE_SIZE) { vmmprintfv("VMM: WARNING: Bad PEB alignment for PID: '%i' (0x%016llx).\n", *pdwPID, *pqwPEB); } else { pObProcess->win.vaPEB = *pqwPEB; } + // WoW64 and PEB32 + if(*pqwWow64Process) { + pObProcess->win.fWow64 = TRUE; + if(*pqwWow64Process & 0xffffffff'00000fff) { + pObProcess->win.vaPEB32 = (DWORD)*pqwPEB + (po->f64VistaOr7 ? -0x1000 : +0x1000); + } else { + pObProcess->win.vaPEB32 = (DWORD)*pqwWow64Process; + } + } } else { szName[14] = 0; // in case of bad string data ... } @@ -1312,7 +2258,6 @@ BOOL VmmWin_EnumEPROCESS64_Post(_In_ PVMM_PROCESS pSystemProcess, _In_opt_ PVMMW szName); Ob_DECREF_NULL(&pObProcess); ctx->cProc++; - return TRUE; } /* @@ -1347,7 +2292,8 @@ BOOL VmmWin_EnumEPROCESS64(_In_ PVMM_PROCESS pSystemProcess, _In_ BOOL fTotalRef pSystemProcess, FALSE, &ctx, - pSystemProcess->win.EPROCESS.va, + 1, + &pSystemProcess->win.EPROCESS.va, ctxVmm->kernel.OffsetEPROCESS.FLink, ctxVmm->kernel.OffsetEPROCESS.cbMaxOffset + 0x20, VmmWin_EnumEPROCESS64_Pre, @@ -1365,55 +2311,56 @@ BOOL VmmWin_EnumEPROCESS64(_In_ PVMM_PROCESS pSystemProcess, _In_ BOOL fTotalRef */ VOID VmmWin_OffsetLocatorEPROCESS32(_In_ PVMM_PROCESS pSystemProcess) { + PVMM_WIN_EPROCESS_OFFSET po = &ctxVmm->kernel.OffsetEPROCESS; BOOL f; WORD i, j, cLoopProtect; - DWORD va1, vaPEB; + DWORD va1, vaPEB, vaP, oP; QWORD paPEB; - BYTE pbSYSTEM[VMMPROC_EPROCESS_MAX_SIZE], pbSMSS[VMMPROC_EPROCESS_MAX_SIZE], pb1[VMMPROC_EPROCESS_MAX_SIZE], pbPage[0x1000]; - PVMM_WIN_EPROCESS_OFFSET poEPROCESS = &ctxVmm->kernel.OffsetEPROCESS; - ZeroMemory(poEPROCESS, sizeof(VMM_WIN_EPROCESS_OFFSET)); + BYTE pbSYSTEM[VMMPROC_EPROCESS32_MAX_SIZE], pbSMSS[VMMPROC_EPROCESS32_MAX_SIZE], pb1[VMMPROC_EPROCESS32_MAX_SIZE], pbPage[0x1000]; //BYTE pbZero[0x800] //QWORD paMax, paDTB_0, paDTB_1; - if(!VmmRead(pSystemProcess, pSystemProcess->win.EPROCESS.va, pbSYSTEM, VMMPROC_EPROCESS_MAX_SIZE)) { return; } + POB_VSET psObOff = NULL, psObVa = NULL; + ZeroMemory(po, sizeof(VMM_WIN_EPROCESS_OFFSET)); + if(!VmmRead(pSystemProcess, pSystemProcess->win.EPROCESS.va, pbSYSTEM, VMMPROC_EPROCESS32_MAX_SIZE)) { return; } if(ctxMain->cfg.fVerboseExtra) { vmmprintf_fn("SYSTEM DTB: %016llx EPROCESS: %016llx\n", pSystemProcess->paDTB, pSystemProcess->win.EPROCESS.va); - Util_PrintHexAscii(pbSYSTEM, VMMPROC_EPROCESS_MAX_SIZE, 0); + Util_PrintHexAscii(pbSYSTEM, VMMPROC_EPROCESS32_MAX_SIZE, 0); } // find offset State (static for now) if(*(PDWORD)(pbSYSTEM + 0x04)) { return; } - poEPROCESS->State = 0x04; + po->State = 0x04; // find offset PML4 (static for now) - poEPROCESS->DTB = 0x18; + po->DTB = 0x18; // find offset for Name - for(i = 0, f = FALSE; i < VMMPROC_EPROCESS_MAX_SIZE - 4; i += 4) { + for(i = 0, f = FALSE; i < VMMPROC_EPROCESS32_MAX_SIZE - 4; i += 4) { if(*(PQWORD)(pbSYSTEM + i) == 0x00006D6574737953) { - poEPROCESS->Name = i; + po->Name = i; f = TRUE; break; } } if(!f) { return; } // find offset for PID, FLink, BLink (assumed to be following eachother) - for(i = 0, f = FALSE; i < VMMPROC_EPROCESS_MAX_SIZE - 4; i += 4) { + for(i = 0, f = FALSE; i < VMMPROC_EPROCESS32_MAX_SIZE - 4; i += 4) { if(*(PDWORD)(pbSYSTEM + i) == 4) { // PID = correct, this is a candidate if(0x80000000 != (0x80000003 & *(PDWORD)(pbSYSTEM + i + 4))) { continue; } // FLink not valid kernel pointer va1 = *(PDWORD)(pbSYSTEM + i + 4) - i - 4; - f = VmmRead(pSystemProcess, va1, pb1, VMMPROC_EPROCESS_MAX_SIZE); + f = VmmRead(pSystemProcess, va1, pb1, VMMPROC_EPROCESS32_MAX_SIZE); if(!f) { continue; } f = FALSE; - if((*(PQWORD)(pb1 + poEPROCESS->Name) != 0x6578652e73736d73) && // smss.exe - (*(PQWORD)(pb1 + poEPROCESS->Name) != 0x7972747369676552) && // Registry - (*(PQWORD)(pb1 + poEPROCESS->Name) != 0x5320657275636553)) // Secure System + if((*(PQWORD)(pb1 + po->Name) != 0x6578652e73736d73) && // smss.exe + (*(PQWORD)(pb1 + po->Name) != 0x7972747369676552) && // Registry + (*(PQWORD)(pb1 + po->Name) != 0x5320657275636553)) // Secure System { continue; } if((*(PDWORD)(pb1 + i + 8) - i - 4) != pSystemProcess->win.EPROCESS.va) { continue; } - poEPROCESS->PID = i; - poEPROCESS->FLink = i + 4; - poEPROCESS->BLink = i + 8; + po->PID = i; + po->FLink = i + 4; + po->BLink = i + 8; f = TRUE; break; } @@ -1422,29 +2369,29 @@ VOID VmmWin_OffsetLocatorEPROCESS32(_In_ PVMM_PROCESS pSystemProcess) // find and read smss.exe { cLoopProtect = 0; - memcpy(pbSMSS, pbSYSTEM, VMMPROC_EPROCESS_MAX_SIZE); + memcpy(pbSMSS, pbSYSTEM, VMMPROC_EPROCESS32_MAX_SIZE); while(++cLoopProtect < 8) { - va1 = *(PDWORD)(pbSMSS + poEPROCESS->FLink) - poEPROCESS->FLink; - f = VmmRead(pSystemProcess, va1, pbSMSS, VMMPROC_EPROCESS_MAX_SIZE) && - (*(PQWORD)(pbSMSS + poEPROCESS->Name) == 0x6578652e73736d73); + va1 = *(PDWORD)(pbSMSS + po->FLink) - po->FLink; + f = VmmRead(pSystemProcess, va1, pbSMSS, VMMPROC_EPROCESS32_MAX_SIZE) && + (*(PQWORD)(pbSMSS + po->Name) == 0x6578652e73736d73); if(f) { break; } } if(!f) { return; } if(ctxMain->cfg.fVerboseExtra) { vmmprintf_fn("EPROCESS smss.exe BELOW:\n"); - Util_PrintHexAscii(pbSMSS, VMMPROC_EPROCESS_MAX_SIZE, 0); + Util_PrintHexAscii(pbSMSS, VMMPROC_EPROCESS32_MAX_SIZE, 0); } } // find offset for ParentPid (_EPROCESS!InheritedFromUniqueProcessId) // (parent pid is assumed to be located between BLink and Name { - for(i = poEPROCESS->BLink; i < poEPROCESS->Name; i += 4) { + for(i = po->BLink; i < po->Name; i += 4) { if((*(PDWORD)(pbSYSTEM + i) == 0) && (*(PDWORD)(pbSMSS + i) == 4)) { - poEPROCESS->PPID = i; + po->PPID = i; break; } } - if(!poEPROCESS->PPID) { return; } + if(!po->PPID) { return; } } // find offset for PEB (in EPROCESS) by comparing SYSTEM and SMSS [or other process on fail - max 4 tries] { @@ -1454,50 +2401,95 @@ VOID VmmWin_OffsetLocatorEPROCESS32(_In_ PVMM_PROCESS pSystemProcess) vaPEB = *(PDWORD)(pbSMSS + i); if(!vaPEB || (vaPEB & 0x80000fff)) { continue; } // Verify potential PEB - if(!VmmVirt2PhysEx(*(PDWORD)(pbSMSS + poEPROCESS->DTB), TRUE, vaPEB, &paPEB)) { continue; } + if(!VmmVirt2PhysEx(*(PDWORD)(pbSMSS + po->DTB), TRUE, vaPEB, &paPEB)) { continue; } if(!VmmReadPage(NULL, paPEB, pbPage)) { continue; } if(*(PWORD)pbPage == 0x5a4d) { continue; } // MZ header -> likely entry point or something not PEB ... - poEPROCESS->PEB = i; + po->PEB = i; f = TRUE; break; } if(f) { break; } // failed locating PEB (paging?) -> try next process in EPROCESS list. - va1 = *(PDWORD)(pbSMSS + poEPROCESS->FLink) - poEPROCESS->FLink; - if(!VmmRead(pSystemProcess, va1, pbSMSS, VMMPROC_EPROCESS_MAX_SIZE)) { return; } + va1 = *(PDWORD)(pbSMSS + po->FLink) - po->FLink; + if(!VmmRead(pSystemProcess, va1, pbSMSS, VMMPROC_EPROCESS32_MAX_SIZE)) { return; } } if(!f) { return; } } - // find offset for SeAuditProcessCreationInfo by looking at SMSS. offset is - // located between PEB+0x044 and PEB+0x04C as observed so far. Look at some - // extra offsets just in case for the future. + // locate various offsets primarily by reading pointers and checking pool + // headers in an efficient way (minimize number of reads). { - for(i = 0x044 + poEPROCESS->PEB; i < 0x058 + poEPROCESS->PEB; i += 4) { - va1 = *(PDWORD)(pbSMSS + i); - f = ((va1 & 0x80000003) == 0x80000000) && - VmmRead(pSystemProcess, va1, pbPage, 0x18) && + if(!(psObVa = ObVSet_New())) { goto fail; } + if(!(psObOff = ObVSet_New())) { goto fail; } + // ObjectTable candidate pointers + for(i = po->Name - 0x0c0; i < po->Name - 0x010; i += 4) { + if(VMM_KADDR32_8(*(PDWORD)(pbSYSTEM + i))) { + ObVSet_Push(psObOff, (i << 16) | 1); + ObVSet_Push(psObVa, *(PDWORD)(pbSYSTEM + i) - 0x10); + } + } + // SeAuditProcessCreationInfo candidate pointers by looking at SMSS. + // Offset is located between PEB+0x044 and PEB+0x04C as observed so far. + // Look at some extra offsets just in case for the future. + for(i = po->PEB + 0x044; i < po->PEB + 0x058; i += 4) { + if(VMM_KADDR32_4(*(PDWORD)(pbSMSS + i))) { + ObVSet_Push(psObOff, (i << 16) | 2); + ObVSet_Push(psObVa, *(PDWORD)(pbSMSS + i)); + } + } + // prefetch result into cache + VmmCachePrefetchPages3(pSystemProcess, psObVa, 0x40, 0); + // interpret result + while(ObVSet_Size(psObVa)) { + oP = (DWORD)ObVSet_Pop(psObOff); + vaP = (DWORD)ObVSet_Pop(psObVa); + if(!VmmRead2(pSystemProcess, vaP, pbPage, 0x40, VMM_FLAG_FORCECACHE_READ)) { continue; } + // ObjectTable + f = (1 == (oP & 0xff)) && (*(PDWORD)(pbPage + 12) == 0x6274624f); // Pool Header: Obtb + if(f) { po->ObjectTable = (WORD)(oP >> 16); } + f = (1 == (oP & 0xff)) && VMM_KADDR32_PAGE(vaP + 0x10) && !*(PDWORD)(pbPage + 0x10 + 0x0c) && VMM_KADDR32_4(*(PDWORD)(pbPage + 0x10 + 0x10)) && VMM_KADDR32_4(*(PDWORD)(pbPage + 0x10 + 0x14)); // page-align (no pool hdr) + if(f) { po->ObjectTable = (WORD)(oP >> 16); } + // SeAuditProcessCreationInfo + f = (2 == (oP & 0xff)) && (*(PQWORD)(pbPage + 0x08) == 0x007600650044005C) && (*(PQWORD)(pbPage + 0x10) == 0x005C006500630069) && // L"\Device\" (*(PWORD)(pbPage + 0x00) < MAX_PATH) && (*(PWORD)(pbPage + 0x00) < *(PWORD)(pbPage + 0x02)); // _UNICODE_STRING length + if(f) { po->SeAuditProcessCreationInfo = (WORD)(oP >> 16); } + } + // check validity + if(!po->ObjectTable) { goto fail; } + if(!po->SeAuditProcessCreationInfo) { goto fail; } + } + // find offset for VadRoot by searching for ExitStatus value assumed to be + // set to: 0x00000103 and existing prior to VadRoot by -12(VISTA)/-4(Win7+) + { + for(i = 0x0e0 + po->Name; i < 0x380; i += 4) { + f = VMM_KADDR32(*(PDWORD)(pbSYSTEM + i)) && ((*(PDWORD)(pbSYSTEM + i - 4) == 0x00000103) || (*(PDWORD)(pbSYSTEM + i - 12) == 0x00000103)); if(f) { break; } } + if(!f && (*(PDWORD)(pbSYSTEM + 0x11c) == *(PDWORD)(pbSYSTEM + +0x120))) { // WINXP + i = 0x11c; + f = TRUE; + } if(!f) { return; } - poEPROCESS->SeAuditProcessCreationInfo = i; + po->VadRoot = i; } // DTB_USER not searched for in 32-bit EPROCESS - poEPROCESS->cbMaxOffset = min(VMMPROC_EPROCESS_MAX_SIZE, 16 + max(max(max(poEPROCESS->State, poEPROCESS->PID), max(poEPROCESS->Name, poEPROCESS->FLink)), max(max(poEPROCESS->DTB_User, poEPROCESS->DTB), max(poEPROCESS->PEB, poEPROCESS->SeAuditProcessCreationInfo)))); - poEPROCESS->fValid = TRUE; + VmmWin_OffsetLocatorEPROCESS_SetMaxOffset(); + po->fValid = TRUE; +fail: + Ob_DECREF(psObVa); + Ob_DECREF(psObOff); } VOID VmmWin_EnumEPROCESS32_Pre(_In_ PVMM_PROCESS pProcess, _In_opt_ PVMMWIN_ENUMERATE_EPROCESS_CONTEXT ctx, _In_ QWORD va, _In_ PBYTE pb, _In_ DWORD cb, _In_ QWORD vaFLink, _In_ QWORD vaBLink, _In_ POB_VSET pVSetAddress, _Inout_ PBOOL pfValidEntry, _Inout_ PBOOL pfValidFLink, _Inout_ PBOOL pfValidBLink) { - if(!ctx || ((va & 0x80000007) != 0x80000000)) { return; } + if(!ctx || !VMM_KADDR32_8(va)) { return; } ObVSet_Push(ctx->pObVSetPrefetchDTB, *(PDWORD)(pb + ctxVmm->kernel.OffsetEPROCESS.DTB) & ~0xfff); - *pfValidFLink = ((vaFLink & 0x80000003) == 0x80000000); - *pfValidBLink = ((vaBLink & 0x80000003) == 0x80000000); + *pfValidFLink = VMM_KADDR32_4(vaFLink); + *pfValidBLink = VMM_KADDR32_4(vaBLink); *pfValidEntry = *pfValidFLink || *pfValidBLink; } -BOOL VmmWin_EnumEPROCESS32_Post(_In_ PVMM_PROCESS pSystemProcess, _In_opt_ PVMMWIN_ENUMERATE_EPROCESS_CONTEXT ctx, _In_ QWORD va, _In_ PBYTE pb, _In_ DWORD cb) +VOID VmmWin_EnumEPROCESS32_Post(_In_ PVMM_PROCESS pSystemProcess, _In_opt_ PVMMWIN_ENUMERATE_EPROCESS_CONTEXT ctx, _In_ QWORD va, _In_ PBYTE pb, _In_ DWORD cb) { PVMM_WIN_EPROCESS_OFFSET po = &ctxVmm->kernel.OffsetEPROCESS; PDWORD pdwDTB, pdwDTB_User, pdwPEB; @@ -1505,7 +2497,7 @@ BOOL VmmWin_EnumEPROCESS32_Post(_In_ PVMM_PROCESS pSystemProcess, _In_opt_ PVMMW LPSTR szName; BOOL fUser; PVMM_PROCESS pObProcess = NULL; - if(!ctx || ((va & 0x80000007) != 0x80000000)) { return FALSE; } + if(!ctx || !VMM_KADDR32_8(va)) { return; } pdwState = (PDWORD)(pb + po->State); pdwPID = (PDWORD)(pb + po->PID); pdwPPID = (PDWORD)(pb + po->PPID); @@ -1514,7 +2506,7 @@ BOOL VmmWin_EnumEPROCESS32_Post(_In_ PVMM_PROCESS pSystemProcess, _In_opt_ PVMMW szName = (LPSTR)(pb + po->Name); pdwPEB = (PDWORD)(pb + po->PEB); if(ctx->pObVSetPrefetchDTB) { // prefetch any physical pages in ctx->pObSetPrefetchDTB on 1st run only - VmmCachePrefetchPages(NULL, ctx->pObVSetPrefetchDTB); + VmmCachePrefetchPages(NULL, ctx->pObVSetPrefetchDTB, 0); Ob_DECREF_NULL(&ctx->pObVSetPrefetchDTB); } if(*pdwDTB && *(PQWORD)szName) { @@ -1535,17 +2527,18 @@ BOOL VmmWin_EnumEPROCESS32_Post(_In_ PVMM_PROCESS pSystemProcess, _In_opt_ PVMMW if(!pObProcess) { vmmprintfv("VMM: WARNING: PID '%i' already exists.\n", *pdwPID); if(++ctx->cNewProcessCollision >= 8) { - return TRUE; + return; } } } if(pObProcess) { pObProcess->win.EPROCESS.va = (DWORD)va; - pObProcess->win.vaSeAuditProcessCreationInfo = *(PDWORD)(pb + po->SeAuditProcessCreationInfo); + // PEB if(*pdwPEB % PAGE_SIZE) { vmmprintfv("VMM: WARNING: Bad PEB alignment for PID: '%i' (0x%08x).\n", *pdwPID, *pdwPEB); } else { pObProcess->win.vaPEB = *pdwPEB; + pObProcess->win.vaPEB32 = *pdwPEB; } } else { szName[14] = 0; // in case of bad string data ... @@ -1560,7 +2553,6 @@ BOOL VmmWin_EnumEPROCESS32_Post(_In_ PVMM_PROCESS pSystemProcess, _In_opt_ PVMMW szName); Ob_DECREF_NULL(&pObProcess); ctx->cProc++; - return TRUE; } BOOL VmmWin_EnumEPROCESS32(_In_ PVMM_PROCESS pSystemProcess, _In_ BOOL fTotalRefresh) @@ -1588,7 +2580,8 @@ BOOL VmmWin_EnumEPROCESS32(_In_ PVMM_PROCESS pSystemProcess, _In_ BOOL fTotalRef pSystemProcess, TRUE, &ctx, - (DWORD)pSystemProcess->win.EPROCESS.va, + 1, + &pSystemProcess->win.EPROCESS.va, ctxVmm->kernel.OffsetEPROCESS.FLink, ctxVmm->kernel.OffsetEPROCESS.cbMaxOffset + 0x20, VmmWin_EnumEPROCESS32_Pre, @@ -1615,27 +2608,6 @@ BOOL VmmWin_EnumerateEPROCESS(_In_ PVMM_PROCESS pSystemProcess, _In_ BOOL fRefre return FALSE; } -VOID VmmWin_ModuleMapInitialize(_In_ PVMM_PROCESS pProcess) -{ - VmmWin_InitializeLdrModules(pProcess); -} - -VOID VmmWin_ScanTagsMemMap(_In_ PVMM_PROCESS pProcess) -{ - // scan for not already known pe name headers - VmmWin_ScanHeaderPE(pProcess); - // scan for heaps - if(ctxVmm->tpSystem == VMM_SYSTEM_WINDOWS_X64) { - VmmWin_ScanPebHeap64(pProcess); - if(pProcess->win.fWow64) { - VmmWin_ScanPebHeap32(pProcess, TRUE); - } - } - if(ctxVmm->tpSystem == VMM_SYSTEM_WINDOWS_X86) { - VmmWin_ScanPebHeap32(pProcess, FALSE); - } -} - // ---------------------------------------------------------------------------- // WINDOWS LIST WALKING FUNCTIONALITY BELOW: // Walk a Windows Linked List in an efficient way that minimizes the number of @@ -1659,7 +2631,8 @@ VOID VmmWin_ScanTagsMemMap(_In_ PVMM_PROCESS pProcess) * -- pProcess * -- f32 * -- ctx = ctx to pass along to callback function (if any) -* -- vaDataStart +* -- cvaDataStart +* -- pvaDataStart * -- oListStart = offset (in bytes) to _LIST_ENTRY from vaDataStart * -- cbData * -- pfnCallback_Pre = optional callback function to gather additional addresses. @@ -1670,11 +2643,12 @@ VOID VmmWin_ListTraversePrefetch( _In_ PVMM_PROCESS pProcess, _In_ BOOL f32, _In_opt_ PVOID ctx, - _In_ QWORD vaDataStart, + _In_ DWORD cvaDataStart, + _In_ PQWORD pvaDataStart, _In_ DWORD oListStart, _In_ DWORD cbData, _In_opt_ VOID(*pfnCallback_Pre)(_In_ PVMM_PROCESS pProcess, _In_opt_ PVOID ctx, _In_ QWORD va, _In_ PBYTE pb, _In_ DWORD cb, _In_ QWORD vaFLink, _In_ QWORD vaBLink, _In_ POB_VSET pVSetAddress, _Inout_ PBOOL pfValidEntry, _Inout_ PBOOL pfValidFLink, _Inout_ PBOOL pfValidBLink), - _In_opt_ BOOL(*pfnCallback_Post)(_In_ PVMM_PROCESS pProcess, _In_opt_ PVOID ctx, _In_ QWORD va, _In_ PBYTE pb, _In_ DWORD cb), + _In_opt_ VOID(*pfnCallback_Post)(_In_ PVMM_PROCESS pProcess, _In_opt_ PVOID ctx, _In_ QWORD va, _In_ PBYTE pb, _In_ DWORD cb), _In_opt_ POB_CONTAINER pPrefetchAddressContainer) { QWORD vaData; @@ -1685,7 +2659,7 @@ VOID VmmWin_ListTraversePrefetch( BOOL fValidEntry, fValidFLink, fValidBLink, fTry1; // 1: Prefetch any addresses stored in optional address container pObVSet_vaAll = ObContainer_GetOb(pPrefetchAddressContainer); - VmmCachePrefetchPages3(pProcess, pObVSet_vaAll, cbData); + VmmCachePrefetchPages3(pProcess, pObVSet_vaAll, cbData, 0); Ob_DECREF_NULL(&pObVSet_vaAll); // 2: Prepare/Allocate and set up initial entry if(!(pObVSet_vaAll = ObVSet_New())) { goto fail; } @@ -1693,8 +2667,11 @@ VOID VmmWin_ListTraversePrefetch( if(!(pObVSet_vaTry2 = ObVSet_New())) { goto fail; } if(!(pObVSet_vaValid = ObVSet_New())) { goto fail; } if(!(pbData = LocalAlloc(0, cbData))) { goto fail; } - ObVSet_Push(pObVSet_vaAll, vaDataStart); - ObVSet_Push(pObVSet_vaTry1, vaDataStart); + while(cvaDataStart) { + cvaDataStart--; + ObVSet_Push(pObVSet_vaAll, pvaDataStart[cvaDataStart]); + ObVSet_Push(pObVSet_vaTry1, pvaDataStart[cvaDataStart]); + } // 3: Initial list walk fTry1 = TRUE; while(TRUE) { @@ -1702,7 +2679,7 @@ VOID VmmWin_ListTraversePrefetch( vaData = ObVSet_Pop(pObVSet_vaTry1); if(!vaData && (0 == ObVSet_Size(pObVSet_vaTry2))) { break; } if(!vaData) { - VmmCachePrefetchPages3(pProcess, pObVSet_vaAll, cbData); + VmmCachePrefetchPages3(pProcess, pObVSet_vaAll, cbData, 0); fTry1 = FALSE; continue; } @@ -1727,8 +2704,8 @@ VOID VmmWin_ListTraversePrefetch( fValidFLink = !(vaFLink & 0x03); fValidBLink = !(vaBLink & 0x03); } else { - fValidFLink = !(vaFLink & 0xffff8000'00000007) || ((vaFLink & 0xffff8000'00000007) == 0xffff8000'00000000); - fValidBLink = !(vaBLink & 0xffff8000'00000007) || ((vaBLink & 0xffff8000'00000007) == 0xffff8000'00000000); + fValidFLink = VMM_KADDR64_8(vaFLink) || VMM_UADDR64_8(vaFLink); + fValidBLink = VMM_KADDR64_8(vaBLink) || VMM_UADDR64_8(vaBLink); } fValidEntry = fValidFLink || fValidBLink; } @@ -1747,7 +2724,7 @@ VOID VmmWin_ListTraversePrefetch( } } // 4: Prefetch additional gathered addresses into cache. - VmmCachePrefetchPages3(pProcess, pObVSet_vaAll, cbData); + VmmCachePrefetchPages3(pProcess, pObVSet_vaAll, cbData, 0); // 5: 2nd main list walk. Call into optional pfnCallback_Post to do the main // processing of the list items. if(pfnCallback_Post) { @@ -1757,8 +2734,8 @@ VOID VmmWin_ListTraversePrefetch( } } } - // 6: Store/Update the optional container with the newly prefetch addresses (if possible). - if(pPrefetchAddressContainer) { + // 6: Store/Update the optional container with the newly prefetch addresses (if possible and desirable). + if(pPrefetchAddressContainer && ctxMain->dev.fVolatile && ctxVmm->ThreadProcCache.fEnabled) { ObContainer_SetOb(pPrefetchAddressContainer, pObVSet_vaAll); } fail: diff --git a/vmm/vmmwin.h b/vmm/vmmwin.h index 25d98947..bf24e9a2 100644 --- a/vmm/vmmwin.h +++ b/vmm/vmmwin.h @@ -22,13 +22,13 @@ typedef struct tdVMMWIN_IAT_ENTRY { /* * Load the size of the required display buffer for sections, imports and export -* into the pModule struct. The size is a direct consequence of the number of +* into the pModuleEx struct. The size is a direct consequence of the number of * functions since fixed line sizes are used for all these types. Loading is * done in a recource efficient way to minimize I/O as much as possible. * -- pProcess * -- pModule */ -VOID VmmWin_PE_SetSizeSectionIATEAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _Inout_ PVMM_MODULEMAP_ENTRY pModule); +VOID VmmWin_PE_SetSizeSectionIATEAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MAP_MODULEENTRY pModule); /* * Walk the export address table (EAT) from a given pProcess and store it in the @@ -41,7 +41,7 @@ VOID VmmWin_PE_SetSizeSectionIATEAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _I * -- return */ _Success_(return) -BOOL VmmWin_PE_LoadEAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MODULEMAP_ENTRY pModule, _Out_writes_opt_(cEATs) PVMMPROC_WINDOWS_EAT_ENTRY pEATs, _In_ DWORD cEATs, _Out_ PDWORD pcEATs); +BOOL VmmWin_PE_LoadEAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MAP_MODULEENTRY pModule, _Out_writes_opt_(cEATs) PVMMPROC_WINDOWS_EAT_ENTRY pEATs, _In_ DWORD cEATs, _Out_ PDWORD pcEATs); /* * Walk the import address table (IAT) from a given pProcess and store it in the @@ -52,7 +52,7 @@ BOOL VmmWin_PE_LoadEAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MODUL * -- cIATs * -- pcIATs = number of actual items of pIATs on exit */ -VOID VmmWin_PE_LoadIAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MODULEMAP_ENTRY pModule, _Out_writes_(*pcIATs) PVMMWIN_IAT_ENTRY pIATs, _In_ DWORD cIATs, _Out_ PDWORD pcIATs); +VOID VmmWin_PE_LoadIAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MAP_MODULEENTRY pModule, _Out_writes_(*pcIATs) PVMMWIN_IAT_ENTRY pIATs, _In_ DWORD cIATs, _Out_ PDWORD pcIATs); /* * Fill the pbDisplayBuffer with a human readable version of the data directories. @@ -67,7 +67,7 @@ VOID VmmWin_PE_LoadIAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _In_ PVMM_MODUL */ VOID VmmWin_PE_DIRECTORY_DisplayBuffer( _In_ PVMM_PROCESS pProcess, - _In_ PVMM_MODULEMAP_ENTRY pModule, + _In_ PVMM_MAP_MODULEENTRY pModule, _Out_writes_bytes_opt_(*pcbDisplayBuffer) PBYTE pbDisplayBufferOpt, _In_ DWORD cbDisplayBufferMax, _Out_opt_ PDWORD pcbDisplayBuffer, @@ -86,7 +86,7 @@ VOID VmmWin_PE_DIRECTORY_DisplayBuffer( */ VOID VmmWin_PE_SECTION_DisplayBuffer( _In_ PVMM_PROCESS pProcess, - _In_ PVMM_MODULEMAP_ENTRY pModule, + _In_ PVMM_MAP_MODULEENTRY pModule, _Out_writes_bytes_opt_(*pcbDisplayBuffer) PBYTE pbDisplayBufferOpt, _In_ DWORD cbDisplayBufferMax, _Out_opt_ PDWORD pcbDisplayBuffer, @@ -94,20 +94,61 @@ VOID VmmWin_PE_SECTION_DisplayBuffer( _Out_writes_opt_(*pcSectionsOpt) PIMAGE_SECTION_HEADER pSectionsOpt); /* -* Initialize the module names into the ctxVMM. This is performed by a PEB/Ldr -* scan of in-process memory structures. This may be unreliable of process is -* obfuscated. +* Try initialize PteMap text descriptions. This function will first try to pop- +* ulate the pre-existing VMMOB_MAP_PTE object in pProcess with module names and +* then, if failed or partially failed, try to initialize from PE file headers. * -- pProcess */ -VOID VmmWin_ModuleMapInitialize(_In_ PVMM_PROCESS pProcess); +BOOL VmmWin_InitializePteMapText(_In_ PVMM_PROCESS pProcess); /* -* Scan the process for various information that is put into the memory map. It -* is recommended to initialize the ModuleMap before calling this function so it -* can skip trying do double work identifying already known modules. +* Initialize the module map containing information about loaded modules in the +* system. This is performed by a PEB/Ldr walk/scan of in-process memory +* structures. This may be unreliable if a process is obfuscated or tampered. * -- pProcess +* -- return +*/ +_Success_(return) +BOOL VmmWin_InitializeLdrModules(_In_ PVMM_PROCESS pProcess); + +/* +* Initialize the meap map containing information about the process heaps in the +* specific process. This is performed by a PEB walk/scan of in-process memory +* structures. This may be unreliable if a process is obfuscated or tampered. +* -- pProcess +* -- return +*/ +BOOL VmmWinHeap_Initialize(_In_ PVMM_PROCESS pProcess); + +/* +* Initialize the thread map for a specific process. +* NB! The threading sub-system is dependent on pdb symbols and may take a small +* amount of time before it's available after system startup. +* -- pProcess +* -- fNonBlocking +* -- return +*/ +BOOL VmmWinThread_Initialize(_In_ PVMM_PROCESS pProcess, _In_ BOOL fNonBlocking); + +/* +* Initialize Handles for a specific process. Extended information text may take +* extra time to initialize. +* -- pProcess +* -- fExtendedText = also fetch extended info such as handle paths/names. +* -- return +*/ +_Success_(return) +BOOL VmmWinHandle_Initialize(_In_ PVMM_PROCESS pProcess, _In_ BOOL fExtendedText); + +/* +* Retrieve a pointer to a VMMWIN_OBJECT_TYPE if possible. Initialization of the +* table takes place on first use. The table only exists in Win7+ and is is +* dependant on PDB symbol functionality for initialization. +* -- iObjectType +* -- return */ -VOID VmmWin_ScanTagsMemMap(_In_ PVMM_PROCESS pProcess); +_Success_(return != NULL) +PVMMWIN_OBJECT_TYPE VmmWin_ObjectTypeGet(_In_ BYTE iObjectType); /* * Try walk the EPROCESS list in the Windows kernel to enumerate processes into @@ -130,7 +171,8 @@ BOOL VmmWin_EnumerateEPROCESS(_In_ PVMM_PROCESS pSystemProcess, _In_ BOOL fRefre * -- pProcess * -- f32 = TRUE if 32-bit, FALSE if 64-bit * -- ctx = ctx to pass along to callback function (if any) -* -- vaDataStart +* -- cvaDataStart +* -- pvaDataStart * -- oListStart = offset (in bytes) to _LIST_ENTRY from vaDataStart * -- cbData * -- pfnCallback_Pre = optional callback function to gather additional addresses. @@ -141,11 +183,12 @@ VOID VmmWin_ListTraversePrefetch( _In_ PVMM_PROCESS pProcess, _In_ BOOL f32, _In_opt_ PVOID ctx, - _In_ QWORD vaDataStart, + _In_ DWORD cvaDataStart, + _In_ PQWORD pvaDataStart, _In_ DWORD oListStart, _In_ DWORD cbData, _In_opt_ VOID(*pfnCallback_Pre)(_In_ PVMM_PROCESS pProcess, _In_opt_ PVOID ctx, _In_ QWORD va, _In_ PBYTE pb, _In_ DWORD cb, _In_ QWORD vaFLink, _In_ QWORD vaBLink, _In_ POB_VSET pVSetAddress, _Inout_ PBOOL pfValidEntry, _Inout_ PBOOL pfValidFLink, _Inout_ PBOOL pfValidBLink), - _In_opt_ BOOL(*pfnCallback_Post)(_In_ PVMM_PROCESS pProcess, _In_opt_ PVOID ctx, _In_ QWORD va, _In_ PBYTE pb, _In_ DWORD cb), + _In_opt_ VOID(*pfnCallback_Post)(_In_ PVMM_PROCESS pProcess, _In_opt_ PVOID ctx, _In_ QWORD va, _In_ PBYTE pb, _In_ DWORD cb), _In_opt_ POB_CONTAINER pPrefetchAddressContainer ); diff --git a/vmm/vmmwininit.c b/vmm/vmmwininit.c index f3c83ef4..e0dfd92c 100644 --- a/vmm/vmmwininit.c +++ b/vmm/vmmwininit.c @@ -7,12 +7,43 @@ // #include "vmm.h" -#include "vmmwin.h" +#include "mm.h" #include "pe.h" #include "pdb.h" #include "util.h" +#include "vmmwin.h" #include "vmmwinreg.h" +/* +* Try initialize threading - this is dependent on available PDB symbols. +*/ +VOID VmmWinInit_TryInitializeThreading() +{ + BOOL f; + DWORD cbEThread = 0; + PVMM_WIN_THEADINFO pti = &ctxVmm->kernel.ThreadInfo; + f = PDB_GetTypeChildOffsetShort(VMMWIN_PDB_HANDLE_KERNEL, "_EPROCESS", L"ThreadListHead", &pti->oThreadListHeadKP) && + PDB_GetTypeChildOffsetShort(VMMWIN_PDB_HANDLE_KERNEL, "_KTHREAD", L"StackBase", &pti->oStackBase) && + PDB_GetTypeChildOffsetShort(VMMWIN_PDB_HANDLE_KERNEL, "_KTHREAD", L"StackLimit", &pti->oStackLimit) && + PDB_GetTypeChildOffsetShort(VMMWIN_PDB_HANDLE_KERNEL, "_KTHREAD", L"State", &pti->oState) && + PDB_GetTypeChildOffsetShort(VMMWIN_PDB_HANDLE_KERNEL, "_KTHREAD", L"Priority", &pti->oPriority) && + PDB_GetTypeChildOffsetShort(VMMWIN_PDB_HANDLE_KERNEL, "_KTHREAD", L"BasePriority", &pti->oBasePriority) && + PDB_GetTypeChildOffsetShort(VMMWIN_PDB_HANDLE_KERNEL, "_KTHREAD", L"Teb", &pti->oTeb) && + PDB_GetTypeChildOffsetShort(VMMWIN_PDB_HANDLE_KERNEL, "_ETHREAD", L"CreateTime", &pti->oCreateTime) && + PDB_GetTypeChildOffsetShort(VMMWIN_PDB_HANDLE_KERNEL, "_ETHREAD", L"ExitTime", &pti->oExitTime) && + PDB_GetTypeChildOffsetShort(VMMWIN_PDB_HANDLE_KERNEL, "_ETHREAD", L"ExitStatus", &pti->oExitStatus) && + PDB_GetTypeChildOffsetShort(VMMWIN_PDB_HANDLE_KERNEL, "_ETHREAD", L"StartAddress", &pti->oStartAddress) && + PDB_GetTypeChildOffsetShort(VMMWIN_PDB_HANDLE_KERNEL, "_ETHREAD", L"ThreadListEntry", &pti->oThreadListEntry) && + PDB_GetTypeChildOffsetShort(VMMWIN_PDB_HANDLE_KERNEL, "_ETHREAD", L"Cid", &pti->oCid) && + PDB_GetTypeSize(VMMWIN_PDB_HANDLE_KERNEL, "_ETHREAD", &cbEThread); + PDB_GetTypeChildOffsetShort(VMMWIN_PDB_HANDLE_KERNEL, "_KTHREAD", L"Process", &pti->oProcessOpt); // optional - does not exist in xp. + PDB_GetTypeChildOffsetShort(VMMWIN_PDB_HANDLE_KERNEL, "_KTHREAD", L"Running", &pti->oRunningOpt); // optional - does not exist in vista/xp. + pti->oMax = (WORD)(cbEThread + 8); + pti->oTebStackBase = ctxVmm->f32 ? 0x004 : 0x008; + pti->oTebStackLimit = ctxVmm->f32 ? 0x008 : 0x010; + ctxVmm->fThreadMapEnabled = f; +} + /* * Try initialize not yet initialized values in the optional windows kernel * context ctxVmm->kernel.opt @@ -180,35 +211,46 @@ QWORD VmmWinInit_FindNtosScan64(PVMM_PROCESS pSystemProcess) QWORD VmmWinInit_FindNtosScanHint64(_In_ PVMM_PROCESS pSystemProcess, _In_ QWORD vaHint) { PBYTE pb; - QWORD vaBase, o, p, vaNtosBase = 0; + QWORD vaBase, o, p, vaNtosTry = 0; DWORD cbRead; CHAR szModuleName[MAX_PATH] = { 0 }; + PIMAGE_DOS_HEADER pDosHeader; + PIMAGE_NT_HEADERS pNtHeader; pb = LocalAlloc(0, 0x00200000); if(!pb) { goto cleanup; } - // Scan back in 2MB chunks a time, (ntoskrnl.exe is loaded in 2MB pages). + // Scan back in 2MB chunks a time, (ntoskrnl.exe is loaded in 2MB pages except in low memory situations). for(vaBase = vaHint & ~0x1fffff; vaBase + 0x02000000 > vaHint; vaBase -= 0x200000) { VmmReadEx(pSystemProcess, vaBase, pb, 0x200000, &cbRead, 0); - // only fail here if all virtual memory in read fails. reason is that kernel is + // Only fail here if all virtual memory in read fails. reason is that kernel is // properly mapped in memory (with NX MZ header in separate page) with empty // space before next valid kernel pages when running Virtualization Based Security. + // Memory pages may be paged out of small pages are used in low-mem situations. if(!cbRead) { goto cleanup; } for(p = 0; p < 0x200000; p += 0x1000) { - // check for (1) MZ header, (2) POOLCODE section, (3) ntoskrnl.exe module name - if(*(PWORD)(pb + p) != 0x5a4d) { continue; } // MZ header + // check for (1) MZ+NT header, (2) POOLCODE section, (3) ntoskrnl.exe module name (if possible to read) + pDosHeader = (PIMAGE_DOS_HEADER)(pb + p); // DOS header + if(pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { continue; } // DOS header signature (MZ) + if(pDosHeader->e_lfanew > 0x800) { continue; } + pNtHeader = (PIMAGE_NT_HEADERS)(pb + p + pDosHeader->e_lfanew); // NT header + if(pNtHeader->Signature != IMAGE_NT_SIGNATURE) { continue; } // NT header signature for(o = 0; o < 0x1000; o += 8) { - if(*(PQWORD)(pb + p + o) == 0x45444F434C4F4F50) { // POOLCODE - PE_GetModuleNameEx(pSystemProcess, vaBase + p, FALSE, pb + p, szModuleName, _countof(szModuleName), NULL); - if(!_stricmp(szModuleName, "ntoskrnl.exe")) { - LocalFree(pb); - return vaBase + p; + if(*(PQWORD)(pb + p + o) == 0x45444F434C4F4F50) { // POOLCODE + if(!PE_GetModuleNameEx(pSystemProcess, vaBase + p, FALSE, pb + p, szModuleName, _countof(szModuleName), NULL)) { + vaNtosTry = vaBase + p; + continue; } + if(_stricmp(szModuleName, "ntoskrnl.exe")) { // not ntoskrnl.exe + continue; + } + LocalFree(pb); + return vaBase + p; } } } } cleanup: LocalFree(pb); - return vaNtosBase; + return vaNtosTry; // on fail try return NtosTry derived from MZ + POOLCODE only } /* @@ -224,24 +266,31 @@ DWORD VmmWinInit_FindNtosScan32(_In_ PVMM_PROCESS pSystemProcess) DWORD o, p, vaNtosTry = 0; PBYTE pb; CHAR szModuleName[MAX_PATH] = { 0 }; + PIMAGE_DOS_HEADER pDosHeader; + PIMAGE_NT_HEADERS pNtHeader; if(!(pb = LocalAlloc(LMEM_ZEROINIT, 0x04000000))) { return 0; } for(p = 0; p < 0x04000000; p += 0x1000) { // read 8MB chunks when required. if(0 == p % 0x00800000) { VmmReadEx(pSystemProcess, 0x80000000ULL + p, pb + p, 0x00800000, NULL, 0); } - // check for (1) MZ header, (2) POOLCODE section, (3) ntoskrnl.exe module name - if(*(PWORD)(pb + p) != 0x5a4d) { continue; } // MZ header + // check for (1) MZ+NT header, (2) POOLCODE section, (3) ntoskrnl.exe module name (if possible to read) + pDosHeader = (PIMAGE_DOS_HEADER)(pb + p); // DOS header + if(pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { continue; } // DOS header signature (MZ) + if(pDosHeader->e_lfanew > 0x800) { continue; } + pNtHeader = (PIMAGE_NT_HEADERS)(pb + p + pDosHeader->e_lfanew); // NT header + if(pNtHeader->Signature != IMAGE_NT_SIGNATURE) { continue; } // NT header signature for(o = 0; o < 0x800; o += 8) { - if(*(PQWORD)(pb + p + o) == 0x45444F434C4F4F50) { // POOLCODE - if(PE_GetModuleNameEx(pSystemProcess, 0x80000000ULL + p, FALSE, pb + p, szModuleName, _countof(szModuleName), NULL)) { - if(!_stricmp(szModuleName, "ntoskrnl.exe")) { - LocalFree(pb); - return 0x80000000 + p; - } - } else { + if(*(PQWORD)(pb + p + o) == 0x45444F434C4F4F50) { // POOLCODE + if(!PE_GetModuleNameEx(pSystemProcess, 0x80000000ULL + p, FALSE, pb + p, szModuleName, _countof(szModuleName), NULL)) { vaNtosTry = 0x80000000 + p; + continue; + } + if(_stricmp(szModuleName, "ntoskrnl.exe")) { // not ntoskrnl.exe + continue; } + LocalFree(pb); + return 0x80000000 + p; } } } @@ -285,7 +334,6 @@ PVMM_PROCESS VmmWinInit_FindNtosScan() } if(!vaKernelBase) { goto fail; } cbKernelSize = PE_GetSize(pObSystemProcess, vaKernelBase); - if(!cbKernelSize) { goto fail; } ctxVmm->kernel.vaBase = vaKernelBase; ctxVmm->kernel.cbSize = cbKernelSize; return pObSystemProcess; @@ -505,7 +553,7 @@ BOOL VmmWinInit_FindPsLoadedModuleListKDBG(_In_ PVMM_PROCESS pSystemProcess) // is the normal way of finding it on 64-bit Windows below Windows 10. // This also works on 32-bit Windows versions - so use this method for // simplicity rather than using a separate 32-bit method. - if(!ctxVmm->kernel.opt.KDBG.va) { + if(!ctxVmm->kernel.opt.KDBG.va && (ctxVmm->f32 || ctxVmm->kernel.dwVersionMajor < 10)) { if(!PE_SectionGetFromName(pSystemProcess, ctxVmm->kernel.vaBase, ".data", &SectionHeader)) { goto fail; } if((SectionHeader.Misc.VirtualSize > 0x00100000) || (SectionHeader.VirtualAddress > 0x01000000)) { goto fail; } if(!(pbData = LocalAlloc(LMEM_ZEROINIT, SectionHeader.Misc.VirtualSize))) { goto fail; } @@ -526,6 +574,13 @@ BOOL VmmWinInit_FindPsLoadedModuleListKDBG(_In_ PVMM_PROCESS pSystemProcess) } } } + // 4: Try locate by querying the PDB for symbols. At this point the PDB + // subsystem may not be fully initialized yet so wait for it to init. + PDB_Initialize_WaitComplete(); + if(PDB_GetSymbolPTR(VMMWIN_PDB_HANDLE_KERNEL, "PsLoadedModuleList", pSystemProcess, &ctxVmm->kernel.vaPsLoadedModuleListPtr)) { + PDB_GetSymbolAddress(VMMWIN_PDB_HANDLE_KERNEL, "PsLoadedModuleList", &ctxVmm->kernel.opt.vaPsLoadedModuleListExp); + return TRUE; + } fail: LocalFree(pbData); return (0 != ctxVmm->kernel.vaPsLoadedModuleListPtr); @@ -586,7 +641,12 @@ QWORD VmmWinInit_FindSystemEPROCESS(_In_ PVMM_PROCESS pSystemProcess) vmmprintfvv_fn("INFO: PsInitialSystemProcess located at %016llx.\n", vaPsInitialSystemProcess); goto success; } - // 2: fail - paging? (or not windows) - this should ideally not happen - but it happens rarely... + // 2: fail - paging? try to retrive using PDB subsystem - this may take some time to initialize + // and download symbols - but it's better than failing totally ... + PDB_Initialize(NULL, FALSE); + PDB_GetSymbolPTR(VMMWIN_PDB_HANDLE_KERNEL, "PsInitialSystemProcess", pSystemProcess, &vaSystemEPROCESS); + if(vaSystemEPROCESS) { goto success; } + // 3: fail - paging? (or not windows) - this should ideally not happen - but it happens rarely... // try scan beginning of ALMOSTRO section for pointers and validate (working on pre-win10 only) if(!PE_SectionGetFromName(pSystemProcess, ctxVmm->kernel.vaBase, "ALMOSTRO", &SectionHeader)) { return 0; } if(!VmmRead(pSystemProcess, ctxVmm->kernel.vaBase + SectionHeader.VirtualAddress, pbALMOSTRO, sizeof(pbALMOSTRO))) { return 0; } @@ -605,6 +665,20 @@ QWORD VmmWinInit_FindSystemEPROCESS(_In_ PVMM_PROCESS pSystemProcess) return vaSystemEPROCESS; } +/* +* Async initialization of remaining actions in VmmWinInit_TryInitialize. +* -- lpParameter +* -- return +*/ +DWORD VmmWinInit_TryInitialize_Async(LPVOID lpParameter) +{ + PDB_Initialize_WaitComplete(); + MmWin_PagingInitialize(TRUE); // initialize full paging (memcompression) + VmmWinInit_TryInitializeThreading(); + VmmWinInit_TryInitializeKernelOptionalValues(); + return 1; +} + /* * Try initialize the VMM from scratch with new WINDOWS support. * -- paDTBOpt @@ -612,6 +686,7 @@ QWORD VmmWinInit_FindSystemEPROCESS(_In_ PVMM_PROCESS pSystemProcess) */ BOOL VmmWinInit_TryInitialize(_In_opt_ QWORD paDTBOpt) { + HANDLE hThreadInitializeAsync; PVMM_PROCESS pObSystemProcess = NULL, pObProcess = NULL; // Fetch Directory Base (DTB (PML4)) and initialize Memory Model. if(paDTBOpt) { @@ -637,6 +712,8 @@ BOOL VmmWinInit_TryInitialize(_In_opt_ QWORD paDTBOpt) goto fail; } vmmprintfvv_fn("INFO: NTOS located at: %016llx.\n", ctxVmm->kernel.vaBase); + // Initialize Paging (Limited Mode) + MmWin_PagingInitialize(FALSE); // Locate System EPROCESS pObSystemProcess->win.EPROCESS.va = VmmWinInit_FindSystemEPROCESS(pObSystemProcess); if(!pObSystemProcess->win.EPROCESS.va) { @@ -650,13 +727,9 @@ BOOL VmmWinInit_TryInitialize(_In_opt_ QWORD paDTBOpt) } ctxVmm->tpSystem = (VMM_MEMORYMODEL_X64 == ctxVmm->tpMemoryModel) ? VMM_SYSTEM_WINDOWS_X64 : VMM_SYSTEM_WINDOWS_X86; // Retrieve operating system version information from 'smss.exe' process - // Optionally retrieve PID of MemCompression and Registry process + // Optionally retrieve PID of Registry process while((pObProcess = VmmProcessGetNext(pObProcess, 0))) { if(pObProcess->dwPPID == 4) { - if(!memcmp("MemCompression", pObProcess->szName, 15)) { - ctxVmm->kernel.MemCompress.dwPid = pObProcess->dwPID; - ctxVmm->kernel.MemCompress.vaEPROCESS = pObProcess->win.EPROCESS.va; - } if(!memcmp("Registry", pObProcess->szName, 9)) { ctxVmm->kernel.dwPidRegistry = pObProcess->dwPID; } @@ -665,13 +738,20 @@ BOOL VmmWinInit_TryInitialize(_In_opt_ QWORD paDTBOpt) } } } - // Optionally fetch PsLoadedModuleList / KDBG - VmmWinInit_FindPsLoadedModuleListKDBG(pObSystemProcess); - Ob_DECREF(pObSystemProcess); - // Initialize PDB and Registry - PDB_Initialize(); - VmmWinReg_Initialize(); + // Initialization functionality: + PDB_Initialize(NULL, TRUE); // Async init of PDB subsystem. + VmmWinInit_FindPsLoadedModuleListKDBG(pObSystemProcess); // Find PsLoadedModuleList and possibly KDBG. + VmmWinReg_Initialize(); // Registry. + // Async Initialization functionality: + hThreadInitializeAsync = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)VmmWinInit_TryInitialize_Async, (LPVOID)NULL, 0, NULL); + if(hThreadInitializeAsync) { + if(ctxMain->cfg.fWaitInitialize) { + WaitForSingleObject(hThreadInitializeAsync, INFINITE); + } + CloseHandle(hThreadInitializeAsync); + } // return + Ob_DECREF(pObSystemProcess); vmmprintf( "Initialized %i-bit Windows %i.%i.%i\n", (ctxVmm->f32 ? 32 : 64), diff --git a/vmm/vmmwinreg.c b/vmm/vmmwinreg.c index 85c7fa3d..0cfa8da0 100644 --- a/vmm/vmmwinreg.c +++ b/vmm/vmmwinreg.c @@ -247,7 +247,7 @@ VOID VmmWinReg_HiveReadEx(_In_ POB_REGISTRY_HIVE pRegistryHive, _In_ DWORD ra, _ PVMM_PROCESS pObProcessRegistry = NULL; DWORD cbP, cMEMs, cbRead = 0; PBYTE pbBuffer; - PMEM_IO_SCATTER_HEADER pMEMs, * ppMEMs; + PMEM_IO_SCATTER_HEADER pMEMs, *ppMEMs; QWORD i, oVA; if(pcbReadOpt) { *pcbReadOpt = 0; } if(!cb) { return; } @@ -726,7 +726,7 @@ VOID VmmWinReg_ListTraversePrefetch_CallbackPost_GetShortName(_In_ LPWSTR wsz, _ * Callback function from VmmWin_ListTraversePrefetch[32|64]. * Set up a single Registry Hive. */ -BOOL VmmWinReg_EnumHive64_Post(_In_ PVMM_PROCESS pProcess, _In_opt_ POB_MAP pHiveMap, _In_ QWORD vaData, _In_ PBYTE pbData, _In_ DWORD cbData) +VOID VmmWinReg_EnumHive64_Post(_In_ PVMM_PROCESS pProcess, _In_opt_ POB_MAP pHiveMap, _In_ QWORD vaData, _In_ PBYTE pbData, _In_ DWORD cbData) { BOOL f; CHAR chDefault = '_'; @@ -736,15 +736,15 @@ BOOL VmmWinReg_EnumHive64_Post(_In_ PVMM_PROCESS pProcess, _In_opt_ POB_MAP pHiv POB_REGISTRY_HIVE pObHive = NULL; PVMMWIN_REGISTRY_OFFSET po = &ctxVmm->pRegistry->Offset; // 1: validity check - if((vaData & 0xffff8000'00000000) != 0xffff8000'00000000) { return FALSE; } // not kernel address - f = pHiveMap && + f = VMM_KADDR64_16(vaData) && + pHiveMap && (*(PDWORD)(pbData + po->CM.Signature) == 0xBEE0BEE0) && // Signature match (*(PQWORD)(pbData + po->CM.StorageMap)) && // _CMHIVE.Hive.Storage.Map (*(PDWORD)(pbData + po->CM.Length)) && // Length > 0 (*(PDWORD)(pbData + po->CM.Length) <= 0x40000000); // Length < 1GB - if(!f) { return TRUE; } + if(!f) { return; } // 2: Allocate and Initialize - if(!(pObHive = Ob_Alloc(OB_TAG_REG_HIVE, LMEM_ZEROINIT, sizeof(OB_REGISTRY_HIVE), VmmWinReg_CallbackCleanup_ObRegistryHive, NULL))) { return TRUE; } + if(!(pObHive = Ob_Alloc(OB_TAG_REG_HIVE, LMEM_ZEROINIT, sizeof(OB_REGISTRY_HIVE), VmmWinReg_CallbackCleanup_ObRegistryHive, NULL))) { return; } pObHive->vaCMHIVE = vaData; pObHive->vaHBASE_BLOCK = *(PQWORD)(pbData + po->CM.BaseBlock); pObHive->cbLength = *(PDWORD)(pbData + po->CM.Length); @@ -775,10 +775,9 @@ BOOL VmmWinReg_EnumHive64_Post(_In_ PVMM_PROCESS pProcess, _In_opt_ POB_MAP pHiv ObMap_Push(pHiveMap, pObHive->vaCMHIVE, pObHive); vmmprintfvv_fn("%04i %s\n", ObMap_Size(pHiveMap), pObHive->szName); Ob_DECREF(pObHive); - return TRUE; } -BOOL VmmWinReg_EnumHive32_Post(_In_ PVMM_PROCESS pProcess, _In_opt_ POB_MAP pHiveMap, _In_ QWORD vaData, _In_ PBYTE pbData, _In_ DWORD cbData) +VOID VmmWinReg_EnumHive32_Post(_In_ PVMM_PROCESS pProcess, _In_opt_ POB_MAP pHiveMap, _In_ QWORD vaData, _In_ PBYTE pbData, _In_ DWORD cbData) { BOOL f; CHAR chDefault = '_'; @@ -788,16 +787,16 @@ BOOL VmmWinReg_EnumHive32_Post(_In_ PVMM_PROCESS pProcess, _In_opt_ POB_MAP pHiv POB_REGISTRY_HIVE pObHive = NULL; PVMMWIN_REGISTRY_OFFSET po = &ctxVmm->pRegistry->Offset; // 1: validity check - if((vaData & 0x80000007) != 0x80000000) { return FALSE; } // not kernel address - f = pHiveMap && + f = VMM_KADDR32_8(vaData) && + pHiveMap && (*(PDWORD)(pbData + po->CM.Signature) == 0xBEE0BEE0) && // Signature match !(*(PDWORD)(pbData + po->CM.BaseBlock) & 0xfff) && // _CMHIVE.BaseBlock on page boundary (*(PQWORD)(pbData + po->CM.StorageMap)) && // _CMHIVE.Hive.Storage.Map (*(PDWORD)(pbData + po->CM.Length)) && // Length > 0 (*(PDWORD)(pbData + po->CM.Length) <= 0x40000000); // Length < 1GB - if(!f) { return TRUE; } + if(!f) { return; } // 2: Allocate and Initialize - if(!(pObHive = Ob_Alloc(OB_TAG_REG_HIVE, LMEM_ZEROINIT, sizeof(OB_REGISTRY_HIVE), VmmWinReg_CallbackCleanup_ObRegistryHive, NULL))) { return TRUE; } + if(!(pObHive = Ob_Alloc(OB_TAG_REG_HIVE, LMEM_ZEROINIT, sizeof(OB_REGISTRY_HIVE), VmmWinReg_CallbackCleanup_ObRegistryHive, NULL))) { return; } pObHive->vaCMHIVE = vaData; pObHive->vaHBASE_BLOCK = *(PDWORD)(pbData + po->CM.BaseBlock); pObHive->cbLength = *(PDWORD)(pbData + po->CM.Length); @@ -827,7 +826,6 @@ BOOL VmmWinReg_EnumHive32_Post(_In_ PVMM_PROCESS pProcess, _In_opt_ POB_MAP pHiv // 4: Attach and Return ObMap_Push(pHiveMap, pObHive->vaCMHIVE, pObHive); // pRegistry->pmHive takes responsibility for pObHive reference vmmprintfvv_fn("%04i %s\n", ObMap_Size(pHiveMap), pObHive->szName); - return TRUE; } /* @@ -850,7 +848,8 @@ POB_MAP VmmWinReg_HiveMap_New() pObProcessSystem, f32, pObHiveMap, - ctxVmm->pRegistry->Offset.vaHintCMHIVE, + 1, + &ctxVmm->pRegistry->Offset.vaHintCMHIVE, ctxVmm->pRegistry->Offset.CM.FLink, ctxVmm->pRegistry->Offset.CM._Size, f32 ? VmmWinReg_EnumHive32_Pre : VmmWinReg_EnumHive64_Pre, diff --git a/vmm/vmmwintcpip.c b/vmm/vmmwintcpip.c index 3fa705a8..eb66bb1e 100644 --- a/vmm/vmmwintcpip.c +++ b/vmm/vmmwintcpip.c @@ -6,8 +6,6 @@ #include "vmmwintcpip.h" #include "pe.h" -#include "vmmproc.h" -#include "vmmwin.h" #include "util.h" #define AF_INET6 23 // Ws2tcpip.h @@ -54,7 +52,7 @@ QWORD VmmWinTcpIp_GetPartitionTable64_PageAligned(_In_ PVMM_PROCESS pSystemProce ObVSet_Push(pObSet, va); } } - VmmCachePrefetchPages(pSystemProcess, pObSet); + VmmCachePrefetchPages(pSystemProcess, pObSet, 0); // 2: filter potential page-aligned candidates while((va = ObVSet_Pop(pObSet))) { VmmReadEx(pSystemProcess, va, pb, 0x38, &cbRead, VMM_FLAG_FORCECACHE_READ); @@ -91,7 +89,7 @@ QWORD VmmWinTcpIp_GetPartitionTable64_PoolHdr(_In_ PVMM_PROCESS pSystemProcess, ObVSet_Push(pObSet, va); } } - VmmCachePrefetchPages3(pSystemProcess, pObSet, 0x48); + VmmCachePrefetchPages3(pSystemProcess, pObSet, 0x48, 0); // 2: filter potential candidates while((va = ObVSet_Pop(pObSet))) { VmmReadEx(pSystemProcess, va - 0x10, pb, 0x48, &cbRead, VMM_FLAG_FORCECACHE_READ); @@ -116,8 +114,8 @@ VOID VmmWinTcpIp_GetPartitionTable64(_In_ PVMM_PROCESS pSystemProcess) { DWORD cbData; PBYTE pbData = NULL; - PVMMOB_MODULEMAP pObModuleMap = NULL; - PVMM_MODULEMAP_ENTRY pModuleMapEntry; + PVMMOB_MAP_MODULE pObModuleMap = NULL; + PVMM_MAP_MODULEENTRY pModuleMapEntry; IMAGE_SECTION_HEADER oSectionHeader; if(ctxVmm->TcpIp.fInitialized) { return; } EnterCriticalSection(&ctxVmm->TcpIp.LockUpdate); @@ -126,18 +124,18 @@ VOID VmmWinTcpIp_GetPartitionTable64(_In_ PVMM_PROCESS pSystemProcess) return; } // 1: fetch tcpip.sys .data section - it contains a pointer to tcpip!PartitionTable [TcPt] - if(!VmmProc_ModuleMapGetSingleEntry(pSystemProcess, L"tcpip.sys", &pObModuleMap, &pModuleMapEntry)) { + if(!(VmmMap_GetModule(pSystemProcess, &pObModuleMap) && (pModuleMapEntry = VmmMap_GetModuleEntry(pObModuleMap, L"tcpip.sys")))) { vmmprintfv_fn("CANNOT LOCATE tcpip.sys.\n") goto fail; } - if(!PE_SectionGetFromName(pSystemProcess, pModuleMapEntry->BaseAddress, ".data", &oSectionHeader)) { + if(!PE_SectionGetFromName(pSystemProcess, pModuleMapEntry->vaBase, ".data", &oSectionHeader)) { vmmprintfv_fn("CANNOT READ tcpip.sys .data PE SECTION.\n") goto fail; } cbData = oSectionHeader.Misc.VirtualSize; if(!cbData || cbData > 0x00100000) { goto fail; } if(!(pbData = LocalAlloc(0, cbData))) { goto fail; } - if(!VmmRead(pSystemProcess, pModuleMapEntry->BaseAddress + oSectionHeader.VirtualAddress, pbData, cbData)) { goto fail; } + if(!VmmRead(pSystemProcess, pModuleMapEntry->vaBase + oSectionHeader.VirtualAddress, pbData, cbData)) { goto fail; } // 2: Locate tcpip!PartitionTable - it can either be in a page-aligned full page or in a smaller allocation with pool header ctxVmm->TcpIp.vaPartitionTable = VmmWinTcpIp_GetPartitionTable64_PageAligned(pSystemProcess, pbData, cbData); if(!ctxVmm->TcpIp.vaPartitionTable) { @@ -288,7 +286,7 @@ BOOL VmmWinTcpIp_TcpE_GetAddressEPs(_In_ PVMM_PROCESS pSystemProcess, _Inout_ PO } } if(0 == ObVSet_Size(pObTcHT)) { goto fail; } - VmmCachePrefetchPages3(pSystemProcess, pObTcHT, cbTcpHT); + VmmCachePrefetchPages3(pSystemProcess, pObTcHT, cbTcpHT, 0); // 3: enumerate possible/interesting TCP hash tables - TcHT. while((va = ObVSet_Pop(pObTcHT))) { ZeroMemory(pbTcHT, cbTcpHT); @@ -302,7 +300,7 @@ BOOL VmmWinTcpIp_TcpE_GetAddressEPs(_In_ PVMM_PROCESS pSystemProcess, _Inout_ PO } } if(0 == ObVSet_Size(pObHTab)) { goto fail; } - VmmCachePrefetchPages3(pSystemProcess, pObHTab, 0x810); + VmmCachePrefetchPages3(pSystemProcess, pObHTab, 0x810, 0); // 4: Enumerate TCP Endpoints 'TcpE' out of the potential 'HTab' while((va = ObVSet_Pop(pObHTab))) { VmmReadEx(pSystemProcess, va, pb, 0x810, &cbRead, VMM_FLAG_FORCECACHE_READ); @@ -321,7 +319,7 @@ BOOL VmmWinTcpIp_TcpE_GetAddressEPs(_In_ PVMM_PROCESS pSystemProcess, _Inout_ PO } } if(0 == ObVSet_Size(pObTcpE)) { goto fail; } - VmmCachePrefetchPages3(pSystemProcess, pObTcpE, 0x10); + VmmCachePrefetchPages3(pSystemProcess, pObTcpE, 0x10, 0); // 5: Verify and transfer to outgoing result set pObTcpE_Located while((va = ObVSet_Pop(pObTcpE))) { VmmReadEx(pSystemProcess, va, pb, 0x10, &cbRead, VMM_FLAG_FORCECACHE_READ); @@ -378,7 +376,7 @@ BOOL VmmWinTcpIp_TcpE_Enumerate(_In_ PVMM_PROCESS pSystemProcess, _In_ POB_VSET }; if(cTcpEs < ObVSet_Size(pSet_TcpE)) { goto fail; } if(!(pObPrefetch = ObVSet_New())) { goto fail; } - VmmCachePrefetchPages3(pSystemProcess, pSet_TcpE, po->_Size); + VmmCachePrefetchPages3(pSystemProcess, pSet_TcpE, po->_Size, 0); // 1: retrieve general info from main struct (TcpE) while((va = ObVSet_Pop(pSet_TcpE))) { VmmReadEx(pSystemProcess, va, pb, po->_Size, &cbRead, VMM_FLAG_FORCECACHE_READ); @@ -401,7 +399,7 @@ BOOL VmmWinTcpIp_TcpE_Enumerate(_In_ PVMM_PROCESS pSystemProcess, _In_ POB_VSET c++; } // 2: retrieve address family and ptr to address - VmmCachePrefetchPages3(pSystemProcess, pObPrefetch, 0x30); + VmmCachePrefetchPages3(pSystemProcess, pObPrefetch, 0x30, 0); Ob_DECREF_NULL(&pObPrefetch); if(!(pObPrefetch = ObVSet_New())) { goto fail; } for(i = 0; i < c; i++) { @@ -428,7 +426,7 @@ BOOL VmmWinTcpIp_TcpE_Enumerate(_In_ PVMM_PROCESS pSystemProcess, _In_ POB_VSET ObVSet_Push(pObPrefetch, pE->_Reserved_vaINET_Dst); } // 3: retrieve src / dst addresses - VmmCachePrefetchPages3(pSystemProcess, pObPrefetch, 0x18); + VmmCachePrefetchPages3(pSystemProcess, pObPrefetch, 0x18, 0); for(i = 0; i < c; i++) { pE = pTcpEs + i; if(pE->AF.fValid) { diff --git a/vmm_example/vmm_example.vcxproj b/vmm_example/vmm_example.vcxproj index 9c215e62..07bcf782 100644 --- a/vmm_example/vmm_example.vcxproj +++ b/vmm_example/vmm_example.vcxproj @@ -84,7 +84,7 @@ copy $(SolutionDir)\files\vmmdll.h $(ProjectDir)\ /y true true - $(SolutionDir)\files\vmm.lib;%(AdditionalDependencies) + $(SolutionDir)\files\leechcore.lib;$(SolutionDir)\files\vmm.lib;%(AdditionalDependencies) UseLinkTimeCodeGeneration $(OutDir)\lib\$(TargetName).pdb diff --git a/vmm_example/vmmdll.h b/vmm_example/vmmdll.h index 8a6b6a38..87f3f191 100644 --- a/vmm_example/vmmdll.h +++ b/vmm_example/vmmdll.h @@ -4,7 +4,7 @@ // (c) Ulf Frisk, 2018-2019 // Author: Ulf Frisk, pcileech@frizk.net // -// Header Version: 2.10.2 +// Header Version: 3.0 // #include @@ -23,11 +23,11 @@ extern "C" { /* * Initialize VMM.DLL with command line parameters. For a more detailed info -* about the parameters please see github wiki for Memory Process File System -* and LeechCore. THIS IS THE PREFERED WAY OF INITIALIZING VMM.DLL +* about the parameters please see github wiki for MemProcFS and LeechCore. +* NB! LeechCore initialization parameters are _also_ valid to this function. * Important parameters are: -* -printf = show printf style outputs) -* -v -vv -vvv = extra verbosity levels) +* -printf = show printf style outputs. +* -v -vv -vvv = extra verbosity levels. * -device = device as on format for LeechCore - please see leechcore.h or * Github documentation for additional information. Some values * are: , fpga, usb3380, hvsavedstate, totalmeltdown, pmem @@ -35,8 +35,19 @@ extern "C" { * documentation for additional information. * -norefresh = disable background refreshes (even if backing memory is * volatile memory). -* -symbolserverdisable = disable symbol server until user change. This -* parameter will take precedence over registry settings. +* -symbolserverdisable = disable symbol server until user change. +* This parameter will take precedence over registry settings. +* -pagefile[0-9] = page file(s) to use in addition to physical memory. +* Normally pagefile.sys have index 0 and swapfile.sys index 1. +* Page files are in constant flux - do not use if time diff +* between memory dump and page files are more than few minutes. +* Example: 'pagefile0 swapfile.sys' +* -waitinitialize = Wait for initialization to complete before returning. +* Normal use is that some initialization is done asynchronously +* and may not be completed when initialization call is completed. +* This includes virtual memory compression, registry and more. +* Example: '-waitinitialize' +* * -- argc * -- argv * -- return = success/fail @@ -78,9 +89,9 @@ VOID VMMDLL_MemFree(_Frees_ptr_opt_ PVOID pvMem); //----------------------------------------------------------------------------- /* -* Options used together with the functions: VMMDLL_GetOption & VMMDLL_SetOption +* Options used together with the functions: VMMDLL_ConfigGet & VMMDLL_ConfigSet * Options are defined with either: VMMDLL_OPT_* in this header file or as -* MEMDEVICE_OPT_* in memdevice.h +* LEECHCORE_OPT_* in leechcore.h * For more detailed information check the sources for individual device types. */ #define VMMDLL_OPT_CORE_PRINTF_ENABLE 0x80000001 // RW @@ -349,17 +360,17 @@ typedef struct tdVMMDLL_PLUGIN_REGINFO { #define VMMDLL_FLAG_ZEROPAD_ON_FAIL 0x0002 // zero pad failed physical memory reads and report success if read within range of physical memory. #define VMMDLL_FLAG_FORCECACHE_READ 0x0008 // force use of cache - fail non-cached pages - only valid for reads, invalid with VMM_FLAG_NOCACHE/VMM_FLAG_ZEROPAD_ON_FAIL. #define VMMDLL_FLAG_NOPAGING 0x0010 // do not try to retrieve memory from paged out memory from pagefile/compressed (even if possible) +#define VMMDLL_FLAG_NOPAGING_IO 0x0020 // do not try to retrieve memory from paged out memory if read would incur additional I/O (even if possible). /* * Read memory in various non-contigious locations specified by the pointers to -* the items in the ppDMAs array. Result for each unit of work will be given +* the items in the ppMEMs array. Result for each unit of work will be given * individually. No upper limit of number of items to read, but no performance * boost will be given if above hardware limit. Max size of each unit of work is * one 4k page (4096 bytes). * -- dwPID - PID of target process, (DWORD)-1 to read physical memory. * -- ppMEMs = array of scatter read headers. -* -- cpMEMs = count of ppDMAs. -* -- pcpDMAsRead = optional count of number of successfully read ppDMAs. +* -- cpMEMs = count of ppMEMs. * -- flags = optional flags as given by VMMDLL_FLAG_* * -- return = the number of successfully read items. */ @@ -384,7 +395,7 @@ BOOL VMMDLL_MemReadPage(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Inout_bytecount_(40 * -- return = success/fail (depending if all requested bytes are read or not). */ _Success_(return) -BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWORD cb); +BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb); /* * Read a contigious amount of memory and report the number of bytes read in pcbRead. @@ -398,7 +409,7 @@ BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWO * read - it's recommended to verify pcbReadOpt parameter. */ _Success_(return) -BOOL VMMDLL_MemReadEx(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWORD cb, _Out_opt_ PDWORD pcbReadOpt, _In_ ULONG64 flags); +BOOL VMMDLL_MemReadEx(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb, _Out_opt_ PDWORD pcbReadOpt, _In_ ULONG64 flags); /* * Prefetch a number of addresses (specified in the pA array) into the memory @@ -426,7 +437,7 @@ BOOL VMMDLL_MemPrefetchPages(_In_ DWORD dwPID, _In_reads_(cPrefetchAddresses) PU * -- return = TRUE on success, FALSE on partial or zero write. */ _Success_(return) -BOOL VMMDLL_MemWrite(_In_ DWORD dwPID, _In_ ULONG64 qwA, _In_ PBYTE pb, _In_ DWORD cb); +BOOL VMMDLL_MemWrite(_In_ DWORD dwPID, _In_ ULONG64 qwA, _In_reads_(cb) PBYTE pb, _In_ DWORD cb); /* * Translate a virtual address to a physical address by walking the page tables @@ -442,106 +453,286 @@ BOOL VMMDLL_MemVirt2Phys(_In_ DWORD dwPID, _In_ ULONG64 qwVA, _Out_ PULONG64 pqw //----------------------------------------------------------------------------- -// VMM PROCESS FUNCTIONALITY BELOW: -// Functionality below is mostly relating to Windows processes. +// VMM PROCESS MAP FUNCTIONALITY BELOW: +// Functionality for retrieving process related collections of items such as +// page table map (PTE), virtual address descriptor map (VAD), loaded modules, +// heaps and threads. //----------------------------------------------------------------------------- -/* -* Retrieve an active process given it's name. Please note that if multiple -* processes with the same name exists only one will be returned. If required to -* parse all processes with the same name please iterate over the PID list by -* calling VMMDLL_PidList together with VMMDLL_ProcessGetInformation. -* -- szProcName = process name case insensitive. -* -- pdwPID = pointer that will receive PID on success. -* -- return -*/ -_Success_(return) -BOOL VMMDLL_PidGetFromName(_In_ LPSTR szProcName, _Out_ PDWORD pdwPID); - -/* -* List the PIDs in the system. -* -- pPIDs = DWORD array of at least number of PIDs in system, or NULL. -* -- pcPIDs = size of (in number of DWORDs) pPIDs array on entry, number of PIDs in system on exit. -* -- return = success/fail. -*/ -_Success_(return) -BOOL VMMDLL_PidList(_Out_opt_ PDWORD pPIDs, _Inout_ PULONG64 pcPIDs); +#define VMMDLL_MAP_PTE_VERSION 1 +#define VMMDLL_MAP_VAD_VERSION 1 +#define VMMDLL_MAP_MODULE_VERSION 1 +#define VMMDLL_MAP_HEAP_VERSION 1 +#define VMMDLL_MAP_THREAD_VERSION 1 +#define VMMDLL_MAP_HANDLE_VERSION 1 -// flags to check for existence in the fPage field of PCILEECH_VMM_MEMMAP_ENTRY +// flags to check for existence in the fPage field of VMMDLL_MAP_PTEENTRY #define VMMDLL_MEMMAP_FLAG_PAGE_W 0x0000000000000002 #define VMMDLL_MEMMAP_FLAG_PAGE_NS 0x0000000000000004 #define VMMDLL_MEMMAP_FLAG_PAGE_NX 0x8000000000000000 #define VMMDLL_MEMMAP_FLAG_PAGE_MASK 0x8000000000000006 -typedef struct tdVMMDLL_MEMMAP_ENTRY { - ULONG64 AddrBase; - ULONG64 cPages; - ULONG64 fPage; +typedef struct tdVMMDLL_MAP_PTEENTRY { + QWORD vaBase; + QWORD cPages; + QWORD fPage; BOOL fWoW64; - CHAR szTag[32]; -} VMMDLL_MEMMAP_ENTRY, *PVMMDLL_MEMMAP_ENTRY; + DWORD cwszText; + LPWSTR wszText; + DWORD _Reserved1[2]; +} VMMDLL_MAP_PTEENTRY, *PVMMDLL_MAP_PTEENTRY; + +typedef struct tdVMMDLL_MAP_VADENTRY { + QWORD vaStart; + QWORD vaEnd; + QWORD vaVad; + // DWORD 0 + DWORD VadType : 3; // Pos 0 + DWORD Protection : 5; // Pos 3 + DWORD fImage : 1; // Pos 8 + DWORD fFile : 1; // Pos 9 + DWORD fPageFile : 1; // Pos 10 + DWORD fPrivateMemory : 1; // Pos 11 + DWORD fTeb : 1; // Pos 12 + DWORD fStack : 1; // Pos 13 + DWORD fSpare : 2; // Pos 14 + DWORD HeapNum : 7; // Pos 16 + DWORD fHeap : 1; // Pos 23 + DWORD cwszDescription : 8; // Pos 24 + // DWORD 1 + DWORD CommitCharge : 31; // Pos 0 + DWORD MemCommit : 1; // Pos 31 + DWORD u2; + DWORD cbPrototypePte; + QWORD vaPrototypePte; + QWORD vaSubsection; + LPWSTR wszText; // optional LPWSTR pointed into VMMDLL_MAP_VAD.wszMultiText + DWORD cwszText; // WCHAR count not including terminating null + DWORD _Reserved1; +} VMMDLL_MAP_VADENTRY, *PVMMDLL_MAP_VADENTRY; + +typedef struct tdVMMDLL_MAP_MODULEENTRY { + QWORD vaBase; + QWORD vaEntry; + DWORD cbImageSize; + BOOL fWoW64; + LPWSTR wszText; + DWORD cwszText; // wchar count not including terminating null + DWORD _Reserved1[7]; +} VMMDLL_MAP_MODULEENTRY, *PVMMDLL_MAP_MODULEENTRY; + +typedef struct tdVMMDLL_MAP_HEAPENTRY { + QWORD vaHeapSegment; + DWORD cPages; + DWORD cPagesUnCommitted : 24; + DWORD HeapId : 7; + DWORD fPrimary : 1; +} VMMDLL_MAP_HEAPENTRY, *PVMMDLL_MAP_HEAPENTRY; + +typedef struct tdVMMDLL_MAP_THREADENTRY { + DWORD dwTID; + DWORD dwPID; + DWORD dwExitStatus; + UCHAR bState; + UCHAR bRunning; + UCHAR bPriority; + UCHAR bBasePriority; + QWORD vaETHREAD; + QWORD vaTeb; + QWORD ftCreateTime; + QWORD ftExitTime; + QWORD vaStartAddress; + QWORD vaStackBaseUser; + QWORD vaStackLimitUser; + QWORD vaStackBaseKernel; + QWORD vaStackLimitKernel; + DWORD _FutureUse[10]; +} VMMDLL_MAP_THREADENTRY, *PVMMDLL_MAP_THREADENTRY; + +typedef struct tdVMMDLL_MAP_HANDLEENTRY { + QWORD vaObject; + DWORD dwHandle; + DWORD dwGrantedAccess : 24; + DWORD iType : 8; + QWORD qwHandleCount; + QWORD qwPointerCount; + QWORD vaObjectCreateInfo; + QWORD vaSecurityDescriptor; + LPWSTR wszText; + DWORD cwszText; + DWORD dwPID; + DWORD dwPoolTag; + DWORD _FutureUse[4]; + DWORD cwszType; + LPWSTR wszType; +} VMMDLL_MAP_HANDLEENTRY, *PVMMDLL_MAP_HANDLEENTRY; + +typedef struct tdVMMDLL_MAP_PTE { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // NULL or multi-wstr pointed into by VMMDLL_MAP_VADENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_PTEENTRY pMap[]; // map entries. +} VMMDLL_MAP_PTE, *PVMMDLL_MAP_PTE; + +typedef struct tdVMMDLL_MAP_VAD { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // NULL or multi-wstr pointed into by VMMDLL_MAP_VADENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_VADENTRY pMap[]; // map entries. +} VMMDLL_MAP_VAD, *PVMMDLL_MAP_VAD; + +typedef struct tdVMMDLL_MAP_MODULE { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // multi-wstr pointed into by VMMDLL_MAP_MODULEENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_MODULEENTRY pMap[]; // map entries. +} VMMDLL_MAP_MODULE, *PVMMDLL_MAP_MODULE; + +typedef struct tdVMMDLL_MAP_HEAP { + DWORD dwVersion; + DWORD _Reserved1[8]; + DWORD cMap; // # map entries. + VMMDLL_MAP_HEAPENTRY pMap[]; // map entries. +} VMMDLL_MAP_HEAP, *PVMMDLL_MAP_HEAP; + +typedef struct tdVMMDLL_MAP_THREAD { + DWORD dwVersion; + DWORD _Reserved[8]; + DWORD cMap; // # map entries. + VMMDLL_MAP_THREADENTRY pMap[]; // map entries. +} VMMDLL_MAP_THREAD, *PVMMDLL_MAP_THREAD; + +typedef struct tdVMMDLL_MAP_HANDLE { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // multi-wstr pointed into by VMMDLL_MAP_HANDLEENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_HANDLEENTRY pMap[]; // map entries. +} VMMDLL_MAP_HANDLE, *PVMMDLL_MAP_HANDLE; /* -* Retrieve memory map entries from the specified process. Memory map entries -* are copied into the user supplied buffer that must be at least of size: -* sizeof(VMMDLL_MEMMAP_ENTRY)*pcMemMapEntries bytes. -* If the pMemMapEntries is set to NULL the number of memory map entries will be -* given in the pcMemMapEntries parameter. +* Retrieve the memory map entries based on hardware page tables (PTE) for the +* process. If pPteMap is set to NULL the number of bytes required will be +* returned in parameter pcbPteMap. +* Entries returned are sorted on VMMDLL_MAP_PTEENTRY.vaBase * -- dwPID -* -- pMemMapEntries = buffer of minimum length sizeof(VMMDLL_MEMMAP_ENTRY)*pcMemMapEntries, or NULL. -* -- pcMemMapEntries = pointer to number of memory map entries. +* -- pPteMap = buffer of minimum byte length *pcbPteMap or NULL. +* -- pcbPteMap = pointer to byte count of pPteMap buffer. * -- fIdentifyModules = try identify modules as well (= slower) * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetMemoryMap(_In_ DWORD dwPID, _Out_opt_ PVMMDLL_MEMMAP_ENTRY pMemMapEntries, _Inout_ PULONG64 pcMemMapEntries, _In_ BOOL fIdentifyModules); +BOOL VMMDLL_ProcessMap_GetPte(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbPteMap) PVMMDLL_MAP_PTE pPteMap, _Inout_ PDWORD pcbPteMap, _In_ BOOL fIdentifyModules); /* -* Retrieve a single memory map entry given a virtual address within that entrys -* range. +* Retrieve memory map entries based on virtual address descriptor (VAD) for +* the process. If pVadMap is set to NULL the number of bytes required +* will be returned in parameter pcbVadMap. +* Entries returned are sorted on VMMDLL_MAP_VADENTRY.vaStart * -- dwPID -* -- pMemMapEntry -* -- va = virtual address in the memory map entry to retrieve. +* -- pVadMap = buffer of minimum byte length *pcbVadMap or NULL. +* -- pcbVadMap = pointer to byte count of pVadMap buffer. * -- fIdentifyModules = try identify modules as well (= slower) * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetMemoryMapEntry(_In_ DWORD dwPID, _Out_ PVMMDLL_MEMMAP_ENTRY pMemMapEntry, _In_ ULONG64 va, _In_ BOOL fIdentifyModules); - -typedef struct tdVMMDLL_MODULEMAP_ENTRY { - ULONG64 BaseAddress; - ULONG64 EntryPoint; - DWORD SizeOfImage; - BOOL fWoW64; - CHAR szName[32]; -} VMMDLL_MODULEMAP_ENTRY, *PVMMDLL_MODULEMAP_ENTRY; +BOOL VMMDLL_ProcessMap_GetVad(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbVadMap) PVMMDLL_MAP_VAD pVadMap, _Inout_ PDWORD pcbVadMap, _In_ BOOL fIdentifyModules); /* -* Retrieve the module entries from the specified process. The module entries -* are copied into the user supplied buffer that must be at least of size: -* sizeof(VMMDLL_MODULEMAP_ENTRY)*pcModuleEntries bytes long. If the -* pcModuleEntries is set to NULL the number of module entries will be given -* in the pcModuleEntries parameter. +* Retrieve the modules (.dlls) for the specified process. If pModuleMap is set +* to NULL the number of bytes required will be returned in parameter pcbModuleMap. * -- dwPID -* -- pModuleEntries = buffer of minimum length sizeof(VMMDLL_MODULEMAP_ENTRY)*pcModuleEntries, or NULL. -* -- pcModuleEntries = pointer to number of memory map entries. +* -- pModuleMap = buffer of minimum byte length *pcbModuleMap or NULL. +* -- pcbModuleMap = pointer to byte count of pModuleMap buffer. * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetModuleMap(_In_ DWORD dwPID, _Out_opt_ PVMMDLL_MODULEMAP_ENTRY pModuleEntries, _Inout_ PULONG64 pcModuleEntries); +BOOL VMMDLL_ProcessMap_GetModule(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbModuleMap) PVMMDLL_MAP_MODULE pModuleMap, _Inout_ PDWORD pcbModuleMap); /* -* Retrieve a module (.exe or .dll or similar) given a module name. +* Retrieve a module map entry (.exe / .dll) given a process and module name. +* NB! PVMMDLL_MAP_MODULEENTRY->wszText will not be set by this function. +* If module name is required use VMMDLL_ProcessMap_GetModule(). * -- dwPID * -- szModuleName -* -- pModuleEntry +* -- pModuleMapEntry * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetModuleFromName(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _Out_ PVMMDLL_MODULEMAP_ENTRY pModuleEntry); +BOOL VMMDLL_ProcessMap_GetModuleFromName(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _Out_ PVMMDLL_MAP_MODULEENTRY pModuleMapEntry); + +/* +* Retrieve the heaps for the specified process. If pHeapMap is set to NULL +* the number of bytes required will be returned in parameter pcbHeapMap. +* -- dwPID +* -- pHeapMap = buffer of minimum byte length *pcbHeapMap or NULL. +* -- pcbHeapMap = pointer to byte count of pHeapMap buffer. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_ProcessMap_GetHeap(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbHeapMap) PVMMDLL_MAP_HEAP pHeapMap, _Inout_ PDWORD pcbHeapMap); + +/* +* Retrieve the threads for the specified process. If pThreadMap is set to NULL +* the number of bytes required will be returned in parameter pcbThreadMap. +* Entries returned are sorted on VMMDLL_MAP_THREADENTRY.dwTID +* -- dwPID +* -- pThreadMap = buffer of minimum byte length *pcbThreadMap or NULL. +* -- pcbThreadMap = pointer to byte count of pThreadMap buffer. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_ProcessMap_GetThread(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbThreadMap) PVMMDLL_MAP_THREAD pThreadMap, _Inout_ PDWORD pcbThreadMap); + +/* +* Retrieve the handles for the specified process. If pHandleMap is set to NULL +* the number of bytes required will be returned in parameter pcbHandleMap. +* Entries returned are sorted on VMMDLL_MAP_HANDLEENTRY.dwHandle +* -- dwPID +* -- pHandleMap = buffer of minimum byte length *pcbHandleMap or NULL. +* -- pcbHandleMap = pointer to byte count of pHandleMap buffer. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_ProcessMap_GetHandle(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbHandleMap) PVMMDLL_MAP_HANDLE pHandleMap, _Inout_ PDWORD pcbHandleMap); + + + +//----------------------------------------------------------------------------- +// VMM PROCESS FUNCTIONALITY BELOW: +// Functionality below is mostly relating to Windows processes. +//----------------------------------------------------------------------------- + +/* +* Retrieve an active process given it's name. Please note that if multiple +* processes with the same name exists only one will be returned. If required to +* parse all processes with the same name please iterate over the PID list by +* calling VMMDLL_PidList together with VMMDLL_ProcessGetInformation. +* -- szProcName = process name case insensitive. +* -- pdwPID = pointer that will receive PID on success. +* -- return +*/ +_Success_(return) +BOOL VMMDLL_PidGetFromName(_In_ LPSTR szProcName, _Out_ PDWORD pdwPID); + +/* +* List the PIDs in the system. +* -- pPIDs = DWORD array of at least number of PIDs in system, or NULL. +* -- pcPIDs = size of (in number of DWORDs) pPIDs array on entry, number of PIDs in system on exit. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_PidList(_Out_writes_opt_(*pcPIDs) PDWORD pPIDs, _Inout_ PULONG64 pcPIDs); #define VMMDLL_PROCESS_INFORMATION_MAGIC 0xc0ffee663df9301e -#define VMMDLL_PROCESS_INFORMATION_VERSION 4 +#define VMMDLL_PROCESS_INFORMATION_VERSION 5 typedef struct tdVMMDLL_PROCESS_INFORMATION { ULONG64 magic; @@ -561,7 +752,7 @@ typedef struct tdVMMDLL_PROCESS_INFORMATION { struct { ULONG64 vaEPROCESS; ULONG64 vaPEB; - ULONG64 vaENTRY; + ULONG64 _Reserved1; BOOL fWow64; DWORD vaPEB32; // WoW64 only } win; @@ -613,37 +804,37 @@ typedef struct tdVMMDLL_IAT_ENTRY { * If the pData == NULL upon entry the number of entries of the pData array must * have in order to be able to hold the data is returned. * -- dwPID -* -- szModule +* -- wszModule * -- pData * -- cData * -- pcData * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetDirectories(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_writes_(16) PIMAGE_DATA_DIRECTORY pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetDirectories(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_writes_(16) PIMAGE_DATA_DIRECTORY pData, _In_ DWORD cData, _Out_ PDWORD pcData); _Success_(return) -BOOL VMMDLL_ProcessGetSections(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PIMAGE_SECTION_HEADER pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetSections(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PIMAGE_SECTION_HEADER pData, _In_ DWORD cData, _Out_ PDWORD pcData); _Success_(return) -BOOL VMMDLL_ProcessGetEAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PVMMDLL_EAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetEAT(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PVMMDLL_EAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); _Success_(return) -BOOL VMMDLL_ProcessGetIAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PVMMDLL_IAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetIAT(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PVMMDLL_IAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); /* * Retrieve the virtual address of a given function inside a process/module. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- szFunctionName * -- return = virtual address of function, zero on fail. */ -ULONG64 VMMDLL_ProcessGetProcAddress(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szFunctionName); +ULONG64 VMMDLL_ProcessGetProcAddress(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szFunctionName); /* * Retrieve the base address of a given module. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- return = virtual address of module base, zero on fail. */ -ULONG64 VMMDLL_ProcessGetModuleBase(_In_ DWORD dwPID, _In_ LPSTR szModuleName); +ULONG64 VMMDLL_ProcessGetModuleBase(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName); @@ -729,7 +920,7 @@ BOOL VMMDLL_WinReg_HiveList(_Out_writes_(cHives) PVMMDLL_REGISTRY_HIVE_INFORMATI * -- ra * -- pb * -- cb -* -- pcbRead +* -- pcbReadOpt * -- flags = flags as in VMMDLL_FLAG_* * -- return = success/fail. NB! reads may report as success even if 0 bytes are * read - it's recommended to verify pcbReadOpt parameter. @@ -901,26 +1092,26 @@ typedef struct tdVMMDLL_WIN_THUNKINFO_EAT { * function. This includes the virtual address of the IAT thunk which is useful * for hooking. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- szImportModuleName * -- szImportFunctionName * -- pThunkIAT * -- return */ _Success_(return) -BOOL VMMDLL_WinGetThunkInfoIAT(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szImportModuleName, _In_ LPSTR szImportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_IAT pThunkInfoIAT); +BOOL VMMDLL_WinGetThunkInfoIAT(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szImportModuleName, _In_ LPSTR szImportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_IAT pThunkInfoIAT); /* * Retrieve information about the export address table EAT thunk for an exported * function. This includes the virtual address of the EAT thunk which is useful * for hooking. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- pThunkEAT * -- return */ _Success_(return) -BOOL VMMDLL_WinGetThunkInfoEAT(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT); +BOOL VMMDLL_WinGetThunkInfoEAT(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT); diff --git a/vmm_example/vmmdll_example.c b/vmm_example/vmmdll_example.c index 81e87593..0192427d 100644 --- a/vmm_example/vmmdll_example.c +++ b/vmm_example/vmmdll_example.c @@ -66,6 +66,38 @@ VOID CallbackList_AddDirectory(_Inout_ HANDLE h, _In_opt_ LPSTR szName, _In_opt_ } } +VOID VadMap_Protection(_In_ PVMMDLL_MAP_VADENTRY pVad, _Out_writes_(6) LPSTR sz) +{ + BYTE vh = (BYTE)pVad->Protection >> 3; + BYTE vl = (BYTE)pVad->Protection & 7; + sz[0] = pVad->fPrivateMemory ? 'p' : '-'; // PRIVATE MEMORY + sz[1] = (vh & 2) ? ((vh & 1) ? 'm' : 'g') : ((vh & 1) ? 'n' : '-'); // -/NO_CACHE/GUARD/WRITECOMBINE + sz[2] = ((vl == 1) || (vl == 3) || (vl == 4) || (vl == 6)) ? 'r' : '-'; // COPY ON WRITE + sz[3] = (vl & 4) ? 'w' : '-'; // WRITE + sz[4] = (vl & 2) ? 'x' : '-'; // EXECUTE + sz[5] = ((vl == 5) || (vl == 7)) ? 'c' : '-'; // COPY ON WRITE + if(sz[1] != '-' && sz[2] == '-' && sz[3] == '-' && sz[4] == '-' && sz[5] == '-') { sz[1] = '-'; } +} + +LPSTR VadMap_Type(_In_ PVMMDLL_MAP_VADENTRY pVad) +{ + if(pVad->fImage) { + return "Image"; + } else if(pVad->fFile) { + return "File "; + } else if(pVad->fHeap) { + return "Heap "; + } else if(pVad->fStack) { + return "Stack"; + } else if(pVad->fTeb) { + return "Teb "; + } else if(pVad->fPageFile) { + return "Pf "; + } else { + return " "; + } +} + // ---------------------------------------------------------------------------- // Main entry point which contains various sample code how to use PCILeech DLL. // Please walk though for different API usage examples. To select device ensure @@ -265,48 +297,104 @@ int main(_In_ int argc, _In_ char* argv[]) // more resilient against anti-reversing techniques that may be employed in // some processes. printf("------------------------------------------------------------\n"); - printf("#06: Get Memory Map of 'explorer.exe'. \n"); + printf("#06: Get PTE Memory Map of 'explorer.exe'. \n"); ShowKeyPress(); - ULONG64 cMemMapEntries = 0; - PVMMDLL_MEMMAP_ENTRY pMemMapEntries; - printf("CALL: VMMDLL_ProcessGetMemoryMap #1\n"); - result = VMMDLL_ProcessGetMemoryMap(dwPID, NULL, &cMemMapEntries, TRUE); + DWORD cbPteMap = 0; + PVMMDLL_MAP_PTE pPteMap = NULL; + PVMMDLL_MAP_PTEENTRY pPteMapEntry; + printf("CALL: VMMDLL_ProcessMap_GetPte #1\n"); + result = VMMDLL_ProcessMap_GetPte(dwPID, NULL, &cbPteMap, TRUE); if(result) { - printf("SUCCESS: VMMDLL_ProcessGetMemoryMap #1\n"); - printf(" Count = %lli\n", cMemMapEntries); + printf("SUCCESS: VMMDLL_ProcessMap_GetPte #1\n"); + printf(" ByteCount = %i\n", cbPteMap); } else { - printf("FAIL: VMMDLL_ProcessGetMemoryMap #1\n"); + printf("FAIL: VMMDLL_ProcessMap_GetPte #1\n"); return 1; } - pMemMapEntries = (PVMMDLL_MEMMAP_ENTRY)LocalAlloc(0, cMemMapEntries * sizeof(VMMDLL_MEMMAP_ENTRY)); - if(!pMemMapEntries) { + pPteMap = (PVMMDLL_MAP_PTE)LocalAlloc(0, cbPteMap); + if(!pPteMap) { printf("FAIL: OutOfMemory\n"); return 1; } - printf("CALL: VMMDLL_ProcessGetMemoryMap #2\n"); - result = VMMDLL_ProcessGetMemoryMap(dwPID, pMemMapEntries, &cMemMapEntries, TRUE); + printf("CALL: VMMDLL_ProcessMap_GetPte #2\n"); + result = VMMDLL_ProcessMap_GetPte(dwPID, pPteMap, &cbPteMap, TRUE); if(result) { - printf("SUCCESS: VMMDLL_ProcessGetMemoryMap #2\n"); + printf("SUCCESS: VMMDLL_ProcessMap_GetPte #2\n"); printf(" # #PAGES ADRESS_RANGE SRWX\n"); printf(" ====================================================\n"); - for(i = 0; i < cMemMapEntries; i++) { + for(i = 0; i < pPteMap->cMap; i++) { + pPteMapEntry = &pPteMap->pMap[i]; + printf( + " %04x %8x %016llx-%016llx %sr%s%s%s%S\n", + i, + (DWORD)pPteMapEntry->cPages, + pPteMapEntry->vaBase, + pPteMapEntry->vaBase + (pPteMapEntry->cPages << 12) - 1, + pPteMapEntry->fPage & VMMDLL_MEMMAP_FLAG_PAGE_NS ? "-" : "s", + pPteMapEntry->fPage & VMMDLL_MEMMAP_FLAG_PAGE_W ? "w" : "-", + pPteMapEntry->fPage & VMMDLL_MEMMAP_FLAG_PAGE_NX ? "-" : "x", + pPteMapEntry->cwszText ? (pPteMapEntry->fWoW64 ? " 32 " : " ") : "", + pPteMapEntry->wszText + ); + } + } else { + printf("FAIL: VMMDLL_ProcessMap_GetPte #2\n"); + return 1; + } + LocalFree(pPteMap); + pPteMap = NULL; + + + // Retrieve the memory map from the virtual address descriptors (VAD). This + // function also makes additional parsing to identify modules and tag the + // memory map with them. + printf("------------------------------------------------------------\n"); + printf("#07: Get VAD Memory Map of 'explorer.exe'. \n"); + ShowKeyPress(); + CHAR szVadProtection[7] = { 0 }; + DWORD cbVadMap = 0; + PVMMDLL_MAP_VAD pVadMap = NULL; + PVMMDLL_MAP_VADENTRY pVadMapEntry; + printf("CALL: VMMDLL_ProcessMap_GetVad #1\n"); + result = VMMDLL_ProcessMap_GetVad(dwPID, NULL, &cbVadMap, TRUE); + if(result) { + printf("SUCCESS: VMMDLL_ProcessMap_GetVad #1\n"); + printf(" ByteCount = %i\n", cbVadMap); + } else { + printf("FAIL: VMMDLL_ProcessMap_GetVad #1\n"); + return 1; + } + pVadMap = (PVMMDLL_MAP_VAD)LocalAlloc(0, cbVadMap); + if(!pVadMap) { + printf("FAIL: OutOfMemory\n"); + return 1; + } + printf("CALL: VMMDLL_ProcessMap_GetVad #2\n"); + result = VMMDLL_ProcessMap_GetVad(dwPID, pVadMap, &cbVadMap, TRUE); + if(result) { + printf("SUCCESS: VMMDLL_ProcessMap_GetVad #2\n"); + printf(" # ADRESS_RANGE KERNEL_ADDR TYPE PROT INFO \n"); + printf(" ============================================================================\n"); + for(i = 0; i < pVadMap->cMap; i++) { + pVadMapEntry = &pVadMap->pMap[i]; + VadMap_Protection(pVadMapEntry, szVadProtection); printf( - " %04x %8x %016llx-%016llx %sr%s%s%s%s\n", + " %04x %016llx-%016llx [%016llx] %s %s %S\n", i, - (DWORD)pMemMapEntries[i].cPages, - pMemMapEntries[i].AddrBase, - pMemMapEntries[i].AddrBase + (pMemMapEntries[i].cPages << 12) - 1, - pMemMapEntries[i].fPage & VMMDLL_MEMMAP_FLAG_PAGE_NS ? "-" : "s", - pMemMapEntries[i].fPage & VMMDLL_MEMMAP_FLAG_PAGE_W ? "w" : "-", - pMemMapEntries[i].fPage & VMMDLL_MEMMAP_FLAG_PAGE_NX ? "-" : "x", - pMemMapEntries[i].szTag[0] ? (pMemMapEntries[i].fWoW64 ? " 32 " : " ") : "", - pMemMapEntries[i].szTag + pVadMapEntry->vaStart, + pVadMapEntry->vaEnd, + pVadMapEntry->vaVad, + VadMap_Type(pVadMapEntry), + szVadProtection, + pVadMapEntry->wszText ); } } else { - printf("FAIL: VMMDLL_ProcessGetMemoryMap #2\n"); + printf("FAIL: VMMDLL_ProcessMap_GetVad #2\n"); return 1; } + LocalFree(pVadMap); + pVadMap = NULL; // Retrieve the list of loaded DLLs from the process. Please note that this @@ -315,44 +403,46 @@ int main(_In_ int argc, _In_ char* argv[]) // processes due to obfuscation and anti-reversing. If that is the case the // memory map may use alternative parsing techniques to list DLLs. printf("------------------------------------------------------------\n"); - printf("#07: Get Module Map of 'explorer.exe'. \n"); + printf("#08: Get Module Map of 'explorer.exe'. \n"); ShowKeyPress(); - ULONG64 cModules = 0; - PVMMDLL_MODULEMAP_ENTRY pModules; - printf("CALL: VMMDLL_ProcessGetModuleMap #1\n"); - result = VMMDLL_ProcessGetModuleMap(dwPID, NULL, &cModules); + DWORD cbModuleMap = 0; + PVMMDLL_MAP_MODULE pModuleMap = NULL; + printf("CALL: VMMDLL_ProcessMap_GetModule #1\n"); + result = VMMDLL_ProcessMap_GetModule(dwPID, NULL, &cbModuleMap); if(result) { - printf("SUCCESS: VMMDLL_ProcessGetModuleMap #1\n"); - printf(" Count = %lli\n", cModules); + printf("SUCCESS: VMMDLL_ProcessMap_GetModule #1\n"); + printf(" ByteCount = %i\n", cbModuleMap); } else { - printf("FAIL: VMMDLL_ProcessGetModuleMap #1\n"); + printf("FAIL: VMMDLL_ProcessMap_GetModule #1\n"); return 1; } - pModules = (PVMMDLL_MODULEMAP_ENTRY)LocalAlloc(0, cModules * sizeof(VMMDLL_MODULEMAP_ENTRY)); - if(!pModules) { + pModuleMap = (PVMMDLL_MAP_MODULE)LocalAlloc(0, cbModuleMap); + if(!pModuleMap) { printf("FAIL: OutOfMemory\n"); return 1; } - printf("CALL: VMMDLL_ProcessGetModuleMap #2\n"); - result = VMMDLL_ProcessGetModuleMap(dwPID, pModules, &cModules); + printf("CALL: VMMDLL_ProcessMap_GetModule #2\n"); + result = VMMDLL_ProcessMap_GetModule(dwPID, pModuleMap, &cbModuleMap); if(result) { - printf("SUCCESS: VMMDLL_ProcessGetModuleMap #2\n"); + printf("SUCCESS: VMMDLL_ProcessMap_GetModule #2\n"); printf(" MODULE_NAME BASE SIZE ENTRY\n"); printf(" ======================================================================================\n"); - for(i = 0; i < cModules; i++) { + for(i = 0; i < pModuleMap->cMap; i++) { printf( - " %-40.40s %i %016llx %08x %016llx\n", - pModules[i].szName, - pModules[i].fWoW64 ? 32 : 64, - pModules[i].BaseAddress, - pModules[i].SizeOfImage, - pModules[i].EntryPoint + " %-40.40S %i %016llx %08x %016llx\n", + pModuleMap->pMap[i].wszText, + pModuleMap->pMap[i].fWoW64 ? 32 : 64, + pModuleMap->pMap[i].vaBase, + pModuleMap->pMap[i].cbImageSize, + pModuleMap->pMap[i].vaEntry ); } } else { - printf("FAIL: VMMDLL_ProcessGetModuleMap #2\n"); + printf("FAIL: VMMDLL_ProcessMap_GetModule #2\n"); return 1; } + LocalFree(pModuleMap); + pModuleMap = NULL; // Retrieve the module of explorer.exe by its name. Note it is also possible @@ -361,41 +451,140 @@ int main(_In_ int argc, _In_ char* argv[]) // This required that the PEB and LDR list in-process haven't been tampered // with ... printf("------------------------------------------------------------\n"); - printf("#08: Get by name 'explorer.exe' in 'explorer.exe'. \n"); + printf("#09: Get module by name 'explorer.exe' in 'explorer.exe'. \n"); ShowKeyPress(); - VMMDLL_MODULEMAP_ENTRY ModuleEntry; - printf("CALL: VMMDLL_ProcessGetModuleFromName\n"); - result = VMMDLL_ProcessGetModuleFromName(dwPID, "explorer.exe", &ModuleEntry); + printf("CALL: VMMDLL_ProcessMap_GetModuleFromName\n"); + VMMDLL_MAP_MODULEENTRY ModuleEntryExplorer; + result = VMMDLL_ProcessMap_GetModuleFromName(dwPID, L"explorer.exe", &ModuleEntryExplorer); if(result) { - printf("SUCCESS: VMMDLL_ProcessGetModuleFromName\n"); + printf("SUCCESS: VMMDLL_ProcessMap_GetModuleFromName\n"); printf(" MODULE_NAME BASE SIZE ENTRY\n"); printf(" ======================================================================================\n"); printf( - " %-40.40s %i %016llx %08x %016llx\n", - ModuleEntry.szName, - ModuleEntry.fWoW64 ? 32 : 64, - ModuleEntry.BaseAddress, - ModuleEntry.SizeOfImage, - ModuleEntry.EntryPoint + " %-40.40S %i %016llx %08x %016llx\n", + L"explorer.exe", + ModuleEntryExplorer.fWoW64 ? 32 : 64, + ModuleEntryExplorer.vaBase, + ModuleEntryExplorer.cbImageSize, + ModuleEntryExplorer.vaEntry ); } else { - printf("FAIL: VMMDLL_ProcessGetModuleFromName\n"); + printf("FAIL: VMMDLL_ProcessMap_GetModuleFromName\n"); return 1; } + // THREADS: Retrieve thread information about threads in the explorer.exe + // process and display on the screen. + printf("------------------------------------------------------------\n"); + printf("#10: Get Thread Information of 'explorer.exe'. \n"); + ShowKeyPress(); + DWORD cbThreadMap = 0; + PVMMDLL_MAP_THREAD pThreadMap = NULL; + PVMMDLL_MAP_THREADENTRY pThreadMapEntry; + printf("CALL: VMMDLL_ProcessMap_GetThread #1\n"); + result = VMMDLL_ProcessMap_GetThread(dwPID, NULL, &cbThreadMap); + if(result) { + printf("SUCCESS: VMMDLL_ProcessMap_GetThread #1\n"); + printf(" ByteCount = %i\n", cbThreadMap); + } else { + printf("FAIL: VMMDLL_ProcessMap_GetThread #1\n"); + return 1; + } + pThreadMap = (PVMMDLL_MAP_THREAD)LocalAlloc(0, cbThreadMap); + if(!pThreadMap) { + printf("FAIL: OutOfMemory\n"); + return 1; + } + printf("CALL: VMMDLL_ProcessMap_GetThread #2\n"); + result = VMMDLL_ProcessMap_GetThread(dwPID, pThreadMap, &cbThreadMap); + if(result) { + printf("SUCCESS: VMMDLL_ProcessMap_GetThread #2\n"); + printf(" # TID PID ADDR_TEB ADDR_ETHREAD ADDR_START STACK\n"); + printf(" ===============================================================================\n"); + for(i = 0; i < pThreadMap->cMap; i++) { + pThreadMapEntry = &pThreadMap->pMap[i]; + printf( + " %04x %8x %8x %016llx %016llx %016llx [%016llx->%016llx]\n", + i, + pThreadMapEntry->dwTID, + pThreadMapEntry->dwPID, + pThreadMapEntry->vaTeb, + pThreadMapEntry->vaETHREAD, + pThreadMapEntry->vaStartAddress, + pThreadMapEntry->vaStackBaseUser, + pThreadMapEntry->vaStackLimitUser + ); + } + } else { + printf("FAIL: VMMDLL_ProcessMap_GetThread #2\n"); + return 1; + } + LocalFree(pThreadMap); + pThreadMap = NULL; + + + // THREADS: Retrieve handle information about handles in the explorer.exe + // process and display on the screen. + printf("------------------------------------------------------------\n"); + printf("#11: Get Handle Information of 'explorer.exe'. \n"); + ShowKeyPress(); + DWORD cbHandleMap = 0; + PVMMDLL_MAP_HANDLE pHandleMap = NULL; + PVMMDLL_MAP_HANDLEENTRY pHandleMapEntry; + printf("CALL: VMMDLL_ProcessMap_GetHandle #1\n"); + result = VMMDLL_ProcessMap_GetHandle(dwPID, NULL, &cbHandleMap); + if(result) { + printf("SUCCESS: VMMDLL_ProcessMap_GetHandle #1\n"); + printf(" ByteCount = %i\n", cbHandleMap); + } else { + printf("FAIL: VMMDLL_ProcessMap_GetHandle #1\n"); + return 1; + } + pHandleMap = (PVMMDLL_MAP_HANDLE)LocalAlloc(0, cbHandleMap); + if(!pHandleMap) { + printf("FAIL: OutOfMemory\n"); + return 1; + } + printf("CALL: VMMDLL_ProcessMap_GetHandle #2\n"); + result = VMMDLL_ProcessMap_GetHandle(dwPID, pHandleMap, &cbHandleMap); + if(result) { + printf("SUCCESS: VMMDLL_ProcessMap_GetHandle #2\n"); + printf(" # HANDLE PID ADDR_OBJECT ACCESS TYPE DESCRIPTION\n"); + printf(" ===========================================================================\n"); + for(i = 0; i < pHandleMap->cMap; i++) { + pHandleMapEntry = &pHandleMap->pMap[i]; + printf( + " %04x %8x %8x %016llx %6x %-16S %S\n", + i, + pHandleMapEntry->dwHandle, + pHandleMapEntry->dwPID, + pHandleMapEntry->vaObject, + pHandleMapEntry->dwGrantedAccess, + pHandleMapEntry->wszType, + pHandleMapEntry->wszText + ); + } + } else { + printf("FAIL: VMMDLL_ProcessMap_GetHandle #2\n"); + return 1; + } + LocalFree(pHandleMap); + pHandleMap = NULL; + + // Write virtual memory at PE header of Explorer.EXE and display the first // 0x80 bytes on-screen - afterwards. Maybe result of write is in there? // (only if device is capable of writes and target system accepts writes) printf("------------------------------------------------------------\n"); - printf("#09: Try write to virtual memory of Explorer.EXE PE header \n"); + printf("#12: Try write to virtual memory of Explorer.EXE PE header \n"); printf(" NB! Write capable device is required for success! \n"); printf(" (1) Read existing data from virtual memory. \n"); printf(" (2) Try write to virtual memory at PE header. \n"); printf(" (3) Read resulting data from virtual memory. \n"); ShowKeyPress(); printf("CALL: VMMDLL_MemRead - BEFORE WRITE\n"); - result = VMMDLL_MemRead(dwPID, ModuleEntry.BaseAddress, pbPage1, 0x1000); + result = VMMDLL_MemRead(dwPID, ModuleEntryExplorer.vaBase, pbPage1, 0x1000); if(result) { printf("SUCCESS: VMMDLL_MemRead - BEFORE WRITE\n"); PrintHexAscii(pbPage1, 0x80); @@ -411,9 +600,9 @@ int main(_In_ int argc, _In_ char* argv[]) 0x79, 0x20, 0x4d, 0x65, 0x6d, 0x50, 0x72, 0x6f, 0x63, 0x46, 0x53, 0x00, }; - VMMDLL_MemWrite(dwPID, ModuleEntry.BaseAddress + 0x58, pbWriteDataVirtual, cbWriteDataVirtual); + VMMDLL_MemWrite(dwPID, ModuleEntryExplorer.vaBase + 0x58, pbWriteDataVirtual, cbWriteDataVirtual); printf("CALL: VMMDLL_MemRead - AFTER WRITE\n"); - result = VMMDLL_MemRead(dwPID, ModuleEntry.BaseAddress, pbPage1, 0x1000); + result = VMMDLL_MemRead(dwPID, ModuleEntryExplorer.vaBase, pbPage1, 0x1000); if(result) { printf("SUCCESS: VMMDLL_MemRead - AFTER WRITE\n"); PrintHexAscii(pbPage1, 0x80); @@ -429,24 +618,25 @@ int main(_In_ int argc, _In_ char* argv[]) // This required that the PEB and LDR list in-process haven't been tampered // with ... printf("------------------------------------------------------------\n"); - printf("#10: Get by name 'kernel32.dll' in 'explorer.exe'. \n"); + printf("#13: Get by name 'kernel32.dll' in 'explorer.exe'. \n"); ShowKeyPress(); - printf("CALL: VMMDLL_ProcessGetModuleFromName\n"); - result = VMMDLL_ProcessGetModuleFromName(dwPID, "kernel32.dll", &ModuleEntry); + printf("CALL: VMMDLL_ProcessMap_GetModuleFromName\n"); + VMMDLL_MAP_MODULEENTRY ModuleEntryKernel32; + result = VMMDLL_ProcessMap_GetModuleFromName(dwPID, L"kernel32.dll", &ModuleEntryKernel32); if(result) { - printf("SUCCESS: VMMDLL_ProcessGetModuleFromName\n"); + printf("SUCCESS: VMMDLL_ProcessMap_GetModuleFromName\n"); printf(" MODULE_NAME BASE SIZE ENTRY\n"); printf(" ======================================================================================\n"); printf( - " %-40.40s %i %016llx %08x %016llx\n", - ModuleEntry.szName, - ModuleEntry.fWoW64 ? 32 : 64, - ModuleEntry.BaseAddress, - ModuleEntry.SizeOfImage, - ModuleEntry.EntryPoint + " %-40.40S %i %016llx %08x %016llx\n", + L"kernel32.dll", + ModuleEntryKernel32.fWoW64 ? 32 : 64, + ModuleEntryKernel32.vaBase, + ModuleEntryKernel32.cbImageSize, + ModuleEntryKernel32.vaEntry ); } else { - printf("FAIL: VMMDLL_ProcessGetModuleFromName\n"); + printf("FAIL: VMMDLL_ProcessMap_GetModuleFromName\n"); return 1; } @@ -456,12 +646,12 @@ int main(_In_ int argc, _In_ char* argv[]) // by default (if possible). If reads should be forced from the DMA device // please specify the flag: VMM_FLAG_NOCACHE printf("------------------------------------------------------------\n"); - printf("#11: Read 0x200 bytes of 'kernel32.dll' in 'explorer.exe'. \n"); + printf("#14: Read 0x200 bytes of 'kernel32.dll' in 'explorer.exe'. \n"); ShowKeyPress(); DWORD cRead; printf("CALL: VMMDLL_MemReadEx\n"); - result = VMMDLL_MemReadEx(dwPID, ModuleEntry.BaseAddress, pbPage2, 0x1000, &cRead, 0); // standard cached read - //result = VMMDLL_MemReadEx(dwPID, ModuleEntry.BaseAddress, pbPage2, 0x1000, &cRead, VMMDLL_FLAG_NOCACHE); // uncached read + result = VMMDLL_MemReadEx(dwPID, ModuleEntryKernel32.vaBase, pbPage2, 0x1000, &cRead, 0); // standard cached read + //result = VMMDLL_MemReadEx(dwPID, ModuleEntryKernel32.vaBase, pbPage2, 0x1000, &cRead, VMMDLL_FLAG_NOCACHE); // uncached read if(result) { printf("SUCCESS: VMMDLL_MemReadEx\n"); PrintHexAscii(pbPage2, min(cRead, 0x200)); @@ -473,26 +663,26 @@ int main(_In_ int argc, _In_ char* argv[]) // List the sections from the module of kernel32.dll. printf("------------------------------------------------------------\n"); - printf("#12: List sections of 'kernel32.dll' in 'explorer.exe'. \n"); + printf("#15: List sections of 'kernel32.dll' in 'explorer.exe'. \n"); ShowKeyPress(); + printf("CALL: VMMDLL_ProcessGetSections #1\n"); DWORD cSections; PIMAGE_SECTION_HEADER pSectionHeaders; - printf("CALL: VMMDLL_ProcessGetSections #1\n"); - result = VMMDLL_ProcessGetSections(dwPID, "kernel32.dll", NULL, 0, &cSections); + result = VMMDLL_ProcessGetSections(dwPID, L"kernel32.dll", NULL, 0, &cSections); if(result) { printf("SUCCESS: VMMDLL_ProcessGetSections #1\n"); - printf(" Count = %lli\n", cModules); + printf(" Count = %i\n", cSections); } else { printf("FAIL: VMMDLL_ProcessGetSections #1\n"); return 1; } pSectionHeaders = (PIMAGE_SECTION_HEADER)LocalAlloc(LMEM_ZEROINIT, cSections * sizeof(IMAGE_SECTION_HEADER)); - if(!pModules) { + if(!pSectionHeaders) { printf("FAIL: OutOfMemory\n"); return 1; } printf("CALL: VMMDLL_ProcessGetSections #2\n"); - result = VMMDLL_ProcessGetSections(dwPID, "kernel32.dll", pSectionHeaders, cSections, &cSections); + result = VMMDLL_ProcessGetSections(dwPID, L"kernel32.dll", pSectionHeaders, cSections, &cSections); if(result) { printf("SUCCESS: VMMDLL_ProcessGetSections #2\n"); printf(" # NAME OFFSET SIZE RWX\n"); @@ -517,7 +707,7 @@ int main(_In_ int argc, _In_ char* argv[]) // Scatter Read memory from each of the sections of kernel32.dll in explorer.exe printf("------------------------------------------------------------\n"); - printf("#13: 0x20 bytes of each 'kernel32.dll' section. \n"); + printf("#16: 0x20 bytes of each 'kernel32.dll' section. \n"); ShowKeyPress(); PPMEM_IO_SCATTER_HEADER ppMEMs = NULL; // Allocate empty scatter entries and populate them with the virtual addresses of @@ -535,7 +725,7 @@ int main(_In_ int argc, _In_ char* argv[]) for(i = 0; i < cSections; i++) { // populate the virtual address of each scatter entry with the address to read // (sections are assumed to be page-aligned in virtual memory. - ppMEMs[i]->qwA = ModuleEntry.BaseAddress + pSectionHeaders[i].VirtualAddress; + ppMEMs[i]->qwA = ModuleEntryKernel32.vaBase + pSectionHeaders[i].VirtualAddress; } // Scatter Read - read all scatter entries in one efficient go. In this // example the internal VMM cache is not to be used, and virtual memory @@ -565,13 +755,13 @@ int main(_In_ int argc, _In_ char* argv[]) // data directories in a PE is always 16 - so this can be used to simplify // calling the functionality somewhat. printf("------------------------------------------------------------\n"); - printf("#14: List directories of 'kernel32.dll' in 'explorer.exe'. \n"); + printf("#17: List directories of 'kernel32.dll' in 'explorer.exe'. \n"); ShowKeyPress(); LPCSTR DIRECTORIES[16] = { "EXPORT", "IMPORT", "RESOURCE", "EXCEPTION", "SECURITY", "BASERELOC", "DEBUG", "ARCHITECTURE", "GLOBALPTR", "TLS", "LOAD_CONFIG", "BOUND_IMPORT", "IAT", "DELAY_IMPORT", "COM_DESCRIPTOR", "RESERVED" }; DWORD cDirectories; IMAGE_DATA_DIRECTORY pDirectories[16]; printf("CALL: VMMDLL_ProcessGetDirectories\n"); - result = VMMDLL_ProcessGetDirectories(dwPID, "kernel32.dll", pDirectories, 16, &cDirectories); + result = VMMDLL_ProcessGetDirectories(dwPID, L"kernel32.dll", pDirectories, 16, &cDirectories); if(result) { printf("SUCCESS: PCIleech_VmmProcess_GetDirectories\n"); printf(" # NAME OFFSET SIZE\n"); @@ -593,12 +783,12 @@ int main(_In_ int argc, _In_ char* argv[]) // Retrieve the export address table (EAT) of kernel32.dll printf("------------------------------------------------------------\n"); - printf("#15: exports of 'kernel32.dll' in 'explorer.exe'. \n"); + printf("#18: exports of 'kernel32.dll' in 'explorer.exe'. \n"); ShowKeyPress(); DWORD cEATs; PVMMDLL_EAT_ENTRY pEATs; printf("CALL: VMMDLL_ProcessGetEAT #1\n"); - result = VMMDLL_ProcessGetEAT(dwPID, "kernel32.dll", NULL, 0, &cEATs); + result = VMMDLL_ProcessGetEAT(dwPID, L"kernel32.dll", NULL, 0, &cEATs); if(result) { printf("SUCCESS: VMMDLL_ProcessGetEAT #1\n"); printf(" Count = %i\n", cEATs); @@ -612,7 +802,7 @@ int main(_In_ int argc, _In_ char* argv[]) return 1; } printf("CALL: VMMDLL_ProcessGetEAT #2\n"); - result = VMMDLL_ProcessGetEAT(dwPID, "kernel32.dll", pEATs, cEATs, &cEATs); + result = VMMDLL_ProcessGetEAT(dwPID, L"kernel32.dll", pEATs, cEATs, &cEATs); if(result) { printf("SUCCESS: VMMDLL_ProcessGetEAT #2\n"); printf(" # OFFSET NAME\n"); @@ -633,12 +823,12 @@ int main(_In_ int argc, _In_ char* argv[]) // Retrieve the import address table (IAT) of kernel32.dll printf("------------------------------------------------------------\n"); - printf("#16: imports of 'kernel32.dll' in 'explorer.exe'. \n"); + printf("#19: imports of 'kernel32.dll' in 'explorer.exe'. \n"); ShowKeyPress(); DWORD cIATs; PVMMDLL_IAT_ENTRY pIATs; printf("CALL: VMMDLL_ProcessGetIAT #1\n"); - result = VMMDLL_ProcessGetIAT(dwPID, "kernel32.dll", NULL, 0, &cIATs); + result = VMMDLL_ProcessGetIAT(dwPID, L"kernel32.dll", NULL, 0, &cIATs); if(result) { printf("SUCCESS: VMMDLL_ProcessGetIAT #1\n"); printf(" Count = %i\n", cIATs); @@ -652,7 +842,7 @@ int main(_In_ int argc, _In_ char* argv[]) return 1; } printf("CALL: VMMDLL_ProcessGetIAT #2\n"); - result = VMMDLL_ProcessGetIAT(dwPID, "kernel32.dll", pIATs, cIATs, &cIATs); + result = VMMDLL_ProcessGetIAT(dwPID, L"kernel32.dll", pIATs, cIATs, &cIATs); if(result) { printf("SUCCESS: VMMDLL_ProcessGetIAT #2\n"); printf(" # VIRTUAL_ADDRESS MODULE!NAME\n"); @@ -678,7 +868,7 @@ int main(_In_ int argc, _In_ char* argv[]) // the API. // Virtual File System: 'List'. printf("------------------------------------------------------------\n"); - printf("#17: call the file system 'List' function on the root dir. \n"); + printf("#20: call the file system 'List' function on the root dir. \n"); ShowKeyPress(); VMMDLL_VFS_FILELIST VfsFileList; VfsFileList.dwVersion = VMMDLL_VFS_FILELIST_VERSION; @@ -698,10 +888,10 @@ int main(_In_ int argc, _In_ char* argv[]) // Virtual File System: 'Read' of 0x100 bytes from the offset 0x1000 // in the physical memory by reading the /pmem physical memory file. printf("------------------------------------------------------------\n"); - printf("#18: call the file system 'Read' function on the pmem file. \n"); + printf("#21: call the file system 'Read' function on the pmem file. \n"); ShowKeyPress(); printf("CALL: VMMDLL_VfsRead\n"); - nt = VMMDLL_VfsRead(L"\\pmem", pbPage1, 0x100, &i, 0x1000); + nt = VMMDLL_VfsRead(L"\\memory.pmem", pbPage1, 0x100, &i, 0x1000); if(nt == VMMDLL_STATUS_SUCCESS) { printf("SUCCESS: VMMDLL_VfsRead\n"); PrintHexAscii(pbPage1, i); @@ -714,7 +904,7 @@ int main(_In_ int argc, _In_ char* argv[]) // Initialize plugin manager so that statistics may be read in the // following read call to the .status built-in module/plugin. printf("------------------------------------------------------------\n"); - printf("#19: initialize virtual file system plugins \n"); + printf("#22: initialize virtual file system plugins \n"); printf(" (this is required for following read call) \n"); ShowKeyPress(); printf("CALL: VMMDLL_VfsInitializePlugins\n"); @@ -729,7 +919,7 @@ int main(_In_ int argc, _In_ char* argv[]) // Virtual File System: 'Read' statistics from the .status module/plugin. printf("------------------------------------------------------------\n"); - printf("#20: call file system 'Read' on .status\\statistics \n"); + printf("#23: call file system 'Read' on .status\\statistics \n"); ShowKeyPress(); printf("CALL: VMMDLL_VfsRead\n"); nt = VMMDLL_VfsRead(L"\\.status\\statistics", pbPage1, 0x1000, &i, 0); @@ -744,10 +934,10 @@ int main(_In_ int argc, _In_ char* argv[]) // Get base virtual address of ntoskrnl.exe printf("------------------------------------------------------------\n"); - printf("#21: get ntoskrnl.exe base virtual address \n"); + printf("#24: get ntoskrnl.exe base virtual address \n"); ShowKeyPress(); printf("CALL: VMMDLL_ProcessGetModuleBase\n"); - va = VMMDLL_ProcessGetModuleBase(4, "ntoskrnl.exe"); + va = VMMDLL_ProcessGetModuleBase(4, L"ntoskrnl.exe"); if(va) { printf("SUCCESS: VMMDLL_ProcessGetModuleBase\n"); printf(" %s = %016llx\n", "ntoskrnl.exe", va); @@ -759,10 +949,10 @@ int main(_In_ int argc, _In_ char* argv[]) // GetProcAddress from ntoskrnl.exe printf("------------------------------------------------------------\n"); - printf("#22: get proc address for ntoskrnl.exe!KeGetCurrentIrql \n"); + printf("#25: get proc address for ntoskrnl.exe!KeGetCurrentIrql \n"); ShowKeyPress(); printf("CALL: VMMDLL_ProcessGetProcAddress\n"); - va = VMMDLL_ProcessGetProcAddress(4, "ntoskrnl.exe", "KeGetCurrentIrql"); + va = VMMDLL_ProcessGetProcAddress(4, L"ntoskrnl.exe", "KeGetCurrentIrql"); if(va) { printf("SUCCESS: VMMDLL_ProcessGetProcAddress\n"); printf(" %s!%s = %016llx\n", "ntoskrnl.exe", "KeGetCurrentIrql", va); @@ -774,12 +964,12 @@ int main(_In_ int argc, _In_ char* argv[]) // Get EAT Thunk from ntoskrnl.exe!KeGetCurrentIrql printf("------------------------------------------------------------\n"); - printf("#23: Address of EAT thunk for ntoskrnl.exe!KeGetCurrentIrql \n"); + printf("#26: Address of EAT thunk for ntoskrnl.exe!KeGetCurrentIrql \n"); ShowKeyPress(); VMMDLL_WIN_THUNKINFO_EAT oThunkInfoEAT; ZeroMemory(&oThunkInfoEAT, sizeof(VMMDLL_WIN_THUNKINFO_EAT)); printf("CALL: VMMDLL_WinGetThunkInfoEAT\n"); - result = VMMDLL_WinGetThunkInfoEAT(4, "ntoskrnl.exe", "KeGetCurrentIrql", &oThunkInfoEAT); + result = VMMDLL_WinGetThunkInfoEAT(4, L"ntoskrnl.exe", "KeGetCurrentIrql", &oThunkInfoEAT); if(result) { printf("SUCCESS: VMMDLL_WinGetThunkInfoEAT\n"); printf(" vaFunction: %016llx\n", oThunkInfoEAT.vaFunction); @@ -794,12 +984,12 @@ int main(_In_ int argc, _In_ char* argv[]) // Get IAT Thunk ntoskrnl.exe -> hal.dll!HalSendNMI printf("------------------------------------------------------------\n"); - printf("#24: Address of IAT thunk for hal.dll!HalSendNMI in ntoskrnl\n"); + printf("#27: Address of IAT thunk for hal.dll!HalSendNMI in ntoskrnl\n"); ShowKeyPress(); VMMDLL_WIN_THUNKINFO_IAT oThunkInfoIAT; ZeroMemory(&oThunkInfoIAT, sizeof(VMMDLL_WIN_THUNKINFO_IAT)); printf("CALL: VMMDLL_WinGetThunkInfoIAT\n"); - result = VMMDLL_WinGetThunkInfoIAT(4, "ntoskrnl.Exe", "hal.Dll", "HalSendNMI", &oThunkInfoIAT); + result = VMMDLL_WinGetThunkInfoIAT(4, L"ntoskrnl.Exe", "hal.Dll", "HalSendNMI", &oThunkInfoIAT); if(result) { printf("SUCCESS: VMMDLL_WinGetThunkInfoIAT\n"); printf(" vaFunction: %016llx\n", oThunkInfoIAT.vaFunction); @@ -814,7 +1004,7 @@ int main(_In_ int argc, _In_ char* argv[]) // List Windows registry hives printf("------------------------------------------------------------\n"); - printf("#25: List Windows Registry Hives. \n"); + printf("#28: List Windows Registry Hives. \n"); ShowKeyPress(); DWORD cWinRegHives; PVMMDLL_REGISTRY_HIVE_INFORMATION pWinRegHives = NULL; @@ -843,7 +1033,7 @@ int main(_In_ int argc, _In_ char* argv[]) // Read 0x100 bytes from offset 0x1000 from the 1st located registry hive memory space printf("------------------------------------------------------------\n"); - printf("#26: Read 0x100 bytes from offset 0x1000 of registry hive \n"); + printf("#29: Read 0x100 bytes from offset 0x1000 of registry hive \n"); ShowKeyPress(); printf("CALL: VMMDLL_WinReg_HiveReadEx\n"); result = VMMDLL_WinReg_HiveReadEx(pWinRegHives[0].vaCMHIVE, 0x1000, pbPage1, 0x100, NULL, 0); diff --git a/vmmpyc/version.h b/vmmpyc/version.h index 7bf3c507..dab1c0f9 100644 --- a/vmmpyc/version.h +++ b/vmmpyc/version.h @@ -1,12 +1,12 @@ #define STRINGIZE2(s) #s #define STRINGIZE(s) STRINGIZE2(s) -#define VERSION_MAJOR 2 -#define VERSION_MINOR 10 -#define VERSION_REVISION 2 -#define VERSION_BUILD 2 +#define VERSION_MAJOR 3 +#define VERSION_MINOR 0 +#define VERSION_REVISION 0 +#define VERSION_BUILD 3 -#define VER_FILE_DESCRIPTION_STR "The Memory Process File System : Python API" +#define VER_FILE_DESCRIPTION_STR "MemProcFS : Python API" #define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD #define VER_FILE_VERSION_STR STRINGIZE(VERSION_MAJOR) \ "." STRINGIZE(VERSION_MINOR) \ diff --git a/vmmpyc/vmmdll.h b/vmmpyc/vmmdll.h index 8a6b6a38..87f3f191 100644 --- a/vmmpyc/vmmdll.h +++ b/vmmpyc/vmmdll.h @@ -4,7 +4,7 @@ // (c) Ulf Frisk, 2018-2019 // Author: Ulf Frisk, pcileech@frizk.net // -// Header Version: 2.10.2 +// Header Version: 3.0 // #include @@ -23,11 +23,11 @@ extern "C" { /* * Initialize VMM.DLL with command line parameters. For a more detailed info -* about the parameters please see github wiki for Memory Process File System -* and LeechCore. THIS IS THE PREFERED WAY OF INITIALIZING VMM.DLL +* about the parameters please see github wiki for MemProcFS and LeechCore. +* NB! LeechCore initialization parameters are _also_ valid to this function. * Important parameters are: -* -printf = show printf style outputs) -* -v -vv -vvv = extra verbosity levels) +* -printf = show printf style outputs. +* -v -vv -vvv = extra verbosity levels. * -device = device as on format for LeechCore - please see leechcore.h or * Github documentation for additional information. Some values * are: , fpga, usb3380, hvsavedstate, totalmeltdown, pmem @@ -35,8 +35,19 @@ extern "C" { * documentation for additional information. * -norefresh = disable background refreshes (even if backing memory is * volatile memory). -* -symbolserverdisable = disable symbol server until user change. This -* parameter will take precedence over registry settings. +* -symbolserverdisable = disable symbol server until user change. +* This parameter will take precedence over registry settings. +* -pagefile[0-9] = page file(s) to use in addition to physical memory. +* Normally pagefile.sys have index 0 and swapfile.sys index 1. +* Page files are in constant flux - do not use if time diff +* between memory dump and page files are more than few minutes. +* Example: 'pagefile0 swapfile.sys' +* -waitinitialize = Wait for initialization to complete before returning. +* Normal use is that some initialization is done asynchronously +* and may not be completed when initialization call is completed. +* This includes virtual memory compression, registry and more. +* Example: '-waitinitialize' +* * -- argc * -- argv * -- return = success/fail @@ -78,9 +89,9 @@ VOID VMMDLL_MemFree(_Frees_ptr_opt_ PVOID pvMem); //----------------------------------------------------------------------------- /* -* Options used together with the functions: VMMDLL_GetOption & VMMDLL_SetOption +* Options used together with the functions: VMMDLL_ConfigGet & VMMDLL_ConfigSet * Options are defined with either: VMMDLL_OPT_* in this header file or as -* MEMDEVICE_OPT_* in memdevice.h +* LEECHCORE_OPT_* in leechcore.h * For more detailed information check the sources for individual device types. */ #define VMMDLL_OPT_CORE_PRINTF_ENABLE 0x80000001 // RW @@ -349,17 +360,17 @@ typedef struct tdVMMDLL_PLUGIN_REGINFO { #define VMMDLL_FLAG_ZEROPAD_ON_FAIL 0x0002 // zero pad failed physical memory reads and report success if read within range of physical memory. #define VMMDLL_FLAG_FORCECACHE_READ 0x0008 // force use of cache - fail non-cached pages - only valid for reads, invalid with VMM_FLAG_NOCACHE/VMM_FLAG_ZEROPAD_ON_FAIL. #define VMMDLL_FLAG_NOPAGING 0x0010 // do not try to retrieve memory from paged out memory from pagefile/compressed (even if possible) +#define VMMDLL_FLAG_NOPAGING_IO 0x0020 // do not try to retrieve memory from paged out memory if read would incur additional I/O (even if possible). /* * Read memory in various non-contigious locations specified by the pointers to -* the items in the ppDMAs array. Result for each unit of work will be given +* the items in the ppMEMs array. Result for each unit of work will be given * individually. No upper limit of number of items to read, but no performance * boost will be given if above hardware limit. Max size of each unit of work is * one 4k page (4096 bytes). * -- dwPID - PID of target process, (DWORD)-1 to read physical memory. * -- ppMEMs = array of scatter read headers. -* -- cpMEMs = count of ppDMAs. -* -- pcpDMAsRead = optional count of number of successfully read ppDMAs. +* -- cpMEMs = count of ppMEMs. * -- flags = optional flags as given by VMMDLL_FLAG_* * -- return = the number of successfully read items. */ @@ -384,7 +395,7 @@ BOOL VMMDLL_MemReadPage(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Inout_bytecount_(40 * -- return = success/fail (depending if all requested bytes are read or not). */ _Success_(return) -BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWORD cb); +BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb); /* * Read a contigious amount of memory and report the number of bytes read in pcbRead. @@ -398,7 +409,7 @@ BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWO * read - it's recommended to verify pcbReadOpt parameter. */ _Success_(return) -BOOL VMMDLL_MemReadEx(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWORD cb, _Out_opt_ PDWORD pcbReadOpt, _In_ ULONG64 flags); +BOOL VMMDLL_MemReadEx(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb, _Out_opt_ PDWORD pcbReadOpt, _In_ ULONG64 flags); /* * Prefetch a number of addresses (specified in the pA array) into the memory @@ -426,7 +437,7 @@ BOOL VMMDLL_MemPrefetchPages(_In_ DWORD dwPID, _In_reads_(cPrefetchAddresses) PU * -- return = TRUE on success, FALSE on partial or zero write. */ _Success_(return) -BOOL VMMDLL_MemWrite(_In_ DWORD dwPID, _In_ ULONG64 qwA, _In_ PBYTE pb, _In_ DWORD cb); +BOOL VMMDLL_MemWrite(_In_ DWORD dwPID, _In_ ULONG64 qwA, _In_reads_(cb) PBYTE pb, _In_ DWORD cb); /* * Translate a virtual address to a physical address by walking the page tables @@ -442,106 +453,286 @@ BOOL VMMDLL_MemVirt2Phys(_In_ DWORD dwPID, _In_ ULONG64 qwVA, _Out_ PULONG64 pqw //----------------------------------------------------------------------------- -// VMM PROCESS FUNCTIONALITY BELOW: -// Functionality below is mostly relating to Windows processes. +// VMM PROCESS MAP FUNCTIONALITY BELOW: +// Functionality for retrieving process related collections of items such as +// page table map (PTE), virtual address descriptor map (VAD), loaded modules, +// heaps and threads. //----------------------------------------------------------------------------- -/* -* Retrieve an active process given it's name. Please note that if multiple -* processes with the same name exists only one will be returned. If required to -* parse all processes with the same name please iterate over the PID list by -* calling VMMDLL_PidList together with VMMDLL_ProcessGetInformation. -* -- szProcName = process name case insensitive. -* -- pdwPID = pointer that will receive PID on success. -* -- return -*/ -_Success_(return) -BOOL VMMDLL_PidGetFromName(_In_ LPSTR szProcName, _Out_ PDWORD pdwPID); - -/* -* List the PIDs in the system. -* -- pPIDs = DWORD array of at least number of PIDs in system, or NULL. -* -- pcPIDs = size of (in number of DWORDs) pPIDs array on entry, number of PIDs in system on exit. -* -- return = success/fail. -*/ -_Success_(return) -BOOL VMMDLL_PidList(_Out_opt_ PDWORD pPIDs, _Inout_ PULONG64 pcPIDs); +#define VMMDLL_MAP_PTE_VERSION 1 +#define VMMDLL_MAP_VAD_VERSION 1 +#define VMMDLL_MAP_MODULE_VERSION 1 +#define VMMDLL_MAP_HEAP_VERSION 1 +#define VMMDLL_MAP_THREAD_VERSION 1 +#define VMMDLL_MAP_HANDLE_VERSION 1 -// flags to check for existence in the fPage field of PCILEECH_VMM_MEMMAP_ENTRY +// flags to check for existence in the fPage field of VMMDLL_MAP_PTEENTRY #define VMMDLL_MEMMAP_FLAG_PAGE_W 0x0000000000000002 #define VMMDLL_MEMMAP_FLAG_PAGE_NS 0x0000000000000004 #define VMMDLL_MEMMAP_FLAG_PAGE_NX 0x8000000000000000 #define VMMDLL_MEMMAP_FLAG_PAGE_MASK 0x8000000000000006 -typedef struct tdVMMDLL_MEMMAP_ENTRY { - ULONG64 AddrBase; - ULONG64 cPages; - ULONG64 fPage; +typedef struct tdVMMDLL_MAP_PTEENTRY { + QWORD vaBase; + QWORD cPages; + QWORD fPage; BOOL fWoW64; - CHAR szTag[32]; -} VMMDLL_MEMMAP_ENTRY, *PVMMDLL_MEMMAP_ENTRY; + DWORD cwszText; + LPWSTR wszText; + DWORD _Reserved1[2]; +} VMMDLL_MAP_PTEENTRY, *PVMMDLL_MAP_PTEENTRY; + +typedef struct tdVMMDLL_MAP_VADENTRY { + QWORD vaStart; + QWORD vaEnd; + QWORD vaVad; + // DWORD 0 + DWORD VadType : 3; // Pos 0 + DWORD Protection : 5; // Pos 3 + DWORD fImage : 1; // Pos 8 + DWORD fFile : 1; // Pos 9 + DWORD fPageFile : 1; // Pos 10 + DWORD fPrivateMemory : 1; // Pos 11 + DWORD fTeb : 1; // Pos 12 + DWORD fStack : 1; // Pos 13 + DWORD fSpare : 2; // Pos 14 + DWORD HeapNum : 7; // Pos 16 + DWORD fHeap : 1; // Pos 23 + DWORD cwszDescription : 8; // Pos 24 + // DWORD 1 + DWORD CommitCharge : 31; // Pos 0 + DWORD MemCommit : 1; // Pos 31 + DWORD u2; + DWORD cbPrototypePte; + QWORD vaPrototypePte; + QWORD vaSubsection; + LPWSTR wszText; // optional LPWSTR pointed into VMMDLL_MAP_VAD.wszMultiText + DWORD cwszText; // WCHAR count not including terminating null + DWORD _Reserved1; +} VMMDLL_MAP_VADENTRY, *PVMMDLL_MAP_VADENTRY; + +typedef struct tdVMMDLL_MAP_MODULEENTRY { + QWORD vaBase; + QWORD vaEntry; + DWORD cbImageSize; + BOOL fWoW64; + LPWSTR wszText; + DWORD cwszText; // wchar count not including terminating null + DWORD _Reserved1[7]; +} VMMDLL_MAP_MODULEENTRY, *PVMMDLL_MAP_MODULEENTRY; + +typedef struct tdVMMDLL_MAP_HEAPENTRY { + QWORD vaHeapSegment; + DWORD cPages; + DWORD cPagesUnCommitted : 24; + DWORD HeapId : 7; + DWORD fPrimary : 1; +} VMMDLL_MAP_HEAPENTRY, *PVMMDLL_MAP_HEAPENTRY; + +typedef struct tdVMMDLL_MAP_THREADENTRY { + DWORD dwTID; + DWORD dwPID; + DWORD dwExitStatus; + UCHAR bState; + UCHAR bRunning; + UCHAR bPriority; + UCHAR bBasePriority; + QWORD vaETHREAD; + QWORD vaTeb; + QWORD ftCreateTime; + QWORD ftExitTime; + QWORD vaStartAddress; + QWORD vaStackBaseUser; + QWORD vaStackLimitUser; + QWORD vaStackBaseKernel; + QWORD vaStackLimitKernel; + DWORD _FutureUse[10]; +} VMMDLL_MAP_THREADENTRY, *PVMMDLL_MAP_THREADENTRY; + +typedef struct tdVMMDLL_MAP_HANDLEENTRY { + QWORD vaObject; + DWORD dwHandle; + DWORD dwGrantedAccess : 24; + DWORD iType : 8; + QWORD qwHandleCount; + QWORD qwPointerCount; + QWORD vaObjectCreateInfo; + QWORD vaSecurityDescriptor; + LPWSTR wszText; + DWORD cwszText; + DWORD dwPID; + DWORD dwPoolTag; + DWORD _FutureUse[4]; + DWORD cwszType; + LPWSTR wszType; +} VMMDLL_MAP_HANDLEENTRY, *PVMMDLL_MAP_HANDLEENTRY; + +typedef struct tdVMMDLL_MAP_PTE { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // NULL or multi-wstr pointed into by VMMDLL_MAP_VADENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_PTEENTRY pMap[]; // map entries. +} VMMDLL_MAP_PTE, *PVMMDLL_MAP_PTE; + +typedef struct tdVMMDLL_MAP_VAD { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // NULL or multi-wstr pointed into by VMMDLL_MAP_VADENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_VADENTRY pMap[]; // map entries. +} VMMDLL_MAP_VAD, *PVMMDLL_MAP_VAD; + +typedef struct tdVMMDLL_MAP_MODULE { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // multi-wstr pointed into by VMMDLL_MAP_MODULEENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_MODULEENTRY pMap[]; // map entries. +} VMMDLL_MAP_MODULE, *PVMMDLL_MAP_MODULE; + +typedef struct tdVMMDLL_MAP_HEAP { + DWORD dwVersion; + DWORD _Reserved1[8]; + DWORD cMap; // # map entries. + VMMDLL_MAP_HEAPENTRY pMap[]; // map entries. +} VMMDLL_MAP_HEAP, *PVMMDLL_MAP_HEAP; + +typedef struct tdVMMDLL_MAP_THREAD { + DWORD dwVersion; + DWORD _Reserved[8]; + DWORD cMap; // # map entries. + VMMDLL_MAP_THREADENTRY pMap[]; // map entries. +} VMMDLL_MAP_THREAD, *PVMMDLL_MAP_THREAD; + +typedef struct tdVMMDLL_MAP_HANDLE { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // multi-wstr pointed into by VMMDLL_MAP_HANDLEENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_HANDLEENTRY pMap[]; // map entries. +} VMMDLL_MAP_HANDLE, *PVMMDLL_MAP_HANDLE; /* -* Retrieve memory map entries from the specified process. Memory map entries -* are copied into the user supplied buffer that must be at least of size: -* sizeof(VMMDLL_MEMMAP_ENTRY)*pcMemMapEntries bytes. -* If the pMemMapEntries is set to NULL the number of memory map entries will be -* given in the pcMemMapEntries parameter. +* Retrieve the memory map entries based on hardware page tables (PTE) for the +* process. If pPteMap is set to NULL the number of bytes required will be +* returned in parameter pcbPteMap. +* Entries returned are sorted on VMMDLL_MAP_PTEENTRY.vaBase * -- dwPID -* -- pMemMapEntries = buffer of minimum length sizeof(VMMDLL_MEMMAP_ENTRY)*pcMemMapEntries, or NULL. -* -- pcMemMapEntries = pointer to number of memory map entries. +* -- pPteMap = buffer of minimum byte length *pcbPteMap or NULL. +* -- pcbPteMap = pointer to byte count of pPteMap buffer. * -- fIdentifyModules = try identify modules as well (= slower) * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetMemoryMap(_In_ DWORD dwPID, _Out_opt_ PVMMDLL_MEMMAP_ENTRY pMemMapEntries, _Inout_ PULONG64 pcMemMapEntries, _In_ BOOL fIdentifyModules); +BOOL VMMDLL_ProcessMap_GetPte(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbPteMap) PVMMDLL_MAP_PTE pPteMap, _Inout_ PDWORD pcbPteMap, _In_ BOOL fIdentifyModules); /* -* Retrieve a single memory map entry given a virtual address within that entrys -* range. +* Retrieve memory map entries based on virtual address descriptor (VAD) for +* the process. If pVadMap is set to NULL the number of bytes required +* will be returned in parameter pcbVadMap. +* Entries returned are sorted on VMMDLL_MAP_VADENTRY.vaStart * -- dwPID -* -- pMemMapEntry -* -- va = virtual address in the memory map entry to retrieve. +* -- pVadMap = buffer of minimum byte length *pcbVadMap or NULL. +* -- pcbVadMap = pointer to byte count of pVadMap buffer. * -- fIdentifyModules = try identify modules as well (= slower) * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetMemoryMapEntry(_In_ DWORD dwPID, _Out_ PVMMDLL_MEMMAP_ENTRY pMemMapEntry, _In_ ULONG64 va, _In_ BOOL fIdentifyModules); - -typedef struct tdVMMDLL_MODULEMAP_ENTRY { - ULONG64 BaseAddress; - ULONG64 EntryPoint; - DWORD SizeOfImage; - BOOL fWoW64; - CHAR szName[32]; -} VMMDLL_MODULEMAP_ENTRY, *PVMMDLL_MODULEMAP_ENTRY; +BOOL VMMDLL_ProcessMap_GetVad(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbVadMap) PVMMDLL_MAP_VAD pVadMap, _Inout_ PDWORD pcbVadMap, _In_ BOOL fIdentifyModules); /* -* Retrieve the module entries from the specified process. The module entries -* are copied into the user supplied buffer that must be at least of size: -* sizeof(VMMDLL_MODULEMAP_ENTRY)*pcModuleEntries bytes long. If the -* pcModuleEntries is set to NULL the number of module entries will be given -* in the pcModuleEntries parameter. +* Retrieve the modules (.dlls) for the specified process. If pModuleMap is set +* to NULL the number of bytes required will be returned in parameter pcbModuleMap. * -- dwPID -* -- pModuleEntries = buffer of minimum length sizeof(VMMDLL_MODULEMAP_ENTRY)*pcModuleEntries, or NULL. -* -- pcModuleEntries = pointer to number of memory map entries. +* -- pModuleMap = buffer of minimum byte length *pcbModuleMap or NULL. +* -- pcbModuleMap = pointer to byte count of pModuleMap buffer. * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetModuleMap(_In_ DWORD dwPID, _Out_opt_ PVMMDLL_MODULEMAP_ENTRY pModuleEntries, _Inout_ PULONG64 pcModuleEntries); +BOOL VMMDLL_ProcessMap_GetModule(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbModuleMap) PVMMDLL_MAP_MODULE pModuleMap, _Inout_ PDWORD pcbModuleMap); /* -* Retrieve a module (.exe or .dll or similar) given a module name. +* Retrieve a module map entry (.exe / .dll) given a process and module name. +* NB! PVMMDLL_MAP_MODULEENTRY->wszText will not be set by this function. +* If module name is required use VMMDLL_ProcessMap_GetModule(). * -- dwPID * -- szModuleName -* -- pModuleEntry +* -- pModuleMapEntry * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetModuleFromName(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _Out_ PVMMDLL_MODULEMAP_ENTRY pModuleEntry); +BOOL VMMDLL_ProcessMap_GetModuleFromName(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _Out_ PVMMDLL_MAP_MODULEENTRY pModuleMapEntry); + +/* +* Retrieve the heaps for the specified process. If pHeapMap is set to NULL +* the number of bytes required will be returned in parameter pcbHeapMap. +* -- dwPID +* -- pHeapMap = buffer of minimum byte length *pcbHeapMap or NULL. +* -- pcbHeapMap = pointer to byte count of pHeapMap buffer. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_ProcessMap_GetHeap(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbHeapMap) PVMMDLL_MAP_HEAP pHeapMap, _Inout_ PDWORD pcbHeapMap); + +/* +* Retrieve the threads for the specified process. If pThreadMap is set to NULL +* the number of bytes required will be returned in parameter pcbThreadMap. +* Entries returned are sorted on VMMDLL_MAP_THREADENTRY.dwTID +* -- dwPID +* -- pThreadMap = buffer of minimum byte length *pcbThreadMap or NULL. +* -- pcbThreadMap = pointer to byte count of pThreadMap buffer. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_ProcessMap_GetThread(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbThreadMap) PVMMDLL_MAP_THREAD pThreadMap, _Inout_ PDWORD pcbThreadMap); + +/* +* Retrieve the handles for the specified process. If pHandleMap is set to NULL +* the number of bytes required will be returned in parameter pcbHandleMap. +* Entries returned are sorted on VMMDLL_MAP_HANDLEENTRY.dwHandle +* -- dwPID +* -- pHandleMap = buffer of minimum byte length *pcbHandleMap or NULL. +* -- pcbHandleMap = pointer to byte count of pHandleMap buffer. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_ProcessMap_GetHandle(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbHandleMap) PVMMDLL_MAP_HANDLE pHandleMap, _Inout_ PDWORD pcbHandleMap); + + + +//----------------------------------------------------------------------------- +// VMM PROCESS FUNCTIONALITY BELOW: +// Functionality below is mostly relating to Windows processes. +//----------------------------------------------------------------------------- + +/* +* Retrieve an active process given it's name. Please note that if multiple +* processes with the same name exists only one will be returned. If required to +* parse all processes with the same name please iterate over the PID list by +* calling VMMDLL_PidList together with VMMDLL_ProcessGetInformation. +* -- szProcName = process name case insensitive. +* -- pdwPID = pointer that will receive PID on success. +* -- return +*/ +_Success_(return) +BOOL VMMDLL_PidGetFromName(_In_ LPSTR szProcName, _Out_ PDWORD pdwPID); + +/* +* List the PIDs in the system. +* -- pPIDs = DWORD array of at least number of PIDs in system, or NULL. +* -- pcPIDs = size of (in number of DWORDs) pPIDs array on entry, number of PIDs in system on exit. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_PidList(_Out_writes_opt_(*pcPIDs) PDWORD pPIDs, _Inout_ PULONG64 pcPIDs); #define VMMDLL_PROCESS_INFORMATION_MAGIC 0xc0ffee663df9301e -#define VMMDLL_PROCESS_INFORMATION_VERSION 4 +#define VMMDLL_PROCESS_INFORMATION_VERSION 5 typedef struct tdVMMDLL_PROCESS_INFORMATION { ULONG64 magic; @@ -561,7 +752,7 @@ typedef struct tdVMMDLL_PROCESS_INFORMATION { struct { ULONG64 vaEPROCESS; ULONG64 vaPEB; - ULONG64 vaENTRY; + ULONG64 _Reserved1; BOOL fWow64; DWORD vaPEB32; // WoW64 only } win; @@ -613,37 +804,37 @@ typedef struct tdVMMDLL_IAT_ENTRY { * If the pData == NULL upon entry the number of entries of the pData array must * have in order to be able to hold the data is returned. * -- dwPID -* -- szModule +* -- wszModule * -- pData * -- cData * -- pcData * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetDirectories(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_writes_(16) PIMAGE_DATA_DIRECTORY pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetDirectories(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_writes_(16) PIMAGE_DATA_DIRECTORY pData, _In_ DWORD cData, _Out_ PDWORD pcData); _Success_(return) -BOOL VMMDLL_ProcessGetSections(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PIMAGE_SECTION_HEADER pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetSections(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PIMAGE_SECTION_HEADER pData, _In_ DWORD cData, _Out_ PDWORD pcData); _Success_(return) -BOOL VMMDLL_ProcessGetEAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PVMMDLL_EAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetEAT(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PVMMDLL_EAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); _Success_(return) -BOOL VMMDLL_ProcessGetIAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PVMMDLL_IAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetIAT(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PVMMDLL_IAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); /* * Retrieve the virtual address of a given function inside a process/module. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- szFunctionName * -- return = virtual address of function, zero on fail. */ -ULONG64 VMMDLL_ProcessGetProcAddress(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szFunctionName); +ULONG64 VMMDLL_ProcessGetProcAddress(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szFunctionName); /* * Retrieve the base address of a given module. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- return = virtual address of module base, zero on fail. */ -ULONG64 VMMDLL_ProcessGetModuleBase(_In_ DWORD dwPID, _In_ LPSTR szModuleName); +ULONG64 VMMDLL_ProcessGetModuleBase(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName); @@ -729,7 +920,7 @@ BOOL VMMDLL_WinReg_HiveList(_Out_writes_(cHives) PVMMDLL_REGISTRY_HIVE_INFORMATI * -- ra * -- pb * -- cb -* -- pcbRead +* -- pcbReadOpt * -- flags = flags as in VMMDLL_FLAG_* * -- return = success/fail. NB! reads may report as success even if 0 bytes are * read - it's recommended to verify pcbReadOpt parameter. @@ -901,26 +1092,26 @@ typedef struct tdVMMDLL_WIN_THUNKINFO_EAT { * function. This includes the virtual address of the IAT thunk which is useful * for hooking. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- szImportModuleName * -- szImportFunctionName * -- pThunkIAT * -- return */ _Success_(return) -BOOL VMMDLL_WinGetThunkInfoIAT(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szImportModuleName, _In_ LPSTR szImportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_IAT pThunkInfoIAT); +BOOL VMMDLL_WinGetThunkInfoIAT(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szImportModuleName, _In_ LPSTR szImportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_IAT pThunkInfoIAT); /* * Retrieve information about the export address table EAT thunk for an exported * function. This includes the virtual address of the EAT thunk which is useful * for hooking. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- pThunkEAT * -- return */ _Success_(return) -BOOL VMMDLL_WinGetThunkInfoEAT(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT); +BOOL VMMDLL_WinGetThunkInfoEAT(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT); diff --git a/vmmpyc/vmmpyc.c b/vmmpyc/vmmpyc.c index 0bb20acc..4262664d 100644 --- a/vmmpyc/vmmpyc.c +++ b/vmmpyc/vmmpyc.c @@ -310,36 +310,37 @@ VMMPYC_MemVirt2Phys(PyObject *self, PyObject *args) // (DWORD, (BOOL)) -> [{...}] static PyObject* -VMMPYC_ProcessGetMemoryMap(PyObject *self, PyObject *args) +VMMPYC_ProcessGetPteMap(PyObject *self, PyObject *args) { PyObject *pyList, *pyDict; BOOL result, fIdentifyModules; DWORD dwPID, i; - ULONG64 cMemMapEntries = 0; - PVMMDLL_MEMMAP_ENTRY pe, pMemMapEntries = NULL; + DWORD cbPteMap = 0; + PVMMDLL_MAP_PTEENTRY pe; + PVMMDLL_MAP_PTE pPteMap = NULL; CHAR sz[5]; if(!PyArg_ParseTuple(args, "k|p", &dwPID, &fIdentifyModules)) { return NULL; } if(!(pyList = PyList_New(0))) { return PyErr_NoMemory(); } Py_BEGIN_ALLOW_THREADS; result = - VMMDLL_ProcessGetMemoryMap(dwPID, NULL, &cMemMapEntries, fIdentifyModules) && - cMemMapEntries && - (pMemMapEntries = LocalAlloc(0, cMemMapEntries * sizeof(VMMDLL_MEMMAP_ENTRY))) && - VMMDLL_ProcessGetMemoryMap(dwPID, pMemMapEntries, &cMemMapEntries, fIdentifyModules); + VMMDLL_ProcessMap_GetPte(dwPID, NULL, &cbPteMap, fIdentifyModules) && + cbPteMap && + (pPteMap = LocalAlloc(0, cbPteMap)) && + VMMDLL_ProcessMap_GetPte(dwPID, pPteMap, &cbPteMap, fIdentifyModules); Py_END_ALLOW_THREADS; if(!result) { Py_DECREF(pyList); - LocalFree(pMemMapEntries); - return PyErr_Format(PyExc_RuntimeError, "VMMPYC_ProcessGetMemoryMap: Failed."); + LocalFree(pPteMap); + return PyErr_Format(PyExc_RuntimeError, "VMMPYC_ProcessGetPteMap: Failed."); } - for(i = 0; i < cMemMapEntries; i++) { + for(i = 0; i < pPteMap->cMap; i++) { if((pyDict = PyDict_New())) { - pe = pMemMapEntries + i; - PyDict_SetItemString_DECREF(pyDict, "va", PyLong_FromUnsignedLongLong(pe->AddrBase)); + pe = pPteMap->pMap + i; + PyDict_SetItemString_DECREF(pyDict, "va", PyLong_FromUnsignedLongLong(pe->vaBase)); PyDict_SetItemString_DECREF(pyDict, "size", PyLong_FromUnsignedLongLong(pe->cPages << 12)); PyDict_SetItemString_DECREF(pyDict, "pages", PyLong_FromUnsignedLongLong(pe->cPages)); PyDict_SetItemString_DECREF(pyDict, "wow64", PyBool_FromLong((long)pe->fWoW64)); - PyDict_SetItemString_DECREF(pyDict, "tag", PyUnicode_FromFormat("%s", pe->szTag)); + PyDict_SetItemString_DECREF(pyDict, "tag", PyUnicode_FromWideChar(pe->wszText, -1)); PyDict_SetItemString_DECREF(pyDict, "flags-pte", PyLong_FromUnsignedLongLong(pe->fPage)); sz[0] = (pe->fPage & VMMDLL_MEMMAP_FLAG_PAGE_NS) ? '-' : 's'; sz[1] = 'r'; @@ -350,42 +351,86 @@ VMMPYC_ProcessGetMemoryMap(PyObject *self, PyObject *args) PyList_Append_DECREF(pyList, pyDict); } } - LocalFree(pMemMapEntries); + LocalFree(pPteMap); return pyList; } -// (DWORD, ULONG64, (DWORD)) -> {} +VOID VMMPYC_ProcessGetVadMap_Protection(_In_ PVMMDLL_MAP_VADENTRY pVad, _Out_writes_(6) LPSTR sz) +{ + BYTE vh = (BYTE)pVad->Protection >> 3; + BYTE vl = (BYTE)pVad->Protection & 7; + sz[0] = pVad->fPrivateMemory ? 'p' : '-'; // PRIVATE MEMORY + sz[1] = (vh & 2) ? ((vh & 1) ? 'm' : 'g') : ((vh & 1) ? 'n' : '-'); // -/NO_CACHE/GUARD/WRITECOMBINE + sz[2] = ((vl == 1) || (vl == 3) || (vl == 4) || (vl == 6)) ? 'r' : '-'; // COPY ON WRITE + sz[3] = (vl & 4) ? 'w' : '-'; // WRITE + sz[4] = (vl & 2) ? 'x' : '-'; // EXECUTE + sz[5] = ((vl == 5) || (vl == 7)) ? 'c' : '-'; // COPY ON WRITE + if(sz[1] != '-' && sz[2] == '-' && sz[3] == '-' && sz[4] == '-' && sz[5] == '-') { sz[1] = '-'; } +} + +LPSTR VMMPYC_ProcessGetVadMap_Type(_In_ PVMMDLL_MAP_VADENTRY pVad) +{ + if(pVad->fImage) { + return "Image"; + } else if(pVad->fFile) { + return "File "; + } else if(pVad->fHeap) { + return "Heap "; + } else if(pVad->fStack) { + return "Stack"; + } else if(pVad->fTeb) { + return "Teb "; + } else if(pVad->fPageFile) { + return "Pf "; + } else { + return " "; + } +} + +// (DWORD, (BOOL)) -> [{...}] static PyObject* -VMMPYC_ProcessGetMemoryMapEntry(PyObject *self, PyObject *args) +VMMPYC_ProcessGetVadMap(PyObject *self, PyObject *args) { - PyObject *pyDict; + PyObject *pyList, *pyDict; BOOL result, fIdentifyModules; - DWORD dwPID; - ULONG64 va; - VMMDLL_MEMMAP_ENTRY e; - CHAR sz[5]; - if(!PyArg_ParseTuple(args, "kK|p", &dwPID, &va, &fIdentifyModules)) { return NULL; } - if(!(pyDict = PyDict_New())) { return PyErr_NoMemory(); } + DWORD dwPID, i; + DWORD cbVadMap = 0; + PVMMDLL_MAP_VADENTRY pe; + PVMMDLL_MAP_VAD pVadMap = NULL; + CHAR szVadProtection[7] = { 0 }; + if(!PyArg_ParseTuple(args, "k|p", &dwPID, &fIdentifyModules)) { return NULL; } + if(!(pyList = PyList_New(0))) { return PyErr_NoMemory(); } Py_BEGIN_ALLOW_THREADS; - result = VMMDLL_ProcessGetMemoryMapEntry(dwPID, &e, va, fIdentifyModules); + result = + VMMDLL_ProcessMap_GetVad(dwPID, NULL, &cbVadMap, fIdentifyModules) && + cbVadMap && + (pVadMap = LocalAlloc(0, cbVadMap)) && + VMMDLL_ProcessMap_GetVad(dwPID, pVadMap, &cbVadMap, fIdentifyModules); Py_END_ALLOW_THREADS; - if(!result) { - Py_DECREF(pyDict); - return PyErr_Format(PyExc_RuntimeError, "VMMDLL_ProcessGetMemoryMapEntry: Failed."); + if(!result) { + Py_DECREF(pyList); + LocalFree(pVadMap); + return PyErr_Format(PyExc_RuntimeError, "VMMPYC_ProcessGetVadMap: Failed."); } - PyDict_SetItemString_DECREF(pyDict, "va", PyLong_FromUnsignedLongLong(e.AddrBase)); - PyDict_SetItemString_DECREF(pyDict, "size", PyLong_FromUnsignedLongLong(e.cPages << 12)); - PyDict_SetItemString_DECREF(pyDict, "pages", PyLong_FromUnsignedLongLong(e.cPages)); - PyDict_SetItemString_DECREF(pyDict, "wow64", PyBool_FromLong((long)e.fWoW64)); - PyDict_SetItemString_DECREF(pyDict, "tag", PyUnicode_FromFormat("%s", e.szTag)); - PyDict_SetItemString_DECREF(pyDict, "flags-pte", PyLong_FromUnsignedLongLong(e.fPage)); - sz[0] = (e.fPage & VMMDLL_MEMMAP_FLAG_PAGE_NS) ? '-' : 's'; - sz[1] = 'r'; - sz[2] = (e.fPage & VMMDLL_MEMMAP_FLAG_PAGE_W) ? 'w' : '-'; - sz[3] = (e.fPage & VMMDLL_MEMMAP_FLAG_PAGE_NX) ? '-' : 'x'; - sz[4] = 0; - PyDict_SetItemString_DECREF(pyDict, "flags", PyUnicode_FromFormat("%s", sz)); - return pyDict; + for(i = 0; i < pVadMap->cMap; i++) { + if((pyDict = PyDict_New())) { + pe = pVadMap->pMap + i; + VMMPYC_ProcessGetVadMap_Protection(pe, szVadProtection); + PyDict_SetItemString_DECREF(pyDict, "start", PyLong_FromUnsignedLongLong(pe->vaStart)); + PyDict_SetItemString_DECREF(pyDict, "end", PyLong_FromUnsignedLongLong(pe->vaEnd)); + PyDict_SetItemString_DECREF(pyDict, "subsection", PyLong_FromUnsignedLongLong(pe->vaSubsection)); + PyDict_SetItemString_DECREF(pyDict, "prototype", PyLong_FromUnsignedLongLong(pe->vaPrototypePte)); + PyDict_SetItemString_DECREF(pyDict, "prototype-len", PyLong_FromUnsignedLong(pe->cbPrototypePte)); + PyDict_SetItemString_DECREF(pyDict, "mem_commit", PyBool_FromLong((long)pe->MemCommit)); + PyDict_SetItemString_DECREF(pyDict, "commit_charge", PyLong_FromUnsignedLong(pe->CommitCharge)); + PyDict_SetItemString_DECREF(pyDict, "protection", PyUnicode_FromFormat("%s", szVadProtection)); + PyDict_SetItemString_DECREF(pyDict, "type", PyUnicode_FromFormat("%s", VMMPYC_ProcessGetVadMap_Type(pe))); + PyDict_SetItemString_DECREF(pyDict, "tag", PyUnicode_FromWideChar(pe->wszText, -1)); + PyList_Append_DECREF(pyList, pyDict); + } + } + LocalFree(pVadMap); + return pyList; } // (DWORD) -> [{...}] @@ -394,35 +439,36 @@ VMMPYC_ProcessGetModuleMap(PyObject *self, PyObject *args) { PyObject *pyList, *pyDict; BOOL result; - DWORD dwPID; - ULONG64 i, cModuleEntries = 0; - PVMMDLL_MODULEMAP_ENTRY pe, pModuleEntries = NULL; + DWORD dwPID, cbModuleMap = 0; + ULONG64 i; + PVMMDLL_MAP_MODULE pModuleMap = NULL; + PVMMDLL_MAP_MODULEENTRY pe; if(!PyArg_ParseTuple(args, "k", &dwPID)) { return NULL; } if(!(pyList = PyList_New(0))) { return PyErr_NoMemory(); } Py_BEGIN_ALLOW_THREADS; result = - VMMDLL_ProcessGetModuleMap(dwPID, NULL, &cModuleEntries) && - cModuleEntries && - (pModuleEntries = LocalAlloc(0, cModuleEntries * sizeof(VMMDLL_MODULEMAP_ENTRY))) && - VMMDLL_ProcessGetModuleMap(dwPID, pModuleEntries, &cModuleEntries); + VMMDLL_ProcessMap_GetModule(dwPID, NULL, &cbModuleMap) && + cbModuleMap && + (pModuleMap = LocalAlloc(0, cbModuleMap)) && + VMMDLL_ProcessMap_GetModule(dwPID, pModuleMap, &cbModuleMap); Py_END_ALLOW_THREADS; if(!result) { Py_DECREF(pyList); - LocalFree(pModuleEntries); + LocalFree(pModuleMap); return PyErr_Format(PyExc_RuntimeError, "VMMPYC_ProcessGetModuleMap: Failed."); } - for(i = 0; i < cModuleEntries; i++) { + for(i = 0; i < pModuleMap->cMap; i++) { if((pyDict = PyDict_New())) { - pe = pModuleEntries + i; - PyDict_SetItemString_DECREF(pyDict, "va", PyLong_FromUnsignedLongLong(pe->BaseAddress)); - PyDict_SetItemString_DECREF(pyDict, "va-entry", PyLong_FromUnsignedLongLong(pe->EntryPoint)); - PyDict_SetItemString_DECREF(pyDict, "size", PyLong_FromUnsignedLong(pe->SizeOfImage)); + pe = pModuleMap->pMap + i; + PyDict_SetItemString_DECREF(pyDict, "va", PyLong_FromUnsignedLongLong(pe->vaBase)); + PyDict_SetItemString_DECREF(pyDict, "va-entry", PyLong_FromUnsignedLongLong(pe->vaEntry)); + PyDict_SetItemString_DECREF(pyDict, "size", PyLong_FromUnsignedLong(pe->cbImageSize)); PyDict_SetItemString_DECREF(pyDict, "wow64", PyBool_FromLong((long)pe->fWoW64)); - PyDict_SetItemString_DECREF(pyDict, "name", PyUnicode_FromFormat("%s", pe->szName)); + PyDict_SetItemString_DECREF(pyDict, "name", PyUnicode_FromWideChar(pe->wszText, -1)); PyList_Append_DECREF(pyList, pyDict); } } - LocalFree(pModuleEntries); + LocalFree(pModuleMap); return pyList; } @@ -430,29 +476,172 @@ VMMPYC_ProcessGetModuleMap(PyObject *self, PyObject *args) static PyObject* VMMPYC_ProcessGetModuleFromName(PyObject *self, PyObject *args) { - PyObject *pyDict; + PyObject *pyDict, *pyUnicodePath; BOOL result; DWORD dwPID; - LPSTR szModuleName; - VMMDLL_MODULEMAP_ENTRY e; - if(!PyArg_ParseTuple(args, "ks", &dwPID, &szModuleName)) { return NULL; } + LPWSTR wszModuleName = NULL; + VMMDLL_MAP_MODULEENTRY e; + if(!PyArg_ParseTuple(args, "kO!", &dwPID, &PyUnicode_Type, &pyUnicodePath)) { return NULL; } if(!(pyDict = PyDict_New())) { return PyErr_NoMemory(); } + if(!(wszModuleName = PyUnicode_AsWideCharString(pyUnicodePath, NULL))) { return NULL; } // wszPath PyMem_Free() required Py_BEGIN_ALLOW_THREADS; - ZeroMemory(&e, sizeof(VMMDLL_MODULEMAP_ENTRY)); - result = VMMDLL_ProcessGetModuleFromName(dwPID, szModuleName, &e); + ZeroMemory(&e, sizeof(VMMDLL_MAP_MODULEENTRY)); + result = VMMDLL_ProcessMap_GetModuleFromName(dwPID, wszModuleName, &e); Py_END_ALLOW_THREADS; + PyMem_Free(wszModuleName); if(!result) { Py_DECREF(pyDict); return PyErr_Format(PyExc_RuntimeError, "VMMPYC_ProcessGetModuleFromName: Failed."); } - PyDict_SetItemString_DECREF(pyDict, "va", PyLong_FromUnsignedLongLong(e.BaseAddress)); - PyDict_SetItemString_DECREF(pyDict, "va-entry", PyLong_FromUnsignedLongLong(e.EntryPoint)); + PyDict_SetItemString_DECREF(pyDict, "va", PyLong_FromUnsignedLongLong(e.vaBase)); + PyDict_SetItemString_DECREF(pyDict, "va-entry", PyLong_FromUnsignedLongLong(e.vaEntry)); PyDict_SetItemString_DECREF(pyDict, "wow64", PyBool_FromLong((long)e.fWoW64)); - PyDict_SetItemString_DECREF(pyDict, "size", PyLong_FromUnsignedLong(e.SizeOfImage)); - PyDict_SetItemString_DECREF(pyDict, "name", PyUnicode_FromFormat("%s", e.szName)); + PyDict_SetItemString_DECREF(pyDict, "size", PyLong_FromUnsignedLong(e.cbImageSize)); + PyDict_SetItemString(pyDict, "name", pyUnicodePath); return pyDict; } + +// (DWORD) -> [{...}] +static PyObject* +VMMPYC_ProcessGetHeapMap(PyObject *self, PyObject *args) +{ + PyObject *pyList, *pyDict; + BOOL result; + DWORD dwPID, i; + DWORD cbHeapMap = 0; + PVMMDLL_MAP_HEAPENTRY pe; + PVMMDLL_MAP_HEAP pHeapMap = NULL; + if(!PyArg_ParseTuple(args, "k", &dwPID)) { return NULL; } + if(!(pyList = PyList_New(0))) { return PyErr_NoMemory(); } + Py_BEGIN_ALLOW_THREADS; + result = + VMMDLL_ProcessMap_GetHeap(dwPID, NULL, &cbHeapMap) && + cbHeapMap && + (pHeapMap = LocalAlloc(0, cbHeapMap)) && + VMMDLL_ProcessMap_GetHeap(dwPID, pHeapMap, &cbHeapMap); + Py_END_ALLOW_THREADS; + if(!result) { + Py_DECREF(pyList); + LocalFree(pHeapMap); + return PyErr_Format(PyExc_RuntimeError, "VMMPYC_ProcessGetHeapMap: Failed."); + } + for(i = 0; i < pHeapMap->cMap; i++) { + if((pyDict = PyDict_New())) { + pe = pHeapMap->pMap + i; + PyDict_SetItemString_DECREF(pyDict, "va", PyLong_FromUnsignedLongLong(pe->vaHeapSegment)); + PyDict_SetItemString_DECREF(pyDict, "size", PyLong_FromUnsignedLong(pe->cPages << 12)); + PyDict_SetItemString_DECREF(pyDict, "size-uncommitted", PyLong_FromUnsignedLong(pe->cPagesUnCommitted << 12)); + PyDict_SetItemString_DECREF(pyDict, "id", PyLong_FromUnsignedLong(pe->HeapId)); + PyDict_SetItemString_DECREF(pyDict, "primary", PyBool_FromLong((long)pe->fPrimary)); + PyList_Append_DECREF(pyList, pyDict); + } + } + LocalFree(pHeapMap); + return pyList; +} + +// (DWORD) -> [{...}] +static PyObject* +VMMPYC_ProcessGetThreadMap(PyObject *self, PyObject *args) +{ + PyObject *pyList, *pyDict; + BOOL result; + DWORD dwPID, i; + DWORD cbThreadMap = 0; + PVMMDLL_MAP_THREADENTRY pe; + PVMMDLL_MAP_THREAD pThreadMap = NULL; + CHAR szTimeUTC[MAX_PATH]; + if(!PyArg_ParseTuple(args, "k", &dwPID)) { return NULL; } + if(!(pyList = PyList_New(0))) { return PyErr_NoMemory(); } + Py_BEGIN_ALLOW_THREADS; + result = + VMMDLL_ProcessMap_GetThread(dwPID, NULL, &cbThreadMap) && + cbThreadMap && + (pThreadMap = LocalAlloc(0, cbThreadMap)) && + VMMDLL_ProcessMap_GetThread(dwPID, pThreadMap, &cbThreadMap); + Py_END_ALLOW_THREADS; + if(!result) { + Py_DECREF(pyList); + LocalFree(pThreadMap); + return PyErr_Format(PyExc_RuntimeError, "VMMPYC_ProcessGetThreadMap: Failed."); + } + for(i = 0; i < pThreadMap->cMap; i++) { + if((pyDict = PyDict_New())) { + pe = pThreadMap->pMap + i; + PyDict_SetItemString_DECREF(pyDict, "tid", PyLong_FromUnsignedLong(pe->dwTID)); + PyDict_SetItemString_DECREF(pyDict, "pid", PyLong_FromUnsignedLong(pe->dwPID)); + PyDict_SetItemString_DECREF(pyDict, "exitstatus", PyLong_FromUnsignedLong(pe->dwExitStatus)); + PyDict_SetItemString_DECREF(pyDict, "state", PyLong_FromUnsignedLong(pe->bState)); + PyDict_SetItemString_DECREF(pyDict, "running", PyLong_FromUnsignedLong(pe->bRunning)); + PyDict_SetItemString_DECREF(pyDict, "priority", PyLong_FromUnsignedLong(pe->bPriority)); + PyDict_SetItemString_DECREF(pyDict, "basepriority", PyLong_FromUnsignedLong(pe->bBasePriority)); + PyDict_SetItemString_DECREF(pyDict, "va-ethread", PyLong_FromUnsignedLongLong(pe->vaETHREAD)); + PyDict_SetItemString_DECREF(pyDict, "va-teb", PyLong_FromUnsignedLongLong(pe->vaTeb)); + PyDict_SetItemString_DECREF(pyDict, "va-start", PyLong_FromUnsignedLongLong(pe->vaStartAddress)); + PyDict_SetItemString_DECREF(pyDict, "va-stackbase", PyLong_FromUnsignedLongLong(pe->vaStackBaseUser)); + PyDict_SetItemString_DECREF(pyDict, "va-stacklimit", PyLong_FromUnsignedLongLong(pe->vaStackLimitUser)); + PyDict_SetItemString_DECREF(pyDict, "va-stackbase-kernel", PyLong_FromUnsignedLongLong(pe->vaStackBaseKernel)); + PyDict_SetItemString_DECREF(pyDict, "va-stacklimit-kernel", PyLong_FromUnsignedLongLong(pe->vaStackLimitKernel)); + PyDict_SetItemString_DECREF(pyDict, "time-create", PyLong_FromUnsignedLongLong(pe->ftCreateTime)); + PyDict_SetItemString_DECREF(pyDict, "time-exit", PyLong_FromUnsignedLongLong(pe->ftExitTime)); + Util_FileTime2String((PFILETIME)&pe->ftCreateTime, szTimeUTC); + PyDict_SetItemString_DECREF(pyDict, "time-create-str", PyUnicode_FromFormat("%s", szTimeUTC)); + Util_FileTime2String((PFILETIME)&pe->ftExitTime, szTimeUTC); + PyDict_SetItemString_DECREF(pyDict, "time-exit-str", PyUnicode_FromFormat("%s", szTimeUTC)); + PyList_Append_DECREF(pyList, pyDict); + } + } + LocalFree(pThreadMap); + return pyList; +} + +// (DWORD) -> [{...}] +static PyObject* +VMMPYC_ProcessGetHandleMap(PyObject *self, PyObject *args) +{ + PyObject *pyList, *pyDict; + BOOL result; + DWORD dwPID, cbHandleMap = 0; + ULONG64 i; + PVMMDLL_MAP_HANDLE pHandleMap = NULL; + PVMMDLL_MAP_HANDLEENTRY pe; + if(!PyArg_ParseTuple(args, "k", &dwPID)) { return NULL; } + if(!(pyList = PyList_New(0))) { return PyErr_NoMemory(); } + Py_BEGIN_ALLOW_THREADS; + result = + VMMDLL_ProcessMap_GetHandle(dwPID, NULL, &cbHandleMap) && + cbHandleMap && + (pHandleMap = LocalAlloc(0, cbHandleMap)) && + VMMDLL_ProcessMap_GetHandle(dwPID, pHandleMap, &cbHandleMap); + Py_END_ALLOW_THREADS; + if(!result) { + Py_DECREF(pyList); + LocalFree(pHandleMap); + return PyErr_Format(PyExc_RuntimeError, "VMMPYC_ProcessGetHandleMap: Failed."); + } + for(i = 0; i < pHandleMap->cMap; i++) { + if((pyDict = PyDict_New())) { + pe = pHandleMap->pMap + i; + PyDict_SetItemString_DECREF(pyDict, "va-object", PyLong_FromUnsignedLongLong(pe->vaObject)); + PyDict_SetItemString_DECREF(pyDict, "handle", PyLong_FromUnsignedLong(pe->dwHandle)); + PyDict_SetItemString_DECREF(pyDict, "access", PyLong_FromUnsignedLong(pe->dwGrantedAccess)); + PyDict_SetItemString_DECREF(pyDict, "typeindex", PyLong_FromUnsignedLong(pe->iType)); + PyDict_SetItemString_DECREF(pyDict, "pid", PyLong_FromUnsignedLong(pe->dwPID)); + PyDict_SetItemString_DECREF(pyDict, "pooltag", PyLong_FromUnsignedLong(pe->dwPoolTag)); + PyDict_SetItemString_DECREF(pyDict, "chandle", PyLong_FromUnsignedLongLong(pe->qwHandleCount)); + PyDict_SetItemString_DECREF(pyDict, "cpointer", PyLong_FromUnsignedLongLong(pe->qwPointerCount)); + PyDict_SetItemString_DECREF(pyDict, "va-object-creatinfo", PyLong_FromUnsignedLongLong(pe->vaObjectCreateInfo)); + PyDict_SetItemString_DECREF(pyDict, "va-securitydescriptor", PyLong_FromUnsignedLongLong(pe->vaSecurityDescriptor)); + PyDict_SetItemString_DECREF(pyDict, "tag", PyUnicode_FromWideChar(pe->wszText, -1)); + PyDict_SetItemString_DECREF(pyDict, "type", PyUnicode_FromWideChar(pe->wszType, -1)); + PyList_Append_DECREF(pyList, pyDict); + } + } + LocalFree(pHandleMap); + return pyList; +} + // (STR) -> DWORD static PyObject* VMMPYC_PidGetFromName(PyObject *self, PyObject *args) @@ -538,13 +727,11 @@ VMMPYC_ProcessGetInformation(PyObject *self, PyObject *args) switch(info.tpSystem) { case VMMDLL_SYSTEM_WINDOWS_X64: PyDict_SetItemString_DECREF(pyDict, "wow64", PyBool_FromLong((long)info.os.win.fWow64)); - PyDict_SetItemString_DECREF(pyDict, "va-entry", PyLong_FromUnsignedLongLong(info.os.win.vaENTRY)); PyDict_SetItemString_DECREF(pyDict, "va-eprocess", PyLong_FromUnsignedLongLong(info.os.win.vaEPROCESS)); PyDict_SetItemString_DECREF(pyDict, "va-peb", PyLong_FromUnsignedLongLong(info.os.win.vaPEB)); PyDict_SetItemString_DECREF(pyDict, "va-peb32", PyLong_FromUnsignedLongLong(info.os.win.vaPEB32)); break; case VMMDLL_SYSTEM_WINDOWS_X86: - PyDict_SetItemString_DECREF(pyDict, "va-entry", PyLong_FromUnsignedLongLong(info.os.win.vaENTRY)); PyDict_SetItemString_DECREF(pyDict, "va-eprocess", PyLong_FromUnsignedLongLong(info.os.win.vaEPROCESS)); PyDict_SetItemString_DECREF(pyDict, "va-peb", PyLong_FromUnsignedLongLong(info.os.win.vaPEB)); break; @@ -559,19 +746,21 @@ VMMPYC_ProcessGetInformation(PyObject *self, PyObject *args) static PyObject* VMMPYC_ProcessGetDirectories(PyObject *self, PyObject *args) { - PyObject *pyList, *pyDict; + PyObject *pyList, *pyDict, *pyUnicodeModule; BOOL result; DWORD i, dwPID, cDirectories; PIMAGE_DATA_DIRECTORY pe, pDirectories = NULL; - LPSTR szModule; + LPWSTR wszModule = NULL; LPCSTR DIRECTORIES[16] = { "EXPORT", "IMPORT", "RESOURCE", "EXCEPTION", "SECURITY", "BASERELOC", "DEBUG", "ARCHITECTURE", "GLOBALPTR", "TLS", "LOAD_CONFIG", "BOUND_IMPORT", "IAT", "DELAY_IMPORT", "COM_DESCRIPTOR", "RESERVED" }; - if(!PyArg_ParseTuple(args, "ks", &dwPID, &szModule)) { return NULL; } + if(!PyArg_ParseTuple(args, "kO!", &dwPID, &PyUnicode_Type, &pyUnicodeModule)) { return NULL; } if(!(pyList = PyList_New(0))) { return PyErr_NoMemory(); } + if(!(wszModule = PyUnicode_AsWideCharString(pyUnicodeModule, NULL))) { return NULL; } // wszPath PyMem_Free() required Py_BEGIN_ALLOW_THREADS; result = (pDirectories = LocalAlloc(0, 16 * sizeof(IMAGE_DATA_DIRECTORY))) && - VMMDLL_ProcessGetDirectories(dwPID, szModule, pDirectories, 16, &cDirectories); + VMMDLL_ProcessGetDirectories(dwPID, wszModule, pDirectories, 16, &cDirectories); Py_END_ALLOW_THREADS; + PyMem_Free(wszModule); if(!result) { Py_DECREF(pyList); LocalFree(pDirectories); @@ -595,22 +784,24 @@ VMMPYC_ProcessGetDirectories(PyObject *self, PyObject *args) static PyObject* VMMPYC_ProcessGetSections(PyObject *self, PyObject *args) { - PyObject *pyList, *pyDict; + PyObject *pyList, *pyDict, *pyUnicodeModule; BOOL result; DWORD i, dwPID, cSections; PIMAGE_SECTION_HEADER pe, pSections = NULL; - LPSTR szModule; + LPWSTR wszModule = NULL; CHAR szName[9]; szName[8] = 0; - if(!PyArg_ParseTuple(args, "ks", &dwPID, &szModule)) { return NULL; } + if(!PyArg_ParseTuple(args, "kO!", &dwPID, &PyUnicode_Type, &pyUnicodeModule)) { return NULL; } if(!(pyList = PyList_New(0))) { return PyErr_NoMemory(); } + if(!(wszModule = PyUnicode_AsWideCharString(pyUnicodeModule, NULL))) { return NULL; } // wszPath PyMem_Free() required Py_BEGIN_ALLOW_THREADS; result = - VMMDLL_ProcessGetSections(dwPID, szModule, NULL, 0, &cSections) && + VMMDLL_ProcessGetSections(dwPID, wszModule, NULL, 0, &cSections) && cSections && (pSections = LocalAlloc(0, cSections * sizeof(IMAGE_SECTION_HEADER))) && - VMMDLL_ProcessGetSections(dwPID, szModule, pSections, cSections, &cSections); + VMMDLL_ProcessGetSections(dwPID, wszModule, pSections, cSections, &cSections); Py_END_ALLOW_THREADS; + PyMem_Free(wszModule); if(!result) { Py_DECREF(pyList); LocalFree(pSections); @@ -624,7 +815,7 @@ VMMPYC_ProcessGetSections(PyObject *self, PyObject *args) PyDict_SetItemString_DECREF(pyDict, "misc-PhysicalAddress", PyLong_FromUnsignedLong(pe->Misc.PhysicalAddress)); PyDict_SetItemString_DECREF(pyDict, "misc-VirtualSize", PyLong_FromUnsignedLong(pe->Misc.VirtualSize)); *(PULONG64)szName = *(PULONG64)pe->Name; - PyDict_SetItemString_DECREF(pyDict, "Name", PyUnicode_FromFormat("%s", szName)); + PyDict_SetItemString(pyDict, "Name", pyUnicodeModule); PyDict_SetItemString_DECREF(pyDict, "NumberOfLinenumbers", PyLong_FromUnsignedLong(pe->NumberOfLinenumbers)); PyDict_SetItemString_DECREF(pyDict, "NumberOfRelocations", PyLong_FromUnsignedLong(pe->NumberOfRelocations)); PyDict_SetItemString_DECREF(pyDict, "PointerToLinenumbers", PyLong_FromUnsignedLong(pe->PointerToLinenumbers)); @@ -643,20 +834,22 @@ VMMPYC_ProcessGetSections(PyObject *self, PyObject *args) static PyObject* VMMPYC_ProcessGetEAT(PyObject *self, PyObject *args) { - PyObject *pyList, *pyDict; + PyObject *pyList, *pyDict, *pyUnicodeModule; BOOL result; DWORD i, dwPID, cEATs; PVMMDLL_EAT_ENTRY pe, pEATs = NULL; - LPSTR szModule; - if(!PyArg_ParseTuple(args, "ks", &dwPID, &szModule)) { return NULL; } + LPWSTR wszModule = NULL; + if(!PyArg_ParseTuple(args, "kO!", &dwPID, &PyUnicode_Type, &pyUnicodeModule)) { return NULL; } if(!(pyList = PyList_New(0))) { return PyErr_NoMemory(); } + if(!(wszModule = PyUnicode_AsWideCharString(pyUnicodeModule, NULL))) { return NULL; } // wszPath PyMem_Free() required Py_BEGIN_ALLOW_THREADS; result = - VMMDLL_ProcessGetEAT(dwPID, szModule, NULL, 0, &cEATs) && + VMMDLL_ProcessGetEAT(dwPID, wszModule, NULL, 0, &cEATs) && cEATs && (pEATs = LocalAlloc(0, cEATs * sizeof(VMMDLL_EAT_ENTRY))) && - VMMDLL_ProcessGetEAT(dwPID, szModule, pEATs, cEATs, &cEATs); + VMMDLL_ProcessGetEAT(dwPID, wszModule, pEATs, cEATs, &cEATs); Py_END_ALLOW_THREADS; + PyMem_Free(wszModule); if(!result) { Py_DECREF(pyList); LocalFree(pEATs); @@ -680,20 +873,22 @@ VMMPYC_ProcessGetEAT(PyObject *self, PyObject *args) static PyObject* VMMPYC_ProcessGetIAT(PyObject *self, PyObject *args) { - PyObject *pyList, *pyDict; + PyObject *pyList, *pyDict, *pyUnicodeModule; BOOL result; DWORD i, dwPID, cIATs; PVMMDLL_IAT_ENTRY pe, pIATs = NULL; - LPSTR szModule; - if(!PyArg_ParseTuple(args, "ks", &dwPID, &szModule)) { return NULL; } + LPWSTR wszModule = NULL; + if(!PyArg_ParseTuple(args, "kO!", &dwPID, &PyUnicode_Type, &pyUnicodeModule)) { return NULL; } if(!(pyList = PyList_New(0))) { return PyErr_NoMemory(); } + if(!(wszModule = PyUnicode_AsWideCharString(pyUnicodeModule, NULL))) { return NULL; } // wszPath PyMem_Free() required Py_BEGIN_ALLOW_THREADS; result = - VMMDLL_ProcessGetIAT(dwPID, szModule, NULL, 0, &cIATs) && + VMMDLL_ProcessGetIAT(dwPID, wszModule, NULL, 0, &cIATs) && cIATs && (pIATs = LocalAlloc(0, cIATs * sizeof(VMMDLL_IAT_ENTRY))) && - VMMDLL_ProcessGetIAT(dwPID, szModule, pIATs, cIATs, &cIATs); + VMMDLL_ProcessGetIAT(dwPID, wszModule, pIATs, cIATs, &cIATs); Py_END_ALLOW_THREADS; + PyMem_Free(wszModule); if(!result) { Py_DECREF(pyList); LocalFree(pIATs); @@ -809,13 +1004,17 @@ VMMPYC_VfsWrite(PyObject *self, PyObject *args) static PyObject* VMMPYC_ProcessGetProcAddress(PyObject *self, PyObject *args) { + PyObject *pyUnicodeModule; ULONG64 va; DWORD dwPID; - LPSTR szModuleName, szProcName; - if(!PyArg_ParseTuple(args, "kss", &dwPID, &szModuleName, &szProcName)) { return NULL; } + LPSTR szProcName; + LPWSTR wszModule = NULL; + if(!PyArg_ParseTuple(args, "kO!s", &dwPID, &PyUnicode_Type, &pyUnicodeModule, &szProcName)) { return NULL; } + if(!(wszModule = PyUnicode_AsWideCharString(pyUnicodeModule, NULL))) { return NULL; } // wszPath PyMem_Free() required Py_BEGIN_ALLOW_THREADS; - va = VMMDLL_ProcessGetProcAddress(dwPID, szModuleName, szProcName); + va = VMMDLL_ProcessGetProcAddress(dwPID, wszModule, szProcName); Py_END_ALLOW_THREADS; + PyMem_Free(wszModule); return va ? PyLong_FromUnsignedLongLong(va) : PyErr_Format(PyExc_RuntimeError, "VMMPYC_ProcessGetProcAddress: Failed."); @@ -825,13 +1024,16 @@ VMMPYC_ProcessGetProcAddress(PyObject *self, PyObject *args) static PyObject* VMMPYC_ProcessGetModuleBase(PyObject *self, PyObject *args) { + PyObject *pyUnicodeModule; ULONG64 va; DWORD dwPID; - LPSTR szModuleName; - if(!PyArg_ParseTuple(args, "ks", &dwPID, &szModuleName)) { return NULL; } + LPWSTR wszModule = NULL; + if(!PyArg_ParseTuple(args, "kO!", &dwPID, &PyUnicode_Type, &pyUnicodeModule)) { return NULL; } + if(!(wszModule = PyUnicode_AsWideCharString(pyUnicodeModule, NULL))) { return NULL; } // wszPath PyMem_Free() required Py_BEGIN_ALLOW_THREADS; - va = VMMDLL_ProcessGetModuleBase(dwPID, szModuleName); + va = VMMDLL_ProcessGetModuleBase(dwPID, wszModule); Py_END_ALLOW_THREADS; + PyMem_Free(wszModule); return va ? PyLong_FromUnsignedLongLong(va) : PyErr_Format(PyExc_RuntimeError, "VMMPYC_ProcessGetModuleBase: Failed."); @@ -841,15 +1043,18 @@ VMMPYC_ProcessGetModuleBase(PyObject *self, PyObject *args) static PyObject* VMMPYC_WinGetThunkInfoEAT(PyObject *self, PyObject *args) { - PyObject *pyDict; + PyObject *pyDict, *pyUnicodeModule; BOOL result; DWORD dwPID; VMMDLL_WIN_THUNKINFO_EAT oThunkInfoEAT = { 0 }; - LPSTR szModuleName, szExportFunctionName; - if(!PyArg_ParseTuple(args, "kss", &dwPID, &szModuleName, &szExportFunctionName)) { return NULL; } + LPSTR szExportFunctionName; + LPWSTR wszModule = NULL; + if(!PyArg_ParseTuple(args, "kO!s", &dwPID, &PyUnicode_Type, &pyUnicodeModule, &szExportFunctionName)) { return NULL; } + if(!(wszModule = PyUnicode_AsWideCharString(pyUnicodeModule, NULL))) { return NULL; } // wszPath PyMem_Free() required Py_BEGIN_ALLOW_THREADS; - result = VMMDLL_WinGetThunkInfoEAT(dwPID, szModuleName, szExportFunctionName, &oThunkInfoEAT); + result = VMMDLL_WinGetThunkInfoEAT(dwPID, wszModule, szExportFunctionName, &oThunkInfoEAT); Py_END_ALLOW_THREADS; + PyMem_Free(wszModule); if(!result || !oThunkInfoEAT.fValid) { return PyErr_Format(PyExc_RuntimeError, "VMMPYC_WinGetThunkInfoEAT: Failed."); } @@ -867,15 +1072,18 @@ VMMPYC_WinGetThunkInfoEAT(PyObject *self, PyObject *args) static PyObject* VMMPYC_WinGetThunkInfoIAT(PyObject *self, PyObject *args) { - PyObject *pyDict; + PyObject *pyDict, *pyUnicodeModule; BOOL result; DWORD dwPID; VMMDLL_WIN_THUNKINFO_IAT oThunkInfoIAT = { 0 }; - LPSTR szModuleName, szImportModuleName, szImportFunctionName; - if(!PyArg_ParseTuple(args, "ksss", &dwPID, &szModuleName, &szImportModuleName, &szImportFunctionName)) { return NULL; } + LPSTR szImportModuleName, szImportFunctionName; + LPWSTR wszModule = NULL; + if(!PyArg_ParseTuple(args, "kO!ss", &dwPID, &PyUnicode_Type, &pyUnicodeModule, &szImportModuleName, &szImportFunctionName)) { return NULL; } + if(!(wszModule = PyUnicode_AsWideCharString(pyUnicodeModule, NULL))) { return NULL; } // wszPath PyMem_Free() required Py_BEGIN_ALLOW_THREADS; - result = VMMDLL_WinGetThunkInfoIAT(dwPID, szModuleName, szImportModuleName, szImportFunctionName, &oThunkInfoIAT); + result = VMMDLL_WinGetThunkInfoIAT(dwPID, wszModule, szImportModuleName, szImportFunctionName, &oThunkInfoIAT); Py_END_ALLOW_THREADS; + PyMem_Free(wszModule); if(!result || !oThunkInfoIAT.fValid) { return PyErr_Format(PyExc_RuntimeError, "VMMPYC_WinGetThunkInfoEAT: Failed."); } @@ -927,7 +1135,7 @@ VMMPYC_WinReg_HiveList(PyObject *self, PyObject *args) } // (ULONG64, DWORD, DWORD, (ULONG64)) -> PBYTE -static PyObject * +static PyObject* VMMPYC_WinReg_HiveRead(PyObject *self, PyObject *args) { PyObject *pyBytes; @@ -1281,10 +1489,13 @@ static PyMethodDef VMMPYC_EmbMethods[] = { {"VMMPYC_MemVirt2Phys", VMMPYC_MemVirt2Phys, METH_VARARGS, "Translate a virtual address into a physical address."}, {"VMMPYC_PidGetFromName", VMMPYC_PidGetFromName, METH_VARARGS, "Locate a process by name and return the PID."}, {"VMMPYC_PidList", VMMPYC_PidList, METH_VARARGS, "List all process PIDs."}, - {"VMMPYC_ProcessGetMemoryMap", VMMPYC_ProcessGetMemoryMap, METH_VARARGS, "Retrieve the memory map for a given process."}, - {"VMMPYC_ProcessGetMemoryMapEntry", VMMPYC_ProcessGetMemoryMapEntry, METH_VARARGS, "Retrieve a single memory map entry for a given process and virtual address."}, + {"VMMPYC_ProcessGetPteMap", VMMPYC_ProcessGetPteMap, METH_VARARGS, "Retrieve the PTE memory map for a given process."}, + {"VMMPYC_ProcessGetVadMap", VMMPYC_ProcessGetVadMap, METH_VARARGS, "Retrieve the VAD memory map for a given process."}, {"VMMPYC_ProcessGetModuleMap", VMMPYC_ProcessGetModuleMap, METH_VARARGS, "Retrieve the module map for a given process."}, {"VMMPYC_ProcessGetModuleFromName", VMMPYC_ProcessGetModuleFromName, METH_VARARGS, "Locate a module by name and return its information."}, + {"VMMPYC_ProcessGetHeapMap", VMMPYC_ProcessGetHeapMap, METH_VARARGS, "Retrieve the heap map for a given process."}, + {"VMMPYC_ProcessGetThreadMap", VMMPYC_ProcessGetThreadMap, METH_VARARGS, "Retrieve the thread map for a given process."}, + {"VMMPYC_ProcessGetHandleMap", VMMPYC_ProcessGetHandleMap, METH_VARARGS, "Retrieve the handle map for a given process."}, {"VMMPYC_ProcessGetInformation", VMMPYC_ProcessGetInformation, METH_VARARGS, "Retrieve process information for a specific process."}, {"VMMPYC_ProcessGetDirectories", VMMPYC_ProcessGetDirectories, METH_VARARGS, "Retrieve the data directories for a specific process and module."}, {"VMMPYC_ProcessGetSections", VMMPYC_ProcessGetSections, METH_VARARGS, "Retrieve the sections for a specific process and module."}, diff --git a/vmmpycplugin/version.h b/vmmpycplugin/version.h index 360299e6..c671831f 100644 --- a/vmmpycplugin/version.h +++ b/vmmpycplugin/version.h @@ -1,12 +1,12 @@ #define STRINGIZE2(s) #s #define STRINGIZE(s) STRINGIZE2(s) -#define VERSION_MAJOR 2 -#define VERSION_MINOR 10 -#define VERSION_REVISION 2 -#define VERSION_BUILD 2 +#define VERSION_MAJOR 3 +#define VERSION_MINOR 0 +#define VERSION_REVISION 0 +#define VERSION_BUILD 3 -#define VER_FILE_DESCRIPTION_STR "The Memory Process File System : Python Plugin Manager" +#define VER_FILE_DESCRIPTION_STR "MemProcFS : Python Plugin Manager" #define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD #define VER_FILE_VERSION_STR STRINGIZE(VERSION_MAJOR) \ "." STRINGIZE(VERSION_MINOR) \ diff --git a/vmmpycplugin/vmmdll.h b/vmmpycplugin/vmmdll.h index 8a6b6a38..87f3f191 100644 --- a/vmmpycplugin/vmmdll.h +++ b/vmmpycplugin/vmmdll.h @@ -4,7 +4,7 @@ // (c) Ulf Frisk, 2018-2019 // Author: Ulf Frisk, pcileech@frizk.net // -// Header Version: 2.10.2 +// Header Version: 3.0 // #include @@ -23,11 +23,11 @@ extern "C" { /* * Initialize VMM.DLL with command line parameters. For a more detailed info -* about the parameters please see github wiki for Memory Process File System -* and LeechCore. THIS IS THE PREFERED WAY OF INITIALIZING VMM.DLL +* about the parameters please see github wiki for MemProcFS and LeechCore. +* NB! LeechCore initialization parameters are _also_ valid to this function. * Important parameters are: -* -printf = show printf style outputs) -* -v -vv -vvv = extra verbosity levels) +* -printf = show printf style outputs. +* -v -vv -vvv = extra verbosity levels. * -device = device as on format for LeechCore - please see leechcore.h or * Github documentation for additional information. Some values * are: , fpga, usb3380, hvsavedstate, totalmeltdown, pmem @@ -35,8 +35,19 @@ extern "C" { * documentation for additional information. * -norefresh = disable background refreshes (even if backing memory is * volatile memory). -* -symbolserverdisable = disable symbol server until user change. This -* parameter will take precedence over registry settings. +* -symbolserverdisable = disable symbol server until user change. +* This parameter will take precedence over registry settings. +* -pagefile[0-9] = page file(s) to use in addition to physical memory. +* Normally pagefile.sys have index 0 and swapfile.sys index 1. +* Page files are in constant flux - do not use if time diff +* between memory dump and page files are more than few minutes. +* Example: 'pagefile0 swapfile.sys' +* -waitinitialize = Wait for initialization to complete before returning. +* Normal use is that some initialization is done asynchronously +* and may not be completed when initialization call is completed. +* This includes virtual memory compression, registry and more. +* Example: '-waitinitialize' +* * -- argc * -- argv * -- return = success/fail @@ -78,9 +89,9 @@ VOID VMMDLL_MemFree(_Frees_ptr_opt_ PVOID pvMem); //----------------------------------------------------------------------------- /* -* Options used together with the functions: VMMDLL_GetOption & VMMDLL_SetOption +* Options used together with the functions: VMMDLL_ConfigGet & VMMDLL_ConfigSet * Options are defined with either: VMMDLL_OPT_* in this header file or as -* MEMDEVICE_OPT_* in memdevice.h +* LEECHCORE_OPT_* in leechcore.h * For more detailed information check the sources for individual device types. */ #define VMMDLL_OPT_CORE_PRINTF_ENABLE 0x80000001 // RW @@ -349,17 +360,17 @@ typedef struct tdVMMDLL_PLUGIN_REGINFO { #define VMMDLL_FLAG_ZEROPAD_ON_FAIL 0x0002 // zero pad failed physical memory reads and report success if read within range of physical memory. #define VMMDLL_FLAG_FORCECACHE_READ 0x0008 // force use of cache - fail non-cached pages - only valid for reads, invalid with VMM_FLAG_NOCACHE/VMM_FLAG_ZEROPAD_ON_FAIL. #define VMMDLL_FLAG_NOPAGING 0x0010 // do not try to retrieve memory from paged out memory from pagefile/compressed (even if possible) +#define VMMDLL_FLAG_NOPAGING_IO 0x0020 // do not try to retrieve memory from paged out memory if read would incur additional I/O (even if possible). /* * Read memory in various non-contigious locations specified by the pointers to -* the items in the ppDMAs array. Result for each unit of work will be given +* the items in the ppMEMs array. Result for each unit of work will be given * individually. No upper limit of number of items to read, but no performance * boost will be given if above hardware limit. Max size of each unit of work is * one 4k page (4096 bytes). * -- dwPID - PID of target process, (DWORD)-1 to read physical memory. * -- ppMEMs = array of scatter read headers. -* -- cpMEMs = count of ppDMAs. -* -- pcpDMAsRead = optional count of number of successfully read ppDMAs. +* -- cpMEMs = count of ppMEMs. * -- flags = optional flags as given by VMMDLL_FLAG_* * -- return = the number of successfully read items. */ @@ -384,7 +395,7 @@ BOOL VMMDLL_MemReadPage(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Inout_bytecount_(40 * -- return = success/fail (depending if all requested bytes are read or not). */ _Success_(return) -BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWORD cb); +BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb); /* * Read a contigious amount of memory and report the number of bytes read in pcbRead. @@ -398,7 +409,7 @@ BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWO * read - it's recommended to verify pcbReadOpt parameter. */ _Success_(return) -BOOL VMMDLL_MemReadEx(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_ PBYTE pb, _In_ DWORD cb, _Out_opt_ PDWORD pcbReadOpt, _In_ ULONG64 flags); +BOOL VMMDLL_MemReadEx(_In_ DWORD dwPID, _In_ ULONG64 qwA, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb, _Out_opt_ PDWORD pcbReadOpt, _In_ ULONG64 flags); /* * Prefetch a number of addresses (specified in the pA array) into the memory @@ -426,7 +437,7 @@ BOOL VMMDLL_MemPrefetchPages(_In_ DWORD dwPID, _In_reads_(cPrefetchAddresses) PU * -- return = TRUE on success, FALSE on partial or zero write. */ _Success_(return) -BOOL VMMDLL_MemWrite(_In_ DWORD dwPID, _In_ ULONG64 qwA, _In_ PBYTE pb, _In_ DWORD cb); +BOOL VMMDLL_MemWrite(_In_ DWORD dwPID, _In_ ULONG64 qwA, _In_reads_(cb) PBYTE pb, _In_ DWORD cb); /* * Translate a virtual address to a physical address by walking the page tables @@ -442,106 +453,286 @@ BOOL VMMDLL_MemVirt2Phys(_In_ DWORD dwPID, _In_ ULONG64 qwVA, _Out_ PULONG64 pqw //----------------------------------------------------------------------------- -// VMM PROCESS FUNCTIONALITY BELOW: -// Functionality below is mostly relating to Windows processes. +// VMM PROCESS MAP FUNCTIONALITY BELOW: +// Functionality for retrieving process related collections of items such as +// page table map (PTE), virtual address descriptor map (VAD), loaded modules, +// heaps and threads. //----------------------------------------------------------------------------- -/* -* Retrieve an active process given it's name. Please note that if multiple -* processes with the same name exists only one will be returned. If required to -* parse all processes with the same name please iterate over the PID list by -* calling VMMDLL_PidList together with VMMDLL_ProcessGetInformation. -* -- szProcName = process name case insensitive. -* -- pdwPID = pointer that will receive PID on success. -* -- return -*/ -_Success_(return) -BOOL VMMDLL_PidGetFromName(_In_ LPSTR szProcName, _Out_ PDWORD pdwPID); - -/* -* List the PIDs in the system. -* -- pPIDs = DWORD array of at least number of PIDs in system, or NULL. -* -- pcPIDs = size of (in number of DWORDs) pPIDs array on entry, number of PIDs in system on exit. -* -- return = success/fail. -*/ -_Success_(return) -BOOL VMMDLL_PidList(_Out_opt_ PDWORD pPIDs, _Inout_ PULONG64 pcPIDs); +#define VMMDLL_MAP_PTE_VERSION 1 +#define VMMDLL_MAP_VAD_VERSION 1 +#define VMMDLL_MAP_MODULE_VERSION 1 +#define VMMDLL_MAP_HEAP_VERSION 1 +#define VMMDLL_MAP_THREAD_VERSION 1 +#define VMMDLL_MAP_HANDLE_VERSION 1 -// flags to check for existence in the fPage field of PCILEECH_VMM_MEMMAP_ENTRY +// flags to check for existence in the fPage field of VMMDLL_MAP_PTEENTRY #define VMMDLL_MEMMAP_FLAG_PAGE_W 0x0000000000000002 #define VMMDLL_MEMMAP_FLAG_PAGE_NS 0x0000000000000004 #define VMMDLL_MEMMAP_FLAG_PAGE_NX 0x8000000000000000 #define VMMDLL_MEMMAP_FLAG_PAGE_MASK 0x8000000000000006 -typedef struct tdVMMDLL_MEMMAP_ENTRY { - ULONG64 AddrBase; - ULONG64 cPages; - ULONG64 fPage; +typedef struct tdVMMDLL_MAP_PTEENTRY { + QWORD vaBase; + QWORD cPages; + QWORD fPage; BOOL fWoW64; - CHAR szTag[32]; -} VMMDLL_MEMMAP_ENTRY, *PVMMDLL_MEMMAP_ENTRY; + DWORD cwszText; + LPWSTR wszText; + DWORD _Reserved1[2]; +} VMMDLL_MAP_PTEENTRY, *PVMMDLL_MAP_PTEENTRY; + +typedef struct tdVMMDLL_MAP_VADENTRY { + QWORD vaStart; + QWORD vaEnd; + QWORD vaVad; + // DWORD 0 + DWORD VadType : 3; // Pos 0 + DWORD Protection : 5; // Pos 3 + DWORD fImage : 1; // Pos 8 + DWORD fFile : 1; // Pos 9 + DWORD fPageFile : 1; // Pos 10 + DWORD fPrivateMemory : 1; // Pos 11 + DWORD fTeb : 1; // Pos 12 + DWORD fStack : 1; // Pos 13 + DWORD fSpare : 2; // Pos 14 + DWORD HeapNum : 7; // Pos 16 + DWORD fHeap : 1; // Pos 23 + DWORD cwszDescription : 8; // Pos 24 + // DWORD 1 + DWORD CommitCharge : 31; // Pos 0 + DWORD MemCommit : 1; // Pos 31 + DWORD u2; + DWORD cbPrototypePte; + QWORD vaPrototypePte; + QWORD vaSubsection; + LPWSTR wszText; // optional LPWSTR pointed into VMMDLL_MAP_VAD.wszMultiText + DWORD cwszText; // WCHAR count not including terminating null + DWORD _Reserved1; +} VMMDLL_MAP_VADENTRY, *PVMMDLL_MAP_VADENTRY; + +typedef struct tdVMMDLL_MAP_MODULEENTRY { + QWORD vaBase; + QWORD vaEntry; + DWORD cbImageSize; + BOOL fWoW64; + LPWSTR wszText; + DWORD cwszText; // wchar count not including terminating null + DWORD _Reserved1[7]; +} VMMDLL_MAP_MODULEENTRY, *PVMMDLL_MAP_MODULEENTRY; + +typedef struct tdVMMDLL_MAP_HEAPENTRY { + QWORD vaHeapSegment; + DWORD cPages; + DWORD cPagesUnCommitted : 24; + DWORD HeapId : 7; + DWORD fPrimary : 1; +} VMMDLL_MAP_HEAPENTRY, *PVMMDLL_MAP_HEAPENTRY; + +typedef struct tdVMMDLL_MAP_THREADENTRY { + DWORD dwTID; + DWORD dwPID; + DWORD dwExitStatus; + UCHAR bState; + UCHAR bRunning; + UCHAR bPriority; + UCHAR bBasePriority; + QWORD vaETHREAD; + QWORD vaTeb; + QWORD ftCreateTime; + QWORD ftExitTime; + QWORD vaStartAddress; + QWORD vaStackBaseUser; + QWORD vaStackLimitUser; + QWORD vaStackBaseKernel; + QWORD vaStackLimitKernel; + DWORD _FutureUse[10]; +} VMMDLL_MAP_THREADENTRY, *PVMMDLL_MAP_THREADENTRY; + +typedef struct tdVMMDLL_MAP_HANDLEENTRY { + QWORD vaObject; + DWORD dwHandle; + DWORD dwGrantedAccess : 24; + DWORD iType : 8; + QWORD qwHandleCount; + QWORD qwPointerCount; + QWORD vaObjectCreateInfo; + QWORD vaSecurityDescriptor; + LPWSTR wszText; + DWORD cwszText; + DWORD dwPID; + DWORD dwPoolTag; + DWORD _FutureUse[4]; + DWORD cwszType; + LPWSTR wszType; +} VMMDLL_MAP_HANDLEENTRY, *PVMMDLL_MAP_HANDLEENTRY; + +typedef struct tdVMMDLL_MAP_PTE { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // NULL or multi-wstr pointed into by VMMDLL_MAP_VADENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_PTEENTRY pMap[]; // map entries. +} VMMDLL_MAP_PTE, *PVMMDLL_MAP_PTE; + +typedef struct tdVMMDLL_MAP_VAD { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // NULL or multi-wstr pointed into by VMMDLL_MAP_VADENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_VADENTRY pMap[]; // map entries. +} VMMDLL_MAP_VAD, *PVMMDLL_MAP_VAD; + +typedef struct tdVMMDLL_MAP_MODULE { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // multi-wstr pointed into by VMMDLL_MAP_MODULEENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_MODULEENTRY pMap[]; // map entries. +} VMMDLL_MAP_MODULE, *PVMMDLL_MAP_MODULE; + +typedef struct tdVMMDLL_MAP_HEAP { + DWORD dwVersion; + DWORD _Reserved1[8]; + DWORD cMap; // # map entries. + VMMDLL_MAP_HEAPENTRY pMap[]; // map entries. +} VMMDLL_MAP_HEAP, *PVMMDLL_MAP_HEAP; + +typedef struct tdVMMDLL_MAP_THREAD { + DWORD dwVersion; + DWORD _Reserved[8]; + DWORD cMap; // # map entries. + VMMDLL_MAP_THREADENTRY pMap[]; // map entries. +} VMMDLL_MAP_THREAD, *PVMMDLL_MAP_THREAD; + +typedef struct tdVMMDLL_MAP_HANDLE { + DWORD dwVersion; + DWORD _Reserved1[5]; + LPWSTR wszMultiText; // multi-wstr pointed into by VMMDLL_MAP_HANDLEENTRY.wszText + DWORD cbMultiText; + DWORD cMap; // # map entries. + VMMDLL_MAP_HANDLEENTRY pMap[]; // map entries. +} VMMDLL_MAP_HANDLE, *PVMMDLL_MAP_HANDLE; /* -* Retrieve memory map entries from the specified process. Memory map entries -* are copied into the user supplied buffer that must be at least of size: -* sizeof(VMMDLL_MEMMAP_ENTRY)*pcMemMapEntries bytes. -* If the pMemMapEntries is set to NULL the number of memory map entries will be -* given in the pcMemMapEntries parameter. +* Retrieve the memory map entries based on hardware page tables (PTE) for the +* process. If pPteMap is set to NULL the number of bytes required will be +* returned in parameter pcbPteMap. +* Entries returned are sorted on VMMDLL_MAP_PTEENTRY.vaBase * -- dwPID -* -- pMemMapEntries = buffer of minimum length sizeof(VMMDLL_MEMMAP_ENTRY)*pcMemMapEntries, or NULL. -* -- pcMemMapEntries = pointer to number of memory map entries. +* -- pPteMap = buffer of minimum byte length *pcbPteMap or NULL. +* -- pcbPteMap = pointer to byte count of pPteMap buffer. * -- fIdentifyModules = try identify modules as well (= slower) * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetMemoryMap(_In_ DWORD dwPID, _Out_opt_ PVMMDLL_MEMMAP_ENTRY pMemMapEntries, _Inout_ PULONG64 pcMemMapEntries, _In_ BOOL fIdentifyModules); +BOOL VMMDLL_ProcessMap_GetPte(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbPteMap) PVMMDLL_MAP_PTE pPteMap, _Inout_ PDWORD pcbPteMap, _In_ BOOL fIdentifyModules); /* -* Retrieve a single memory map entry given a virtual address within that entrys -* range. +* Retrieve memory map entries based on virtual address descriptor (VAD) for +* the process. If pVadMap is set to NULL the number of bytes required +* will be returned in parameter pcbVadMap. +* Entries returned are sorted on VMMDLL_MAP_VADENTRY.vaStart * -- dwPID -* -- pMemMapEntry -* -- va = virtual address in the memory map entry to retrieve. +* -- pVadMap = buffer of minimum byte length *pcbVadMap or NULL. +* -- pcbVadMap = pointer to byte count of pVadMap buffer. * -- fIdentifyModules = try identify modules as well (= slower) * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetMemoryMapEntry(_In_ DWORD dwPID, _Out_ PVMMDLL_MEMMAP_ENTRY pMemMapEntry, _In_ ULONG64 va, _In_ BOOL fIdentifyModules); - -typedef struct tdVMMDLL_MODULEMAP_ENTRY { - ULONG64 BaseAddress; - ULONG64 EntryPoint; - DWORD SizeOfImage; - BOOL fWoW64; - CHAR szName[32]; -} VMMDLL_MODULEMAP_ENTRY, *PVMMDLL_MODULEMAP_ENTRY; +BOOL VMMDLL_ProcessMap_GetVad(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbVadMap) PVMMDLL_MAP_VAD pVadMap, _Inout_ PDWORD pcbVadMap, _In_ BOOL fIdentifyModules); /* -* Retrieve the module entries from the specified process. The module entries -* are copied into the user supplied buffer that must be at least of size: -* sizeof(VMMDLL_MODULEMAP_ENTRY)*pcModuleEntries bytes long. If the -* pcModuleEntries is set to NULL the number of module entries will be given -* in the pcModuleEntries parameter. +* Retrieve the modules (.dlls) for the specified process. If pModuleMap is set +* to NULL the number of bytes required will be returned in parameter pcbModuleMap. * -- dwPID -* -- pModuleEntries = buffer of minimum length sizeof(VMMDLL_MODULEMAP_ENTRY)*pcModuleEntries, or NULL. -* -- pcModuleEntries = pointer to number of memory map entries. +* -- pModuleMap = buffer of minimum byte length *pcbModuleMap or NULL. +* -- pcbModuleMap = pointer to byte count of pModuleMap buffer. * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetModuleMap(_In_ DWORD dwPID, _Out_opt_ PVMMDLL_MODULEMAP_ENTRY pModuleEntries, _Inout_ PULONG64 pcModuleEntries); +BOOL VMMDLL_ProcessMap_GetModule(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbModuleMap) PVMMDLL_MAP_MODULE pModuleMap, _Inout_ PDWORD pcbModuleMap); /* -* Retrieve a module (.exe or .dll or similar) given a module name. +* Retrieve a module map entry (.exe / .dll) given a process and module name. +* NB! PVMMDLL_MAP_MODULEENTRY->wszText will not be set by this function. +* If module name is required use VMMDLL_ProcessMap_GetModule(). * -- dwPID * -- szModuleName -* -- pModuleEntry +* -- pModuleMapEntry * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetModuleFromName(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _Out_ PVMMDLL_MODULEMAP_ENTRY pModuleEntry); +BOOL VMMDLL_ProcessMap_GetModuleFromName(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _Out_ PVMMDLL_MAP_MODULEENTRY pModuleMapEntry); + +/* +* Retrieve the heaps for the specified process. If pHeapMap is set to NULL +* the number of bytes required will be returned in parameter pcbHeapMap. +* -- dwPID +* -- pHeapMap = buffer of minimum byte length *pcbHeapMap or NULL. +* -- pcbHeapMap = pointer to byte count of pHeapMap buffer. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_ProcessMap_GetHeap(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbHeapMap) PVMMDLL_MAP_HEAP pHeapMap, _Inout_ PDWORD pcbHeapMap); + +/* +* Retrieve the threads for the specified process. If pThreadMap is set to NULL +* the number of bytes required will be returned in parameter pcbThreadMap. +* Entries returned are sorted on VMMDLL_MAP_THREADENTRY.dwTID +* -- dwPID +* -- pThreadMap = buffer of minimum byte length *pcbThreadMap or NULL. +* -- pcbThreadMap = pointer to byte count of pThreadMap buffer. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_ProcessMap_GetThread(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbThreadMap) PVMMDLL_MAP_THREAD pThreadMap, _Inout_ PDWORD pcbThreadMap); + +/* +* Retrieve the handles for the specified process. If pHandleMap is set to NULL +* the number of bytes required will be returned in parameter pcbHandleMap. +* Entries returned are sorted on VMMDLL_MAP_HANDLEENTRY.dwHandle +* -- dwPID +* -- pHandleMap = buffer of minimum byte length *pcbHandleMap or NULL. +* -- pcbHandleMap = pointer to byte count of pHandleMap buffer. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_ProcessMap_GetHandle(_In_ DWORD dwPID, _Out_writes_bytes_opt_(*pcbHandleMap) PVMMDLL_MAP_HANDLE pHandleMap, _Inout_ PDWORD pcbHandleMap); + + + +//----------------------------------------------------------------------------- +// VMM PROCESS FUNCTIONALITY BELOW: +// Functionality below is mostly relating to Windows processes. +//----------------------------------------------------------------------------- + +/* +* Retrieve an active process given it's name. Please note that if multiple +* processes with the same name exists only one will be returned. If required to +* parse all processes with the same name please iterate over the PID list by +* calling VMMDLL_PidList together with VMMDLL_ProcessGetInformation. +* -- szProcName = process name case insensitive. +* -- pdwPID = pointer that will receive PID on success. +* -- return +*/ +_Success_(return) +BOOL VMMDLL_PidGetFromName(_In_ LPSTR szProcName, _Out_ PDWORD pdwPID); + +/* +* List the PIDs in the system. +* -- pPIDs = DWORD array of at least number of PIDs in system, or NULL. +* -- pcPIDs = size of (in number of DWORDs) pPIDs array on entry, number of PIDs in system on exit. +* -- return = success/fail. +*/ +_Success_(return) +BOOL VMMDLL_PidList(_Out_writes_opt_(*pcPIDs) PDWORD pPIDs, _Inout_ PULONG64 pcPIDs); #define VMMDLL_PROCESS_INFORMATION_MAGIC 0xc0ffee663df9301e -#define VMMDLL_PROCESS_INFORMATION_VERSION 4 +#define VMMDLL_PROCESS_INFORMATION_VERSION 5 typedef struct tdVMMDLL_PROCESS_INFORMATION { ULONG64 magic; @@ -561,7 +752,7 @@ typedef struct tdVMMDLL_PROCESS_INFORMATION { struct { ULONG64 vaEPROCESS; ULONG64 vaPEB; - ULONG64 vaENTRY; + ULONG64 _Reserved1; BOOL fWow64; DWORD vaPEB32; // WoW64 only } win; @@ -613,37 +804,37 @@ typedef struct tdVMMDLL_IAT_ENTRY { * If the pData == NULL upon entry the number of entries of the pData array must * have in order to be able to hold the data is returned. * -- dwPID -* -- szModule +* -- wszModule * -- pData * -- cData * -- pcData * -- return = success/fail. */ _Success_(return) -BOOL VMMDLL_ProcessGetDirectories(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_writes_(16) PIMAGE_DATA_DIRECTORY pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetDirectories(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_writes_(16) PIMAGE_DATA_DIRECTORY pData, _In_ DWORD cData, _Out_ PDWORD pcData); _Success_(return) -BOOL VMMDLL_ProcessGetSections(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PIMAGE_SECTION_HEADER pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetSections(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PIMAGE_SECTION_HEADER pData, _In_ DWORD cData, _Out_ PDWORD pcData); _Success_(return) -BOOL VMMDLL_ProcessGetEAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PVMMDLL_EAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetEAT(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PVMMDLL_EAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); _Success_(return) -BOOL VMMDLL_ProcessGetIAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PVMMDLL_IAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); +BOOL VMMDLL_ProcessGetIAT(_In_ DWORD dwPID, _In_ LPWSTR wszModule, _Out_opt_ PVMMDLL_IAT_ENTRY pData, _In_ DWORD cData, _Out_ PDWORD pcData); /* * Retrieve the virtual address of a given function inside a process/module. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- szFunctionName * -- return = virtual address of function, zero on fail. */ -ULONG64 VMMDLL_ProcessGetProcAddress(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szFunctionName); +ULONG64 VMMDLL_ProcessGetProcAddress(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szFunctionName); /* * Retrieve the base address of a given module. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- return = virtual address of module base, zero on fail. */ -ULONG64 VMMDLL_ProcessGetModuleBase(_In_ DWORD dwPID, _In_ LPSTR szModuleName); +ULONG64 VMMDLL_ProcessGetModuleBase(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName); @@ -729,7 +920,7 @@ BOOL VMMDLL_WinReg_HiveList(_Out_writes_(cHives) PVMMDLL_REGISTRY_HIVE_INFORMATI * -- ra * -- pb * -- cb -* -- pcbRead +* -- pcbReadOpt * -- flags = flags as in VMMDLL_FLAG_* * -- return = success/fail. NB! reads may report as success even if 0 bytes are * read - it's recommended to verify pcbReadOpt parameter. @@ -901,26 +1092,26 @@ typedef struct tdVMMDLL_WIN_THUNKINFO_EAT { * function. This includes the virtual address of the IAT thunk which is useful * for hooking. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- szImportModuleName * -- szImportFunctionName * -- pThunkIAT * -- return */ _Success_(return) -BOOL VMMDLL_WinGetThunkInfoIAT(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szImportModuleName, _In_ LPSTR szImportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_IAT pThunkInfoIAT); +BOOL VMMDLL_WinGetThunkInfoIAT(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szImportModuleName, _In_ LPSTR szImportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_IAT pThunkInfoIAT); /* * Retrieve information about the export address table EAT thunk for an exported * function. This includes the virtual address of the EAT thunk which is useful * for hooking. * -- dwPID -* -- szModuleName +* -- wszModuleName * -- pThunkEAT * -- return */ _Success_(return) -BOOL VMMDLL_WinGetThunkInfoEAT(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT); +BOOL VMMDLL_WinGetThunkInfoEAT(_In_ DWORD dwPID, _In_ LPWSTR wszModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT); diff --git a/vmmpycplugin/vmmpycplugin.c b/vmmpycplugin/vmmpycplugin.c index 5bd97ca2..ee913a44 100644 --- a/vmmpycplugin/vmmpycplugin.c +++ b/vmmpycplugin/vmmpycplugin.c @@ -348,8 +348,10 @@ BOOL VmmPyPlugin_PythonInitialize(_In_ HMODULE hDllPython) VOID PYTHON_Close() { - PY2C_Callback_Close(); - Py_FinalizeEx(); + __try { + PY2C_Callback_Close(); + Py_FinalizeEx(); + } __except(EXCEPTION_EXECUTE_HANDLER) { ; } } /*