diff --git a/README.md b/README.md index 2540017..3695088 100644 --- a/README.md +++ b/README.md @@ -137,3 +137,9 @@ v2.0 * 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. diff --git a/files/leechcore.dll b/files/leechcore.dll index c269e89..16519ec 100644 Binary files a/files/leechcore.dll and b/files/leechcore.dll differ diff --git a/files/leechcore.h b/files/leechcore.h index d49a089..46af16a 100644 --- a/files/leechcore.h +++ b/files/leechcore.h @@ -111,7 +111,7 @@ // (c) Ulf Frisk, 2018-2019 // Author: Ulf Frisk, pcileech@frizk.net // -// Header Version: 1.0.2 +// Header Version: 1.1.0 // #ifndef __LEECHCORE_H__ #define __LEECHCORE_H__ @@ -229,7 +229,10 @@ typedef struct tdLEECHCORE_PAGESTAT_MINIMAL { } LEECHCORE_PAGESTAT_MINIMAL, *PLEECHCORE_PAGESTAT_MINIMAL; /* -* Open a connection to the target device. +* Open a connection to the target device. The LeechCore initialization may fail +* if the underlying device cannot be opened or if the LeechCore is already +* initialized. If already initialized please connect with device EXISTING or +* call LeechCore_Close() before opening a new device. * -- pInformation * -- result */ diff --git a/files/leechsvc.exe b/files/leechsvc.exe index cb2c423..c55e7d1 100644 Binary files a/files/leechsvc.exe and b/files/leechsvc.exe differ diff --git a/files/vmm.dll b/files/vmm.dll index b4d4be8..838340e 100644 Binary files a/files/vmm.dll and b/files/vmm.dll differ diff --git a/files/vmm.lib b/files/vmm.lib index 28d1f43..7be0dfa 100644 Binary files a/files/vmm.lib and b/files/vmm.lib differ diff --git a/files/vmmdll.h b/files/vmmdll.h index 353159a..42befa8 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.0 +// Header Version: 2.1 // #include @@ -26,13 +26,15 @@ extern "C" { * about the parameters please see github wiki for Memory Process File System * and LeechCore. THIS IS THE PREFERED WAY OF INITIALIZING VMM.DLL * Important parameters are: -* -vdll = show printf style outputs) +* -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 * -remote = remote LeechCore instance - please see leechcore.h or Github * documentation for additional information. +* -norefresh = disable background refreshes (even if backing memory is +* volatile memory). * -- argc * -- argv * -- return = success/fail @@ -289,7 +291,7 @@ typedef struct tdVMMDLL_PLUGIN_REGINFO { * -- ppMEMs = array of scatter read headers. * -- cpMEMs = count of ppDMAs. * -- pcpDMAsRead = optional count of number of successfully read ppDMAs. -* -- flags = optional flags as given by VMM_FLAG_* +* -- flags = optional flags as given by VMMDLL_FLAG_* * -- return = the number of successfully read items. */ DWORD VMMDLL_MemReadScatter(_In_ DWORD dwPID, _Inout_ PPMEM_IO_SCATTER_HEADER ppMEMs, _In_ DWORD cpMEMs, _In_ DWORD flags); @@ -322,13 +324,25 @@ BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwVA, _Out_ PBYTE pb, _In_ DW * -- pb * -- cb * -- pcbRead -* -- flags = flags as in VMM_FLAG_* +* -- 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. */ _Success_(return) BOOL VMMDLL_MemReadEx(_In_ DWORD dwPID, _In_ ULONG64 qwVA, _Out_ 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 +* cache. This function is to be used to batch larger known reads into local +* cache before making multiple smaller reads - which will then happen from +* the cache. Function exists for performance reasons. +* -- dwPID = PID of target process, (DWORD)-1 for physical memory. +* -- pPrefetchAddresses = array of addresses to read into cache. +* -- cPrefetchAddresses +*/ +_Success_(return) +BOOL VMMDLL_MemPrefetchPages(_In_ DWORD dwPID, _In_reads_(cPrefetchAddresses) PULONG64 pPrefetchAddresses, _In_ DWORD cPrefetchAddresses); + /* * Write a contigious arbitrary amount of memory. Please note some virtual memory * such as pages of executables (such as DLLs) may be shared between different @@ -527,6 +541,87 @@ BOOL VMMDLL_ProcessGetEAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PVMMD _Success_(return) BOOL VMMDLL_ProcessGetIAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _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 +* -- szFunctionName +* -- return = virtual address of function, zero on fail. +*/ +ULONG64 VMMDLL_ProcessGetProcAddress(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szFunctionName); + +/* +* Retrieve the base address of a given module. +* -- dwPID +* -- szModuleName +* -- return = virtual address of module base, zero on fail. +*/ +ULONG64 VMMDLL_ProcessGetModuleBase(_In_ DWORD dwPID, _In_ LPSTR szModuleName); + + + +//----------------------------------------------------------------------------- +// WINDOWS SPECIFIC UTILITY FUNCTIONS BELOW: +//----------------------------------------------------------------------------- + +typedef struct tdVMMDLL_WIN_THUNKINFO_IAT { + BOOL fValid; + BOOL f32; // if TRUE fn is a 32-bit/4-byte entry, otherwise 64-bit/8-byte entry. + ULONG64 vaThunk; // address of import address table 'thunk'. + ULONG64 vaFunction; // value if import address table 'thunk' == address of imported function. + ULONG64 vaNameModule; // address of name string for imported module. + ULONG64 vaNameFunction; // address of name string for imported function. +} VMMDLL_WIN_THUNKINFO_IAT, *PVMMDLL_WIN_THUNKINFO_IAT; + +typedef struct tdVMMDLL_WIN_THUNKINFO_EAT { + BOOL fValid; + DWORD valueThunk; // value of export address table 'thunk'. + ULONG64 vaThunk; // address of import address table 'thunk'. + ULONG64 vaNameFunction; // address of name string for exported function. + ULONG64 vaFunction; // address of exported function (module base + value parameter). +} VMMDLL_WIN_THUNKINFO_EAT, *PVMMDLL_WIN_THUNKINFO_EAT; + +/* +* Retrieve information about the import address table IAT thunk for an imported +* function. This includes the virtual address of the IAT thunk which is useful +* for hooking. +* -- dwPID +* -- szModuleName +* -- 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); + +/* +* 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 +* -- pThunkEAT +* -- return +*/ +_Success_(return) +BOOL VMMDLL_WinGetThunkInfoEAT(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT); + +/* +* Decompress compressed memory page stored in the MemCompression process. +* -- vaCompressedData = virtual address in 'MemCompression' to decompress. +* -- cbCompressedData = length of compressed data in 'MemCompression' to decompress (or zero for auto-detect). +* -- pbDecompressedPage +* -- pcbCompressedData = optional ptr to receive length of compressed buffer. +* -- return +*/ +_Success_(return) +BOOL VMMDLL_WinMemCompression_DecompressPage( + _In_ ULONG64 vaCompressedData, + _In_opt_ DWORD cbCompressedData, + _Out_writes_(4096) PBYTE pbDecompressedPage, + _Out_opt_ PDWORD pcbCompressedData +); //----------------------------------------------------------------------------- diff --git a/files/vmmpy.py b/files/vmmpy.py index 6b62ebe..65d65fe 100644 --- a/files/vmmpy.py +++ b/files/vmmpy.py @@ -9,7 +9,7 @@ # # https://github.com/ufrisk/ # -# (c) Ulf Frisk, 2018 +# (c) Ulf Frisk, 2018-2019 # Author: Ulf Frisk, pcileech@frizk.net # @@ -56,7 +56,7 @@ def VmmPy_Close(): N/A Example: - VmmPy_Close() --> True + VmmPy_Close() """ VMMPYC_Close() @@ -485,6 +485,58 @@ def VmmPy_VfsWrite(path_file, bytes_data, offset = 0): VmmPy_VfsWrite(path_file, bytes_data, offset) +#------------------------------------------------------------------------------ +# VmmPy WINDOWS ONLY FUNCTIONALITY BELOW: +#------------------------------------------------------------------------------ + +def VmmPy_WinGetThunkInfoEAT(pid, module_name, exported_function): + """Retrieve information about a single export address table (EAT) entry. This may be useful for hooking. + + Keyword arguments: + pid -- int: the process identifier (pid) when reading process virtual memory. + module_name -- str: name of the module to retrieve. + exported_function -- str: name of the exported function to retrieve. + return -- dict: information about the EAT entry. + + Example: + VmmPy_WinGetThunkInfoEAT(4, 'ntoskrnl.exe', 'KeGetCurrentIrql') --> {'vaFunction': 18446735288139539584, 'valueThunk': 1479808, 'vaNameFunction': 18446735288147899428, 'vaThunk': 18446735288147849312} + """ + return VMMPYC_WinGetThunkInfoEAT(pid, module_name, exported_function) + + + +def VmmPy_WinGetThunkInfoIAT(pid, module_name, imported_module_name, imported_module_function): + """Retrieve information about a single import address table (IAT) entry. This may be useful for hooking. + + Keyword arguments: + pid -- int: the process identifier (pid) when reading process virtual memory. + module_name -- str: name of the module to retrieve. + imported_module_name -- str: name of the imported module to retrieve. + imported_module_function -- str: name of the imported function to retrieve. + return -- dict: information about the IAT entry. + + Example: + VmmPy_WinGetThunkInfoIAT(4, 'ntoskrnl.exe', 'hal.dll', 'HalSendNMI') --> {'32': False, 'vaFunction': 18446735288149190896, 'vaNameFunction': 18446735288143568050, 'vaNameModule': 18446735288143568362, 'vaThunk': 18446735288143561136} + """ + return VMMPYC_WinGetThunkInfoIAT(pid, module_name, imported_module_name, imported_module_function) + + + +def VmmPy_WinDecompressPage(va_compressed, len_compressed = 0): + """Decompress a page stored in the MemCompression process in Windows 10. + + Keyword arguments: + va_compressed -- int: the virtual address inside 'MemCompression' where the compressed buffer starts. + len_compressed -- int: optional length of the compressed buffer (leave out for auto-detect). + return -- dict: containing decompressed data and size of compressed buffer. + + Example: + VmmPy_WinDecompressPage(0x00000210bfb40000) --> {'c': 456, 'b': b'...'} + """ + return VMMPYC_WinMemCompression_DecompressPage(va_compressed, len_compressed) + + + #------------------------------------------------------------------------------ # VmmPy UTIL FUNCTIONALITY BELOW: #------------------------------------------------------------------------------ diff --git a/files/vmmpyc.pyd b/files/vmmpyc.pyd index 4932d84..0b9a4c2 100644 Binary files a/files/vmmpyc.pyd and b/files/vmmpyc.pyd differ diff --git a/vmm/leechcore.h b/vmm/leechcore.h index a2da3e6..46af16a 100644 --- a/vmm/leechcore.h +++ b/vmm/leechcore.h @@ -68,13 +68,14 @@ // placed in same directory as the executable file. // // PMEM : load the rekall winpmem driver into the kernel and connect to it -// to acquire memory. The driver file 'winpmem_x64.sys' is found in -// the Rekall directory after most recent version has been installed. -// Copy 'winpmem_x64.sys' to the directory of leechcore.dll and run -// executable as elevated admin using syntax below: +// to acquire memory. The signed driver `.sys` file may be found at: +// https://github.com/Velocidex/c-aff4/tree/master/tools/pmem/resources/winpmem +// Download the driver file `att_winpmem_64.sys` and copy it to the +// directory of leechcore.dll and run executable as elevated admin +// using syntax below: // Syntax: -// PMEM (use winpmem_x64.sys in directory of executable) -// PMEM:// +// PMEM (use att_winpmem_64.sys in directory of executable) +// PMEM:// // // TOTALMELTDOWN : read/write - requires a Windows 7 system vulnerable to the // "Total Meltdown" vulnerability - CVE-2018-1038. @@ -110,7 +111,7 @@ // (c) Ulf Frisk, 2018-2019 // Author: Ulf Frisk, pcileech@frizk.net // -// Header Version: 1.0 +// Header Version: 1.1.0 // #ifndef __LEECHCORE_H__ #define __LEECHCORE_H__ @@ -151,6 +152,7 @@ typedef long long unsigned int QWORD, *PQWORD, ULONG64, *PULONG64; #define _Printf_format_string_ #define _Inout_updates_bytes_(x) #define _In_reads_(cbDataIn) +#define _Out_writes_opt_(x) #define _Success_(return) #endif /* LINUX */ @@ -227,7 +229,10 @@ typedef struct tdLEECHCORE_PAGESTAT_MINIMAL { } LEECHCORE_PAGESTAT_MINIMAL, *PLEECHCORE_PAGESTAT_MINIMAL; /* -* Open a connection to the target device. +* Open a connection to the target device. The LeechCore initialization may fail +* if the underlying device cannot be opened or if the LeechCore is already +* initialized. If already initialized please connect with device EXISTING or +* call LeechCore_Close() before opening a new device. * -- pInformation * -- result */ @@ -455,9 +460,9 @@ DLLEXPORT BOOL LeechCore_CommandData( _In_ ULONG64 fOption, _In_reads_(cbDataIn) PBYTE pbDataIn, _In_ DWORD cbDataIn, - _Out_writes_(cbDataOut) PBYTE pbDataOut, + _Out_writes_opt_(cbDataOut) PBYTE pbDataOut, _In_ DWORD cbDataOut, - _Out_ PDWORD pcbDataOut + _Out_opt_ PDWORD pcbDataOut ); #ifdef __cplusplus diff --git a/vmm/m_ldrmodules.c b/vmm/m_ldrmodules.c index 34df75a..9e46e32 100644 --- a/vmm/m_ldrmodules.c +++ b/vmm/m_ldrmodules.c @@ -35,7 +35,7 @@ typedef struct tdOBLDRMODULES_CACHE_ENTRY { */ POBLDRMODULES_CACHE_ENTRY LdrModule_GetEAT(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ PVMM_MODULEMAP_ENTRY pModule) { - DWORD i, o, cEATs; + DWORD i, o, cEATs = 0; PVMMPROC_WINDOWS_EAT_ENTRY pEATs = NULL; POBLDRMODULES_CACHE_ENTRY pObCacheEntry = NULL; PVMM_PROCESS pProcess = (PVMM_PROCESS)ctx->pProcess; @@ -47,10 +47,9 @@ POBLDRMODULES_CACHE_ENTRY LdrModule_GetEAT(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ VmmOb_DECREF(pObCacheEntry); pObCacheEntry = NULL; // 2: retrieve exported functions - cEATs = LDRMODULES_MAX_IATEAT; pEATs = LocalAlloc(0, LDRMODULES_MAX_IATEAT * sizeof(VMMPROC_WINDOWS_EAT_ENTRY)); if(!pEATs) { goto fail; } - VmmWin_PE_LoadEAT_DisplayBuffer(ctx->pProcess, pModule, pEATs, &cEATs); + VmmWin_PE_LoadEAT_DisplayBuffer(ctx->pProcess, pModule, pEATs, LDRMODULES_MAX_IATEAT, &cEATs); if(!cEATs) { goto fail; } // 3: fill "display buffer" pObCacheEntry = VmmOb_Alloc('EA', LMEM_ZEROINIT, sizeof(OBLDRMODULES_CACHE_ENTRY) + (QWORD)cEATs * 64 + 1, NULL, NULL); @@ -87,7 +86,7 @@ fail: */ POBLDRMODULES_CACHE_ENTRY LdrModule_GetIAT(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ PVMM_MODULEMAP_ENTRY pModule) { - DWORD i, o, cIATs; + DWORD i, o, cIATs = 0; PVMMWIN_IAT_ENTRY pIATs = NULL; POBLDRMODULES_CACHE_ENTRY pObCacheEntry = NULL; PVMM_PROCESS pProcess = (PVMM_PROCESS)ctx->pProcess; @@ -99,10 +98,9 @@ POBLDRMODULES_CACHE_ENTRY LdrModule_GetIAT(_In_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ VmmOb_DECREF(pObCacheEntry); pObCacheEntry = NULL; // 2: retrieve exported functions - cIATs = LDRMODULES_MAX_IATEAT; pIATs = LocalAlloc(0, LDRMODULES_MAX_IATEAT * sizeof(VMMWIN_IAT_ENTRY)); if(!pIATs) { goto fail; } - VmmWin_PE_LoadIAT_DisplayBuffer(ctx->pProcess, pModule, pIATs, &cIATs); + VmmWin_PE_LoadIAT_DisplayBuffer(ctx->pProcess, pModule, pIATs, LDRMODULES_MAX_IATEAT, &cIATs); if(!cIATs) { goto fail; } // 3: fill "display buffer" pObCacheEntry = VmmOb_Alloc('IA', LMEM_ZEROINIT, sizeof(OBLDRMODULES_CACHE_ENTRY) + (QWORD)cIATs * 128 + 1, NULL, NULL); diff --git a/vmm/mm_x64_winpaged.c b/vmm/mm_x64_winpaged.c new file mode 100644 index 0000000..3d204ad --- /dev/null +++ b/vmm/mm_x64_winpaged.c @@ -0,0 +1,54 @@ +// mm_x64_winpaged.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_winpaged.h" + +#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) + +_Success_(return) +BOOL MmX64WinPaged_MemCompression_DecompressPage(_In_ QWORD vaCompressedData, _In_opt_ DWORD cbCompressedData, _Out_writes_(4096) PBYTE pbDecompressedPage, _Out_opt_ PDWORD pcbCompressedData) +{ + BOOL result = FALSE; + DWORD i, cbReadCompressedData = 0, cbDecompressed = 0; + BYTE pbCompressed[0x1000] = { 0 }; + PVMM_PROCESS pObProcess = NULL; + if(pcbCompressedData) { *pcbCompressedData = 0; } + if(!ctxVmm->fn.RtlDecompressBuffer) { return FALSE; } + if(cbCompressedData > 0x1000) { return FALSE; } + if(!ctxVmm->kernel.dwPidMemCompression) { return FALSE; } + if(!(pObProcess = VmmProcessGet(ctxVmm->kernel.dwPidMemCompression))) { return FALSE; } + // buffer size specified - use value! + if(cbCompressedData) { + result = + VmmRead(pObProcess, vaCompressedData, pbCompressed, cbCompressedData) && + (VMM_STATUS_SUCCESS == ctxVmm->fn.RtlDecompressBuffer(COMPRESS_ALGORITHM_XPRESS, pbDecompressedPage, 0x1000, pbCompressed, cbCompressedData, &cbDecompressed)) && + (cbDecompressed == 0x1000); + VmmOb_DECREF(pObProcess); pObProcess = NULL; + if(pcbCompressedData) { *pcbCompressedData = cbCompressedData; } + return result; + } + // buffer not specified - try auto-detect! + VmmReadEx(pObProcess, vaCompressedData, pbCompressed, 0x1000, &cbReadCompressedData, VMM_FLAG_ZEROPAD_ON_FAIL); + VmmOb_DECREF(pObProcess); pObProcess = NULL; + if(cbReadCompressedData < 0x10) { return FALSE; } + for(i = 0x10; i < 0x1000; i++) { + result = + (VMM_STATUS_SUCCESS == ctxVmm->fn.RtlDecompressBuffer(COMPRESS_ALGORITHM_XPRESS, pbDecompressedPage, 0x1000, pbCompressed, i, &cbDecompressed)) && + (cbDecompressed == 0x1000); + if(result) { + if(pcbCompressedData) { *pcbCompressedData = i; } + return TRUE; + } + } + return FALSE; +} diff --git a/vmm/mm_x64_winpaged.h b/vmm/mm_x64_winpaged.h new file mode 100644 index 0000000..4b387ea --- /dev/null +++ b/vmm/mm_x64_winpaged.h @@ -0,0 +1,26 @@ +// mm_x64_winpaged.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_WINPAGED_H__ +#define __MM_X64_WINPAGED_H__ +#include "vmm.h" + +/* +* Decompress compressed memory page stored in the MemCompression process. +* -- vaCompressedData = virtual address in 'MemCompression' to decompress. +* -- cbCompressedData = length of compressed data in 'MemCompression' to decompress. +* -- pbDecompressedPage +* -- return +*/ +_Success_(return) +BOOL MmX64WinPaged_MemCompression_DecompressPage( + _In_ QWORD vaCompressedData, + _In_opt_ DWORD cbCompressedData, + _Out_writes_(4096) PBYTE pbDecompressedPage, + _Out_opt_ PDWORD pcbCompressedData +); + +#endif /* __MM_X64_WINPAGED_H__ */ diff --git a/vmm/pe.c b/vmm/pe.c index be5fbc1..6e9c56c 100644 --- a/vmm/pe.c +++ b/vmm/pe.c @@ -5,8 +5,8 @@ // (c) Ulf Frisk, 2018-2019 // Author: Ulf Frisk, pcileech@frizk.net // - #include "vmm.h" +#include "pe.h" PIMAGE_NT_HEADERS PE_HeaderGetVerify(_In_ PVMM_PROCESS pProcess, _In_opt_ QWORD vaModuleBase, _Inout_ PBYTE pbModuleHeader, _Out_opt_ PBOOL pfHdr32) { @@ -40,7 +40,102 @@ QWORD PE_GetSize(_In_ PVMM_PROCESS pProcess, _In_opt_ QWORD vaModuleBase) return cbSize; } -QWORD PE_GetProcAddress(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaModuleBase, _In_ LPSTR lpProcName) +_Success_(return) +BOOL PE_GetThunkInfoIAT(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaModuleBase, _In_ LPSTR szImportModuleName, _In_ LPSTR szImportProcName, _Out_ PPE_THUNKINFO_IAT pThunkInfoIAT) +{ + BYTE pbModuleHeader[0x1000] = { 0 }; + PIMAGE_NT_HEADERS64 ntHeader64; + PIMAGE_NT_HEADERS32 ntHeader32; + QWORD i, oImportDirectory; + PIMAGE_IMPORT_DESCRIPTOR pIID; + PQWORD pIAT64, pHNA64; + PDWORD pIAT32, pHNA32; + DWORD cbModule, cbRead; + PBYTE pbModule = NULL; + BOOL f32, fFnName; + DWORD c, j; + LPSTR szNameFunction, szNameModule; + // load both 32/64 bit ntHeader (only one will be valid) + if(!(ntHeader64 = PE_HeaderGetVerify(pProcess, vaModuleBase, pbModuleHeader, &f32))) { goto fail; } + ntHeader32 = (PIMAGE_NT_HEADERS32)ntHeader64; + cbModule = f32 ? + ntHeader32->OptionalHeader.SizeOfImage : + ntHeader64->OptionalHeader.SizeOfImage; + if(cbModule > 0x02000000) { goto fail; } + oImportDirectory = f32 ? + ntHeader32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress : + ntHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; + if(!oImportDirectory || (oImportDirectory >= cbModule)) { goto fail; } + if(!(pbModule = LocalAlloc(LMEM_ZEROINIT, cbModule))) { goto fail; } + VmmReadEx(pProcess, vaModuleBase, pbModule, cbModule, &cbRead, VMM_FLAG_ZEROPAD_ON_FAIL); + if(cbRead <= 0x2000) { goto fail; } + // Walk imported modules / functions + pIID = (PIMAGE_IMPORT_DESCRIPTOR)(pbModule + oImportDirectory); + i = 0, c = 0; + while((oImportDirectory + (i + 1) * sizeof(IMAGE_IMPORT_DESCRIPTOR) < cbModule) && pIID[i].FirstThunk) { + if(pIID[i].Name > cbModule - 64) { i++; continue; } + if(f32) { + // 32-bit PE + j = 0; + pIAT32 = (PDWORD)(pbModule + pIID[i].FirstThunk); + pHNA32 = (PDWORD)(pbModule + pIID[i].OriginalFirstThunk); + while(TRUE) { + if((QWORD)(pIAT32 + j) + sizeof(DWORD) - (QWORD)pbModule > cbModule) { break; } + if((QWORD)(pHNA32 + j) + sizeof(DWORD) - (QWORD)pbModule > cbModule) { break; } + if(!pIAT32[j]) { break; } + if(!pHNA32[j]) { break; } + fFnName = (pHNA32[j] < cbModule - 40); + szNameFunction = (LPSTR)(pbModule + pHNA32[j] + 2); + szNameModule = (LPSTR)(pbModule + pIID[i].Name); + if(fFnName && !strcmp(szNameFunction, szImportProcName) && !_stricmp(szNameModule, szImportModuleName)) { + pThunkInfoIAT->fValid = TRUE; + pThunkInfoIAT->f32 = TRUE; + pThunkInfoIAT->vaThunk = vaModuleBase + pIID[i].FirstThunk + sizeof(DWORD) * j; + pThunkInfoIAT->vaFunction = pIAT32[j]; + pThunkInfoIAT->vaNameFunction = vaModuleBase + pHNA32[j] + 2; + pThunkInfoIAT->vaNameModule = vaModuleBase + pIID[i].Name; + LocalFree(pbModule); + return TRUE; + } + c++; + j++; + } + } else { + // 64-bit PE + j = 0; + pIAT64 = (PQWORD)(pbModule + pIID[i].FirstThunk); + pHNA64 = (PQWORD)(pbModule + pIID[i].OriginalFirstThunk); + while(TRUE) { + if((QWORD)(pIAT64 + j) + sizeof(QWORD) - (QWORD)pbModule > cbModule) { break; } + if((QWORD)(pHNA64 + j) + sizeof(QWORD) - (QWORD)pbModule > cbModule) { break; } + if(!pIAT64[j]) { break; } + if(!pHNA64[j]) { break; } + fFnName = (pHNA64[j] < cbModule - 40); + szNameFunction = (LPSTR)(pbModule + pHNA64[j] + 2); + szNameModule = (LPSTR)(pbModule + pIID[i].Name); + if(fFnName && !strcmp(szNameFunction, szImportProcName) && !_stricmp(szNameModule, szImportModuleName)) { + pThunkInfoIAT->fValid = TRUE; + pThunkInfoIAT->f32 = FALSE; + pThunkInfoIAT->vaThunk = vaModuleBase + pIID[i].FirstThunk + sizeof(QWORD) * j; + pThunkInfoIAT->vaFunction = pIAT64[j]; + pThunkInfoIAT->vaNameFunction = vaModuleBase + pHNA64[j] + 2; + pThunkInfoIAT->vaNameModule = vaModuleBase + pIID[i].Name; + LocalFree(pbModule); + return TRUE; + } + c++; + j++; + } + } + i++; + } +fail: + LocalFree(pbModule); + return FALSE; +} + +_Success_(return) +BOOL PE_GetThunkInfoEAT(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaModuleBase, _In_ LPSTR szProcName, _Out_ PPE_THUNKINFO_EAT pThunkInfoEAT) { BYTE pbModuleHeader[0x1000] = { 0 }; PIMAGE_NT_HEADERS32 ntHeader32; @@ -49,7 +144,6 @@ QWORD PE_GetProcAddress(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaModuleBase, _In PWORD pwNameOrdinals; DWORD i, cbProcName, cbExportDirectoryOffset, cbRead = 0; LPSTR sz; - QWORD vaFnPtr; QWORD vaExportDirectory; DWORD cbExportDirectory; PBYTE pbExportDirectory = NULL; @@ -76,7 +170,7 @@ QWORD PE_GetProcAddress(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaModuleBase, _In if((vaRVAAddrNames < vaExportDirectory) || (vaRVAAddrNames > vaExportDirectory + cbExportDirectory - exp->NumberOfNames * sizeof(DWORD))) { goto cleanup; } if((vaNameOrdinals < vaExportDirectory) || (vaNameOrdinals > vaExportDirectory + cbExportDirectory - exp->NumberOfNames * sizeof(WORD))) { goto cleanup; } if((vaRVAAddrFunctions < vaExportDirectory) || (vaRVAAddrFunctions > vaExportDirectory + cbExportDirectory - exp->NumberOfNames * sizeof(DWORD))) { goto cleanup; } - cbProcName = (DWORD)strnlen_s(lpProcName, MAX_PATH) + 1; + cbProcName = (DWORD)strnlen_s(szProcName, MAX_PATH) + 1; cbExportDirectoryOffset = (DWORD)(vaExportDirectory - vaModuleBase); pdwRVAAddrNames = (PDWORD)(pbExportDirectory + exp->AddressOfNames - cbExportDirectoryOffset); pwNameOrdinals = (PWORD)(pbExportDirectory + exp->AddressOfNameOrdinals - cbExportDirectoryOffset); @@ -84,16 +178,27 @@ QWORD PE_GetProcAddress(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaModuleBase, _In for(i = 0; i < exp->NumberOfNames; i++) { if(pdwRVAAddrNames[i] - cbExportDirectoryOffset + cbProcName > cbExportDirectory) { continue; } sz = (LPSTR)(pbExportDirectory + pdwRVAAddrNames[i] - cbExportDirectoryOffset); - if(0 == memcmp(sz, lpProcName, cbProcName)) { + if(0 == memcmp(sz, szProcName, cbProcName)) { if(pwNameOrdinals[i] >= exp->NumberOfFunctions) { goto cleanup; } - vaFnPtr = (QWORD)(vaModuleBase + pdwRVAAddrFunctions[pwNameOrdinals[i]]); + pThunkInfoEAT->fValid = TRUE; + pThunkInfoEAT->vaFunction = (QWORD)(vaModuleBase + pdwRVAAddrFunctions[pwNameOrdinals[i]]); + pThunkInfoEAT->valueThunk = pdwRVAAddrFunctions[pwNameOrdinals[i]]; + pThunkInfoEAT->vaThunk = vaExportDirectory + exp->AddressOfFunctions - cbExportDirectoryOffset + sizeof(DWORD) * pwNameOrdinals[i]; + pThunkInfoEAT->vaNameFunction = vaExportDirectory + pdwRVAAddrNames[i] - cbExportDirectoryOffset; LocalFree(pbExportDirectory); - return vaFnPtr; + return TRUE; } } cleanup: LocalFree(pbExportDirectory); - return 0; + return FALSE; +} + +QWORD PE_GetProcAddress(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaModuleBase, _In_ LPSTR lpProcName) +{ + PE_THUNKINFO_EAT oThunkInfoEAT = { 0 }; + PE_GetThunkInfoEAT(pProcess, vaModuleBase, lpProcName, &oThunkInfoEAT); + return oThunkInfoEAT.vaFunction; } WORD PE_SectionGetNumberOfEx(_In_ PVMM_PROCESS pProcess, _In_opt_ QWORD vaModuleBase, _In_reads_opt_(0x1000) PBYTE pbModuleHeaderOpt) diff --git a/vmm/pe.h b/vmm/pe.h index 47818d7..5912892 100644 --- a/vmm/pe.h +++ b/vmm/pe.h @@ -15,6 +15,23 @@ 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 tdPE_THUNKINFO_IAT { + BOOL fValid; + BOOL f32; // if TRUE fn is a 32-bit/4-byte entry, otherwise 64-bit/8-byte entry. + ULONG64 vaThunk; // address of import address table 'thunk'. + ULONG64 vaFunction; // value if import address table 'thunk' == address of imported function. + ULONG64 vaNameModule; // address of name string for imported module. + ULONG64 vaNameFunction; // address of name string for imported function. +} PE_THUNKINFO_IAT, *PPE_THUNKINFO_IAT; + +typedef struct tdPE_THUNKINFO_EAT { + BOOL fValid; + DWORD valueThunk; // value of export address table 'thunk'. + ULONG64 vaThunk; // address of import address table 'thunk'. + ULONG64 vaNameFunction; // address of name string for exported function. + ULONG64 vaFunction; // address of exported function (module base + value parameter). +} PE_THUNKINFO_EAT, *PPE_THUNKINFO_EAT; + /* * Retrieve the size of the module given its base. * -- pProcess @@ -32,6 +49,31 @@ QWORD PE_GetSize(_In_ PVMM_PROCESS pProcess, _In_opt_ QWORD vaModuleBase); */ QWORD PE_GetProcAddress(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaModuleBase, _In_ LPSTR lpProcName); +/* +* Lookup the virtual address of an exported function or symbol in the module supplied +* among with additional information returned in the pThunkInfoEAT struct. +* -- pProcess +* -- vaModuleBase = PE module base address. +* -- szProcName +* -- pThunkInfoEAT +* -- return +*/ +_Success_(return) +BOOL PE_GetThunkInfoEAT(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaModuleBase, _In_ LPSTR szProcName, _Out_ PPE_THUNKINFO_EAT pThunkInfoEAT); + +/* +* Retrieve an import address table (IAT) entry for a specific function. +* This may be useful for IAT patching functionality. +* -- pProcess +* -- vaModuleBase +* -- szImportModuleName +* -- szImportProcName +* -- pThunkInfoIAT +* -- return +*/ +_Success_(return) +BOOL PE_GetThunkInfoIAT(_In_ PVMM_PROCESS pProcess, _In_ QWORD vaModuleBase, _In_ LPSTR szImportModuleName, _In_ LPSTR szImportProcName, _Out_ PPE_THUNKINFO_IAT pThunkInfoIAT); + /* * Retrieve the module name and optionally the module size. * -- pProcess diff --git a/vmm/statistics.c b/vmm/statistics.c index a2c6141..ca23bcd 100644 --- a/vmm/statistics.c +++ b/vmm/statistics.c @@ -12,47 +12,34 @@ VOID _PageStatPrintMemMap(_Inout_ PPAGE_STATISTICS ps) { - BOOL fIsLinePrinted = FALSE; - QWORD i, qwAddrBase, qwAddrEnd; + QWORD i, qwAddrEnd; if(!ps->i.fIsFirstPrintCompleted) { - vmmprintf(" Memory Map: \n START END #PAGES \n"); + printf(" Memory Map: \n START END #PAGES \n"); } - if(!ps->i.MemMapIdx && !ps->i.MemMap[0]) { - vmmprintf(" \n \n"); + if(!ps->i.MemMapIdx) { + printf(" \n \n"); return; } - if(ps->i.MemMapPrintCommitIdx >= PAGE_STATISTICS_MEM_MAP_MAX_ENTRY - 4) { - vmmprintf(" Maximum number of memory map entries reached. \n \n"); + if(ps->i.MemMapIdx >= PAGE_STATISTICS_MEM_MAP_MAX_ENTRY - 2) { + printf(" Maximum number of memory map entries reached. \n \n"); return; } - qwAddrBase = ps->i.qwAddrBase + ps->i.MemMapPrintCommitPages * 0x1000; - for(i = ps->i.MemMapPrintCommitIdx; i < PAGE_STATISTICS_MEM_MAP_MAX_ENTRY; i++) { - if(!ps->i.MemMap[i] && i == 0) { - continue; - } - if(!ps->i.MemMap[i] || (i == PAGE_STATISTICS_MEM_MAP_MAX_ENTRY - 1)) { + for(i = max(1, ps->i.MemMapPrintIdx); i <= ps->i.MemMapIdx; i++) { + if(!ps->i.MemMap[i].cPages) { break; } - qwAddrEnd = qwAddrBase + 0x1000 * (QWORD)ps->i.MemMap[i]; - if((i % 2) == 0) { - fIsLinePrinted = TRUE; - vmmprintf( - " %016llx - %016llx %08x \n", - qwAddrBase, - qwAddrEnd - 1, - ps->i.MemMap[i]); - if(i >= ps->i.MemMapPrintCommitIdx + 2) { - ps->i.MemMapPrintCommitPages += ps->i.MemMap[ps->i.MemMapPrintCommitIdx++]; - ps->i.MemMapPrintCommitPages += ps->i.MemMap[ps->i.MemMapPrintCommitIdx++]; - - } - } - qwAddrBase = qwAddrEnd; + qwAddrEnd = ps->i.MemMap[i].qwAddrBase + ((QWORD)ps->i.MemMap[i].cPages << 12); + printf( + " %016llx - %016llx %08x \n", + ps->i.MemMap[i].qwAddrBase, + qwAddrEnd - 1, + ps->i.MemMap[i].cPages); } - if(!fIsLinePrinted) { // print extra line for formatting reasons. - vmmprintf(" (No memory successfully read yet) \n"); + ps->i.MemMapPrintIdx = ps->i.MemMapIdx; + if(!ps->i.MemMap[1].cPages) { // print extra line for formatting reasons. + printf(" (No memory successfully read yet) \n"); } - vmmprintf(" \n"); + printf(" \n"); } VOID _PageStatShowUpdate(_Inout_ PPAGE_STATISTICS ps) @@ -135,55 +122,54 @@ VOID _PageStatThreadLoop(_In_ PPAGE_STATISTICS ps) ExitThread(0); } -VOID PageStatClose(_Inout_ PPAGE_STATISTICS ps) +VOID PageStatClose(_In_opt_ PPAGE_STATISTICS *ppPageStat) { BOOL status; DWORD dwExitCode; - ps->i.fUpdate = TRUE; - ps->i.fThreadExit = TRUE; - while((status = GetExitCodeThread(ps->i.hThread, &dwExitCode)) && STILL_ACTIVE == dwExitCode) { + if(!ppPageStat || !*ppPageStat) { return; } + (*ppPageStat)->i.fUpdate = TRUE; + (*ppPageStat)->i.fThreadExit = TRUE; + while((status = GetExitCodeThread((*ppPageStat)->i.hThread, &dwExitCode)) && STILL_ACTIVE == dwExitCode) { SwitchToThread(); } if(!status) { Sleep(200); } + LocalFree(*ppPageStat); + *ppPageStat = NULL; } -VOID PageStatInitialize(_Inout_ PPAGE_STATISTICS ps, _In_ QWORD qwAddrBase, _In_ QWORD qwAddrMax, _In_ LPSTR szAction, _In_ BOOL fKMD, _In_ BOOL fMemMap) +_Success_(return) +BOOL PageStatInitialize(_Out_ PPAGE_STATISTICS *ppPageStat, _In_ QWORD qwAddrBase, _In_ QWORD qwAddrMax, _In_ LPSTR szAction, _In_ BOOL fKMD, _In_ BOOL fMemMap) { - memset(ps, 0, sizeof(PAGE_STATISTICS)); + PPAGE_STATISTICS ps; + ps = *ppPageStat = LocalAlloc(LMEM_ZEROINIT, sizeof(PAGE_STATISTICS)); + if(!ps) { return FALSE; } ps->qwAddr = qwAddrBase; ps->cPageTotal = (qwAddrMax - qwAddrBase + 1) / 4096; ps->szAction = szAction; ps->fKMD = fKMD; ps->i.fMemMap = fMemMap; - ps->i.qwAddrBase = qwAddrBase; ps->i.qwTickCountStart = GetTickCount64(); ps->i.hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)_PageStatThreadLoop, ps, 0, NULL); + return TRUE; } -VOID PageStatUpdate(_Inout_opt_ PPAGE_STATISTICS ps, _In_ QWORD qwAddr, _In_ QWORD cPageSuccessAdd, _In_ QWORD cPageFailAdd) +VOID PageStatUpdate(_In_opt_ PPAGE_STATISTICS pPageStat, _In_ QWORD qwAddr, _In_ QWORD cPageSuccessAdd, _In_ QWORD cPageFailAdd) { - if(!ps) { return; } - ps->qwAddr = qwAddr; - ps->cPageSuccess += cPageSuccessAdd; - ps->cPageFail += cPageFailAdd; - // add to memory map, even == success, odd = fail. - if(ps->i.MemMapIdx < PAGE_STATISTICS_MEM_MAP_MAX_ENTRY - 2) { - if(cPageSuccessAdd) { - if(ps->i.MemMapIdx % 2 == 1) { - ps->i.MemMapIdx++; - } - ps->i.MemMap[ps->i.MemMapIdx] += (DWORD)cPageSuccessAdd; - } - if(cPageFailAdd) { - if(ps->i.MemMapIdx % 2 == 0) { - ps->i.MemMapIdx++; - } - ps->i.MemMap[ps->i.MemMapIdx] += (DWORD)cPageFailAdd; + if(!pPageStat) { return; } + pPageStat->qwAddr = qwAddr; + pPageStat->cPageSuccess += cPageSuccessAdd; + pPageStat->cPageFail += cPageFailAdd; + // add to memory map + if(cPageSuccessAdd && (pPageStat->i.MemMapIdx < PAGE_STATISTICS_MEM_MAP_MAX_ENTRY - 1)) { + if(!pPageStat->i.MemMapIdx || (qwAddr - (cPageSuccessAdd << 12)) != (pPageStat->i.MemMap[pPageStat->i.MemMapIdx].qwAddrBase + ((QWORD)pPageStat->i.MemMap[pPageStat->i.MemMapIdx].cPages << 12))) { + pPageStat->i.MemMapIdx++; + pPageStat->i.MemMap[pPageStat->i.MemMapIdx].qwAddrBase = qwAddr - (cPageSuccessAdd << 12); } + pPageStat->i.MemMap[pPageStat->i.MemMapIdx].cPages += (DWORD)cPageSuccessAdd; } - ps->i.fUpdate = TRUE; + pPageStat->i.fUpdate = TRUE; } // ---------------------------------------------------------------------------- @@ -199,6 +185,7 @@ const LPSTR NAMES_VMM_STATISTICS_CALL[] = { "VMMDLL_MemReadEx", "VMMDLL_MemWrite", "VMMDLL_MemVirt2Phys", + "VMMDLL_MemPrefetchPages", "VMMDLL_PidList", "VMMDLL_PidGetFromName", "VMMDLL_ProcessGetInformation", @@ -210,6 +197,11 @@ const LPSTR NAMES_VMM_STATISTICS_CALL[] = { "VMMDLL_ProcessGetSections", "VMMDLL_ProcessGetEAT", "VMMDLL_ProcessGetIAT", + "VMMDLL_ProcessGetProcAddress", + "VMMDLL_ProcessGetModuleBase", + "VMMDLL_WinGetThunkEAT", + "VMMDLL_WinGetThunkIAT", + "VMMDLL_WinMemCompression_DecompressPage", "PluginManager_List", "PluginManager_Read", "PluginManager_Write", @@ -268,17 +260,17 @@ VOID Statistics_CallToString(_In_opt_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcb) LEECHCORE_STATISTICS LeechCoreStatistics = { 0 }; DWORD cbLeechCoreStatistics = sizeof(LEECHCORE_STATISTICS); if(!pb) { - *pcb = 71 * (STATISTICS_ID_MAX + LEECHCORE_STATISTICS_ID_MAX + 6); + *pcb = 79 * (STATISTICS_ID_MAX + LEECHCORE_STATISTICS_ID_MAX + 6); return; } QueryPerformanceFrequency((PLARGE_INTEGER)&qwFreq); o += snprintf( pb + o, cb - o, - "FUNCTION CALL STATISTICS: \n" \ - "VALUES IN DECIMAL, TIME IN MICROSECONDS uS, STATISTICS = %s \n" \ - "FUNCTION CALL NAME CALLS TIME AVG TIME TOTAL\n" \ - "======================================================================\n", + "FUNCTION CALL STATISTICS: \n" \ + "VALUES IN DECIMAL, TIME IN MICROSECONDS uS, STATISTICS = %s \n" \ + "FUNCTION CALL NAME CALLS TIME AVG TIME TOTAL\n" \ + "==============================================================================\n", ctxMain->pvStatistics ? "ENABLED " : "DISABLED" ); // statistics @@ -290,7 +282,7 @@ VOID Statistics_CallToString(_In_opt_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcb) o += snprintf( pb + o, cb - o, - "%-32.32s %8i %8i %16lli\n", + "%-40.40s %8i %8i %16lli\n", NAMES_VMM_STATISTICS_CALL[i], (DWORD)pStat->c, (DWORD)(uS / pStat->c), @@ -302,7 +294,7 @@ VOID Statistics_CallToString(_In_opt_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcb) o += snprintf( pb + o, cb - o, - "%-32.32s %8i %8i %16lli\n", + "%-40.40s %8i %8i %16lli\n", NAMES_VMM_STATISTICS_CALL[i], 0, 0, 0ULL); } @@ -315,7 +307,7 @@ VOID Statistics_CallToString(_In_opt_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcb) o += snprintf( pb + o, cb - o, - "%-32.32s %8i %8i %16lli\n", + "%-40.40s %8i %8i %16lli\n", LEECHCORE_STATISTICS_NAME[i], (DWORD)LeechCoreStatistics.Call[i].c, (DWORD)(uS / LeechCoreStatistics.Call[i].c), @@ -325,11 +317,11 @@ VOID Statistics_CallToString(_In_opt_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcb) o += snprintf( pb + o, cb - o, - "%-32.32s %8i %8i %16lli\n", + "%-40.40s %8i %8i %16lli\n", LEECHCORE_STATISTICS_NAME[i], 0, 0, 0ULL); } } } - *pcb = o; + *pcb = o - 1; } diff --git a/vmm/statistics.h b/vmm/statistics.h index 31a0293..298f28c 100644 --- a/vmm/statistics.h +++ b/vmm/statistics.h @@ -7,7 +7,7 @@ #define __STATISTICS_H__ #include "vmm.h" -#define PAGE_STATISTICS_MEM_MAP_MAX_ENTRY 4096 +#define PAGE_STATISTICS_MEM_MAP_MAX_ENTRY 2048 typedef struct tdPageStatistics { QWORD qwAddr; @@ -24,70 +24,80 @@ typedef struct tdPageStatistics { HANDLE hThread; WORD wConsoleCursorPosition; QWORD qwTickCountStart; - QWORD qwAddrBase; QWORD MemMapIdx; - QWORD MemMapPrintCommitIdx; - QWORD MemMapPrintCommitPages; - DWORD MemMap[PAGE_STATISTICS_MEM_MAP_MAX_ENTRY]; + QWORD MemMapPrintIdx; + struct { + QWORD qwAddrBase; + DWORD cPages; + } MemMap[PAGE_STATISTICS_MEM_MAP_MAX_ENTRY]; } i; } PAGE_STATISTICS, *PPAGE_STATISTICS; /* * Initialize the page statistics. This will also start displaying the page statistics * on the screen asynchronously. PageStatClose must be called to stop this. -* -- ps = ptr to the PAGE_STATISTICS struct to initialize. +* -- ps = ptr to NULL pPageStat PageStatInitialize will initialize. Must be free'd with PageStatClose. * -- qwAddrBase = the base address that the statistics will be based upon. * -- qwAddrMax = the maximum address. * -- szAction = the text shown as action. * -- fKMD = is KMD mode. * -- fPageMap = display read memory map when PageStatClose is called. +* -- return */ -VOID PageStatInitialize(_Inout_ PPAGE_STATISTICS ps, _In_ QWORD qwAddrBase, _In_ QWORD qwAddrMax, _In_ LPSTR szAction, _In_ BOOL fKMD, _In_ BOOL fMemMap); +_Success_(return) +BOOL PageStatInitialize(_Out_ PPAGE_STATISTICS *ppPageStat, _In_ QWORD qwAddrBase, _In_ QWORD qwAddrMax, _In_ LPSTR szAction, _In_ BOOL fKMD, _In_ BOOL fMemMap); /* * Do one last update of the on-screen page statistics, display the read memory map if * previously set in PageStatInitialize and stop the on-screen updates. -* -- ps = ptr to the PAGE_STATISTICS struct to stop using. +* -- pPageStat = ptr to the PPAGE_STATISTICS struct to close and free. */ -VOID PageStatClose(_Inout_ PPAGE_STATISTICS ps); +VOID PageStatClose(_In_opt_ PPAGE_STATISTICS *ppPageStat); /* * Update the page statistics with the current address and with successfully and failed * pages. Should not be called before PageStatInitialize and not after PageStatClose. * This function must be used if the memory map should be shown; otherwise it's possible * to alter the PPAGE_STATISTICS struct members directly. -* -- ps = pointer to page statistics struct (optional). +* -- pPageStat = pointer to page statistics struct. * -- qwAddr = new address (after completed operation). * -- cPageSuccessAdd = number of successfully read pages. * -- cPageFailAdd = number of pages that failed. */ -VOID PageStatUpdate(_Inout_opt_ PPAGE_STATISTICS ps, _In_ QWORD qwAddr, _In_ QWORD cPageSuccessAdd, _In_ QWORD cPageFailAdd); +VOID PageStatUpdate(_In_opt_ PPAGE_STATISTICS pPageStat, _In_ QWORD qwAddr, _In_ QWORD cPageSuccessAdd, _In_ QWORD cPageFailAdd); -#define STATISTICS_ID_INITIALIZE 0x00 -#define STATISTICS_ID_VMMDLL_VfsList 0x01 -#define STATISTICS_ID_VMMDLL_VfsRead 0x02 -#define STATISTICS_ID_VMMDLL_VfsWrite 0x03 -#define STATISTICS_ID_VMMDLL_VfsInitializePlugins 0x04 -#define STATISTICS_ID_VMMDLL_MemReadEx 0x05 -#define STATISTICS_ID_VMMDLL_MemWrite 0x06 -#define STATISTICS_ID_VMMDLL_MemVirt2Phys 0x07 -#define STATISTICS_ID_VMMDLL_PidList 0x08 -#define STATISTICS_ID_VMMDLL_PidGetFromName 0x09 -#define STATISTICS_ID_VMMDLL_ProcessGetInformation 0x0a -#define STATISTICS_ID_VMMDLL_ProcessGetMemoryMap 0x0b -#define STATISTICS_ID_VMMDLL_ProcessGetMemoryMapEntry 0x0c -#define STATISTICS_ID_VMMDLL_ProcessGetModuleMap 0x0d -#define STATISTICS_ID_VMMDLL_ProcessGetModuleFromName 0x0e -#define STATISTICS_ID_VMMDLL_ProcessGetDirectories 0x0f -#define STATISTICS_ID_VMMDLL_ProcessGetSections 0x10 -#define STATISTICS_ID_VMMDLL_ProcessGetEAT 0x11 -#define STATISTICS_ID_VMMDLL_ProcessGetIAT 0x12 -#define STATISTICS_ID_PluginManager_List 0x13 -#define STATISTICS_ID_PluginManager_Read 0x14 -#define STATISTICS_ID_PluginManager_Write 0x15 -#define STATISTICS_ID_PluginManager_Notify 0x16 -#define STATISTICS_ID_MAX 0x16 -#define STATISTICS_ID_NOLOG 0xffffffff +// NB! also update statistics.c!NAMES_VMM_STATISTICS_CALL +#define STATISTICS_ID_INITIALIZE 0x00 +#define STATISTICS_ID_VMMDLL_VfsList 0x01 +#define STATISTICS_ID_VMMDLL_VfsRead 0x02 +#define STATISTICS_ID_VMMDLL_VfsWrite 0x03 +#define STATISTICS_ID_VMMDLL_VfsInitializePlugins 0x04 +#define STATISTICS_ID_VMMDLL_MemReadEx 0x05 +#define STATISTICS_ID_VMMDLL_MemWrite 0x06 +#define STATISTICS_ID_VMMDLL_MemVirt2Phys 0x07 +#define STATISTICS_ID_VMMDLL_MemPrefetchPages 0x08 +#define STATISTICS_ID_VMMDLL_PidList 0x09 +#define STATISTICS_ID_VMMDLL_PidGetFromName 0x0a +#define STATISTICS_ID_VMMDLL_ProcessGetInformation 0x0b +#define STATISTICS_ID_VMMDLL_ProcessGetMemoryMap 0x0c +#define STATISTICS_ID_VMMDLL_ProcessGetMemoryMapEntry 0x0d +#define STATISTICS_ID_VMMDLL_ProcessGetModuleMap 0x0e +#define STATISTICS_ID_VMMDLL_ProcessGetModuleFromName 0x0f +#define STATISTICS_ID_VMMDLL_ProcessGetDirectories 0x10 +#define STATISTICS_ID_VMMDLL_ProcessGetSections 0x11 +#define STATISTICS_ID_VMMDLL_ProcessGetEAT 0x12 +#define STATISTICS_ID_VMMDLL_ProcessGetIAT 0x13 +#define STATISTICS_ID_VMMDLL_ProcessGetProcAddress 0x14 +#define STATISTICS_ID_VMMDLL_ProcessGetModuleBase 0x15 +#define STATISTICS_ID_VMMDLL_WinGetThunkEAT 0x16 +#define STATISTICS_ID_VMMDLL_WinGetThunkIAT 0x17 +#define STATISTICS_ID_VMMDLL_WinMemCompression_DecompressPage 0x18 +#define STATISTICS_ID_PluginManager_List 0x19 +#define STATISTICS_ID_PluginManager_Read 0x1a +#define STATISTICS_ID_PluginManager_Write 0x1b +#define STATISTICS_ID_PluginManager_Notify 0x1c +#define STATISTICS_ID_MAX 0x1c +#define STATISTICS_ID_NOLOG 0xffffffff VOID Statistics_CallSetEnabled(_In_ BOOL fEnabled); BOOL Statistics_CallGetEnabled(); diff --git a/vmm/version.h b/vmm/version.h index 43c1b06..5720f62 100644 --- a/vmm/version.h +++ b/vmm/version.h @@ -2,7 +2,7 @@ #define STRINGIZE(s) STRINGIZE2(s) #define VERSION_MAJOR 2 -#define VERSION_MINOR 0 +#define VERSION_MINOR 1 #define VERSION_REVISION 0 #define VERSION_BUILD 0 diff --git a/vmm/vmm.c b/vmm/vmm.c index 1b43acf..30b9181 100644 --- a/vmm/vmm.c +++ b/vmm/vmm.c @@ -610,7 +610,7 @@ VOID VmmCache2Initialize(_In_ WORD wTblTag) * -- pProcess * -- pObPrefetchAddresses */ -VOID VmmCachePrefetch(_In_ PVMM_PROCESS pProcess, _In_opt_ PVMMOB_DATASET pObPrefetchAddresses) +VOID VmmCachePrefetchPages(_In_opt_ PVMM_PROCESS pProcess, _In_opt_ PVMMOB_DATASET pObPrefetchAddresses) { QWORD va; DWORD i, c = 0; @@ -624,7 +624,11 @@ VOID VmmCachePrefetch(_In_ PVMM_PROCESS pProcess, _In_opt_ PVMMOB_DATASET pObPre c++; } } - VmmReadScatterVirtual(pProcess, ppMEMs, c, 0); + if(pProcess) { + VmmReadScatterVirtual(pProcess, ppMEMs, c, 0); + } else { + VmmReadScatterPhysical(ppMEMs, c, 0); + } LocalFree(ppMEMs); } @@ -860,6 +864,7 @@ PVMM_PROCESS VmmProcessCreateEntry(_In_ BOOL fTotalRefresh, _In_ DWORD dwPID, _I if(!pProcess) { goto fail; } InitializeCriticalSectionAndSpinCount(&pProcess->LockUpdate, 4096); memcpy(pProcess->szName, szName, 16); + pProcess->szName[15] = 0; pProcess->dwPID = dwPID; pProcess->dwState = dwState; pProcess->paDTB = paDTB; @@ -1350,7 +1355,7 @@ BOOL VmmReadString_Unicode2Ansi(_In_ PVMM_PROCESS pProcess, _In_ QWORD qwVA, _Ou BOOL result; WCHAR wsz[0x1000]; if(cch) { sz[0] = 0; } - if(!cch || cch > 0x1000) { return FALSE; } + if((cch < 2) || (cch > 0x1000)) { return FALSE; } result = VmmRead(pProcess, qwVA, (PBYTE)wsz, cch << 1); if(!result) { return FALSE; } for(i = 0; i < cch - 1; i++) { @@ -1394,6 +1399,15 @@ VOID VmmInitializeMemoryModel(_In_ VMM_MEMORYMODEL_TP tp) } } +VOID VmmInitializeFunctions() +{ + HMODULE hNtDll = NULL; + if((hNtDll = LoadLibraryA("ntdll.dll"))) { + ctxVmm->fn.RtlDecompressBuffer = (VMMFN_RtlDecompressBuffer*)GetProcAddress(hNtDll, "RtlDecompressBuffer"); + FreeLibrary(hNtDll); + } +} + BOOL VmmInitialize() { // 1: allocate & initialize @@ -1411,6 +1425,7 @@ BOOL VmmInitialize() // 5: OTHER INIT: VmmObContainer_Initialize(&ctxVmm->ObCEPROCESSCachePrefetch, NULL); InitializeCriticalSection(&ctxVmm->MasterLock); + VmmInitializeFunctions(); return TRUE; fail: VmmClose(); diff --git a/vmm/vmm.h b/vmm/vmm.h index ebc9597..45811e7 100644 --- a/vmm/vmm.h +++ b/vmm/vmm.h @@ -299,8 +299,24 @@ typedef struct tdVMM_KERNELINFO { QWORD vaEntry; QWORD vaPsLoadedModuleList; QWORD vaKDBG; + DWORD dwPidMemCompression; } VMM_KERNELINFO; +typedef NTSTATUS VMMFN_RtlDecompressBuffer( + USHORT CompressionFormat, + PUCHAR UncompressedBuffer, + ULONG UncompressedBufferSize, + PUCHAR CompressedBuffer, + ULONG CompressedBufferSize, + PULONG FinalUncompressedSize +); + +typedef struct tdVMM_DYNAMIC_LOAD_FUNCTIONS { + // functions below may be loaded on startup + // NB! null checks are required before use! + VMMFN_RtlDecompressBuffer *RtlDecompressBuffer; // ntdll.dll!RtlDecompressBuffer +} VMM_DYNAMIC_LOAD_FUNCTIONS; + typedef struct tdVMM_CONTEXT { CRITICAL_SECTION MasterLock; VMMOBCONTAINER PROC; // contains VMM_PROCESS_TABLE @@ -320,6 +336,7 @@ typedef struct tdVMM_CONTEXT { } ThreadProcCache; VMM_STATISTICS stat; VMM_KERNELINFO kernel; + VMM_DYNAMIC_LOAD_FUNCTIONS fn; PVOID pVmmVfsModuleList; VMMOBCONTAINER ObCEPROCESSCachePrefetch; VMM_CACHE_TABLE PHYS; @@ -763,7 +780,7 @@ VOID VmmCacheInvalidate(_In_ QWORD pa); * -- pProcess * -- pObPrefetchAddresses */ -VOID VmmCachePrefetch(_In_ PVMM_PROCESS pProcess, _In_opt_ PVMMOB_DATASET pObPrefetchAddresses); +VOID VmmCachePrefetchPages(_In_opt_ PVMM_PROCESS pProcess, _In_opt_ PVMMOB_DATASET pObPrefetchAddresses); /* * Initialize the memory model specified and discard any previous memory models diff --git a/vmm/vmm.vcxproj b/vmm/vmm.vcxproj index 5c855bb..e091a8b 100644 --- a/vmm/vmm.vcxproj +++ b/vmm/vmm.vcxproj @@ -116,6 +116,7 @@ copy $(ProjectDir)\vmmdll.h $(SolutionDir)\files\ /y + @@ -136,6 +137,7 @@ copy $(ProjectDir)\vmmdll.h $(SolutionDir)\files\ /y + diff --git a/vmm/vmm.vcxproj.filters b/vmm/vmm.vcxproj.filters index 68571dd..e05ca6f 100644 --- a/vmm/vmm.vcxproj.filters +++ b/vmm/vmm.vcxproj.filters @@ -75,6 +75,9 @@ Header Files + + Header Files + @@ -125,6 +128,9 @@ Source Files + + Source Files + diff --git a/vmm/vmmdll.c b/vmm/vmmdll.c index a1f26c5..c946933 100644 --- a/vmm/vmmdll.c +++ b/vmm/vmmdll.c @@ -15,6 +15,7 @@ #include "vmmproc.h" #include "vmmwin.h" #include "vmmvfs.h" +#include "mm_x64_winpaged.h" // ---------------------------------------------------------------------------- // Synchronization macro below. The VMM isn't thread safe so it's important to @@ -22,7 +23,7 @@ // with internal VMM housekeeping functionality. // ---------------------------------------------------------------------------- -#define CALL_SYNCHRONIZED_IMPLEMENTATION_VMM(id, fn) { \ +#define CALL_SYNCHRONIZED_IMPLEMENTATION_VMM(id, fn) { \ QWORD tm; \ BOOL result; \ if(!ctxVmm) { return FALSE; } \ @@ -34,16 +35,16 @@ return result; \ } -#define CALL_SYNCHRONIZED_IMPLEMENTATION_VMM_NTSTATUS(id, fn) { \ - QWORD tm; \ - NTSTATUS nt; \ - if(!ctxVmm) { return ((NTSTATUS)0xC0000001L); } /* UNSUCCESSFUL */ \ - tm = Statistics_CallStart(); \ - VmmLockAcquire(); \ - nt = fn; \ - VmmLockRelease(); \ - Statistics_CallEnd(id, tm); \ - return nt; \ +#define CALL_SYNCHRONIZED_IMPLEMENTATION_VMM_RETURN(id, RetTp, RetValFail, fn) { \ + QWORD tm; \ + RetTp retVal; \ + if(!ctxVmm) { return ((RetTp)RetValFail); } /* UNSUCCESSFUL */ \ + tm = Statistics_CallStart(); \ + VmmLockAcquire(); \ + retVal = fn; \ + VmmLockRelease(); \ + Statistics_CallEnd(id, tm); \ + return retVal; \ } //----------------------------------------------------------------------------- @@ -172,8 +173,8 @@ VOID VmmDll_PrintHelp() " Valid options: , PMEM, FPGA, TOTALMELTDOWN \n" \ " --- \n" \ " = memory dump file name optionally including path.\n" \ - " PMEM = use rekall winpmem 'winpmem_x64.sys' to aquire live memory. \n" \ - " PMEM://c:\\path\\to\\winpmem_x64.sys = path to rekall winpmem driver.\n" \ + " PMEM = use winpmem 'winpmem_64.sys' to acquire live memory. \n" \ + " PMEM://c:\\path\\to\\winpmem_64.sys = path to winpmem driver. \n" \ " --- \n" \ " Below acquisition devices require pcileech.dll and are not built-in: \n" \ " TOTALMELTDOWN = use CVE-2018-1038 (vulnerable windows 7 only) \n" \ @@ -430,15 +431,19 @@ BOOL VMMDLL_VfsList(_In_ LPCWSTR wcsPath, _Inout_ PVMMDLL_VFS_FILELIST pFileList NTSTATUS VMMDLL_VfsRead(_In_ LPCWSTR wcsFileName, _Out_ LPVOID pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ ULONG64 cbOffset) { - CALL_SYNCHRONIZED_IMPLEMENTATION_VMM_NTSTATUS( + CALL_SYNCHRONIZED_IMPLEMENTATION_VMM_RETURN( STATISTICS_ID_VMMDLL_VfsRead, + NTSTATUS, + VMMDLL_STATUS_UNSUCCESSFUL, VmmVfs_Read(wcsFileName, pb, cb, pcbRead, cbOffset)) } NTSTATUS VMMDLL_VfsWrite(_In_ LPCWSTR wcsFileName, _In_ LPVOID pb, _In_ DWORD cb, _Out_ PDWORD pcbWrite, _In_ ULONG64 cbOffset) { - CALL_SYNCHRONIZED_IMPLEMENTATION_VMM_NTSTATUS( + CALL_SYNCHRONIZED_IMPLEMENTATION_VMM_RETURN( STATISTICS_ID_VMMDLL_VfsWrite, + NTSTATUS, + VMMDLL_STATUS_UNSUCCESSFUL, VmmVfs_Write(wcsFileName, pb, cb, pcbWrite, cbOffset)) } @@ -551,6 +556,38 @@ BOOL VMMDLL_MemReadPage(_In_ DWORD dwPID, _In_ ULONG64 qwVA, _Inout_bytecount_(4 return VMMDLL_MemReadEx(dwPID, qwVA, pbPage, 4096, &dwRead, 0) && (dwRead == 4096); } +_Success_(return) +BOOL VMMDLL_MemPrefetchPages_Impl(_In_ DWORD dwPID, _In_reads_(cPrefetchAddresses) PULONG64 pPrefetchAddresses, _In_ DWORD cPrefetchAddresses) +{ + DWORD i; + BOOL result = FALSE; + PVMM_PROCESS pObProcess = NULL; + PVMMOB_DATASET pObPrefetchAddresses = NULL; + if(dwPID != (DWORD)-1) { + pObProcess = VmmProcessGet(dwPID); + if(!pObProcess) { goto fail; } + } + pObPrefetchAddresses = VmmObDataSet_Alloc(TRUE); + if(!pObPrefetchAddresses) { goto fail; } + for(i = 0; i < cPrefetchAddresses; i++) { + VmmObDataSet_Put(pObPrefetchAddresses, pPrefetchAddresses[i] & ~0xfff); + } + VmmCachePrefetchPages(pObProcess, pObPrefetchAddresses); + result = TRUE; +fail: + VmmOb_DECREF(pObPrefetchAddresses); + VmmOb_DECREF(pObProcess); + return result; +} + +_Success_(return) +BOOL VMMDLL_MemPrefetchPages(_In_ DWORD dwPID, _In_reads_(cPrefetchAddresses) PULONG64 pPrefetchAddresses, _In_ DWORD cPrefetchAddresses) +{ + CALL_SYNCHRONIZED_IMPLEMENTATION_VMM( + STATISTICS_ID_VMMDLL_MemPrefetchPages, + VMMDLL_MemPrefetchPages_Impl(dwPID, pPrefetchAddresses, cPrefetchAddresses)) +} + _Success_(return) BOOL VMMDLL_MemWrite_Impl(_In_ DWORD dwPID, _In_ ULONG64 qwVA, _In_ PBYTE pb, _In_ DWORD cb) { @@ -873,7 +910,7 @@ BOOL VMMDLL_ProcessGet_Directories_Sections_IAT_EAT_Impl( i = PE_EatGetNumberOf(pObProcess, pModule->BaseAddress); if(!pEAT) { *pcData = i; goto success; } if(cData < i) { goto fail; } - VmmWin_PE_LoadEAT_DisplayBuffer(pObProcess, pModule, (PVMMPROC_WINDOWS_EAT_ENTRY)pEAT, &cData); + VmmWin_PE_LoadEAT_DisplayBuffer(pObProcess, pModule, (PVMMPROC_WINDOWS_EAT_ENTRY)pEAT, cData, &cData); *pcData = cData; goto success; } @@ -882,7 +919,7 @@ BOOL VMMDLL_ProcessGet_Directories_Sections_IAT_EAT_Impl( i = PE_IatGetNumberOf(pObProcess, pModule->BaseAddress); if(!pIAT) { *pcData = i; goto success; } if(cData < i) { goto fail; } - VmmWin_PE_LoadIAT_DisplayBuffer(pObProcess, pModule, (PVMMWIN_IAT_ENTRY)pIAT, &cData); + VmmWin_PE_LoadIAT_DisplayBuffer(pObProcess, pModule, (PVMMWIN_IAT_ENTRY)pIAT, cData, &cData); *pcData = cData; goto success; } @@ -928,6 +965,107 @@ BOOL VMMDLL_ProcessGetIAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PVMMD VMMDLL_ProcessGet_Directories_Sections_IAT_EAT_Impl(dwPID, szModule, cData, pcData, NULL, NULL, NULL, pData, FALSE, FALSE, FALSE, TRUE)) } +ULONG64 VMMDLL_ProcessGetProcAddress_Impl(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szFunctionName) +{ + QWORD vaFn = 0; + VMMDLL_MODULEMAP_ENTRY 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); + } + VmmOb_DECREF(pObProcess); + return vaFn; +} + +ULONG64 VMMDLL_ProcessGetProcAddress(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szFunctionName) +{ + CALL_SYNCHRONIZED_IMPLEMENTATION_VMM_RETURN( + STATISTICS_ID_VMMDLL_ProcessGetIAT, + ULONG64, + 0, + VMMDLL_ProcessGetProcAddress_Impl(dwPID, szModuleName, szFunctionName)) +} + +ULONG64 VMMDLL_ProcessGetModuleBase_Impl(_In_ DWORD dwPID, _In_ LPSTR szModuleName) +{ + 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; + } + VmmOb_DECREF(pObProcess); + return vaModuleBase; +} + +ULONG64 VMMDLL_ProcessGetModuleBase(_In_ DWORD dwPID, _In_ LPSTR szModuleName) +{ + CALL_SYNCHRONIZED_IMPLEMENTATION_VMM_RETURN( + STATISTICS_ID_VMMDLL_ProcessGetModuleBase, + ULONG64, + 0, + VMMDLL_ProcessGetModuleBase_Impl(dwPID, szModuleName)) +} + +_Success_(return) +BOOL VMMDLL_WinGetThunkInfoEAT_Impl(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT) +{ + BOOL result; + VMMDLL_MODULEMAP_ENTRY oModuleEntry = { 0 }; + PVMM_PROCESS pObProcess = NULL; + pObProcess = VmmProcessGet(dwPID); + if(!pObProcess) { return 0; } + if(VMMDLL_ProcessGetModuleFromName_Impl(dwPID, szModuleName, &oModuleEntry)) { + result = PE_GetThunkInfoEAT(pObProcess, oModuleEntry.BaseAddress, szExportFunctionName, (PPE_THUNKINFO_EAT)pThunkInfoEAT); + } + VmmOb_DECREF(pObProcess); + return result; +} + +_Success_(return) +BOOL VMMDLL_WinGetThunkInfoEAT(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT) +{ + CALL_SYNCHRONIZED_IMPLEMENTATION_VMM( + STATISTICS_ID_VMMDLL_WinGetThunkEAT, + VMMDLL_WinGetThunkInfoEAT_Impl(dwPID, szModuleName, 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 result = FALSE; + VMMDLL_MODULEMAP_ENTRY oModuleEntry = { 0 }; + 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); + } + VmmOb_DECREF(pObProcess); + return result; +} + +_Success_(return) +BOOL VMMDLL_WinGetThunkInfoIAT(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szImportModuleName, _In_ LPSTR szImportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_IAT pThunkInfoIAT) +{ + CALL_SYNCHRONIZED_IMPLEMENTATION_VMM( + STATISTICS_ID_VMMDLL_WinGetThunkIAT, + VMMDLL_WinGetThunkInfoIAT_Impl(dwPID, szModuleName, szImportModuleName, szImportFunctionName, pThunkInfoIAT)) +} + +_Success_(return) +BOOL VMMDLL_WinMemCompression_DecompressPage(_In_ ULONG64 vaCompressedData, _In_opt_ DWORD cbCompressedData, _Out_writes_(4096) PBYTE pbDecompressedPage, _Out_opt_ PDWORD pcbCompressedData) +{ + CALL_SYNCHRONIZED_IMPLEMENTATION_VMM( + STATISTICS_ID_VMMDLL_WinMemCompression_DecompressPage, + MmX64WinPaged_MemCompression_DecompressPage(vaCompressedData, cbCompressedData, pbDecompressedPage, pcbCompressedData)) +} + _Success_(return) BOOL VMMDLL_UtilFillHexAscii(_In_ PBYTE pb, _In_ DWORD cb, _In_ DWORD cbInitialOffset, _Inout_opt_ LPSTR sz, _Out_ PDWORD pcsz) { diff --git a/vmm/vmmdll.def b/vmm/vmmdll.def index 502d011..5e61a51 100644 --- a/vmm/vmmdll.def +++ b/vmm/vmmdll.def @@ -23,6 +23,7 @@ EXPORTS VMMDLL_MemReadPage VMMDLL_MemRead VMMDLL_MemReadEx + VMMDLL_MemPrefetchPages VMMDLL_MemWrite VMMDLL_MemVirt2Phys @@ -38,5 +39,11 @@ EXPORTS VMMDLL_ProcessGetSections VMMDLL_ProcessGetEAT VMMDLL_ProcessGetIAT + VMMDLL_ProcessGetProcAddress + VMMDLL_ProcessGetModuleBase + VMMDLL_WinGetThunkInfoIAT + VMMDLL_WinGetThunkInfoEAT + + VMMDLL_WinMemCompression_DecompressPage VMMDLL_UtilFillHexAscii diff --git a/vmm/vmmdll.h b/vmm/vmmdll.h index 353159a..42befa8 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.0 +// Header Version: 2.1 // #include @@ -26,13 +26,15 @@ extern "C" { * about the parameters please see github wiki for Memory Process File System * and LeechCore. THIS IS THE PREFERED WAY OF INITIALIZING VMM.DLL * Important parameters are: -* -vdll = show printf style outputs) +* -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 * -remote = remote LeechCore instance - please see leechcore.h or Github * documentation for additional information. +* -norefresh = disable background refreshes (even if backing memory is +* volatile memory). * -- argc * -- argv * -- return = success/fail @@ -289,7 +291,7 @@ typedef struct tdVMMDLL_PLUGIN_REGINFO { * -- ppMEMs = array of scatter read headers. * -- cpMEMs = count of ppDMAs. * -- pcpDMAsRead = optional count of number of successfully read ppDMAs. -* -- flags = optional flags as given by VMM_FLAG_* +* -- flags = optional flags as given by VMMDLL_FLAG_* * -- return = the number of successfully read items. */ DWORD VMMDLL_MemReadScatter(_In_ DWORD dwPID, _Inout_ PPMEM_IO_SCATTER_HEADER ppMEMs, _In_ DWORD cpMEMs, _In_ DWORD flags); @@ -322,13 +324,25 @@ BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwVA, _Out_ PBYTE pb, _In_ DW * -- pb * -- cb * -- pcbRead -* -- flags = flags as in VMM_FLAG_* +* -- 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. */ _Success_(return) BOOL VMMDLL_MemReadEx(_In_ DWORD dwPID, _In_ ULONG64 qwVA, _Out_ 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 +* cache. This function is to be used to batch larger known reads into local +* cache before making multiple smaller reads - which will then happen from +* the cache. Function exists for performance reasons. +* -- dwPID = PID of target process, (DWORD)-1 for physical memory. +* -- pPrefetchAddresses = array of addresses to read into cache. +* -- cPrefetchAddresses +*/ +_Success_(return) +BOOL VMMDLL_MemPrefetchPages(_In_ DWORD dwPID, _In_reads_(cPrefetchAddresses) PULONG64 pPrefetchAddresses, _In_ DWORD cPrefetchAddresses); + /* * Write a contigious arbitrary amount of memory. Please note some virtual memory * such as pages of executables (such as DLLs) may be shared between different @@ -527,6 +541,87 @@ BOOL VMMDLL_ProcessGetEAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PVMMD _Success_(return) BOOL VMMDLL_ProcessGetIAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _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 +* -- szFunctionName +* -- return = virtual address of function, zero on fail. +*/ +ULONG64 VMMDLL_ProcessGetProcAddress(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szFunctionName); + +/* +* Retrieve the base address of a given module. +* -- dwPID +* -- szModuleName +* -- return = virtual address of module base, zero on fail. +*/ +ULONG64 VMMDLL_ProcessGetModuleBase(_In_ DWORD dwPID, _In_ LPSTR szModuleName); + + + +//----------------------------------------------------------------------------- +// WINDOWS SPECIFIC UTILITY FUNCTIONS BELOW: +//----------------------------------------------------------------------------- + +typedef struct tdVMMDLL_WIN_THUNKINFO_IAT { + BOOL fValid; + BOOL f32; // if TRUE fn is a 32-bit/4-byte entry, otherwise 64-bit/8-byte entry. + ULONG64 vaThunk; // address of import address table 'thunk'. + ULONG64 vaFunction; // value if import address table 'thunk' == address of imported function. + ULONG64 vaNameModule; // address of name string for imported module. + ULONG64 vaNameFunction; // address of name string for imported function. +} VMMDLL_WIN_THUNKINFO_IAT, *PVMMDLL_WIN_THUNKINFO_IAT; + +typedef struct tdVMMDLL_WIN_THUNKINFO_EAT { + BOOL fValid; + DWORD valueThunk; // value of export address table 'thunk'. + ULONG64 vaThunk; // address of import address table 'thunk'. + ULONG64 vaNameFunction; // address of name string for exported function. + ULONG64 vaFunction; // address of exported function (module base + value parameter). +} VMMDLL_WIN_THUNKINFO_EAT, *PVMMDLL_WIN_THUNKINFO_EAT; + +/* +* Retrieve information about the import address table IAT thunk for an imported +* function. This includes the virtual address of the IAT thunk which is useful +* for hooking. +* -- dwPID +* -- szModuleName +* -- 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); + +/* +* 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 +* -- pThunkEAT +* -- return +*/ +_Success_(return) +BOOL VMMDLL_WinGetThunkInfoEAT(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT); + +/* +* Decompress compressed memory page stored in the MemCompression process. +* -- vaCompressedData = virtual address in 'MemCompression' to decompress. +* -- cbCompressedData = length of compressed data in 'MemCompression' to decompress (or zero for auto-detect). +* -- pbDecompressedPage +* -- pcbCompressedData = optional ptr to receive length of compressed buffer. +* -- return +*/ +_Success_(return) +BOOL VMMDLL_WinMemCompression_DecompressPage( + _In_ ULONG64 vaCompressedData, + _In_opt_ DWORD cbCompressedData, + _Out_writes_(4096) PBYTE pbDecompressedPage, + _Out_opt_ PDWORD pcbCompressedData +); //----------------------------------------------------------------------------- diff --git a/vmm/vmmproc.c b/vmm/vmmproc.c index 3979ec2..6fa488a 100644 --- a/vmm/vmmproc.c +++ b/vmm/vmmproc.c @@ -230,11 +230,9 @@ BOOL VmmProcPHYS_ScanForKernel(_Out_ PQWORD ppaPML4, _In_ QWORD paBase, _In_ QWO LEECHCORE_PAGESTAT_MINIMAL PageStatMinimal; BOOL result; // initialize / allocate memory - pbBuffer8M = LocalAlloc(0, 0x800000); - pPageStat = (PPAGE_STATISTICS)LocalAlloc(LMEM_ZEROINIT, sizeof(PAGE_STATISTICS)); - if(!pbBuffer8M || !pPageStat) { goto fail; } paCurrent = paBase; - PageStatInitialize(pPageStat, paCurrent, paMax, szDescription, FALSE, FALSE); + if(!(pbBuffer8M = LocalAlloc(0, 0x800000))) { goto fail; } + if(!PageStatInitialize(&pPageStat, paCurrent, paMax, szDescription, FALSE, FALSE)) { goto fail; } PageStatMinimal.h = (HANDLE)pPageStat; PageStatMinimal.pfnPageStatUpdate = PageStatUpdate; // loop kmd-find @@ -247,8 +245,7 @@ BOOL VmmProcPHYS_ScanForKernel(_Out_ PQWORD ppaPML4, _In_ QWORD paBase, _In_ QWO result = VmmProcPHYS_VerifyWindowsEPROCESS(pbBuffer8M, 0x00800000, o + i, ppaPML4); if(result) { pPageStat->szAction = "Windows System PageDirectoryBase/PML4 located"; - PageStatClose(pPageStat); - LocalFree(pPageStat); + PageStatClose(&pPageStat); LocalFree(pbBuffer8M); return TRUE; } @@ -257,8 +254,7 @@ BOOL VmmProcPHYS_ScanForKernel(_Out_ PQWORD ppaPML4, _In_ QWORD paBase, _In_ QWO } } fail: - if(pPageStat) { PageStatClose(pPageStat); } - LocalFree(pPageStat); + PageStatClose(&pPageStat); LocalFree(pbBuffer8M); *ppaPML4 = 0; return FALSE; diff --git a/vmm/vmmwin.c b/vmm/vmmwin.c index e5d4553..2155c82 100644 --- a/vmm/vmmwin.c +++ b/vmm/vmmwin.c @@ -126,8 +126,8 @@ VOID VmmWin_PE_DIRECTORY_DisplayBuffer( } } - -VOID VmmWin_PE_LoadEAT_DisplayBuffer(_Inout_ PVMM_PROCESS pProcess, _Inout_ PVMM_MODULEMAP_ENTRY pModule, _Out_writes_opt_(*pcEATs) PVMMPROC_WINDOWS_EAT_ENTRY pEATs, _Inout_ PDWORD pcEATs) +_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) { BYTE pbModuleHeader[0x1000] = { 0 }; PIMAGE_NT_HEADERS64 ntHeader64; @@ -138,10 +138,9 @@ VOID VmmWin_PE_LoadEAT_DisplayBuffer(_Inout_ PVMM_PROCESS pProcess, _Inout_ PVMM QWORD i, oNameOrdinal, ooName, oName, oFunction, wOrdinalFnIdx; DWORD vaFunctionOffset; BOOL fHdr32; - DWORD cEATs = *pcEATs; *pcEATs = 0; // 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->BaseAddress, pbModuleHeader, &fHdr32))) { goto fail; } ntHeader32 = (PIMAGE_NT_HEADERS32)ntHeader64; // Load Export Address Table (EAT) oExportDirectory = fHdr32 ? @@ -150,9 +149,9 @@ VOID VmmWin_PE_LoadEAT_DisplayBuffer(_Inout_ PVMM_PROCESS pProcess, _Inout_ PVMM cbExportDirectory = fHdr32 ? ntHeader32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size : ntHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size; - if(!oExportDirectory || !cbExportDirectory || cbExportDirectory > 0x01000000) { goto cleanup; } - if(!(pbExportDirectory = LocalAlloc(0, cbExportDirectory))) { goto cleanup; } - if(!VmmRead(pProcess, pModule->BaseAddress + oExportDirectory, pbExportDirectory, (DWORD)cbExportDirectory)) { goto cleanup; } + 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; } // Walk exported functions pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)pbExportDirectory; for(i = 0; i < pExportDirectory->NumberOfNames && i < cEATs; i++) { @@ -175,11 +174,14 @@ VOID VmmWin_PE_LoadEAT_DisplayBuffer(_Inout_ PVMM_PROCESS pProcess, _Inout_ PVMM strncpy_s(pEATs[i].szFunction, 40, (LPSTR)(pbExportDirectory - oExportDirectory + oName), _TRUNCATE); } *pcEATs = (DWORD)i; -cleanup: LocalFree(pbExportDirectory); + return TRUE; +fail: + LocalFree(pbExportDirectory); + return FALSE; } -VOID VmmWin_PE_LoadIAT_DisplayBuffer(_Inout_ PVMM_PROCESS pProcess, _Inout_ PVMM_MODULEMAP_ENTRY pModule, _Out_writes_(*pcIATs) PVMMWIN_IAT_ENTRY pIATs, _Inout_ PDWORD pcIATs) +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) { BYTE pbModuleHeader[0x1000] = { 0 }; PIMAGE_NT_HEADERS64 ntHeader64; @@ -191,7 +193,7 @@ VOID VmmWin_PE_LoadIAT_DisplayBuffer(_Inout_ PVMM_PROCESS pProcess, _Inout_ PVMM PBYTE pbModule; DWORD cbModule, cbRead; BOOL fHdr32, fFnName; - DWORD c, j, cIATs = *pcIATs; + DWORD c, j; *pcIATs = 0; // Load the module if(pModule->SizeOfImage > 0x02000000) { return; } @@ -325,7 +327,7 @@ VOID VmmWin_ScanLdrModules64(_In_ PVMM_PROCESS pProcess, _Inout_ PVMM_MODULEMAP_ DWORD iModuleLdr; // prefetch existing addresses (if any) & allocate new vaModuleLdr DataSet pObDataSet_vaModuleLdr = VmmObContainer_GetOb(&pProcess->pObProcessPersistent->ObCLdrModulesCachePrefetch64); - VmmCachePrefetch(pProcess, pObDataSet_vaModuleLdr); + VmmCachePrefetchPages(pProcess, pObDataSet_vaModuleLdr); VmmOb_DECREF(pObDataSet_vaModuleLdr); pObDataSet_vaModuleLdr = VmmObDataSet_Alloc(TRUE); if(!pObDataSet_vaModuleLdr) { goto fail; } @@ -453,7 +455,7 @@ BOOL VmmWin_ScanLdrModules32(_In_ PVMM_PROCESS pProcess, _Inout_ PVMM_MODULEMAP_ DWORD iModuleLdr; // prefetch existing addresses (if any) & allocate new vaModuleLdr DataSet pObDataSet_vaModuleLdr = VmmObContainer_GetOb(&pProcess->pObProcessPersistent->ObCLdrModulesCachePrefetch32); - VmmCachePrefetch(pProcess, pObDataSet_vaModuleLdr); + VmmCachePrefetchPages(pProcess, pObDataSet_vaModuleLdr); VmmOb_DECREF(pObDataSet_vaModuleLdr); pObDataSet_vaModuleLdr = VmmObDataSet_Alloc(TRUE); if(!pObDataSet_vaModuleLdr) { goto fail; } @@ -955,15 +957,15 @@ VOID VmmWin_OffsetLocatorEPROCESS64(_In_ PVMM_PROCESS pSystemProcess) // find offset for PEB (in EPROCESS) for(i = 0x300, f = FALSE; i < 0x480; i += 8) { if(*(PQWORD)(pb0 + i)) { continue; } - vaPEB = *(PQWORD)(pb1 + i); - if(!vaPEB || (vaPEB & 0xffff800000000fff)) { continue; } - // Verify potential PEB - if(!VmmVirt2PhysEx(*(PQWORD)(pb1 + pOffsetEPROCESS->DTB), TRUE, vaPEB, &paPEB)) { continue; } - if(!VmmReadPhysicalPage(paPEB, pbPage)) { continue; } - if(*(PWORD)pbPage == 0x5a4d) { continue; } // MZ header -> likely entry point or something not PEB ... - pOffsetEPROCESS->PEB = i; - f = TRUE; - break; +vaPEB = *(PQWORD)(pb1 + i); +if(!vaPEB || (vaPEB & 0xffff800000000fff)) { continue; } +// Verify potential PEB +if(!VmmVirt2PhysEx(*(PQWORD)(pb1 + pOffsetEPROCESS->DTB), TRUE, vaPEB, &paPEB)) { continue; } +if(!VmmReadPhysicalPage(paPEB, pbPage)) { continue; } +if(*(PWORD)pbPage == 0x5a4d) { continue; } // MZ header -> likely entry point or something not PEB ... +pOffsetEPROCESS->PEB = i; +f = TRUE; +break; } if(!f) { return; } // find "optional" offset for user cr3/pml4 (post meltdown only) @@ -1016,7 +1018,7 @@ BOOL VmmWin_EnumerateEPROCESS64(_In_ PVMM_PROCESS pSystemProcess, _In_ BOOL fTot PVMM_PROCESS pObProcess = NULL; QWORD vaSystemEPROCESS, vaEPROCESS, cNewProcessCollision = 0; DWORD iProc = 0; - BOOL fShowTerminated; + BOOL fShowTerminated, fUser; PVMM_WIN_EPROCESS_OFFSET pOffsetEPROCESS = &ctxVmm->kernel.OffsetEPROCESS; PVMMOB_DATASET pObSetAddressEPROCESS = NULL; fShowTerminated = ctxVmm->flags & VMM_FLAG_PROCESS_SHOW_TERMINATED; @@ -1040,7 +1042,7 @@ BOOL VmmWin_EnumerateEPROCESS64(_In_ PVMM_PROCESS pSystemProcess, _In_ BOOL fTot pqwPEB = (PQWORD)(pb + pOffsetEPROCESS->PEB); // prefetch pages into cache (if any) pObSetAddressEPROCESS = VmmObContainer_GetOb(&ctxVmm->ObCEPROCESSCachePrefetch); - VmmCachePrefetch(pSystemProcess, pObSetAddressEPROCESS); + VmmCachePrefetchPages(pSystemProcess, pObSetAddressEPROCESS); VmmOb_DECREF(pObSetAddressEPROCESS); // initialize address set if(!(pObSetAddressEPROCESS = VmmObDataSet_Alloc(TRUE))) { return FALSE; } @@ -1054,6 +1056,9 @@ BOOL VmmWin_EnumerateEPROCESS64(_In_ PVMM_PROCESS pSystemProcess, _In_ BOOL fTot VmmOb_DECREF(pObProcess); pObProcess = NULL; if(*pqwDTB && *(PQWORD)szName && (fShowTerminated || !*pdwState)) { + fUser = + !((*pdwPID == 4) || ((*pdwState == 0) && (*pqwPEB == 0))) || + (*(PQWORD)(szName + 0x00) == 0x72706d6f436d654d) && (*(PDWORD)(szName + 0x08) == 0x69737365); // MemCompression "process" pObProcess = VmmProcessCreateEntry( fTotalRefresh, *pdwPID, @@ -1061,7 +1066,7 @@ BOOL VmmWin_EnumerateEPROCESS64(_In_ PVMM_PROCESS pSystemProcess, _In_ BOOL fTot ~0xfff & *pqwDTB, pOffsetEPROCESS->DTB_User ? (~0xfff & *pqwDTB_User) : 0, szName, - !((*pdwPID == 4) || ((*pdwState == 0) && (*pqwPEB == 0)))); + fUser); if(!pObProcess) { vmmprintfv("VMM: WARNING: PID '%i' already exists.\n", *pdwPID); if(++cNewProcessCollision >= 8) { @@ -1233,7 +1238,7 @@ BOOL VmmWin_EnumerateEPROCESS32(_In_ PVMM_PROCESS pSystemProcess, _In_ BOOL fTot PVMM_PROCESS pObProcess = NULL; DWORD vaSystemEPROCESS, vaEPROCESS, cPID = 0, cNewProcessCollision = 0; DWORD iProc = 0; - BOOL fShowTerminated; + BOOL fShowTerminated, fUser; PVMM_WIN_EPROCESS_OFFSET pOffsetEPROCESS = &ctxVmm->kernel.OffsetEPROCESS; PVMMOB_DATASET pObSetAddressEPROCESS = NULL; fShowTerminated = ctxVmm->flags & VMM_FLAG_PROCESS_SHOW_TERMINATED; @@ -1257,7 +1262,7 @@ BOOL VmmWin_EnumerateEPROCESS32(_In_ PVMM_PROCESS pSystemProcess, _In_ BOOL fTot pdwPEB = (PDWORD)(pb + pOffsetEPROCESS->PEB); // prefetch pages into cache (if any) pObSetAddressEPROCESS = VmmObContainer_GetOb(&ctxVmm->ObCEPROCESSCachePrefetch); - VmmCachePrefetch(pSystemProcess, pObSetAddressEPROCESS); + VmmCachePrefetchPages(pSystemProcess, pObSetAddressEPROCESS); VmmOb_DECREF(pObSetAddressEPROCESS); // initialize address set if(!(pObSetAddressEPROCESS = VmmObDataSet_Alloc(TRUE))) { return FALSE; } @@ -1272,6 +1277,9 @@ BOOL VmmWin_EnumerateEPROCESS32(_In_ PVMM_PROCESS pSystemProcess, _In_ BOOL fTot VmmOb_DECREF(pObProcess); pObProcess = NULL; if(*pdwDTB && *(PQWORD)szName && (fShowTerminated || !*pdwState)) { + fUser = + !((*pdwPID == 4) || ((*pdwState == 0) && (*pdwPEB == 0))) || + ((*(PQWORD)(szName + 0x00) == 0x72706d6f436d654d) && (*(PDWORD)(szName + 0x08) == 0x69737365)); // MemCompression "process" pObProcess = VmmProcessCreateEntry( fTotalRefresh, *pdwPID, @@ -1279,7 +1287,7 @@ BOOL VmmWin_EnumerateEPROCESS32(_In_ PVMM_PROCESS pSystemProcess, _In_ BOOL fTot *pdwDTB & 0xffffffe0, pOffsetEPROCESS->DTB_User ? (~0xfff & *pdwDTB_User) : 0, szName, - !((*pdwPID == 4) || ((*pdwState == 0) && (*pdwPEB == 0)))); + fUser); } if(pObProcess) { pObProcess->os.win.vaEPROCESS = vaEPROCESS; diff --git a/vmm/vmmwin.h b/vmm/vmmwin.h index fda2a40..7d45e68 100644 --- a/vmm/vmmwin.h +++ b/vmm/vmmwin.h @@ -36,9 +36,12 @@ VOID VmmWin_PE_SetSizeSectionIATEAT_DisplayBuffer(_In_ PVMM_PROCESS pProcess, _I * -- pProcess * -- pModule * -- pEATs -* -- pcEATs = number max items of pEATs on entry, number of actual items of pEATs on exit +* -- cEATs +* -- pcEATs = number of actual items of pEATs written. +* -- return */ -VOID VmmWin_PE_LoadEAT_DisplayBuffer(_Inout_ PVMM_PROCESS pProcess, _Inout_ PVMM_MODULEMAP_ENTRY pModule, _Out_writes_opt_(*pcEATs) PVMMPROC_WINDOWS_EAT_ENTRY pEATs, _Inout_ PDWORD pcEATs); +_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); /* * Walk the import address table (IAT) from a given pProcess and store it in the @@ -46,9 +49,10 @@ VOID VmmWin_PE_LoadEAT_DisplayBuffer(_Inout_ PVMM_PROCESS pProcess, _Inout_ PVMM * -- pProcess * -- pModule * -- pIATs -* -- pcIATs = number max items of pIATs on entry, number of actual items of pIATs on exit +* -- cIATs +* -- pcIATs = number of actual items of pIATs on exit */ -VOID VmmWin_PE_LoadIAT_DisplayBuffer(_Inout_ PVMM_PROCESS pProcess, _Inout_ PVMM_MODULEMAP_ENTRY pModule, _Out_writes_(*pcIATs) PVMMWIN_IAT_ENTRY pIATs, _Inout_ PDWORD pcIATs); +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); /* * Fill the pbDisplayBuffer with a human readable version of the data directories. diff --git a/vmm/vmmwininit.c b/vmm/vmmwininit.c index ac32be3..eface21 100644 --- a/vmm/vmmwininit.c +++ b/vmm/vmmwininit.c @@ -456,7 +456,7 @@ fail: */ BOOL VmmWinInit_TryInitialize(_In_opt_ QWORD paDTBOpt) { - PVMM_PROCESS pObSystemProcess = NULL; + PVMM_PROCESS pObSystemProcess = NULL, pObProcess = NULL; QWORD vaPsInitialSystemProcess, vaSystemEPROCESS; // Fetch Directory Base (DTB (PML4)) and initialize Memory Model. if(paDTBOpt) { @@ -503,6 +503,14 @@ BOOL VmmWinInit_TryInitialize(_In_opt_ QWORD paDTBOpt) // Optionally fetch PsLoadedModuleList / KDBG VmmWinInit_FindPsLoadedModuleListKDBG(pObSystemProcess); VmmOb_DECREF(pObSystemProcess); + // Optionally retrieve PID of MemCompression process + while((pObProcess = VmmProcessGetNext(pObProcess))) { + if(memcmp("MemCompression", pObProcess->szName, 15)) { continue; } + ctxVmm->kernel.dwPidMemCompression = pObProcess->dwPID; + VmmOb_DECREF(pObProcess); + pObProcess = NULL; + break; + } return TRUE; fail: VmmInitializeMemoryModel(VMM_MEMORYMODEL_NA); // clean memory model diff --git a/vmm_example/leechcore.h b/vmm_example/leechcore.h index a2da3e6..46af16a 100644 --- a/vmm_example/leechcore.h +++ b/vmm_example/leechcore.h @@ -68,13 +68,14 @@ // placed in same directory as the executable file. // // PMEM : load the rekall winpmem driver into the kernel and connect to it -// to acquire memory. The driver file 'winpmem_x64.sys' is found in -// the Rekall directory after most recent version has been installed. -// Copy 'winpmem_x64.sys' to the directory of leechcore.dll and run -// executable as elevated admin using syntax below: +// to acquire memory. The signed driver `.sys` file may be found at: +// https://github.com/Velocidex/c-aff4/tree/master/tools/pmem/resources/winpmem +// Download the driver file `att_winpmem_64.sys` and copy it to the +// directory of leechcore.dll and run executable as elevated admin +// using syntax below: // Syntax: -// PMEM (use winpmem_x64.sys in directory of executable) -// PMEM:// +// PMEM (use att_winpmem_64.sys in directory of executable) +// PMEM:// // // TOTALMELTDOWN : read/write - requires a Windows 7 system vulnerable to the // "Total Meltdown" vulnerability - CVE-2018-1038. @@ -110,7 +111,7 @@ // (c) Ulf Frisk, 2018-2019 // Author: Ulf Frisk, pcileech@frizk.net // -// Header Version: 1.0 +// Header Version: 1.1.0 // #ifndef __LEECHCORE_H__ #define __LEECHCORE_H__ @@ -151,6 +152,7 @@ typedef long long unsigned int QWORD, *PQWORD, ULONG64, *PULONG64; #define _Printf_format_string_ #define _Inout_updates_bytes_(x) #define _In_reads_(cbDataIn) +#define _Out_writes_opt_(x) #define _Success_(return) #endif /* LINUX */ @@ -227,7 +229,10 @@ typedef struct tdLEECHCORE_PAGESTAT_MINIMAL { } LEECHCORE_PAGESTAT_MINIMAL, *PLEECHCORE_PAGESTAT_MINIMAL; /* -* Open a connection to the target device. +* Open a connection to the target device. The LeechCore initialization may fail +* if the underlying device cannot be opened or if the LeechCore is already +* initialized. If already initialized please connect with device EXISTING or +* call LeechCore_Close() before opening a new device. * -- pInformation * -- result */ @@ -455,9 +460,9 @@ DLLEXPORT BOOL LeechCore_CommandData( _In_ ULONG64 fOption, _In_reads_(cbDataIn) PBYTE pbDataIn, _In_ DWORD cbDataIn, - _Out_writes_(cbDataOut) PBYTE pbDataOut, + _Out_writes_opt_(cbDataOut) PBYTE pbDataOut, _In_ DWORD cbDataOut, - _Out_ PDWORD pcbDataOut + _Out_opt_ PDWORD pcbDataOut ); #ifdef __cplusplus diff --git a/vmm_example/vmmdll.h b/vmm_example/vmmdll.h index 353159a..42befa8 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.0 +// Header Version: 2.1 // #include @@ -26,13 +26,15 @@ extern "C" { * about the parameters please see github wiki for Memory Process File System * and LeechCore. THIS IS THE PREFERED WAY OF INITIALIZING VMM.DLL * Important parameters are: -* -vdll = show printf style outputs) +* -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 * -remote = remote LeechCore instance - please see leechcore.h or Github * documentation for additional information. +* -norefresh = disable background refreshes (even if backing memory is +* volatile memory). * -- argc * -- argv * -- return = success/fail @@ -289,7 +291,7 @@ typedef struct tdVMMDLL_PLUGIN_REGINFO { * -- ppMEMs = array of scatter read headers. * -- cpMEMs = count of ppDMAs. * -- pcpDMAsRead = optional count of number of successfully read ppDMAs. -* -- flags = optional flags as given by VMM_FLAG_* +* -- flags = optional flags as given by VMMDLL_FLAG_* * -- return = the number of successfully read items. */ DWORD VMMDLL_MemReadScatter(_In_ DWORD dwPID, _Inout_ PPMEM_IO_SCATTER_HEADER ppMEMs, _In_ DWORD cpMEMs, _In_ DWORD flags); @@ -322,13 +324,25 @@ BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwVA, _Out_ PBYTE pb, _In_ DW * -- pb * -- cb * -- pcbRead -* -- flags = flags as in VMM_FLAG_* +* -- 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. */ _Success_(return) BOOL VMMDLL_MemReadEx(_In_ DWORD dwPID, _In_ ULONG64 qwVA, _Out_ 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 +* cache. This function is to be used to batch larger known reads into local +* cache before making multiple smaller reads - which will then happen from +* the cache. Function exists for performance reasons. +* -- dwPID = PID of target process, (DWORD)-1 for physical memory. +* -- pPrefetchAddresses = array of addresses to read into cache. +* -- cPrefetchAddresses +*/ +_Success_(return) +BOOL VMMDLL_MemPrefetchPages(_In_ DWORD dwPID, _In_reads_(cPrefetchAddresses) PULONG64 pPrefetchAddresses, _In_ DWORD cPrefetchAddresses); + /* * Write a contigious arbitrary amount of memory. Please note some virtual memory * such as pages of executables (such as DLLs) may be shared between different @@ -527,6 +541,87 @@ BOOL VMMDLL_ProcessGetEAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PVMMD _Success_(return) BOOL VMMDLL_ProcessGetIAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _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 +* -- szFunctionName +* -- return = virtual address of function, zero on fail. +*/ +ULONG64 VMMDLL_ProcessGetProcAddress(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szFunctionName); + +/* +* Retrieve the base address of a given module. +* -- dwPID +* -- szModuleName +* -- return = virtual address of module base, zero on fail. +*/ +ULONG64 VMMDLL_ProcessGetModuleBase(_In_ DWORD dwPID, _In_ LPSTR szModuleName); + + + +//----------------------------------------------------------------------------- +// WINDOWS SPECIFIC UTILITY FUNCTIONS BELOW: +//----------------------------------------------------------------------------- + +typedef struct tdVMMDLL_WIN_THUNKINFO_IAT { + BOOL fValid; + BOOL f32; // if TRUE fn is a 32-bit/4-byte entry, otherwise 64-bit/8-byte entry. + ULONG64 vaThunk; // address of import address table 'thunk'. + ULONG64 vaFunction; // value if import address table 'thunk' == address of imported function. + ULONG64 vaNameModule; // address of name string for imported module. + ULONG64 vaNameFunction; // address of name string for imported function. +} VMMDLL_WIN_THUNKINFO_IAT, *PVMMDLL_WIN_THUNKINFO_IAT; + +typedef struct tdVMMDLL_WIN_THUNKINFO_EAT { + BOOL fValid; + DWORD valueThunk; // value of export address table 'thunk'. + ULONG64 vaThunk; // address of import address table 'thunk'. + ULONG64 vaNameFunction; // address of name string for exported function. + ULONG64 vaFunction; // address of exported function (module base + value parameter). +} VMMDLL_WIN_THUNKINFO_EAT, *PVMMDLL_WIN_THUNKINFO_EAT; + +/* +* Retrieve information about the import address table IAT thunk for an imported +* function. This includes the virtual address of the IAT thunk which is useful +* for hooking. +* -- dwPID +* -- szModuleName +* -- 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); + +/* +* 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 +* -- pThunkEAT +* -- return +*/ +_Success_(return) +BOOL VMMDLL_WinGetThunkInfoEAT(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT); + +/* +* Decompress compressed memory page stored in the MemCompression process. +* -- vaCompressedData = virtual address in 'MemCompression' to decompress. +* -- cbCompressedData = length of compressed data in 'MemCompression' to decompress (or zero for auto-detect). +* -- pbDecompressedPage +* -- pcbCompressedData = optional ptr to receive length of compressed buffer. +* -- return +*/ +_Success_(return) +BOOL VMMDLL_WinMemCompression_DecompressPage( + _In_ ULONG64 vaCompressedData, + _In_opt_ DWORD cbCompressedData, + _Out_writes_(4096) PBYTE pbDecompressedPage, + _Out_opt_ PDWORD pcbCompressedData +); //----------------------------------------------------------------------------- diff --git a/vmm_example/vmmdll_example.c b/vmm_example/vmmdll_example.c index d322fec..f3b17fa 100644 --- a/vmm_example/vmmdll_example.c +++ b/vmm_example/vmmdll_example.c @@ -64,6 +64,8 @@ int main(_In_ int argc, _In_ char* argv[]) BOOL result; NTSTATUS nt; DWORD i, dwPID; + DWORD dw = 0; + QWORD va; BYTE pbPage1[0x1000], pbPage2[0x1000]; #ifdef _INITIALIZE_FROM_FILE @@ -86,12 +88,12 @@ int main(_In_ int argc, _In_ char* argv[]) printf("------------------------------------------------------------\n"); printf("#01: Initialize from TotalMeltdown: \n"); ShowKeyPress(); - printf("CALL: VMMDLL_InitializeTotalMeltdown\n"); - result = VMMDLL_InitializeTotalMeltdown(); + printf("CALL: VMMDLL_Initialize\n"); + result = result = VMMDLL_Initialize(3, (LPSTR[]) { "", "-device", "totalmeltdown" }); if(result) { - printf("SUCCESS: VMMDLL_InitializeTotalMeltdown\n"); + printf("SUCCESS: VMMDLL_Initialize\n"); } else { - printf("FAIL: VMMDLL_InitializeTotalMeltdown\n"); + printf("FAIL: VMMDLL_Initialize\n"); return 1; } #endif /* _INITIALIZE_FROM_TOTALMELTDOWN */ @@ -101,12 +103,12 @@ int main(_In_ int argc, _In_ char* argv[]) printf("------------------------------------------------------------\n"); printf("#01: Initialize from FPGA: \n"); ShowKeyPress(); - printf("CALL: VMMDLL_InitializeFPGA\n"); - result = VMMDLL_InitializeFPGA(NULL, NULL); + printf("CALL: VMMDLL_Initialize\n"); + result = VMMDLL_Initialize(3, (LPSTR[]) { "", "-device", "fpga" }); if(result) { - printf("SUCCESS: VMMDLL_InitializeFPGA\n"); + printf("SUCCESS: VMMDLL_Initialize\n"); } else { - printf("FAIL: VMMDLL_InitializeFPGA\n"); + printf("FAIL: VMMDLL_Initialize\n"); return 1; } // Retrieve the ID of the FPPA (SP605/PCIeScreamer/AC701 ...) and the bitstream version @@ -572,6 +574,78 @@ int main(_In_ int argc, _In_ char* argv[]) } + // Get base virtual address of ntoskrnl.exe + printf("------------------------------------------------------------\n"); + printf("#17: get ntoskrnl.exe base virtual address \n"); + ShowKeyPress(); + printf("CALL: VMMDLL_ProcessGetModuleBase\n"); + va = VMMDLL_ProcessGetModuleBase(4, "ntoskrnl.exe"); + if(va) { + printf("SUCCESS: VMMDLL_ProcessGetModuleBase\n"); + printf(" %s = %016llx\n", "ntoskrnl.exe", va); + } else { + printf("FAIL: VMMDLL_ProcessGetModuleBase\n"); + return 1; + } + + + // GetProcAddress from ntoskrnl.exe + printf("------------------------------------------------------------\n"); + printf("#18: get proc address for ntoskrnl.exe!KeGetCurrentIrql \n"); + ShowKeyPress(); + printf("CALL: VMMDLL_ProcessGetProcAddress\n"); + va = VMMDLL_ProcessGetProcAddress(4, "ntoskrnl.exe", "KeGetCurrentIrql"); + if(va) { + printf("SUCCESS: VMMDLL_ProcessGetProcAddress\n"); + printf(" %s!%s = %016llx\n", "ntoskrnl.exe", "KeGetCurrentIrql", va); + } else { + printf("FAIL: VMMDLL_ProcessGetProcAddress\n"); + return 1; + } + + + // Get EAT Thunk from ntoskrnl.exe!KeGetCurrentIrql + printf("------------------------------------------------------------\n"); + printf("#19: 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); + if(result) { + printf("SUCCESS: VMMDLL_WinGetThunkInfoEAT\n"); + printf(" vaFunction: %016llx\n", oThunkInfoEAT.vaFunction); + printf(" vaThunk: %016llx\n", oThunkInfoEAT.vaThunk); + printf(" valueThunk: %08x\n", oThunkInfoEAT.valueThunk); + printf(" vaNameFunc: %016llx\n", oThunkInfoEAT.vaNameFunction); + } else { + printf("FAIL: VMMDLL_WinGetThunkInfoEAT\n"); + return 1; + } + + + // Get IAT Thunk ntoskrnl.exe -> hal.dll!HalSendNMI + printf("------------------------------------------------------------\n"); + printf("#20: 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); + if(result) { + printf("SUCCESS: VMMDLL_WinGetThunkInfoIAT\n"); + printf(" vaFunction: %016llx\n", oThunkInfoIAT.vaFunction); + printf(" vaThunk: %016llx\n", oThunkInfoIAT.vaThunk); + printf(" vaNameFunction: %016llx\n", oThunkInfoIAT.vaNameFunction); + printf(" vaNameModule: %016llx\n", oThunkInfoIAT.vaNameModule); + } + else { + printf("FAIL: VMMDLL_WinGetThunkInfoEAT\n"); + return 1; + } + + + // Finish everything and exit! printf("------------------------------------------------------------\n"); printf("#99: FINISHED EXAMPLES! \n"); diff --git a/vmmpyc/leechcore.h b/vmmpyc/leechcore.h index a2da3e6..46af16a 100644 --- a/vmmpyc/leechcore.h +++ b/vmmpyc/leechcore.h @@ -68,13 +68,14 @@ // placed in same directory as the executable file. // // PMEM : load the rekall winpmem driver into the kernel and connect to it -// to acquire memory. The driver file 'winpmem_x64.sys' is found in -// the Rekall directory after most recent version has been installed. -// Copy 'winpmem_x64.sys' to the directory of leechcore.dll and run -// executable as elevated admin using syntax below: +// to acquire memory. The signed driver `.sys` file may be found at: +// https://github.com/Velocidex/c-aff4/tree/master/tools/pmem/resources/winpmem +// Download the driver file `att_winpmem_64.sys` and copy it to the +// directory of leechcore.dll and run executable as elevated admin +// using syntax below: // Syntax: -// PMEM (use winpmem_x64.sys in directory of executable) -// PMEM:// +// PMEM (use att_winpmem_64.sys in directory of executable) +// PMEM:// // // TOTALMELTDOWN : read/write - requires a Windows 7 system vulnerable to the // "Total Meltdown" vulnerability - CVE-2018-1038. @@ -110,7 +111,7 @@ // (c) Ulf Frisk, 2018-2019 // Author: Ulf Frisk, pcileech@frizk.net // -// Header Version: 1.0 +// Header Version: 1.1.0 // #ifndef __LEECHCORE_H__ #define __LEECHCORE_H__ @@ -151,6 +152,7 @@ typedef long long unsigned int QWORD, *PQWORD, ULONG64, *PULONG64; #define _Printf_format_string_ #define _Inout_updates_bytes_(x) #define _In_reads_(cbDataIn) +#define _Out_writes_opt_(x) #define _Success_(return) #endif /* LINUX */ @@ -227,7 +229,10 @@ typedef struct tdLEECHCORE_PAGESTAT_MINIMAL { } LEECHCORE_PAGESTAT_MINIMAL, *PLEECHCORE_PAGESTAT_MINIMAL; /* -* Open a connection to the target device. +* Open a connection to the target device. The LeechCore initialization may fail +* if the underlying device cannot be opened or if the LeechCore is already +* initialized. If already initialized please connect with device EXISTING or +* call LeechCore_Close() before opening a new device. * -- pInformation * -- result */ @@ -455,9 +460,9 @@ DLLEXPORT BOOL LeechCore_CommandData( _In_ ULONG64 fOption, _In_reads_(cbDataIn) PBYTE pbDataIn, _In_ DWORD cbDataIn, - _Out_writes_(cbDataOut) PBYTE pbDataOut, + _Out_writes_opt_(cbDataOut) PBYTE pbDataOut, _In_ DWORD cbDataOut, - _Out_ PDWORD pcbDataOut + _Out_opt_ PDWORD pcbDataOut ); #ifdef __cplusplus diff --git a/vmmpyc/version.h b/vmmpyc/version.h index ac777a4..3faa79f 100644 --- a/vmmpyc/version.h +++ b/vmmpyc/version.h @@ -2,7 +2,7 @@ #define STRINGIZE(s) STRINGIZE2(s) #define VERSION_MAJOR 2 -#define VERSION_MINOR 0 +#define VERSION_MINOR 1 #define VERSION_REVISION 0 #define VERSION_BUILD 0 diff --git a/vmmpyc/vmmdll.h b/vmmpyc/vmmdll.h index 353159a..42befa8 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.0 +// Header Version: 2.1 // #include @@ -26,13 +26,15 @@ extern "C" { * about the parameters please see github wiki for Memory Process File System * and LeechCore. THIS IS THE PREFERED WAY OF INITIALIZING VMM.DLL * Important parameters are: -* -vdll = show printf style outputs) +* -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 * -remote = remote LeechCore instance - please see leechcore.h or Github * documentation for additional information. +* -norefresh = disable background refreshes (even if backing memory is +* volatile memory). * -- argc * -- argv * -- return = success/fail @@ -289,7 +291,7 @@ typedef struct tdVMMDLL_PLUGIN_REGINFO { * -- ppMEMs = array of scatter read headers. * -- cpMEMs = count of ppDMAs. * -- pcpDMAsRead = optional count of number of successfully read ppDMAs. -* -- flags = optional flags as given by VMM_FLAG_* +* -- flags = optional flags as given by VMMDLL_FLAG_* * -- return = the number of successfully read items. */ DWORD VMMDLL_MemReadScatter(_In_ DWORD dwPID, _Inout_ PPMEM_IO_SCATTER_HEADER ppMEMs, _In_ DWORD cpMEMs, _In_ DWORD flags); @@ -322,13 +324,25 @@ BOOL VMMDLL_MemRead(_In_ DWORD dwPID, _In_ ULONG64 qwVA, _Out_ PBYTE pb, _In_ DW * -- pb * -- cb * -- pcbRead -* -- flags = flags as in VMM_FLAG_* +* -- 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. */ _Success_(return) BOOL VMMDLL_MemReadEx(_In_ DWORD dwPID, _In_ ULONG64 qwVA, _Out_ 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 +* cache. This function is to be used to batch larger known reads into local +* cache before making multiple smaller reads - which will then happen from +* the cache. Function exists for performance reasons. +* -- dwPID = PID of target process, (DWORD)-1 for physical memory. +* -- pPrefetchAddresses = array of addresses to read into cache. +* -- cPrefetchAddresses +*/ +_Success_(return) +BOOL VMMDLL_MemPrefetchPages(_In_ DWORD dwPID, _In_reads_(cPrefetchAddresses) PULONG64 pPrefetchAddresses, _In_ DWORD cPrefetchAddresses); + /* * Write a contigious arbitrary amount of memory. Please note some virtual memory * such as pages of executables (such as DLLs) may be shared between different @@ -527,6 +541,87 @@ BOOL VMMDLL_ProcessGetEAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _Out_opt_ PVMMD _Success_(return) BOOL VMMDLL_ProcessGetIAT(_In_ DWORD dwPID, _In_ LPSTR szModule, _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 +* -- szFunctionName +* -- return = virtual address of function, zero on fail. +*/ +ULONG64 VMMDLL_ProcessGetProcAddress(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szFunctionName); + +/* +* Retrieve the base address of a given module. +* -- dwPID +* -- szModuleName +* -- return = virtual address of module base, zero on fail. +*/ +ULONG64 VMMDLL_ProcessGetModuleBase(_In_ DWORD dwPID, _In_ LPSTR szModuleName); + + + +//----------------------------------------------------------------------------- +// WINDOWS SPECIFIC UTILITY FUNCTIONS BELOW: +//----------------------------------------------------------------------------- + +typedef struct tdVMMDLL_WIN_THUNKINFO_IAT { + BOOL fValid; + BOOL f32; // if TRUE fn is a 32-bit/4-byte entry, otherwise 64-bit/8-byte entry. + ULONG64 vaThunk; // address of import address table 'thunk'. + ULONG64 vaFunction; // value if import address table 'thunk' == address of imported function. + ULONG64 vaNameModule; // address of name string for imported module. + ULONG64 vaNameFunction; // address of name string for imported function. +} VMMDLL_WIN_THUNKINFO_IAT, *PVMMDLL_WIN_THUNKINFO_IAT; + +typedef struct tdVMMDLL_WIN_THUNKINFO_EAT { + BOOL fValid; + DWORD valueThunk; // value of export address table 'thunk'. + ULONG64 vaThunk; // address of import address table 'thunk'. + ULONG64 vaNameFunction; // address of name string for exported function. + ULONG64 vaFunction; // address of exported function (module base + value parameter). +} VMMDLL_WIN_THUNKINFO_EAT, *PVMMDLL_WIN_THUNKINFO_EAT; + +/* +* Retrieve information about the import address table IAT thunk for an imported +* function. This includes the virtual address of the IAT thunk which is useful +* for hooking. +* -- dwPID +* -- szModuleName +* -- 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); + +/* +* 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 +* -- pThunkEAT +* -- return +*/ +_Success_(return) +BOOL VMMDLL_WinGetThunkInfoEAT(_In_ DWORD dwPID, _In_ LPSTR szModuleName, _In_ LPSTR szExportFunctionName, _Out_ PVMMDLL_WIN_THUNKINFO_EAT pThunkInfoEAT); + +/* +* Decompress compressed memory page stored in the MemCompression process. +* -- vaCompressedData = virtual address in 'MemCompression' to decompress. +* -- cbCompressedData = length of compressed data in 'MemCompression' to decompress (or zero for auto-detect). +* -- pbDecompressedPage +* -- pcbCompressedData = optional ptr to receive length of compressed buffer. +* -- return +*/ +_Success_(return) +BOOL VMMDLL_WinMemCompression_DecompressPage( + _In_ ULONG64 vaCompressedData, + _In_opt_ DWORD cbCompressedData, + _Out_writes_(4096) PBYTE pbDecompressedPage, + _Out_opt_ PDWORD pcbCompressedData +); //----------------------------------------------------------------------------- diff --git a/vmmpyc/vmmpyc.c b/vmmpyc/vmmpyc.c index e039473..0bb625b 100644 --- a/vmmpyc/vmmpyc.c +++ b/vmmpyc/vmmpyc.c @@ -48,6 +48,16 @@ VMMPYC_Initialize(PyObject *self, PyObject *args) return Py_BuildValue("s", NULL); // None returned on success. } +// () -> None +static PyObject* +VMMPYC_Close(PyObject *self, PyObject *args) +{ + Py_BEGIN_ALLOW_THREADS; + VMMDLL_Close(); + Py_END_ALLOW_THREADS; + return Py_BuildValue("s", NULL); // None returned on success. +} + //----------------------------------------------------------------------------- @@ -63,9 +73,9 @@ VMMPYC_ConfigGet(PyObject *self, PyObject *args) BOOL result; ULONG64 fOption, qwValue = 0; if(!PyArg_ParseTuple(args, "K", &fOption)) { return NULL; } - Py_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; result = VMMDLL_ConfigGet(fOption, &qwValue); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS; if(!result) { return PyErr_Format(PyExc_RuntimeError, "VMMPYC_ConfigGet: Unable to retrieve config value for setting."); } return PyLong_FromUnsignedLongLong(qwValue); } @@ -77,9 +87,9 @@ VMMPYC_ConfigSet(PyObject *self, PyObject *args) BOOL result; ULONG64 fOption, qwValue = 0; if(!PyArg_ParseTuple(args, "KK", &fOption, &qwValue)) { return NULL; } - Py_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; result = VMMDLL_ConfigSet(fOption, qwValue); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS; if(!result) { return PyErr_Format(PyExc_RuntimeError, "VMMPYC_ConfigSet: Unable to set config value for setting."); } return Py_BuildValue("s", NULL); // None returned on success. } @@ -139,9 +149,9 @@ VMMPYC_MemReadScatter(PyObject *self, PyObject *args) } Py_DECREF(pyListSrc); // call c-dll for vmm - Py_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; result = VMMDLL_MemReadScatter(dwPID, ppMEMs, cMEMs, flags); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS; if(!result) { LocalFree(pb); return PyErr_Format(PyExc_RuntimeError, "VMMPYC_MemReadScatter: Failed."); @@ -177,9 +187,9 @@ VMMPYC_MemRead(PyObject *self, PyObject *args) if(cb > 0x01000000) { return PyErr_Format(PyExc_RuntimeError, "VMMPYC_MemRead: Read larger than maxium supported (0x01000000) bytes requested."); } pb = LocalAlloc(0, cb); if(!pb) { return PyErr_NoMemory(); } - Py_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; result = VMMDLL_MemReadEx(dwPID, qwA, pb, cb, &cbRead, flags); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS; if(!result) { LocalFree(pb); return PyErr_Format(PyExc_RuntimeError, "VMMPYC_MemRead: Failed."); @@ -213,10 +223,10 @@ VMMPYC_MemWrite(PyObject *self, PyObject *args) } iResult = PyBuffer_ToContiguous(pb, &pyBuffer, cb, 'C'); PyBuffer_Release(&pyBuffer); - Py_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; result = (iResult == 0) && VMMDLL_MemWrite(dwPID, va, pb, (DWORD)cb); LocalFree(pb); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS; if(!result) { return PyErr_Format(PyExc_RuntimeError, "VMMPYC_MemWrite: Failed."); } return Py_BuildValue("s", NULL); // None returned on success. } @@ -229,9 +239,9 @@ VMMPYC_MemVirt2Phys(PyObject *self, PyObject *args) DWORD dwPID; ULONG64 va, pa; if(!PyArg_ParseTuple(args, "kK", &dwPID, &va)) { return NULL; } - Py_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; result = VMMDLL_MemVirt2Phys(dwPID, va, &pa); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS; if(!result) { return PyErr_Format(PyExc_RuntimeError, "VMMPYC_MemVirt2Phys: Failed."); } return PyLong_FromUnsignedLongLong(pa); } @@ -248,13 +258,13 @@ VMMPYC_ProcessGetMemoryMap(PyObject *self, PyObject *args) 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 + 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); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS; if(!result) { Py_DECREF(pyList); LocalFree(pMemMapEntries); @@ -294,9 +304,9 @@ VMMPYC_ProcessGetMemoryMapEntry(PyObject *self, PyObject *args) CHAR sz[5]; if(!PyArg_ParseTuple(args, "kK|p", &dwPID, &va, &fIdentifyModules)) { return NULL; } if(!(pyDict = PyDict_New())) { return PyErr_NoMemory(); } - Py_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; result = VMMDLL_ProcessGetMemoryMapEntry(dwPID, &e, va, fIdentifyModules); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS; if(!result) { Py_DECREF(pyDict); return PyErr_Format(PyExc_RuntimeError, "VMMDLL_ProcessGetMemoryMapEntry: Failed."); @@ -327,13 +337,13 @@ VMMPYC_ProcessGetModuleMap(PyObject *self, PyObject *args) PVMMDLL_MODULEMAP_ENTRY pe, pModuleEntries = NULL; if(!PyArg_ParseTuple(args, "k", &dwPID)) { return NULL; } if(!(pyList = PyList_New(0))) { return PyErr_NoMemory(); } - Py_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; result = VMMDLL_ProcessGetModuleMap(dwPID, NULL, &cModuleEntries) && cModuleEntries && (pModuleEntries = LocalAlloc(0, cModuleEntries * sizeof(VMMDLL_MODULEMAP_ENTRY))) && VMMDLL_ProcessGetModuleMap(dwPID, pModuleEntries, &cModuleEntries); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS; if(!result) { Py_DECREF(pyList); LocalFree(pModuleEntries); @@ -365,10 +375,10 @@ VMMPYC_ProcessGetModuleFromName(PyObject *self, PyObject *args) VMMDLL_MODULEMAP_ENTRY e; if(!PyArg_ParseTuple(args, "ks", &dwPID, &szModuleName)) { return NULL; } if(!(pyDict = PyDict_New())) { return PyErr_NoMemory(); } - Py_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; ZeroMemory(&e, sizeof(VMMDLL_MODULEMAP_ENTRY)); result = VMMDLL_ProcessGetModuleFromName(dwPID, szModuleName, &e); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS; if(!result) { Py_DECREF(pyDict); return PyErr_Format(PyExc_RuntimeError, "VMMPYC_ProcessGetModuleFromName: Failed."); @@ -389,9 +399,9 @@ VMMPYC_PidGetFromName(PyObject *self, PyObject *args) DWORD dwPID; LPSTR szProcessName; if(!PyArg_ParseTuple(args, "s", &szProcessName)) { return NULL; } - Py_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; result = VMMDLL_PidGetFromName(szProcessName, &dwPID); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS; if(!result) { return PyErr_Format(PyExc_RuntimeError, "VMMPYC_PidGetFromName: Failed."); } return PyLong_FromLong(dwPID); } @@ -405,12 +415,12 @@ VMMPYC_PidList(PyObject *self, PyObject *args) ULONG64 cPIDs = 0; DWORD i, *pPIDs = NULL; if(!(pyList = PyList_New(0))) { return PyErr_NoMemory(); } - Py_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; result = VMMDLL_PidList(NULL, &cPIDs) && (pPIDs = LocalAlloc(LMEM_ZEROINIT, cPIDs * sizeof(DWORD))) && VMMDLL_PidList(pPIDs, &cPIDs); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS; if(!result) { Py_DECREF(pyList); LocalFree(pPIDs); @@ -434,12 +444,12 @@ VMMPYC_ProcessGetInformation(PyObject *self, PyObject *args) SIZE_T cbInfo = sizeof(VMMDLL_PROCESS_INFORMATION); if(!PyArg_ParseTuple(args, "k", &dwPID)) { return NULL; } if(!(pyDict = PyDict_New())) { return PyErr_NoMemory(); } - Py_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; ZeroMemory(&info, sizeof(VMMDLL_PROCESS_INFORMATION)); info.magic = VMMDLL_PROCESS_INFORMATION_MAGIC; info.wVersion = VMMDLL_PROCESS_INFORMATION_VERSION; result = VMMDLL_ProcessGetInformation(dwPID, &info, &cbInfo); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS; if(!result) { Py_DECREF(pyDict); return PyErr_Format(PyExc_RuntimeError, "VMMPYC_ProcessGetInformation: Failed."); @@ -481,11 +491,11 @@ VMMPYC_ProcessGetDirectories(PyObject *self, PyObject *args) 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(!(pyList = PyList_New(0))) { return PyErr_NoMemory(); } - Py_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; result = (pDirectories = LocalAlloc(0, 16 * sizeof(IMAGE_DATA_DIRECTORY))) && VMMDLL_ProcessGetDirectories(dwPID, szModule, pDirectories, 16, &cDirectories); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS; if(!result) { Py_DECREF(pyList); LocalFree(pDirectories); @@ -518,13 +528,13 @@ VMMPYC_ProcessGetSections(PyObject *self, PyObject *args) szName[8] = 0; if(!PyArg_ParseTuple(args, "ks", &dwPID, &szModule)) { return NULL; } if(!(pyList = PyList_New(0))) { return PyErr_NoMemory(); } - Py_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; result = VMMDLL_ProcessGetSections(dwPID, szModule, NULL, 0, &cSections) && cSections && (pSections = LocalAlloc(0, cSections * sizeof(IMAGE_SECTION_HEADER))) && VMMDLL_ProcessGetSections(dwPID, szModule, pSections, cSections, &cSections); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS; if(!result) { Py_DECREF(pyList); LocalFree(pSections); @@ -564,13 +574,13 @@ VMMPYC_ProcessGetEAT(PyObject *self, PyObject *args) LPSTR szModule; if(!PyArg_ParseTuple(args, "ks", &dwPID, &szModule)) { return NULL; } if(!(pyList = PyList_New(0))) { return PyErr_NoMemory(); } - Py_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; result = VMMDLL_ProcessGetEAT(dwPID, szModule, NULL, 0, &cEATs) && cEATs && (pEATs = LocalAlloc(0, cEATs * sizeof(VMMDLL_EAT_ENTRY))) && VMMDLL_ProcessGetEAT(dwPID, szModule, pEATs, cEATs, &cEATs); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS; if(!result) { Py_DECREF(pyList); LocalFree(pEATs); @@ -601,13 +611,13 @@ VMMPYC_ProcessGetIAT(PyObject *self, PyObject *args) LPSTR szModule; if(!PyArg_ParseTuple(args, "ks", &dwPID, &szModule)) { return NULL; } if(!(pyList = PyList_New(0))) { return PyErr_NoMemory(); } - Py_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; result = VMMDLL_ProcessGetIAT(dwPID, szModule, NULL, 0, &cIATs) && cIATs && (pIATs = LocalAlloc(0, cIATs * sizeof(VMMDLL_IAT_ENTRY))) && VMMDLL_ProcessGetIAT(dwPID, szModule, pIATs, cIATs, &cIATs); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS; if(!result) { Py_DECREF(pyList); LocalFree(pIATs); @@ -650,7 +660,7 @@ VMMPYC_UtilFillHexAscii(PyObject *self, PyObject *args) } iResult = PyBuffer_ToContiguous(pb, &pyBuffer, cb, 'C'); PyBuffer_Release(&pyBuffer); - Py_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; result = (iResult == 0) && VMMDLL_UtilFillHexAscii(pb, cb, cbInitialOffset, NULL, &csz) && @@ -658,8 +668,8 @@ VMMPYC_UtilFillHexAscii(PyObject *self, PyObject *args) (sz = (LPSTR)LocalAlloc(0, csz)) && VMMDLL_UtilFillHexAscii(pb, cb, cbInitialOffset, sz, &csz); LocalFree(pb); - Py_END_ALLOW_THREADS - if(!result || !sz) { + Py_END_ALLOW_THREADS; + if(!result || !sz) { LocalFree(sz); return PyErr_Format(PyExc_RuntimeError, "VMMPYC_UtilFillHexAscii: Failed."); } @@ -690,9 +700,9 @@ VMMPYC_VfsRead(PyObject *self, PyObject *args) } pb = LocalAlloc(0, cb); if(!pb) { return PyErr_NoMemory(); } - Py_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; nt = VMMDLL_VfsRead(wszFileName, pb, cb, &cbRead, cbOffset); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS; if(nt != VMMDLL_STATUS_SUCCESS) { LocalFree(pb); return PyErr_Format(PyExc_RuntimeError, "VMMPYC_VfsRead: Failed."); @@ -734,14 +744,124 @@ VMMPYC_VfsWrite(PyObject *self, PyObject *args) } iResult = PyBuffer_ToContiguous(pb, &pyBuffer, cb, 'C'); PyBuffer_Release(&pyBuffer); - Py_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; result = (iResult == 0) && (VMMDLL_STATUS_SUCCESS == VMMDLL_VfsWrite(wszFileName, pb, cb, &cbWritten, cbOffset)); LocalFree(pb); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS; if(!result) { return PyErr_Format(PyExc_RuntimeError, "VMMPYC_VfsWrite: Failed."); } return Py_BuildValue("s", NULL); // None returned on success. } +// (DWORD, STR, STR) -> ULONG64 +static PyObject* +VMMPYC_ProcessGetProcAddress(PyObject *self, PyObject *args) +{ + ULONG64 va; + DWORD dwPID; + LPSTR szModuleName, szProcName; + if(!PyArg_ParseTuple(args, "kss", &dwPID, &szModuleName, &szProcName)) { return NULL; } + Py_BEGIN_ALLOW_THREADS; + va = VMMDLL_ProcessGetProcAddress(dwPID, szModuleName, szProcName); + Py_END_ALLOW_THREADS; + return va ? + PyLong_FromUnsignedLongLong(va) : + PyErr_Format(PyExc_RuntimeError, "VMMPYC_ProcessGetProcAddress: Failed."); +} + +// (DWORD, STR) -> ULONG64 +static PyObject* +VMMPYC_ProcessGetModuleBase(PyObject *self, PyObject *args) +{ + ULONG64 va; + DWORD dwPID; + LPSTR szModuleName; + if(!PyArg_ParseTuple(args, "ks", &dwPID, &szModuleName)) { return NULL; } + Py_BEGIN_ALLOW_THREADS; + va = VMMDLL_ProcessGetModuleBase(dwPID, szModuleName); + Py_END_ALLOW_THREADS; + return va ? + PyLong_FromUnsignedLongLong(va) : + PyErr_Format(PyExc_RuntimeError, "VMMPYC_ProcessGetModuleBase: Failed."); +} + +// (DWORD, STR, STR) -> {...} +static PyObject* +VMMPYC_WinGetThunkInfoEAT(PyObject *self, PyObject *args) +{ + PyObject *pyDict; + BOOL result; + DWORD dwPID; + VMMDLL_WIN_THUNKINFO_EAT oThunkInfoEAT = { 0 }; + LPSTR szModuleName, szExportFunctionName; + if(!PyArg_ParseTuple(args, "kss", &dwPID, &szModuleName, &szExportFunctionName)) { return NULL; } + Py_BEGIN_ALLOW_THREADS; + result = VMMDLL_WinGetThunkInfoEAT(dwPID, szModuleName, szExportFunctionName, &oThunkInfoEAT); + Py_END_ALLOW_THREADS; + if(!result || !oThunkInfoEAT.fValid) { + return PyErr_Format(PyExc_RuntimeError, "VMMPYC_WinGetThunkInfoEAT: Failed."); + } + pyDict = PyDict_New(); + if(pyDict) { + PyDict_SetItemString(pyDict, "vaFunction", PyLong_FromUnsignedLongLong(oThunkInfoEAT.vaFunction)); + PyDict_SetItemString(pyDict, "valueThunk", PyLong_FromUnsignedLong(oThunkInfoEAT.valueThunk)); + PyDict_SetItemString(pyDict, "vaNameFunction", PyLong_FromUnsignedLongLong(oThunkInfoEAT.vaNameFunction)); + PyDict_SetItemString(pyDict, "vaThunk", PyLong_FromUnsignedLongLong(oThunkInfoEAT.vaThunk)); + } + return pyDict; +} + +// (DWORD, STR, STR, STR) -> {...} +static PyObject* +VMMPYC_WinGetThunkInfoIAT(PyObject *self, PyObject *args) +{ + PyObject *pyDict; + 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; } + Py_BEGIN_ALLOW_THREADS; + result = VMMDLL_WinGetThunkInfoIAT(dwPID, szModuleName, szImportModuleName, szImportFunctionName, &oThunkInfoIAT); + Py_END_ALLOW_THREADS; + if(!result || !oThunkInfoIAT.fValid) { + return PyErr_Format(PyExc_RuntimeError, "VMMPYC_WinGetThunkInfoEAT: Failed."); + } + pyDict = PyDict_New(); + if(pyDict) { + PyDict_SetItemString(pyDict, "32", PyBool_FromLong(oThunkInfoIAT.f32 ? 1 : 0)); + PyDict_SetItemString(pyDict, "vaFunction", PyLong_FromUnsignedLongLong(oThunkInfoIAT.vaFunction)); + PyDict_SetItemString(pyDict, "vaNameFunction", PyLong_FromUnsignedLongLong(oThunkInfoIAT.vaNameFunction)); + PyDict_SetItemString(pyDict, "vaNameModule", PyLong_FromUnsignedLongLong(oThunkInfoIAT.vaNameModule)); + PyDict_SetItemString(pyDict, "vaThunk", PyLong_FromUnsignedLongLong(oThunkInfoIAT.vaThunk)); + } + return pyDict; +} + +// (ULONG64, DWORD) -> {b: PBYTE, c: DWORD} +static PyObject* +VMMPYC_WinMemCompression_DecompressPage(PyObject *self, PyObject *args) +{ + PyObject *pyDict; + BOOL result; + DWORD cb, cbCompressed; + ULONG64 va; + BYTE pbDecompressed[0x1000] = { 0 }; + if(!PyArg_ParseTuple(args, "Kk", &va, &cb)) { return NULL; } + Py_BEGIN_ALLOW_THREADS; + result = VMMDLL_WinMemCompression_DecompressPage(va, cb, pbDecompressed, &cbCompressed); + Py_END_ALLOW_THREADS; + if(!result) { + return PyErr_Format(PyExc_RuntimeError, "VMMPYC_WinMemCompression_DecompressPage: Failed."); + } + pyDict = PyDict_New(); + if(pyDict) { + PyDict_SetItemString(pyDict, "c", PyLong_FromUnsignedLong(cbCompressed)); + PyDict_SetItemString(pyDict, "b", PyBytes_FromStringAndSize(pbDecompressed, 0x1000)); + } + return pyDict; +} + + typedef struct tdVMMPYC_VFSLIST { struct tdVMMPYC_VFSLIST *FLink; @@ -788,20 +908,20 @@ VMMPYC_VfsList(PyObject *self, PyObject *args) PVMMPYC_VFSLIST pE = NULL, pE_Next; if(!PyArg_ParseTuple(args, "s", &szPath)) { return NULL; } if(!(pyDict = PyDict_New())) { return PyErr_NoMemory(); } - Py_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; { // char* -> wchar* for(i = 0; i < MAX_PATH - 1; i++) { wszPath[i] = szPath[i]; if(0 == szPath[i]) { break; } } - wszPath[MAX_PATH - 1] = 0; + wszPath[MAX_PATH - 1] = 0; } hFileList.h = &pE; hFileList.pfnAddFile = VMMPYC_VfsList_AddFile; hFileList.pfnAddDirectory = VMMPYC_VfsList_AddDirectory; result = VMMDLL_VfsList(wszPath, &hFileList); pE = *(PVMMPYC_VFSLIST*)hFileList.h; - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS; while(pE) { if((PyDict_Attr = PyDict_New())) { PyDict_SetItemString(PyDict_Attr, "f_isdir", PyBool_FromLong(pE->fIsDir ? 1 : 0)); @@ -825,6 +945,7 @@ VMMPYC_VfsList(PyObject *self, PyObject *args) static PyMethodDef VMMPYC_EmbMethods[] = { {"VMMPYC_Initialize", VMMPYC_Initialize, METH_VARARGS, "Initialize the VMM"}, + {"VMMPYC_Close", VMMPYC_Close, METH_VARARGS, "Try close the VMM"}, {"VMMPYC_ConfigGet", VMMPYC_ConfigGet, METH_VARARGS, "Get a device specific option value."}, {"VMMPYC_ConfigSet", VMMPYC_ConfigSet, METH_VARARGS, "Set a device specific option value."}, {"VMMPYC_MemReadScatter", VMMPYC_MemReadScatter, METH_VARARGS, "Read multiple 4kB page sized and aligned chunks of memory given as an address list."}, @@ -842,9 +963,14 @@ static PyMethodDef VMMPYC_EmbMethods[] = { {"VMMPYC_ProcessGetSections", VMMPYC_ProcessGetSections, METH_VARARGS, "Retrieve the sections for a specific process and module."}, {"VMMPYC_ProcessGetEAT", VMMPYC_ProcessGetEAT, METH_VARARGS, "Retrieve the export address table (EAT) for a specific process and module."}, {"VMMPYC_ProcessGetIAT", VMMPYC_ProcessGetIAT, METH_VARARGS, "Retrieve the import address table (IAT) for a specific process and module."}, + {"VMMPYC_ProcessGetProcAddress", VMMPYC_ProcessGetProcAddress, METH_VARARGS, "Retrieve the proc address of a given module!function."}, + {"VMMPYC_ProcessGetModuleBase", VMMPYC_ProcessGetModuleBase, METH_VARARGS, "Retrieve the module base address given a module."}, + {"VMMPYC_WinGetThunkInfoEAT", VMMPYC_WinGetThunkInfoEAT, METH_VARARGS, "Retrieve information about the export address table (EAT) thunk. (useful for patching)."}, + {"VMMPYC_WinGetThunkInfoIAT", VMMPYC_WinGetThunkInfoIAT, METH_VARARGS, "Retrieve information about the import address table (IAT) thunk. (useful for patching)."}, {"VMMPYC_VfsRead", VMMPYC_VfsRead, METH_VARARGS, "Read from a file in the virtual file system."}, {"VMMPYC_VfsWrite", VMMPYC_VfsWrite, METH_VARARGS, "Write to a file in the virtual file system."}, {"VMMPYC_VfsList", VMMPYC_VfsList, METH_VARARGS, "List files and folder for a specific directory in the Virutal File System."}, + {"VMMPYC_WinMemCompression_DecompressPage", VMMPYC_WinMemCompression_DecompressPage, METH_VARARGS, "Decompress compressed memory in the MemCompression process (if any)."}, {"VMMPYC_UtilFillHexAscii", VMMPYC_UtilFillHexAscii, METH_VARARGS, "Convert a bytes object into a human readable 'memory dump' style type of string."}, {NULL, NULL, 0, NULL} };