diff --git a/.gitignore b/.gitignore index ea12f6e..83c4c1a 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,6 @@ info.db /files/x86/lib /files/x86/vmm_example.exe /vmmsharp/obj +/vmmrust/memprocfs/target +/vmmrust/memprocfs_example/target +/vmmrust/m_example_plugin/target diff --git a/MemProcFS.sln b/MemProcFS.sln index 63dd749..fd4d782 100644 --- a/MemProcFS.sln +++ b/MemProcFS.sln @@ -103,6 +103,39 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution README.md = README.md EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "vmmrust", "vmmrust", "{CA129E44-E6CB-4730-8CE0-B45894B01177}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "m_example_plugin", "m_example_plugin", "{0CA8FB8A-9763-4130-ADBD-C67EFF4897C7}" + ProjectSection(SolutionItems) = preProject + vmmrust\m_example_plugin\Cargo.toml = vmmrust\m_example_plugin\Cargo.toml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "memprocfs", "memprocfs", "{626E4023-93CC-4D9B-A557-905FF349777F}" + ProjectSection(SolutionItems) = preProject + vmmrust\memprocfs\Cargo.toml = vmmrust\memprocfs\Cargo.toml + vmmrust\memprocfs\README.md = vmmrust\memprocfs\README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "memprocfs_example", "memprocfs_example", "{30C76595-D55E-452F-A9C6-EFC23ADC36DF}" + ProjectSection(SolutionItems) = preProject + vmmrust\memprocfs_example\Cargo.toml = vmmrust\memprocfs_example\Cargo.toml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F634AD49-3F87-4AF4-AA2B-6F0F3560C210}" + ProjectSection(SolutionItems) = preProject + vmmrust\memprocfs_example\src\main.rs = vmmrust\memprocfs_example\src\main.rs + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F36BD36B-329E-4A24-8522-FB31442439E6}" + ProjectSection(SolutionItems) = preProject + vmmrust\memprocfs\src\lib_memprocfs.rs = vmmrust\memprocfs\src\lib_memprocfs.rs + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{FDE47FDC-7D70-41DB-A2E9-3CC2D0B16FF0}" + ProjectSection(SolutionItems) = preProject + vmmrust\m_example_plugin\src\lib.rs = vmmrust\m_example_plugin\src\lib.rs + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM64 = Debug|ARM64 @@ -197,6 +230,12 @@ Global {F64725D0-147F-43D1-95AE-FDE1E8C1EDCD} = {1390A2F3-1ECE-4A66-8B60-6FDCB599BE63} {D65E9BD1-4BB8-4418-BC41-82884843D377} = {F64725D0-147F-43D1-95AE-FDE1E8C1EDCD} {A8744524-49F8-4CAA-BD85-7C1EACA1164F} = {F64725D0-147F-43D1-95AE-FDE1E8C1EDCD} + {0CA8FB8A-9763-4130-ADBD-C67EFF4897C7} = {CA129E44-E6CB-4730-8CE0-B45894B01177} + {626E4023-93CC-4D9B-A557-905FF349777F} = {CA129E44-E6CB-4730-8CE0-B45894B01177} + {30C76595-D55E-452F-A9C6-EFC23ADC36DF} = {CA129E44-E6CB-4730-8CE0-B45894B01177} + {F634AD49-3F87-4AF4-AA2B-6F0F3560C210} = {30C76595-D55E-452F-A9C6-EFC23ADC36DF} + {F36BD36B-329E-4A24-8522-FB31442439E6} = {626E4023-93CC-4D9B-A557-905FF349777F} + {FDE47FDC-7D70-41DB-A2E9-3CC2D0B16FF0} = {0CA8FB8A-9763-4130-ADBD-C67EFF4897C7} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {CE6C3AA7-477A-4329-B4E3-5F219ACF4063} diff --git a/includes/lib32/leechcore.lib b/includes/lib32/leechcore.lib index ee94b87..ad4f187 100644 Binary files a/includes/lib32/leechcore.lib and b/includes/lib32/leechcore.lib differ diff --git a/includes/lib32/vmm.lib b/includes/lib32/vmm.lib index 3968e6b..59ea00e 100644 Binary files a/includes/lib32/vmm.lib and b/includes/lib32/vmm.lib differ diff --git a/includes/lib64/leechcore.lib b/includes/lib64/leechcore.lib index 0b746bc..bb929f4 100644 Binary files a/includes/lib64/leechcore.lib and b/includes/lib64/leechcore.lib differ diff --git a/includes/lib64/vmm.lib b/includes/lib64/vmm.lib index b82f7e7..f4dcf22 100644 Binary files a/includes/lib64/vmm.lib and b/includes/lib64/vmm.lib differ diff --git a/includes/libarm64/leechcore.lib b/includes/libarm64/leechcore.lib index c241f37..1881fea 100644 Binary files a/includes/libarm64/leechcore.lib and b/includes/libarm64/leechcore.lib differ diff --git a/includes/libarm64/vmm.lib b/includes/libarm64/vmm.lib index 39abf1c..f4ca272 100644 Binary files a/includes/libarm64/vmm.lib and b/includes/libarm64/vmm.lib differ diff --git a/includes/vmmdll.h b/includes/vmmdll.h index 04959e5..63f2931 100644 --- a/includes/vmmdll.h +++ b/includes/vmmdll.h @@ -7,17 +7,11 @@ // Windows may access both UTF-8 *U and Wide-Char *W versions of functions // while Linux may only access UTF-8 versions. Some functionality may also // be degraded or unavailable on Linux. -// -// v5 API -// ====== -// v5 of the API support multiple concurrent parallel analysis tasks. -// To accomodate this significant changes have been done to the API which is -// largely incompatible (but very similar) to the earlier API versions. // // (c) Ulf Frisk, 2018-2023 // Author: Ulf Frisk, pcileech@frizk.net // -// Header Version: 5.3 +// Header Version: 5.4 // #include "leechcore.h" @@ -485,7 +479,7 @@ BOOL VMMDLL_InitializePlugins(_In_ VMM_HANDLE hVMM); #define VMMDLL_PLUGIN_CONTEXT_MAGIC 0xc0ffee663df9301c #define VMMDLL_PLUGIN_CONTEXT_VERSION 5 #define VMMDLL_PLUGIN_REGINFO_MAGIC 0xc0ffee663df9301d -#define VMMDLL_PLUGIN_REGINFO_VERSION 15 +#define VMMDLL_PLUGIN_REGINFO_VERSION 16 #define VMMDLL_PLUGIN_FORENSIC_JSONDATA_VERSION 0xc0ee0002 #define VMMDLL_PLUGIN_NOTIFY_VERBOSITYCHANGE 0x01 @@ -503,6 +497,8 @@ typedef struct tdVMMDLL_CSV_HANDLE *VMMDLL_CSV_HANDLE; #define VMMDLL_MID_MAIN ((VMMDLL_MODULE_ID)0x80000001) #define VMMDLL_MID_PYTHON ((VMMDLL_MODULE_ID)0x80000002) +#define VMMDLL_MID_DEBUG ((VMMDLL_MODULE_ID)0x80000003) +#define VMMDLL_MID_RUST ((VMMDLL_MODULE_ID)0x80000004) typedef struct tdVMMDLL_PLUGIN_CONTEXT { ULONG64 magic; @@ -554,7 +550,8 @@ typedef struct tdVMMDLL_PLUGIN_REGINFO { VMMDLL_SYSTEM_TP tpSystem; HMODULE hDLL; BOOL(*pfnPluginManager_Register)(_In_ VMM_HANDLE H, struct tdVMMDLL_PLUGIN_REGINFO *pPluginRegInfo); - DWORD _Reserved[32]; + LPSTR uszPathVmmDLL; + DWORD _Reserved[30]; // python plugin information - not for general use struct { BOOL fPythonStandalone; @@ -901,7 +898,7 @@ BOOL VMMDLL_Scatter_PrepareEx(_In_ VMMDLL_SCATTER_HANDLE hS, _In_ QWORD va, _In_ * -- return */ EXPORTED_FUNCTION _Success_(return) -BOOL VMMDLL_Scatter_PrepareWrite(_In_ VMMDLL_SCATTER_HANDLE hS, _In_ QWORD va, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb); +BOOL VMMDLL_Scatter_PrepareWrite(_In_ VMMDLL_SCATTER_HANDLE hS, _In_ QWORD va, _In_reads_(cb) PBYTE pb, _In_ DWORD cb); /* * Retrieve and Write memory previously populated. @@ -1001,6 +998,7 @@ typedef enum tdVMMDLL_PTE_TP { VMMDLL_PTE_TP_DEMANDZERO = 4, VMMDLL_PTE_TP_COMPRESSED = 5, VMMDLL_PTE_TP_PAGEFILE = 6, + VMMDLL_PTE_TP_FILE = 7, } VMMDLL_PTE_TP, *PVMMDLL_PTE_TP; typedef struct tdVMMDLL_MAP_PTEENTRY { @@ -1831,6 +1829,9 @@ BOOL VMMDLL_MemSearch( #define VMMDLL_MAP_PFN_VERSION 1 +#define VMMDLL_PFN_FLAG_NORMAL 0 +#define VMMDLL_PFN_FLAG_EXTENDED 1 + static LPCSTR VMMDLL_PFN_TYPE_TEXT[] = { "Zero", "Free", "Standby", "Modifiy", "ModNoWr", "Bad", "Active", "Transit" }; static LPCSTR VMMDLL_PFN_TYPEEXTENDED_TEXT[] = { "-", "Unused", "ProcPriv", "PageTable", "LargePage", "DriverLock", "Shareable", "File" }; @@ -1919,12 +1920,31 @@ typedef struct tdVMMDLL_MAP_PFN { EXPORTED_FUNCTION _Success_(return) BOOL VMMDLL_Map_GetPfn( _In_ VMM_HANDLE hVMM, - _In_ DWORD pPfns[], + _In_reads_(cPfns) DWORD pPfns[], _In_ DWORD cPfns, _Out_writes_bytes_opt_(*pcbPfnMap) PVMMDLL_MAP_PFN pPfnMap, _Inout_ PDWORD pcbPfnMap ); +/* +* Retrieve PFN information: +* CALLER FREE: VMMDLL_MemFree(*ppPfnMap) +* -- hVMM +* -- pPfns = PFNs to retrieve. +* -- cPfns = number of PFNs to retrieve. +* -- ppPfnMap = ptr to receive result on success. must be free'd with VMMDLL_MemFree(). +* -- flags = optional flags as specified by VMMDLL_PFN_FLAG_* +* -- return = success/fail. +*/ +EXPORTED_FUNCTION _Success_(return) +BOOL VMMDLL_Map_GetPfnEx( + _In_ VMM_HANDLE hVMM, + _In_reads_(cPfns) DWORD pPfns[], + _In_ DWORD cPfns, + _Out_ PVMMDLL_MAP_PFN *ppPfnMap, + _In_ DWORD flags +); + //----------------------------------------------------------------------------- diff --git a/m_vmemd/version.h b/m_vmemd/version.h index 44827e8..2d21fb1 100644 --- a/m_vmemd/version.h +++ b/m_vmemd/version.h @@ -2,9 +2,9 @@ #define STRINGIZE(s) STRINGIZE2(s) #define VERSION_MAJOR 5 -#define VERSION_MINOR 3 -#define VERSION_REVISION 1 -#define VERSION_BUILD 100 +#define VERSION_MINOR 4 +#define VERSION_REVISION 0 +#define VERSION_BUILD 102 #define VER_FILE_DESCRIPTION_STR "MemProcFS : Plugin vmemd" #define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD diff --git a/memprocfs/version.h b/memprocfs/version.h index befaf6e..c43f4d0 100644 --- a/memprocfs/version.h +++ b/memprocfs/version.h @@ -2,9 +2,9 @@ #define STRINGIZE(s) STRINGIZE2(s) #define VERSION_MAJOR 5 -#define VERSION_MINOR 3 -#define VERSION_REVISION 1 -#define VERSION_BUILD 100 +#define VERSION_MINOR 4 +#define VERSION_REVISION 0 +#define VERSION_BUILD 102 #define VER_FILE_DESCRIPTION_STR "MemProcFS" #define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD diff --git a/vmm/Makefile b/vmm/Makefile index 9d203b5..f2fc72d 100644 --- a/vmm/Makefile +++ b/vmm/Makefile @@ -8,34 +8,51 @@ CC=gcc CFLAGS += -std=c11 -I. -I../includes -D LINUX -D _GNU_SOURCE -D SQLITE_THREADSAFE=2 -D SQLITE_CORE -shared -fPIC -l:leechcore.so -fvisibility=hidden -L. -lm -pthread `pkg-config liblz4 openssl --libs --cflags` # DEBUG FLAGS BELOW -# export ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1:detect_invalid_pointer_pairs=2 -# CFLAGS += -g -O0 -Wextra -Wno-unused-parameter -Wno-cast-function-type -# CFLAGS += -fsanitize=address -fsanitize=leak -fno-omit-frame-pointer -fsanitize=undefined -fsanitize=bounds-strict -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow -# CFLAGS += -fsanitize=pointer-compare -fsanitize=pointer-subtract -fanalyzer +#export ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1:detect_invalid_pointer_pairs=2 +#CFLAGS += -g -O0 -Wextra -Wno-unused-parameter -Wno-cast-function-type +#CFLAGS += -fsanitize=address -fsanitize=leak -fno-omit-frame-pointer -fsanitize=undefined -fsanitize=bounds-strict -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow +#CFLAGS += -fsanitize=pointer-compare -fsanitize=pointer-subtract -fanalyzer # DEBUG FLAGS ABOVE CFLAGS += -fPIE -fPIC -pie -fstack-protector -D_FORTIFY_SOURCE=2 -O1 -Wl,-z,noexecstack CFLAGS += -Wall -Wno-format-truncation -Wno-enum-compare -Wno-pointer-sign -Wno-multichar -Wno-unused-variable -Wno-unused-value CFLAGS += -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast LDFLAGS += -Wl,-rpath,'$$ORIGIN' -g -ldl -shared DEPS = vmmdll.h -OBJ = oscompatibility.o charutil.o util.o pe.o vmmdll.o vmmdll_core.o vmmdll_scatter.o \ - vmm.o mm_x64.o mm_x86.o mm_x86pae.o mm_pfn.o mm_vad.o mm_win.o fc.o \ +OBJ = oscompatibility.o charutil.o util.o pe.o vmmdll.o vmmdll_core.o \ + vmmdll_scatter.o vmm.o fc.o \ + mm/mm_x64.o mm/mm_x86.o mm/mm_x86pae.o mm/mm_pfn.o mm/mm_vad.o \ + mm/mm_win.o \ pluginmanager.o pdb.o infodb.o sqlite/sqlite3.o \ ob/ob_cachemap.o ob/ob_compressed.o ob/ob_container.o ob/ob_core.o \ ob/ob_counter.o ob/ob_map.o ob/ob_memfile.o ob/ob_set.o ob/ob_strmap.o \ statistics.o sysquery.o vmmevil.o vmmheap.o vmmlog.o vmmnet.o \ vmmproc.o vmmvm.o vmmwininit.o vmmwin.o vmmwinobj.o vmmwinpool.o \ vmmwinreg.o vmmwinsvc.o vmmwork.o \ - m_vfsfc.o m_vfsproc.o m_vfsroot.o m_conf.o m_winreg.o \ - m_fc_csv.o m_fc_handle.o m_fc_json.o m_fc_module.o m_fc_ntfs.o \ - m_fc_proc.o m_fc_registry.o m_fc_sys.o m_fc_thread.o m_fc_timeline.o \ - m_file_handles_vads.o m_file_modules.o m_findevil.o \ - m_misc_bitlocker.o m_misc_procinfo.o m_misc_web.o m_misc_view.o \ - m_phys2virt.o m_proc_handle.o m_proc_heap.o m_proc_ldrmodules.o \ - m_proc_memmap.o m_proc_minidump.o m_proc_thread.o m_proc_token.o \ - m_search.o m_sys.o m_sys_driver.o m_sys_mem.o m_sys_net.o m_sys_obj.o \ - m_sys_pool.c m_sys_proc.o m_sys_svc.o m_sys_task.o m_sys_user.o \ - m_virt2phys.o m_vm.o + modules/m_vfsroot.o modules/m_vfsproc.o modules/m_vfsfc.o \ + modules/m_conf.o modules/m_vm.o modules/m_winreg.o \ + modules/m_fc_csv.o modules/m_fc_handle.o modules/m_fc_json.o \ + modules/m_fc_module.o modules/m_fc_ntfs.o modules/m_fc_proc.o \ + modules/m_fc_registry.o modules/m_fc_sys.o modules/m_fc_thread.o \ + modules/m_fc_timeline.o \ + modules/m_misc_bitlocker.o modules/m_misc_procinfo.o \ + modules/m_misc_view.o modules/m_misc_web.o \ + modules/m_sys.o modules/m_sys_driver.o modules/m_sys_mem.o \ + modules/m_sys_net.o modules/m_sys_obj.o modules/m_sys_pool.c \ + modules/m_sys_proc.o modules/m_sys_svc.o modules/m_sys_syscall.o \ + modules/m_sys_task.o modules/m_sys_user.o \ + modules/m_findevil.o modules/m_phys2virt.o modules/m_search.o \ + modules/m_virt2phys.o \ + modules/m_proc_file_handles_vads.o modules/m_proc_file_modules.o \ + modules/m_proc_handle.o modules/m_proc_heap.o \ + modules/m_proc_ldrmodules.o modules/m_proc_memmap.o \ + modules/m_proc_minidump.o modules/m_proc_thread.o \ + modules/m_proc_token.o + + + + + + %.o: %.c $(DEPS) $(CC) -c -o $@ $< $(CFLAGS) diff --git a/vmm/fc.c b/vmm/fc.c index 6ed9413..cafda51 100644 --- a/vmm/fc.c +++ b/vmm/fc.c @@ -1039,7 +1039,7 @@ VOID FcInitialize_ThreadProc(_In_ VMM_HANDLE H, _In_ QWORD qwNotUsed) H->fc->db.fSingleThread = FALSE; H->fc->fInitFinish = TRUE; if(H->fAbort) { goto fail; } - PluginManager_Notify(H, VMMDLL_PLUGIN_NOTIFY_FORENSIC_INIT, NULL, 100); + PluginManager_Notify(H, VMMDLL_PLUGIN_NOTIFY_FORENSIC_INIT, NULL, 0); PluginManager_Notify(H, VMMDLL_PLUGIN_NOTIFY_FORENSIC_INIT_COMPLETE, NULL, 0); Statistics_CallEnd(H, STATISTICS_ID_FORENSIC_FcInitialize, tmStart); fResult = TRUE; diff --git a/vmm/fc.h b/vmm/fc.h index adc8ed3..0b62424 100644 --- a/vmm/fc.h +++ b/vmm/fc.h @@ -16,7 +16,7 @@ #define __FC_H__ #include "oscompatibility.h" #include "vmm.h" -#include "mm_pfn.h" +#include "mm/mm_pfn.h" #include "sqlite/sqlite3.h" #define FC_SQL_POOL_CONNECTION_NUM 4 diff --git a/vmm/mm.h b/vmm/mm/mm.h similarity index 99% rename from vmm/mm.h rename to vmm/mm/mm.h index e7eb20e..12d972c 100644 --- a/vmm/mm.h +++ b/vmm/mm/mm.h @@ -5,7 +5,7 @@ // #ifndef __MM_H__ #define __MM_H__ -#include "vmm.h" +#include "../vmm.h" /* * Initialize the X86 32-bit protected mode memory model. diff --git a/vmm/mm_pfn.c b/vmm/mm/mm_pfn.c similarity index 99% rename from vmm/mm_pfn.c rename to vmm/mm/mm_pfn.c index 86ca957..dc07af3 100644 --- a/vmm/mm_pfn.c +++ b/vmm/mm/mm_pfn.c @@ -4,10 +4,10 @@ // (c) Ulf Frisk, 2020-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "vmm.h" + +#include "mm.h" #include "mm_pfn.h" -#include "pdb.h" -#include "util.h" +#include "../pdb.h" typedef struct tdMMPFN_CONTEXT { BOOL fValid; diff --git a/vmm/mm_pfn.h b/vmm/mm/mm_pfn.h similarity index 99% rename from vmm/mm_pfn.h rename to vmm/mm/mm_pfn.h index 1ece3f8..136c36c 100644 --- a/vmm/mm_pfn.h +++ b/vmm/mm/mm_pfn.h @@ -6,7 +6,7 @@ // #ifndef __MM_PFN_H__ #define __MM_PFN_H__ -#include "vmm.h" +#include "../vmm.h" static LPCSTR MMPFN_TYPE_TEXT[] = { "Zero", "Free", "Standby", "Modifiy", "ModNoWr", "Bad", "Active", "Transit" }; static LPCSTR MMPFN_TYPEEXTENDED_TEXT[] = { "-", "Unused", "ProcPriv", "PageTable", "LargePage", "DriverLock", "Shareable", "File" }; diff --git a/vmm/mm_vad.c b/vmm/mm/mm_vad.c similarity index 99% rename from vmm/mm_vad.c rename to vmm/mm/mm_vad.c index ae43920..4fc8cf6 100644 --- a/vmm/mm_vad.c +++ b/vmm/mm/mm_vad.c @@ -3,10 +3,9 @@ // (c) Ulf Frisk, 2019-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "vmm.h" + #include "mm.h" -#include "vmmwindef.h" -#include "util.h" +#include "../util.h" #define MMVAD_POOLTAG_VAD 'Vad ' #define MMVAD_POOLTAG_VADF 'VadF' diff --git a/vmm/mm_win.c b/vmm/mm/mm_win.c similarity index 99% rename from vmm/mm_win.c rename to vmm/mm/mm_win.c index 06cbffe..059dcb3 100644 --- a/vmm/mm_win.c +++ b/vmm/mm/mm_win.c @@ -3,12 +3,10 @@ // (c) Ulf Frisk, 2019-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "vmm.h" + #include "mm.h" -#include "pdb.h" -#include "pe.h" -#include "statistics.h" -#include "util.h" +#include "../pdb.h" +#include "../statistics.h" #define MM_LOOP_PROTECT_ADD(flags) ((flags & ~0x00ff0000) | ((((flags >> 16) & 0xff) + 1) << 16)) #define MM_LOOP_PROTECT_MAX(flags) (((flags >> 16) & 0xff) > 4) @@ -1458,6 +1456,7 @@ BOOL MmWinX64_ReadPaged(_In_ VMM_HANDLE H, _In_ PVMM_PROCESS pProcess, _In_opt_ // MmCa - control area: _CONTROL_AREA // MmCi - image control area. if(MMWINX64_PTE_IS_FILE_BACKED(H, pte)) { + if(ptp && !*ptp) { *ptp = VMM_PTE_TP_FILE; } InterlockedIncrement64(&H->vmm.stat.page.cFailFileMapped); return FALSE; } diff --git a/vmm/mm_x64.c b/vmm/mm/mm_x64.c similarity index 99% rename from vmm/mm_x64.c rename to vmm/mm/mm_x64.c index 8e5520c..bd5d263 100644 --- a/vmm/mm_x64.c +++ b/vmm/mm/mm_x64.c @@ -3,8 +3,8 @@ // (c) Ulf Frisk, 2018-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "vmm.h" -#include "vmmproc.h" + +#include "mm.h" #define MMX64_MEMMAP_DISPLAYBUFFER_LINE_LENGTH 89 #define MMX64_PTE_IS_TRANSITION(H, pte, iPML) ((((pte & 0x0c01) == 0x0800) && (iPML == 1) && (H->vmm.tpSystem == VMM_SYSTEM_WINDOWS_X64)) ? ((pte & 0xffffdffffffff000) | 0x005) : 0) diff --git a/vmm/mm_x86.c b/vmm/mm/mm_x86.c similarity index 99% rename from vmm/mm_x86.c rename to vmm/mm/mm_x86.c index 5a92158..ae777ba 100644 --- a/vmm/mm_x86.c +++ b/vmm/mm/mm_x86.c @@ -3,8 +3,8 @@ // (c) Ulf Frisk, 2018-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "vmm.h" -#include "vmmproc.h" + +#include "mm.h" #define MMX86_MEMMAP_DISPLAYBUFFER_LINE_LENGTH 70 #define MMX86_PTE_IS_TRANSITION(H, pte, iPML) ((((pte & 0x0c01) == 0x0800) && (iPML == 1) && (H->vmm.tpSystem == VMM_SYSTEM_WINDOWS_X86)) ? ((pte & 0xfffff000) | 0x005) : 0) diff --git a/vmm/mm_x86pae.c b/vmm/mm/mm_x86pae.c similarity index 99% rename from vmm/mm_x86pae.c rename to vmm/mm/mm_x86pae.c index e6b26f8..4cf2c5a 100644 --- a/vmm/mm_x86pae.c +++ b/vmm/mm/mm_x86pae.c @@ -3,8 +3,8 @@ // (c) Ulf Frisk, 2018-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "vmm.h" -#include "vmmproc.h" + +#include "mm.h" #define MMX86PAE_MEMMAP_DISPLAYBUFFER_LINE_LENGTH 70 #define MMX86PAE_PTE_IS_TRANSITION(H, pte, iPML) ((((pte & 0x0c01) == 0x0800) && (iPML == 1) && (H->vmm.tpSystem == VMM_SYSTEM_WINDOWS_X86)) ? ((pte & 0xffffdffffffff000) | 0x005) : 0) diff --git a/vmm/m_conf.c b/vmm/modules/m_conf.c similarity index 96% rename from vmm/m_conf.c rename to vmm/modules/m_conf.c index cda2dd9..632922d 100644 --- a/vmm/m_conf.c +++ b/vmm/modules/m_conf.c @@ -4,23 +4,8 @@ // Author: Ulf Frisk, pcileech@frizk.net // -/* -* The m_conf module registers itself with the name 'conf' with the plugin manager. -* -* The module showcase a "root" directory module as well as a stateless module. -* -* The module implements listing of directories as well as read and write. -* Read/Write happens, if allowed, to various configuration and status settings -* related to the VMM and Memory Process File System. -*/ - -#include "pdb.h" -#include "pluginmanager.h" -#include "util.h" -#include "vmm.h" -#include "vmmproc.h" -#include "vmmwinreg.h" -#include "statistics.h" +#include "modules.h" +#include "../vmmwinreg.h" /* * Read : function as specified by the module manager. The module manager will diff --git a/vmm/m_fc_csv.c b/vmm/modules/m_fc_csv.c similarity index 98% rename from vmm/m_fc_csv.c rename to vmm/modules/m_fc_csv.c index a1fe1ba..ae7441e 100644 --- a/vmm/m_fc_csv.c +++ b/vmm/modules/m_fc_csv.c @@ -6,11 +6,7 @@ // Author: Ulf Frisk, pcileech@frizk.net // -#include "fc.h" -#include "vmm.h" -#include "pluginmanager.h" -#include "charutil.h" -#include "util.h" +#include "modules.h" #define MFCCSV_TIMELINE_LINEHEADER "Time,Type,Action,PID,Value32,Value64,Text\n" diff --git a/vmm/m_fc_handle.c b/vmm/modules/m_fc_handle.c similarity index 96% rename from vmm/m_fc_handle.c rename to vmm/modules/m_fc_handle.c index 1841d4e..7c2e717 100644 --- a/vmm/m_fc_handle.c +++ b/vmm/modules/m_fc_handle.c @@ -8,11 +8,8 @@ // Author: Ulf Frisk, pcileech@frizk.net // -#include "fc.h" -#include "vmm.h" -#include "vmmwin.h" -#include "pluginmanager.h" -#include "util.h" +#include "modules.h" +#include "../vmmwin.h" static LPSTR MFCHANDLE_CSV_HANDLE = "PID,Handle,Object,Access,Type,Tag,HandleCount,Device,Description\n"; diff --git a/vmm/m_fc_json.c b/vmm/modules/m_fc_json.c similarity index 97% rename from vmm/m_fc_json.c rename to vmm/modules/m_fc_json.c index e31cc3d..0a1b8c3 100644 --- a/vmm/m_fc_json.c +++ b/vmm/modules/m_fc_json.c @@ -6,13 +6,8 @@ // Author: Ulf Frisk, pcileech@frizk.net // -#include "fc.h" -#include "vmm.h" -#include "pluginmanager.h" -#include "charutil.h" -#include "util.h" -#include "version.h" -#include "resource.h" +#include "modules.h" +#include "../version.h" /* * Read the json version of the timeline info files. diff --git a/vmm/m_fc_module.c b/vmm/modules/m_fc_module.c similarity index 98% rename from vmm/m_fc_module.c rename to vmm/modules/m_fc_module.c index 1e49330..77f971a 100644 --- a/vmm/m_fc_module.c +++ b/vmm/modules/m_fc_module.c @@ -8,12 +8,7 @@ // Author: Ulf Frisk, pcileech@frizk.net // -#include "fc.h" -#include "vmm.h" -#include "pe.h" -#include "pluginmanager.h" -#include "charutil.h" -#include "util.h" +#include "modules.h" static LPSTR MFCMODULE_CSV_MODULES = "PID,Process,Name,Wow64,Size,Start,End,#Imports,#Exports,#Sections,Path,KernelPath,PdbPath,PdbAge,PdbHexGUID,VerCompanyName,VerFileDescription,VerFileVersion,VerInternalName,VerLegalCopyright,VerOriginalFilename,VerProductName,VerProductVersion\n"; static LPSTR MFCMODULE_CSV_UNLOADEDMODULES = "PID,Process,ModuleName,UnloadTime,Wow64,Size,Start,End\n"; diff --git a/vmm/m_fc_ntfs.c b/vmm/modules/m_fc_ntfs.c similarity index 99% rename from vmm/m_fc_ntfs.c rename to vmm/modules/m_fc_ntfs.c index ff4fa3e..945f933 100644 --- a/vmm/m_fc_ntfs.c +++ b/vmm/modules/m_fc_ntfs.c @@ -9,11 +9,7 @@ // Author: Ulf Frisk, pcileech@frizk.net // -#include "fc.h" -#include "vmm.h" -#include "pluginmanager.h" -#include "charutil.h" -#include "util.h" +#include "modules.h" #define M_NTFS_INFO_LINELENGTH_UTF8 98 #define M_NTFS_INFO_LINELENGTH_JSON 98 diff --git a/vmm/m_fc_proc.c b/vmm/modules/m_fc_proc.c similarity index 99% rename from vmm/m_fc_proc.c rename to vmm/modules/m_fc_proc.c index 813ba7c..0e384ba 100644 --- a/vmm/m_fc_proc.c +++ b/vmm/modules/m_fc_proc.c @@ -8,11 +8,8 @@ // Author: Ulf Frisk, pcileech@frizk.net // -#include "fc.h" -#include "vmm.h" -#include "vmmwin.h" -#include "pluginmanager.h" -#include "util.h" +#include "modules.h" +#include "../vmmwin.h" static LPSTR MFCPROC_CSV_PROCESS = "PID,PPID,State,ShortName,Name,IntegrityLevel,User,CreateTime,ExitTime,Wow64,EPROCESS,PEB,PEB32,DTB,UserDTB,UserPath,KernelPath,CommandLine\n"; diff --git a/vmm/m_fc_registry.c b/vmm/modules/m_fc_registry.c similarity index 98% rename from vmm/m_fc_registry.c rename to vmm/modules/m_fc_registry.c index 1ad2b87..9ffbd95 100644 --- a/vmm/m_fc_registry.c +++ b/vmm/modules/m_fc_registry.c @@ -8,13 +8,9 @@ // Author: Ulf Frisk, pcileech@frizk.net // -#include "fc.h" -#include "vmm.h" -#include "vmmwinreg.h" -#include "pluginmanager.h" -#include "charutil.h" -#include "util.h" -#include "version.h" +#include "modules.h" +#include "../version.h" +#include "../vmmwinreg.h" static LPSTR FC_SQL_SCHEMA_REGISTRY = "DROP VIEW IF EXISTS v_registry; " \ diff --git a/vmm/m_fc_sys.c b/vmm/modules/m_fc_sys.c similarity index 93% rename from vmm/m_fc_sys.c rename to vmm/modules/m_fc_sys.c index 6fbd020..157679d 100644 --- a/vmm/m_fc_sys.c +++ b/vmm/modules/m_fc_sys.c @@ -8,11 +8,7 @@ // Author: Ulf Frisk, pcileech@frizk.net // -#include "fc.h" -#include "vmm.h" -#include "vmmwin.h" -#include "pluginmanager.h" -#include "util.h" +#include "modules.h" PVOID MFcSys_FcInitialize(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP) { diff --git a/vmm/m_fc_thread.c b/vmm/modules/m_fc_thread.c similarity index 98% rename from vmm/m_fc_thread.c rename to vmm/modules/m_fc_thread.c index 9f2b658..8e01bdf 100644 --- a/vmm/m_fc_thread.c +++ b/vmm/modules/m_fc_thread.c @@ -8,11 +8,7 @@ // Author: Ulf Frisk, pcileech@frizk.net // -#include "fc.h" -#include "vmm.h" -#include "pluginmanager.h" -#include "util.h" -#include "vmmwindef.h" +#include "modules.h" static LPSTR MFCPROC_CSV_THREAD = "PID,TID,ETHREAD,State,WaitReason,CreateTime,ExitTime,Running,BasePriority,Priority,ExitStatus,StartAddress,IP,SP,TEB,StackBaseUser,StackLimitUser,StackBaseKernel,StackLimitKernel,TrapFrame\n"; diff --git a/vmm/m_fc_timeline.c b/vmm/modules/m_fc_timeline.c similarity index 97% rename from vmm/m_fc_timeline.c rename to vmm/modules/m_fc_timeline.c index 351b8ee..35be1fa 100644 --- a/vmm/m_fc_timeline.c +++ b/vmm/modules/m_fc_timeline.c @@ -6,11 +6,7 @@ // Author: Ulf Frisk, pcileech@frizk.net // -#include "fc.h" -#include "vmm.h" -#include "pluginmanager.h" -#include "charutil.h" -#include "util.h" +#include "modules.h" /* * Read the text version of the timeline info files. diff --git a/vmm/m_findevil.c b/vmm/modules/m_findevil.c similarity index 98% rename from vmm/m_findevil.c rename to vmm/modules/m_findevil.c index d23797d..38f1edf 100644 --- a/vmm/m_findevil.c +++ b/vmm/modules/m_findevil.c @@ -3,12 +3,10 @@ // (c) Ulf Frisk, 2020-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "pluginmanager.h" -#include "charutil.h" -#include "util.h" -#include "mm.h" -#include "vmm.h" -#include "vmmevil.h" + +#include "modules.h" +#include "../mm/mm.h" +#include "../vmmevil.h" #define MFINDEVIL_LINELENGTH_X64 214ULL #define MFINDEVIL_LINEHEADER " # PID Process Type Address Description" diff --git a/vmm/m_misc_bitlocker.c b/vmm/modules/m_misc_bitlocker.c similarity index 99% rename from vmm/m_misc_bitlocker.c rename to vmm/modules/m_misc_bitlocker.c index bb171d5..adc8e4e 100644 --- a/vmm/m_misc_bitlocker.c +++ b/vmm/modules/m_misc_bitlocker.c @@ -3,8 +3,8 @@ // (c) Ulf Frisk, 2022-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "pluginmanager.h" -#include "charutil.h" + +#include "modules.h" LPSTR szMBDE_README = "Bitlocker plugin for MemProcFS: \n" \ diff --git a/vmm/m_misc_procinfo.c b/vmm/modules/m_misc_procinfo.c similarity index 98% rename from vmm/m_misc_procinfo.c rename to vmm/modules/m_misc_procinfo.c index fb92740..a0197d7 100644 --- a/vmm/m_misc_procinfo.c +++ b/vmm/modules/m_misc_procinfo.c @@ -4,10 +4,7 @@ // Author: Ulf Frisk, pcileech@frizk.net // -#include "vmm.h" -#include "statistics.h" -#include "util.h" -#include "mm_pfn.h" +#include "modules.h" typedef struct tdOB_MMISCINFO_CONTEXT { OB ObHdr; diff --git a/vmm/m_misc_view.c b/vmm/modules/m_misc_view.c similarity index 99% rename from vmm/m_misc_view.c rename to vmm/modules/m_misc_view.c index 24ce0a1..feaf2f3 100644 --- a/vmm/m_misc_view.c +++ b/vmm/modules/m_misc_view.c @@ -4,9 +4,7 @@ // Author: Ulf Frisk, pcileech@frizk.net // -#include "vmm.h" -#include "pluginmanager.h" -#include "charutil.h" +#include "modules.h" // forward declarations of VFS implemented functionality in core VMMDLL. BOOL VMMDLL_VfsList_Impl(_In_ VMM_HANDLE H, _In_ LPSTR uszPath, _Inout_ PHANDLE pFileList); diff --git a/vmm/m_misc_web.c b/vmm/modules/m_misc_web.c similarity index 99% rename from vmm/m_misc_web.c rename to vmm/modules/m_misc_web.c index 4ce3479..4d08530 100644 --- a/vmm/m_misc_web.c +++ b/vmm/modules/m_misc_web.c @@ -4,13 +4,9 @@ // Author: Ulf Frisk, pcileech@frizk.net // -#include "fc.h" -#include "vmm.h" -#include "vmmwinobj.h" -#include "pluginmanager.h" -#include "util.h" -#include "charutil.h" -#include "sqlite/sqlite3.h" +#include "modules.h" +#include "../sqlite/sqlite3.h" +#include "../vmmwinobj.h" LPSTR szMWEB_README = "Web plugin for MemProcFS: \n" \ diff --git a/vmm/m_phys2virt.c b/vmm/modules/m_phys2virt.c similarity index 99% rename from vmm/m_phys2virt.c rename to vmm/modules/m_phys2virt.c index 6120d67..39bbba0 100644 --- a/vmm/m_phys2virt.c +++ b/vmm/modules/m_phys2virt.c @@ -3,9 +3,8 @@ // (c) Ulf Frisk, 2019-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "pluginmanager.h" -#include "util.h" -#include "vmm.h" + +#include "modules.h" LPCSTR szMPHYS2VIRT_README = "Information about the phys2virt module \n" \ diff --git a/vmm/m_file_handles_vads.c b/vmm/modules/m_proc_file_handles_vads.c similarity index 94% rename from vmm/m_file_handles_vads.c rename to vmm/modules/m_proc_file_handles_vads.c index 5dae2e6..2683911 100644 --- a/vmm/m_file_handles_vads.c +++ b/vmm/modules/m_proc_file_handles_vads.c @@ -3,12 +3,9 @@ // (c) Ulf Frisk, 2020-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "pluginmanager.h" -#include "util.h" -#include "vmm.h" -#include "vmmdll.h" -#include "vmmwin.h" -#include "vmmwinobj.h" + +#include "modules.h" +#include "../vmmwinobj.h" _Success_(return == 0) NTSTATUS M_FileHandlesVads_Read(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctx, _Out_writes_to_(cb, *pcbRead) PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset, _In_ BOOL fHandles) @@ -72,7 +69,7 @@ BOOL M_FileVads_List(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, _Inout return M_FileHandlesVads_List(H, ctxP, pFileList, FALSE); } -VOID M_FileHandlesVads_Initialize(_In_ VMM_HANDLE H, _Inout_ PVMMDLL_PLUGIN_REGINFO pRI) +VOID M_ProcFileHandlesVads_Initialize(_In_ VMM_HANDLE H, _Inout_ PVMMDLL_PLUGIN_REGINFO pRI) { if((pRI->magic != VMMDLL_PLUGIN_REGINFO_MAGIC) || (pRI->wVersion != VMMDLL_PLUGIN_REGINFO_VERSION)) { return; } if(!((pRI->tpSystem == VMM_SYSTEM_WINDOWS_X64) || (pRI->tpSystem == VMM_SYSTEM_WINDOWS_X86))) { return; } diff --git a/vmm/m_file_modules.c b/vmm/modules/m_proc_file_modules.c similarity index 97% rename from vmm/m_file_modules.c rename to vmm/modules/m_proc_file_modules.c index 29ce78c..5db6fb2 100644 --- a/vmm/m_file_modules.c +++ b/vmm/modules/m_proc_file_modules.c @@ -3,10 +3,8 @@ // (c) Ulf Frisk, 2019-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "pluginmanager.h" -#include "vmm.h" -#include "pe.h" -#include "util.h" + +#include "modules.h" typedef struct tdFILEMODULES_FILENTRY { DWORD cb; @@ -161,7 +159,7 @@ NTSTATUS M_FileModules_Write(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP * -- H * -- pRI */ -VOID M_FileModules_Initialize(_In_ VMM_HANDLE H, _Inout_ PVMMDLL_PLUGIN_REGINFO pRI) +VOID M_ProcFileModules_Initialize(_In_ VMM_HANDLE H, _Inout_ PVMMDLL_PLUGIN_REGINFO pRI) { if((pRI->magic != VMMDLL_PLUGIN_REGINFO_MAGIC) || (pRI->wVersion != VMMDLL_PLUGIN_REGINFO_VERSION)) { return; } if((pRI->tpSystem != VMM_SYSTEM_WINDOWS_X64) && (pRI->tpSystem != VMM_SYSTEM_WINDOWS_X86)) { return; } diff --git a/vmm/m_proc_handle.c b/vmm/modules/m_proc_handle.c similarity index 98% rename from vmm/m_proc_handle.c rename to vmm/modules/m_proc_handle.c index c872818..3be28f9 100644 --- a/vmm/m_proc_handle.c +++ b/vmm/modules/m_proc_handle.c @@ -3,12 +3,10 @@ // (c) Ulf Frisk, 2019-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "pluginmanager.h" -#include "util.h" -#include "vmm.h" -#include "vmmdll.h" -#include "vmmwin.h" -#include "vmmwinobj.h" + +#include "modules.h" +#include "../vmmwin.h" +#include "../vmmwinobj.h" #define MHANDLE_LINELENGTH 222ULL #define MHANDLE_LINEHEADER " # PID Handle Object Address Access Type Description" diff --git a/vmm/m_proc_heap.c b/vmm/modules/m_proc_heap.c similarity index 99% rename from vmm/m_proc_heap.c rename to vmm/modules/m_proc_heap.c index afe174a..db5a74c 100644 --- a/vmm/m_proc_heap.c +++ b/vmm/modules/m_proc_heap.c @@ -3,11 +3,8 @@ // (c) Ulf Frisk, 2022-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "pluginmanager.h" -#include "charutil.h" -#include "util.h" -#include "vmm.h" -#include "vmmdll.h" + +#include "modules.h" LPCSTR szMHEAP_README = "Information about the heap process module \n" \ diff --git a/vmm/m_proc_ldrmodules.c b/vmm/modules/m_proc_ldrmodules.c similarity index 99% rename from vmm/m_proc_ldrmodules.c rename to vmm/modules/m_proc_ldrmodules.c index 9a6c0af..a43f327 100644 --- a/vmm/m_proc_ldrmodules.c +++ b/vmm/modules/m_proc_ldrmodules.c @@ -3,12 +3,8 @@ // (c) Ulf Frisk, 2018-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "pluginmanager.h" -#include "vmm.h" -#include "vmmwin.h" -#include "charutil.h" -#include "util.h" -#include "pe.h" + +#include "modules.h" #define LDRMODULES_CACHE_TP_EAT 1 #define LDRMODULES_CACHE_TP_IAT 2 diff --git a/vmm/m_proc_memmap.c b/vmm/modules/m_proc_memmap.c similarity index 99% rename from vmm/m_proc_memmap.c rename to vmm/modules/m_proc_memmap.c index 85b93d7..f64ea90 100644 --- a/vmm/m_proc_memmap.c +++ b/vmm/modules/m_proc_memmap.c @@ -3,12 +3,9 @@ // (c) Ulf Frisk, 2019-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "pluginmanager.h" -#include "charutil.h" -#include "util.h" -#include "mm.h" -#include "vmm.h" -#include "vmmdll.h" + +#include "modules.h" +#include "../mm/mm.h" #define MEMMAP_PTE_LINELENGTH_X86 112ULL #define MEMMAP_PTE_LINELENGTH_X64 128ULL diff --git a/vmm/m_proc_minidump.c b/vmm/modules/m_proc_minidump.c similarity index 99% rename from vmm/m_proc_minidump.c rename to vmm/modules/m_proc_minidump.c index cebdf63..eb96fca 100644 --- a/vmm/m_proc_minidump.c +++ b/vmm/modules/m_proc_minidump.c @@ -3,16 +3,11 @@ // (c) Ulf Frisk, 2020-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "pluginmanager.h" -#include "pe.h" -#include "charutil.h" -#include "util.h" -#include "vmm.h" -#include "vmmdll.h" -#include "vmmwin.h" -#include "vmmwindef.h" -#include "vmmwinreg.h" -#include "version.h" + +#include "modules.h" +#include "../version.h" +#include "../vmmwinobj.h" +#include "../vmmwinreg.h" #ifdef _WIN32 diff --git a/vmm/m_proc_thread.c b/vmm/modules/m_proc_thread.c similarity index 98% rename from vmm/m_proc_thread.c rename to vmm/modules/m_proc_thread.c index 370d78b..1aac019 100644 --- a/vmm/m_proc_thread.c +++ b/vmm/modules/m_proc_thread.c @@ -3,12 +3,8 @@ // (c) Ulf Frisk, 2019-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "pluginmanager.h" -#include "charutil.h" -#include "util.h" -#include "vmm.h" -#include "vmmdll.h" -#include "vmmwindef.h" + +#include "modules.h" #define MTHREAD_INFOFILE_LENGTH 803ULL #define MTHREAD_LINELENGTH 250ULL diff --git a/vmm/m_proc_token.c b/vmm/modules/m_proc_token.c similarity index 98% rename from vmm/m_proc_token.c rename to vmm/modules/m_proc_token.c index 630d609..5eede7f 100644 --- a/vmm/m_proc_token.c +++ b/vmm/modules/m_proc_token.c @@ -3,14 +3,10 @@ // (c) Ulf Frisk, 2019-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "pluginmanager.h" -#include "util.h" -#include "vmm.h" -#include "vmmdll.h" -#include "vmmwin.h" -#include "vmmwindef.h" -#include "charutil.h" -#include "infodb.h" + +#include "modules.h" +#include "../infodb.h" +#include "../vmmwin.h" #ifdef _WIN32 #include #endif /* _WIN32 */ diff --git a/vmm/m_search.c b/vmm/modules/m_search.c similarity index 99% rename from vmm/m_search.c rename to vmm/modules/m_search.c index e9dd24d..54b69a6 100644 --- a/vmm/m_search.c +++ b/vmm/modules/m_search.c @@ -3,9 +3,8 @@ // (c) Ulf Frisk, 2022-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "vmm.h" -#include "util.h" -#include "pluginmanager.h" + +#include "modules.h" LPCSTR szSEARCH_README = "Information about the search module \n" \ diff --git a/vmm/m_sys.c b/vmm/modules/m_sys.c similarity index 98% rename from vmm/m_sys.c rename to vmm/modules/m_sys.c index d0973e8..26e6cb4 100644 --- a/vmm/m_sys.c +++ b/vmm/modules/m_sys.c @@ -6,12 +6,10 @@ // (c) Ulf Frisk, 2019-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "vmm.h" -#include "vmmwin.h" -#include "vmmwinreg.h" -#include "sysquery.h" -#include "util.h" -#include "charutil.h" + +#include "modules.h" +#include "../sysquery.h" +#include "../vmmwinreg.h" VOID MSys_QueryTimeZone(_In_ VMM_HANDLE H, _Out_writes_(49) LPSTR uszTimeZone, _In_ BOOL fLine) { diff --git a/vmm/m_sys_cert.c b/vmm/modules/m_sys_cert.c similarity index 99% rename from vmm/m_sys_cert.c rename to vmm/modules/m_sys_cert.c index fecc9a6..0806d29 100644 --- a/vmm/m_sys_cert.c +++ b/vmm/modules/m_sys_cert.c @@ -6,12 +6,10 @@ // (c) Ulf Frisk, 2020-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "vmm.h" -#include "charutil.h" -#include "util.h" -#include "infodb.h" -#include "vmmwin.h" -#include "vmmwinreg.h" + +#include "modules.h" +#include "../infodb.h" +#include "../vmmwinreg.h" #define MSYSCERT_LINE_LENGTH 228ULL diff --git a/vmm/m_sys_driver.c b/vmm/modules/m_sys_driver.c similarity index 99% rename from vmm/m_sys_driver.c rename to vmm/modules/m_sys_driver.c index c609f64..3d40ebd 100644 --- a/vmm/m_sys_driver.c +++ b/vmm/modules/m_sys_driver.c @@ -6,13 +6,9 @@ // (c) Ulf Frisk, 2022-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "vmm.h" -#include "fc.h" -#include "charutil.h" -#include "util.h" -#include "pluginmanager.h" -#include "vmmwinobj.h" -#include "vmmwindef.h" + +#include "modules.h" +#include "../vmmwinobj.h" static LPSTR MSYSDRIVER_CSV_DRIVERS = "Name,ObjectAddress,Size,Start,End,ServiceKey,DriverName,DriverPath\n"; static LPSTR MSYSDRIVER_CSV_DEVICES = "Name,ObjectAddress,Depth,AttachedDeviceAddress,DriverName,DriverPath,DriverObjectAddress,ExtraInfo\n"; diff --git a/vmm/m_sys_mem.c b/vmm/modules/m_sys_mem.c similarity index 99% rename from vmm/m_sys_mem.c rename to vmm/modules/m_sys_mem.c index 82c27b7..1b54897 100644 --- a/vmm/m_sys_mem.c +++ b/vmm/modules/m_sys_mem.c @@ -6,9 +6,8 @@ // (c) Ulf Frisk, 2020-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "pluginmanager.h" -#include "mm_pfn.h" -#include "util.h" + +#include "modules.h" #define MSYSMEM_PFNMAP_LINELENGTH 56ULL #define MSYSMEM_PHYSMEMMAP_LINELENGTH 33ULL diff --git a/vmm/m_sys_net.c b/vmm/modules/m_sys_net.c similarity index 99% rename from vmm/m_sys_net.c rename to vmm/modules/m_sys_net.c index 854cae7..81feada 100644 --- a/vmm/m_sys_net.c +++ b/vmm/modules/m_sys_net.c @@ -8,9 +8,8 @@ // (c) Ulf Frisk, 2020-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "vmm.h" -#include "util.h" -#include "fc.h" + +#include "modules.h" LPCSTR szMSYSNET_README = "Information about the sys net module \n" \ diff --git a/vmm/m_sys_obj.c b/vmm/modules/m_sys_obj.c similarity index 98% rename from vmm/m_sys_obj.c rename to vmm/modules/m_sys_obj.c index 712a047..25b76d5 100644 --- a/vmm/m_sys_obj.c +++ b/vmm/modules/m_sys_obj.c @@ -6,12 +6,9 @@ // (c) Ulf Frisk, 2021-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "vmm.h" -#include "fc.h" -#include "charutil.h" -#include "util.h" -#include "pluginmanager.h" -#include "vmmwinobj.h" + +#include "modules.h" +#include "../vmmwinobj.h" #define MSYSOBJ_LINELENGTH 200ULL #define MSYSOBJ_LINEHEADER " # Object Address Type Description" diff --git a/vmm/m_sys_pool.c b/vmm/modules/m_sys_pool.c similarity index 99% rename from vmm/m_sys_pool.c rename to vmm/modules/m_sys_pool.c index c9b2fb4..c0c1d0a 100644 --- a/vmm/m_sys_pool.c +++ b/vmm/modules/m_sys_pool.c @@ -6,9 +6,8 @@ // (c) Ulf Frisk, 2021-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "vmm.h" -#include "util.h" -#include "charutil.h" + +#include "modules.h" LPCSTR szMSYSPOOL_README = "Information about the pool module \n" \ diff --git a/vmm/m_sys_proc.c b/vmm/modules/m_sys_proc.c similarity index 99% rename from vmm/m_sys_proc.c rename to vmm/modules/m_sys_proc.c index 14cbeca..570405f 100644 --- a/vmm/m_sys_proc.c +++ b/vmm/modules/m_sys_proc.c @@ -7,10 +7,9 @@ // (c) Ulf Frisk, 2020-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "vmm.h" -#include "vmmwin.h" -#include "charutil.h" -#include "util.h" + +#include "modules.h" +#include "../vmmwin.h" // ---------------------------------------------------------------------------- // ProcTree functionality below: diff --git a/vmm/m_sys_svc.c b/vmm/modules/m_sys_svc.c similarity index 99% rename from vmm/m_sys_svc.c rename to vmm/modules/m_sys_svc.c index f534756..2c4b627 100644 --- a/vmm/m_sys_svc.c +++ b/vmm/modules/m_sys_svc.c @@ -6,10 +6,8 @@ // (c) Ulf Frisk, 2020-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "vmm.h" -#include "util.h" -#include "fc.h" -#include "pluginmanager.h" + +#include "modules.h" static LPSTR MSYSSVC_CSV_SERVICES = "PID,Ordinal,ServiceName,DisplayName,User,StartType,State,Type1,Type2,ObjectAddress,ImagePath,DriverpathOrCmdline\n"; diff --git a/vmm/m_sys_syscall.c b/vmm/modules/m_sys_syscall.c similarity index 99% rename from vmm/m_sys_syscall.c rename to vmm/modules/m_sys_syscall.c index a51083c..dae7a55 100644 --- a/vmm/m_sys_syscall.c +++ b/vmm/modules/m_sys_syscall.c @@ -6,10 +6,8 @@ // (c) Ulf Frisk, 2020-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "pluginmanager.h" -#include "vmm.h" -#include "pdb.h" -#include "util.h" + +#include "modules.h" typedef struct tdKSERVICE_DESCRIPTOR_TABLE32 { DWORD ServiceTableBase; diff --git a/vmm/m_sys_task.c b/vmm/modules/m_sys_task.c similarity index 99% rename from vmm/m_sys_task.c rename to vmm/modules/m_sys_task.c index 543331c..d2a2fb3 100644 --- a/vmm/m_sys_task.c +++ b/vmm/modules/m_sys_task.c @@ -9,12 +9,9 @@ // (c) Ulf Frisk, 2021-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "vmm.h" -#include "vmmwinreg.h" -#include "charutil.h" -#include "util.h" -#include "fc.h" -#include "pluginmanager.h" + +#include "modules.h" +#include "../vmmwinreg.h" static LPSTR MSYSTASK_CSV_TASKS = "GUID,TaskName,TaskPath,User,TimeMostRecent,CommandLine,Parameters,TimeReg,TimeCreate,TimeLastRun,TimeCompleted\n"; diff --git a/vmm/m_sys_user.c b/vmm/modules/m_sys_user.c similarity index 97% rename from vmm/m_sys_user.c rename to vmm/modules/m_sys_user.c index d04750b..7b3bc69 100644 --- a/vmm/m_sys_user.c +++ b/vmm/modules/m_sys_user.c @@ -5,9 +5,8 @@ // (c) Ulf Frisk, 2022-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "charutil.h" -#include "pluginmanager.h" -#include "util.h" + +#include "modules.h" #define MSYSUSER_LINELENGTH 120ULL #define MSYSUSER_LINEHEADER " # Username SID" diff --git a/vmm/m_vfsfc.c b/vmm/modules/m_vfsfc.c similarity index 99% rename from vmm/m_vfsfc.c rename to vmm/modules/m_vfsfc.c index ba578fd..0ca7f90 100644 --- a/vmm/m_vfsfc.c +++ b/vmm/modules/m_vfsfc.c @@ -4,9 +4,7 @@ // Author: Ulf Frisk, pcileech@frizk.net // -#include "fc.h" -#include "pluginmanager.h" -#include "util.h" +#include "modules.h" LPCSTR szMFC_README = "Information about MemProcFS forensics: \n" \ diff --git a/vmm/m_vfsproc.c b/vmm/modules/m_vfsproc.c similarity index 99% rename from vmm/m_vfsproc.c rename to vmm/modules/m_vfsproc.c index c2c98f9..e3eaada 100644 --- a/vmm/m_vfsproc.c +++ b/vmm/modules/m_vfsproc.c @@ -3,11 +3,10 @@ // (c) Ulf Frisk, 2020-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "pluginmanager.h" -#include "vmmproc.h" -#include "vmmwin.h" -#include "util.h" +#include "modules.h" +#include "../vmmproc.h" +#include "../vmmwin.h" /* * Read process root file. diff --git a/vmm/m_vfsroot.c b/vmm/modules/m_vfsroot.c similarity index 99% rename from vmm/m_vfsroot.c rename to vmm/modules/m_vfsroot.c index 93c09ed..14af25c 100644 --- a/vmm/m_vfsroot.c +++ b/vmm/modules/m_vfsroot.c @@ -6,14 +6,12 @@ // (c) Ulf Frisk, 2020-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "pluginmanager.h" -#include "pe.h" -#include "sysquery.h" -#include "version.h" -#include "vmm.h" -#include "vmmwindef.h" -#include "vmmwininit.h" -#include "vmmwinobj.h" // DEBUG + +#include "modules.h" +#include "../sysquery.h" +#include "../version.h" +#include "../vmmwin.h" +#include "../vmmwininit.h" #define KDBG64_KiProcessorBlock 0x218 #define KDBG64_ContextKPRCB 0x338 diff --git a/vmm/m_virt2phys.c b/vmm/modules/m_virt2phys.c similarity index 99% rename from vmm/m_virt2phys.c rename to vmm/modules/m_virt2phys.c index bbf8809..9d82519 100644 --- a/vmm/m_virt2phys.c +++ b/vmm/modules/m_virt2phys.c @@ -3,9 +3,8 @@ // (c) Ulf Frisk, 2018-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "pluginmanager.h" -#include "util.h" -#include "vmm.h" + +#include "modules.h" LPCSTR szMVIRT2PHYS_README = "Information about the virt2phys module \n" \ diff --git a/vmm/m_vm.c b/vmm/modules/m_vm.c similarity index 98% rename from vmm/m_vm.c rename to vmm/modules/m_vm.c index af05670..38cf3b5 100644 --- a/vmm/m_vm.c +++ b/vmm/modules/m_vm.c @@ -3,10 +3,8 @@ // (c) Ulf Frisk, 2022-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "vmm.h" -#include "charutil.h" -#include "pluginmanager.h" -#include "util.h" + +#include "modules.h" #define MVM_LINELENGTH 127ULL #define MVM_LINEHEADER " # VmMemPID Type Flags ObjectAddress MaxGPA Name OsType OsBuild Mount" diff --git a/vmm/m_winreg.c b/vmm/modules/m_winreg.c similarity index 99% rename from vmm/m_winreg.c rename to vmm/modules/m_winreg.c index fd63a16..7ef4abb 100644 --- a/vmm/m_winreg.c +++ b/vmm/modules/m_winreg.c @@ -3,10 +3,9 @@ // (c) Ulf Frisk, 2019-2023 // Author: Ulf Frisk, pcileech@frizk.net // -#include "vmm.h" -#include "vmmwinreg.h" -#include "charutil.h" -#include "util.h" + +#include "modules.h" +#include "../vmmwinreg.h" #define KEY_INFO_META_SIZE 59ULL #define KEY_META_BUFFER_SIZE 0xA000 diff --git a/vmm/modules/modules.h b/vmm/modules/modules.h new file mode 100644 index 0000000..fde9a48 --- /dev/null +++ b/vmm/modules/modules.h @@ -0,0 +1,21 @@ +// modules.h : common includes used by modules. +// +// (c) Ulf Frisk, 023 +// Author: Ulf Frisk, pcileech@frizk.net +// +#ifndef __MODULES_H__ +#define __MODULES_H__ + +#include "../vmmdll.h" +#include "../vmm.h" + +#include "../fc.h" +#include "../charutil.h" +#include "../pdb.h" +#include "../pe.h" +#include "../pluginmanager.h" +#include "../statistics.h" +#include "../util.h" +#include "../vmmwindef.h" + +#endif /* __MODULES_H__ */ diff --git a/vmm/m_modules.h b/vmm/modules/modules_init.h similarity index 94% rename from vmm/m_modules.h rename to vmm/modules/modules_init.h index 09fba46..8396ba9 100644 --- a/vmm/m_modules.h +++ b/vmm/modules/modules_init.h @@ -1,11 +1,13 @@ -// m_modules.h : definitions related to initialization of built-in modules. +// modules_init.h : definitions related to initialization of built-in modules. // // (c) Ulf Frisk, 2018-2023 // Author: Ulf Frisk, pcileech@frizk.net // #ifndef __M_MODULES_H__ #define __M_MODULES_H__ -#include "vmmdll.h" + +#include "../vmmdll.h" + /* * Initialization function for the build-in virtual file system root folder module. @@ -66,8 +68,8 @@ VOID M_FcThread_Initialize(_In_ VMM_HANDLE H, _Inout_ PVMMDLL_PLUGIN_REGINFO pPl /* * Initialization functions for PROCESS related modules. */ -VOID M_FileHandlesVads_Initialize(_In_ VMM_HANDLE H, _Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); -VOID M_FileModules_Initialize(_In_ VMM_HANDLE H, _Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); +VOID M_ProcFileHandlesVads_Initialize(_In_ VMM_HANDLE H, _Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); +VOID M_ProcFileModules_Initialize(_In_ VMM_HANDLE H, _Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); VOID M_ProcHandle_Initialize(_In_ VMM_HANDLE H, _Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); VOID M_ProcHeap_Initialize(_In_ VMM_HANDLE H, _Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); VOID M_ProcLdrModules_Initialize(_In_ VMM_HANDLE H, _Inout_ PVMMDLL_PLUGIN_REGINFO pPluginRegInfo); @@ -83,8 +85,8 @@ VOID(*g_pfnModulesAllInternal[])(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_REGINFO M_VfsProc_Initialize, M_VfsFc_Initialize, // per-process modules - M_FileHandlesVads_Initialize, - M_FileModules_Initialize, + M_ProcFileHandlesVads_Initialize, + M_ProcFileModules_Initialize, M_FindEvil_Initialize, M_Phys2Virt_Initialize, M_ProcHandle_Initialize, @@ -110,6 +112,7 @@ VOID(*g_pfnModulesAllInternal[])(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_REGINFO M_SysPool_Initialize, M_SysProc_Initialize, M_SysSvc_Initialize, + M_SysSyscall_Initialize, M_SysTask_Initialize, M_SysUser_Initialize, M_WinReg_Initialize, @@ -128,7 +131,6 @@ VOID(*g_pfnModulesAllInternal[])(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_REGINFO #ifdef _WIN32 // windows-only global modules M_SysCert_Initialize, // req: winapi - M_SysSyscall_Initialize, // req: full symbols #endif /* _WIN32 */ }; diff --git a/vmm/ob/ob.h b/vmm/ob/ob.h index cb2d2b4..4b1ef28 100644 --- a/vmm/ob/ob.h +++ b/vmm/ob/ob.h @@ -16,6 +16,7 @@ typedef unsigned __int64 QWORD, *PQWORD; // OB_DEBUG is not working (as currently coded) with arm32 due to alignment issues. #if _WIN32 || _WIN64 || __i386__ || __amd64__ || __aarch64__ #define OB_DEBUG +//#define OB_DEBUG_MEMZERO #endif /* _WIN32 || _WIN64 || __i386__ || __amd64__ || __aarch64__ */ #define OB_HEADER_MAGIC 0x0c0efefe typedef struct tdVMM_HANDLE *VMM_HANDLE; diff --git a/vmm/ob/ob_core.c b/vmm/ob/ob_core.c index 2a437df..90a133a 100644 --- a/vmm/ob/ob_core.c +++ b/vmm/ob/ob_core.c @@ -104,6 +104,9 @@ PVOID Ob_XDECREF(_In_opt_ PVOID pObIn) if(pOb->_pfnRef_0) { pOb->_pfnRef_0(pOb); } pOb->_magic1 = 0; pOb->_magic2 = 0; +#ifdef OB_DEBUG_MEMZERO + ZeroMemory(pOb, sizeof(OB) + pOb->cbData); +#endif /* OB_DEBUG_MEMZERO */ LocalFree(pOb); } else if((c == 1) && pOb->_pfnRef_1) { pOb->_pfnRef_1(pOb); diff --git a/vmm/pluginmanager.c b/vmm/pluginmanager.c index 39bd920..d848a5f 100644 --- a/vmm/pluginmanager.c +++ b/vmm/pluginmanager.c @@ -10,8 +10,8 @@ #include "vmm.h" #include "vmmdll.h" #include "vmmlog.h" -#include "m_modules.h" #include "fc.h" +#include "modules/modules_init.h" // // This file contains functionality related to keeping track of plugins, both @@ -667,7 +667,7 @@ BOOL PluginManager_Register(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_REGINFO pRegI pModule = (PPLUGIN_ENTRY)LocalAlloc(LMEM_ZEROINIT, sizeof(PLUGIN_ENTRY)); if(!pModule) { return FALSE; } pModule->hDLL = pRegInfo->hDLL; - pModule->MID = InterlockedIncrement(&H->vmm.PluginManager.dwNextMID); + pModule->MID = InterlockedIncrement(&H->vmm.PluginManager.NextMID); strncpy_s(pModule->uszName, 32, uszPluginName, _TRUNCATE); pModule->dwNameHash = CharUtil_HashNameFsU(pModule->uszName, TRUE); pModule->fRootModule = pRegInfo->reg_info.fRootModule; @@ -771,6 +771,7 @@ VOID PluginManager_Initialize_RegInfoInit(_In_ VMM_HANDLE H, _Out_ PVMMDLL_PLUGI pRI->wVersion = VMMDLL_PLUGIN_REGINFO_VERSION; pRI->wSize = sizeof(VMMDLL_PLUGIN_REGINFO); pRI->hDLL = hDLL; + pRI->uszPathVmmDLL = H->cfg.szPathLibraryVmm; pRI->tpMemoryModel = (VMMDLL_MEMORYMODEL_TP)H->vmm.tpMemoryModel; pRI->tpSystem = (VMMDLL_SYSTEM_TP)H->vmm.tpSystem; pRI->pfnPluginManager_Register = PluginManager_Register; diff --git a/vmm/statistics.c b/vmm/statistics.c index e5cfad6..5a5bffd 100644 --- a/vmm/statistics.c +++ b/vmm/statistics.c @@ -142,26 +142,26 @@ BOOL Statistics_CallToString(_In_ VMM_HANDLE H, _Out_opt_ LPSTR *psz, _Out_ PDWO /* * Start a call statistics logging session. * -- H -* -- dwMID = module ID (MID) +* -- MID = module ID (MID) * -- dwLogLevel = log level as defined by LOGLEVEL_* * -- pProcess * -- ps * -- uszText */ -VOID VmmStatisticsLogStart(_In_ VMM_HANDLE H, _In_ DWORD dwMID, _In_ VMMLOG_LEVEL dwLogLevel, _In_opt_ PVMM_PROCESS pProcess, _Out_ PVMMSTATISTICS_LOG ps, _In_ LPSTR uszText) +VOID VmmStatisticsLogStart(_In_ VMM_HANDLE H, _In_ VMM_MODULE_ID MID, _In_ VMMLOG_LEVEL dwLogLevel, _In_opt_ PVMM_PROCESS pProcess, _Out_ PVMMSTATISTICS_LOG ps, _In_ LPSTR uszText) { - ps->f = VmmLogIsActive(H, dwMID, dwLogLevel); + ps->f = VmmLogIsActive(H, MID, dwLogLevel); if(H->fAbort || !ps->f) { return; } ps->dwPID = pProcess ? pProcess->dwPID : 0; - ps->dwMID = dwMID; + ps->MID = MID; ps->dwLogLevel = dwLogLevel; ps->v[0] = GetTickCount64(); LcGetOption(H->hLC, LC_OPT_CORE_STATISTICS_CALL_COUNT | LC_STATISTICS_ID_READSCATTER, &ps->v[1]); ps->v[2] = H->vmm.stat.cPhysReadSuccess; if(ps->dwPID) { - VmmLog(H, ps->dwMID, ps->dwLogLevel, "%s START: [pid=%i]", uszText, ps->dwPID); + VmmLog(H, ps->MID, ps->dwLogLevel, "%s START: [pid=%i]", uszText, ps->dwPID); } else { - VmmLog(H, ps->dwMID, ps->dwLogLevel, "%s START:", uszText); + VmmLog(H, ps->MID, ps->dwLogLevel, "%s START:", uszText); } } @@ -179,8 +179,8 @@ VOID VmmStatisticsLogEnd(_In_ VMM_HANDLE H, _In_ PVMMSTATISTICS_LOG ps, _In_ LPS LcGetOption(H->hLC, LC_OPT_CORE_STATISTICS_CALL_COUNT | LC_STATISTICS_ID_READSCATTER, &v[1]); v[2] = H->vmm.stat.cPhysReadSuccess; if(ps->dwPID) { - VmmLog(H, ps->dwMID, ps->dwLogLevel, "%s END: [pid=%i time=%llims scatter=0x%llx pages=0x%llx]", uszText, ps->dwPID, (v[0] - ps->v[0]), (v[1] - ps->v[1]), (v[2] - ps->v[2])); + VmmLog(H, ps->MID, ps->dwLogLevel, "%s END: [pid=%i time=%llims scatter=0x%llx pages=0x%llx]", uszText, ps->dwPID, (v[0] - ps->v[0]), (v[1] - ps->v[1]), (v[2] - ps->v[2])); } else { - VmmLog(H, ps->dwMID, ps->dwLogLevel, "%s END: [time=%llims scatter=0x%llx pages=0x%llx]", uszText, (v[0] - ps->v[0]), (v[1] - ps->v[1]), (v[2] - ps->v[2])); + VmmLog(H, ps->MID, ps->dwLogLevel, "%s END: [time=%llims scatter=0x%llx pages=0x%llx]", uszText, (v[0] - ps->v[0]), (v[1] - ps->v[1]), (v[2] - ps->v[2])); } } diff --git a/vmm/statistics.h b/vmm/statistics.h index 80af7c7..ffe39de 100644 --- a/vmm/statistics.h +++ b/vmm/statistics.h @@ -62,6 +62,7 @@ typedef enum tdSTATISTICS_ID { STATISTICS_ID_VMMDLL_Map_GetVM, STATISTICS_ID_VMMDLL_Map_GetServices, STATISTICS_ID_VMMDLL_Map_GetPfn, + STATISTICS_ID_VMMDLL_Map_GetPfnEx, STATISTICS_ID_VMMDLL_ProcessGetDirectories, STATISTICS_ID_VMMDLL_ProcessGetSections, STATISTICS_ID_VMMDLL_ProcessGetProcAddress, @@ -144,6 +145,7 @@ static LPCSTR STATISTICS_ID_STR[STATISTICS_ID_MAX] = { [STATISTICS_ID_VMMDLL_Map_GetVM] = "MMDLL_Map_GetVM", [STATISTICS_ID_VMMDLL_Map_GetServices] = "VMMDLL_Map_GetServices", [STATISTICS_ID_VMMDLL_Map_GetPfn] = "VMMDLL_Map_GetPfn", + [STATISTICS_ID_VMMDLL_Map_GetPfnEx] = "VMMDLL_Map_GetPfnEx", [STATISTICS_ID_VMMDLL_ProcessGetDirectories] = "VMMDLL_ProcessGetDirectories", [STATISTICS_ID_VMMDLL_ProcessGetSections] = "VMMDLL_ProcessGetSections", [STATISTICS_ID_VMMDLL_ProcessGetProcAddress] = "VMMDLL_ProcessGetProcAddress", @@ -197,7 +199,7 @@ BOOL Statistics_CallToString(_In_ VMM_HANDLE H, _Out_opt_ LPSTR *psz, _Out_ PDWO typedef struct tdVMMSTATISTICS_LOG { BOOL f; DWORD dwPID; - DWORD dwMID; + DWORD MID; VMMLOG_LEVEL dwLogLevel; QWORD v[3]; } VMMSTATISTICS_LOG, *PVMMSTATISTICS_LOG; @@ -205,13 +207,13 @@ typedef struct tdVMMSTATISTICS_LOG { /* * Start a call statistics logging session. * -- H -* -- dwMID = module ID (MID) +* -- MID = module ID (MID) * -- dwLogLevel = log level as defined by LOGLEVEL_* * -- pProcess * -- pLogStatistics * -- uszText */ -VOID VmmStatisticsLogStart(_In_ VMM_HANDLE H, _In_ DWORD dwMID, _In_ VMMLOG_LEVEL dwLogLevel, _In_opt_ PVMM_PROCESS pProcess, _Out_ PVMMSTATISTICS_LOG pStatisticsLog, _In_ LPSTR uszText); +VOID VmmStatisticsLogStart(_In_ VMM_HANDLE H, _In_ VMM_MODULE_ID MID, _In_ VMMLOG_LEVEL dwLogLevel, _In_opt_ PVMM_PROCESS pProcess, _Out_ PVMMSTATISTICS_LOG pStatisticsLog, _In_ LPSTR uszText); /* * End a statistics logging session. diff --git a/vmm/version.h b/vmm/version.h index 63b7300..e0d5be8 100644 --- a/vmm/version.h +++ b/vmm/version.h @@ -2,9 +2,9 @@ #define STRINGIZE(s) STRINGIZE2(s) #define VERSION_MAJOR 5 -#define VERSION_MINOR 3 -#define VERSION_REVISION 1 -#define VERSION_BUILD 100 +#define VERSION_MINOR 4 +#define VERSION_REVISION 0 +#define VERSION_BUILD 102 #define VER_FILE_DESCRIPTION_STR "MemProcFS : Core" #define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD diff --git a/vmm/vmm.c b/vmm/vmm.c index dc54416..cffdd96 100644 --- a/vmm/vmm.c +++ b/vmm/vmm.c @@ -5,8 +5,8 @@ // #include "vmm.h" -#include "mm.h" -#include "mm_pfn.h" +#include "mm/mm.h" +#include "mm/mm_pfn.h" #include "pdb.h" #include "vmmheap.h" #include "vmmproc.h" diff --git a/vmm/vmm.h b/vmm/vmm.h index 09c04f9..c354c4c 100644 --- a/vmm/vmm.h +++ b/vmm/vmm.h @@ -147,6 +147,7 @@ typedef enum tdVMM_PTE_TP { VMM_PTE_TP_DEMANDZERO = 4, VMM_PTE_TP_COMPRESSED = 5, VMM_PTE_TP_PAGEFILE = 6, + VMM_PTE_TP_FILE = 7, } VMM_PTE_TP, *PVMM_PTE_TP; // OBJECT TYPE table exists on Win7+ It's initialized on first use and it will @@ -731,6 +732,8 @@ typedef struct tdVMM_MAP_SERVICEENTRY { QWORD _Reserved; } VMM_MAP_SERVICEENTRY, *PVMM_MAP_SERVICEENTRY; +typedef DWORD VMM_MODULE_ID; + typedef enum tdVMM_EVIL_TP { // EVIL types - sorted by "evilness" VMM_EVIL_TP_PE_NA, // _NA VMM_EVIL_TP_PE_INJECTED, // MODULE @@ -1261,6 +1264,7 @@ typedef struct tdVMMCONFIG { CHAR szMemMapStr[2048]; CHAR szLogFile[MAX_PATH]; CHAR szLogLevel[MAX_PATH]; + CHAR szPathLibraryVmm[MAX_PATH]; } VMMCONFIG, *PVMMCONFIG; typedef struct tdVMMCONFIG_PDB { @@ -1601,9 +1605,9 @@ typedef struct tdVMM_CONTEXT { PVOID FLinkForensic; PVOID Root; PVOID Proc; - DWORD dwNextMID; DWORD cIngestPhysmem; DWORD cIngestVirtmem; + VMM_MODULE_ID NextMID; } PluginManager; CRITICAL_SECTION LockUpdateMap; // lock for global maps - such as MapUser CRITICAL_SECTION LockUpdateModule; // lock for internal modules diff --git a/vmm/vmm.vcxproj b/vmm/vmm.vcxproj index 3a9b3ac..bc6d235 100644 --- a/vmm/vmm.vcxproj +++ b/vmm/vmm.vcxproj @@ -327,9 +327,10 @@ - - - + + + + @@ -344,6 +345,7 @@ + @@ -363,56 +365,57 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -441,7 +444,6 @@ - diff --git a/vmm/vmm.vcxproj.filters b/vmm/vmm.vcxproj.filters index 83d9bd1..9da2631 100644 --- a/vmm/vmm.vcxproj.filters +++ b/vmm/vmm.vcxproj.filters @@ -37,6 +37,12 @@ {36acfda3-7a80-4829-affe-6ab4d6ef5c9f} + + {e6b89b0f-2ea4-4de1-b26d-49376653099f} + + + {205ba541-835d-4dc2-a488-6113fe0438d1} + @@ -81,21 +87,12 @@ Header Files - - Header Files - - - Header Files - Header Files Header Files - - Header Files - Header Files\includes @@ -147,6 +144,21 @@ Header Files\includes + + Header Files\mm + + + Header Files\mm + + + Header Files\modules + + + Header Files\modules + + + Header Files + @@ -179,63 +191,18 @@ Source Files - - Source Files\modules - - - Source Files\modules - - - Source Files\modules - Source Files Source Files - - Source Files\mm - - - Source Files\mm - - - Source Files\mm - - - Source Files\mm - - - Source Files\mm - - - Source Files\modules - - - Source Files\modules - - - Source Files\modules - - - Source Files\modules - Source Files - - Source Files\mm - Source Files - - Source Files\modules - - - Source Files\modules - Source Files @@ -254,24 +221,9 @@ Source Files\ob - - Source Files\modules - - - Source Files\modules - - - Source Files\modules - Source Files - - Source Files\modules - - - Source Files\modules - Source Files\ob @@ -281,69 +233,24 @@ Source Files - - Source Files\modules - - - Source Files\modules - - - Source Files\modules - - - Source Files\modules - - - Source Files\modules - - - Source Files\modules - - - Source Files\modules - - - Source Files\modules - - - Source Files\modules - Source Files\ob - - Source Files\modules - Source Files\ob - - Source Files\modules - - - Source Files\modules - - - Source Files\modules - Source Files Source Files - - Source Files\modules - Source Files Source Files - - Source Files\modules - Source Files\ob @@ -353,33 +260,6 @@ Source Files - - Source Files\modules - - - Source Files\modules - - - Source Files\modules - - - Source Files\modules - - - Source Files\modules - - - Source Files\modules - - - Source Files\modules - - - Source Files\modules - - - Source Files\modules - Source Files @@ -389,28 +269,160 @@ Source Files - - Source Files\modules - - - Source Files\modules - - - Source Files\modules - - - Source Files\modules - - - Source Files\modules - Source Files - + + Source Files\mm + + + Source Files\mm + + + Source Files\mm + + + Source Files\mm + + + Source Files\mm + + + Source Files\mm + + Source Files\modules - + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + + Source Files\modules + + Source Files\modules diff --git a/vmm/vmmdll.c b/vmm/vmmdll.c index faa0763..9abbd35 100644 --- a/vmm/vmmdll.c +++ b/vmm/vmmdll.c @@ -21,7 +21,7 @@ #include "vmmwinobj.h" #include "vmmwinreg.h" #include "vmmvm.h" -#include "mm_pfn.h" +#include "mm/mm_pfn.h" // tags for external allocations: #define OB_TAG_API_MAP_EAT 'EAT ' @@ -1874,7 +1874,7 @@ _Success_(return) BOOL VMMDLL_Map_GetServicesW(_In_ VMM_HANDLE H, _Out_ PVMMDLL_ } _Success_(return) -BOOL VMMDLL_Map_GetPfn_Impl(_In_ VMM_HANDLE H, _In_ DWORD pPfns[], _In_ DWORD cPfns, _Out_writes_bytes_opt_(*pcbMapDst) PVMMDLL_MAP_PFN pMapDst, _Inout_ PDWORD pcbMapDst) +BOOL VMMDLL_Map_GetPfn_Impl(_In_ VMM_HANDLE H, _In_reads_(cPfns) DWORD pPfns[], _In_ DWORD cPfns, _Out_writes_bytes_opt_(*pcbMapDst) PVMMDLL_MAP_PFN pMapDst, _Inout_ PDWORD pcbMapDst) { BOOL fResult = FALSE; POB_SET psObPfns = NULL; @@ -1905,13 +1905,50 @@ fail: } _Success_(return) -BOOL VMMDLL_Map_GetPfn(_In_ VMM_HANDLE H, _In_ DWORD pPfns[], _In_ DWORD cPfns, _Out_writes_bytes_opt_(*pcbPfnMap) PVMMDLL_MAP_PFN pPfnMap, _Inout_ PDWORD pcbPfnMap) +BOOL VMMDLL_Map_GetPfnEx_Impl(_In_ VMM_HANDLE H, _In_reads_(cPfns) DWORD pPfns[], _In_ DWORD cPfns, _Out_ PVMMDLL_MAP_PFN *ppMapDst, _In_ DWORD flags) +{ + BOOL fResult = FALSE; + POB_SET psObPfns = NULL; + PMMPFNOB_MAP pObMapSrc = NULL; + PVMMDLL_MAP_PFN pMapDst = NULL; + DWORD i, cbDst = 0, cbDstData; + BOOL fExtended = flags & VMMDLL_PFN_FLAG_EXTENDED; + *ppMapDst = NULL; + cbDstData = cPfns * sizeof(VMMDLL_MAP_PFNENTRY); + cbDst = sizeof(VMMDLL_MAP_PFN) + cbDstData; + if(!(pMapDst = VmmDllCore_MemAllocExternal(H, OB_TAG_API_MAP_SERVICES, cbDst, sizeof(VMMDLL_MAP_PFN)))) { goto fail; } // VMMDLL_MemFree() + if(!(psObPfns = ObSet_New(H))) { goto fail; } + for(i = 0; i < cPfns; i++) { + ObSet_Push(psObPfns, pPfns[i]); + } + if(!MmPfn_Map_GetPfnScatter(H, psObPfns, &pObMapSrc, fExtended)) { goto fail; } + ZeroMemory(pMapDst, cbDst); + pMapDst->dwVersion = VMMDLL_MAP_PFN_VERSION; + pMapDst->cMap = pObMapSrc->cMap; + memcpy(pMapDst->pMap, pObMapSrc->pMap, pMapDst->cMap * sizeof(VMMDLL_MAP_PFNENTRY)); + *ppMapDst = pMapDst; +fail: + if(pMapDst && !*ppMapDst) { VMMDLL_MemFree(pMapDst); pMapDst = NULL; } + Ob_DECREF(psObPfns); + Ob_DECREF(pObMapSrc); + return *ppMapDst ? TRUE : FALSE; +} + +_Success_(return) +BOOL VMMDLL_Map_GetPfn(_In_ VMM_HANDLE H, _In_reads_(cPfns) DWORD pPfns[], _In_ DWORD cPfns, _Out_writes_bytes_opt_(*pcbPfnMap) PVMMDLL_MAP_PFN pPfnMap, _Inout_ PDWORD pcbPfnMap) { CALL_IMPLEMENTATION_VMM(H, STATISTICS_ID_VMMDLL_Map_GetPfn, VMMDLL_Map_GetPfn_Impl(H, pPfns, cPfns, pPfnMap, pcbPfnMap)) } +BOOL VMMDLL_Map_GetPfnEx(_In_ VMM_HANDLE H, _In_reads_(cPfns) DWORD pPfns[], _In_ DWORD cPfns, _Out_ PVMMDLL_MAP_PFN *ppPfnMap, _In_ DWORD flags) +{ + CALL_IMPLEMENTATION_VMM(H, + STATISTICS_ID_VMMDLL_Map_GetPfnEx, + VMMDLL_Map_GetPfnEx_Impl(H, pPfns, cPfns, ppPfnMap, flags)) +} + _Success_(return) BOOL VMMDLL_PidList_Impl(_In_ VMM_HANDLE H, _Out_writes_opt_(*pcPIDs) PDWORD pPIDs, _Inout_ PSIZE_T pcPIDs) { diff --git a/vmm/vmmdll.h b/vmm/vmmdll.h index 04959e5..63f2931 100644 --- a/vmm/vmmdll.h +++ b/vmm/vmmdll.h @@ -7,17 +7,11 @@ // Windows may access both UTF-8 *U and Wide-Char *W versions of functions // while Linux may only access UTF-8 versions. Some functionality may also // be degraded or unavailable on Linux. -// -// v5 API -// ====== -// v5 of the API support multiple concurrent parallel analysis tasks. -// To accomodate this significant changes have been done to the API which is -// largely incompatible (but very similar) to the earlier API versions. // // (c) Ulf Frisk, 2018-2023 // Author: Ulf Frisk, pcileech@frizk.net // -// Header Version: 5.3 +// Header Version: 5.4 // #include "leechcore.h" @@ -485,7 +479,7 @@ BOOL VMMDLL_InitializePlugins(_In_ VMM_HANDLE hVMM); #define VMMDLL_PLUGIN_CONTEXT_MAGIC 0xc0ffee663df9301c #define VMMDLL_PLUGIN_CONTEXT_VERSION 5 #define VMMDLL_PLUGIN_REGINFO_MAGIC 0xc0ffee663df9301d -#define VMMDLL_PLUGIN_REGINFO_VERSION 15 +#define VMMDLL_PLUGIN_REGINFO_VERSION 16 #define VMMDLL_PLUGIN_FORENSIC_JSONDATA_VERSION 0xc0ee0002 #define VMMDLL_PLUGIN_NOTIFY_VERBOSITYCHANGE 0x01 @@ -503,6 +497,8 @@ typedef struct tdVMMDLL_CSV_HANDLE *VMMDLL_CSV_HANDLE; #define VMMDLL_MID_MAIN ((VMMDLL_MODULE_ID)0x80000001) #define VMMDLL_MID_PYTHON ((VMMDLL_MODULE_ID)0x80000002) +#define VMMDLL_MID_DEBUG ((VMMDLL_MODULE_ID)0x80000003) +#define VMMDLL_MID_RUST ((VMMDLL_MODULE_ID)0x80000004) typedef struct tdVMMDLL_PLUGIN_CONTEXT { ULONG64 magic; @@ -554,7 +550,8 @@ typedef struct tdVMMDLL_PLUGIN_REGINFO { VMMDLL_SYSTEM_TP tpSystem; HMODULE hDLL; BOOL(*pfnPluginManager_Register)(_In_ VMM_HANDLE H, struct tdVMMDLL_PLUGIN_REGINFO *pPluginRegInfo); - DWORD _Reserved[32]; + LPSTR uszPathVmmDLL; + DWORD _Reserved[30]; // python plugin information - not for general use struct { BOOL fPythonStandalone; @@ -901,7 +898,7 @@ BOOL VMMDLL_Scatter_PrepareEx(_In_ VMMDLL_SCATTER_HANDLE hS, _In_ QWORD va, _In_ * -- return */ EXPORTED_FUNCTION _Success_(return) -BOOL VMMDLL_Scatter_PrepareWrite(_In_ VMMDLL_SCATTER_HANDLE hS, _In_ QWORD va, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb); +BOOL VMMDLL_Scatter_PrepareWrite(_In_ VMMDLL_SCATTER_HANDLE hS, _In_ QWORD va, _In_reads_(cb) PBYTE pb, _In_ DWORD cb); /* * Retrieve and Write memory previously populated. @@ -1001,6 +998,7 @@ typedef enum tdVMMDLL_PTE_TP { VMMDLL_PTE_TP_DEMANDZERO = 4, VMMDLL_PTE_TP_COMPRESSED = 5, VMMDLL_PTE_TP_PAGEFILE = 6, + VMMDLL_PTE_TP_FILE = 7, } VMMDLL_PTE_TP, *PVMMDLL_PTE_TP; typedef struct tdVMMDLL_MAP_PTEENTRY { @@ -1831,6 +1829,9 @@ BOOL VMMDLL_MemSearch( #define VMMDLL_MAP_PFN_VERSION 1 +#define VMMDLL_PFN_FLAG_NORMAL 0 +#define VMMDLL_PFN_FLAG_EXTENDED 1 + static LPCSTR VMMDLL_PFN_TYPE_TEXT[] = { "Zero", "Free", "Standby", "Modifiy", "ModNoWr", "Bad", "Active", "Transit" }; static LPCSTR VMMDLL_PFN_TYPEEXTENDED_TEXT[] = { "-", "Unused", "ProcPriv", "PageTable", "LargePage", "DriverLock", "Shareable", "File" }; @@ -1919,12 +1920,31 @@ typedef struct tdVMMDLL_MAP_PFN { EXPORTED_FUNCTION _Success_(return) BOOL VMMDLL_Map_GetPfn( _In_ VMM_HANDLE hVMM, - _In_ DWORD pPfns[], + _In_reads_(cPfns) DWORD pPfns[], _In_ DWORD cPfns, _Out_writes_bytes_opt_(*pcbPfnMap) PVMMDLL_MAP_PFN pPfnMap, _Inout_ PDWORD pcbPfnMap ); +/* +* Retrieve PFN information: +* CALLER FREE: VMMDLL_MemFree(*ppPfnMap) +* -- hVMM +* -- pPfns = PFNs to retrieve. +* -- cPfns = number of PFNs to retrieve. +* -- ppPfnMap = ptr to receive result on success. must be free'd with VMMDLL_MemFree(). +* -- flags = optional flags as specified by VMMDLL_PFN_FLAG_* +* -- return = success/fail. +*/ +EXPORTED_FUNCTION _Success_(return) +BOOL VMMDLL_Map_GetPfnEx( + _In_ VMM_HANDLE hVMM, + _In_reads_(cPfns) DWORD pPfns[], + _In_ DWORD cPfns, + _Out_ PVMMDLL_MAP_PFN *ppPfnMap, + _In_ DWORD flags +); + //----------------------------------------------------------------------------- diff --git a/vmm/vmmdll_core.c b/vmm/vmmdll_core.c index c0b43f7..ea3d679 100644 --- a/vmm/vmmdll_core.c +++ b/vmm/vmmdll_core.c @@ -335,6 +335,7 @@ VOID VmmDllCore_CloseHandle(_In_opt_ _Post_ptr_invalid_ VMM_HANDLE H, _In_ BOOL VmmDllCore_CloseHandle_VmmParentDetach(H); // Close logging (last) Statistics_CallSetEnabled(H, FALSE); + VmmLog(H, MID_CORE, LOGLEVEL_VERBOSE, "SHUTDOWN COMPLETED (%p).\n", H); VmmLog_Close(H); LocalFree(H); } @@ -621,6 +622,9 @@ BOOL VmmDllCore_InitializeConfig(_In_ VMM_HANDLE H, _In_ DWORD argc, _In_ char * H->dev.dwPrintfVerbosity |= H->cfg.fVerbose ? LC_CONFIG_PRINTF_V : 0; H->dev.dwPrintfVerbosity |= H->cfg.fVerboseExtra ? LC_CONFIG_PRINTF_VV : 0; H->dev.dwPrintfVerbosity |= H->cfg.fVerboseExtraTlp ? LC_CONFIG_PRINTF_VVV : 0; + Util_GetPathLib(H->cfg.szPathLibraryVmm); + strncat_s(H->cfg.szPathLibraryVmm, _countof(H->cfg.szPathLibraryVmm), "vmm", _TRUNCATE); + strncat_s(H->cfg.szPathLibraryVmm, _countof(H->cfg.szPathLibraryVmm), VMM_LIBRARY_FILETYPE, _TRUNCATE); return (H->dev.szDevice[0] != 0); } diff --git a/vmm/vmmdll_scatter.c b/vmm/vmmdll_scatter.c index f16be7a..4c88193 100644 --- a/vmm/vmmdll_scatter.c +++ b/vmm/vmmdll_scatter.c @@ -142,7 +142,7 @@ BOOL VMMDLL_Scatter_PrepareInternal(_In_ PSCATTER_CONTEXT ctx, _In_ QWORD va, _I } _Success_(return) -BOOL VMMDLL_Scatter_PrepareWriteInternal(_In_ PSCATTER_CONTEXT ctx, _In_ QWORD va, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb) +BOOL VMMDLL_Scatter_PrepareWriteInternal(_In_ PSCATTER_CONTEXT ctx, _In_ QWORD va, _In_reads_(cb) PBYTE pb, _In_ DWORD cb) { DWORD cMEMsRequired; PSCATTER_RANGE_WRITE pRangeWr; @@ -206,7 +206,7 @@ BOOL VMMDLL_Scatter_Prepare(_In_ VMMDLL_SCATTER_HANDLE hS, _In_ QWORD va, _In_ D * -- return */ _Success_(return) -BOOL VMMDLL_Scatter_PrepareWrite(_In_ VMMDLL_SCATTER_HANDLE hS, _In_ QWORD va, _Out_writes_(cb) PBYTE pb, _In_ DWORD cb) +BOOL VMMDLL_Scatter_PrepareWrite(_In_ VMMDLL_SCATTER_HANDLE hS, _In_ QWORD va, _In_reads_(cb) PBYTE pb, _In_ DWORD cb) { SCATTER_CALL_SYNCHRONIZED_IMPLEMENTATION(hS, VMMDLL_Scatter_PrepareWriteInternal((PSCATTER_CONTEXT)hS, va, pb, cb)); } diff --git a/vmm/vmmlog.c b/vmm/vmmlog.c index d469095..7db6574 100644 --- a/vmm/vmmlog.c +++ b/vmm/vmmlog.c @@ -21,7 +21,7 @@ static LPCSTR VMMLOG_LEVEL_STR[] = { }; typedef struct tdVMMLOG_MODULE_MODULEINFO { - DWORD dwMID; // module id + DWORD MID; // module id VMMLOG_LEVEL dwLevelD; // log level display (other than default) VMMLOG_LEVEL dwLevelF; // log level file (other than default) LPSTR uszName; @@ -40,14 +40,14 @@ typedef struct tdVMMLOG_CONTEXT { /* * Helper function to get a log module info object. */ -inline PVMMLOG_CONTEXT_MODULEINFO VmmLog_GetModuleInfo(_In_ VMM_HANDLE H, _In_ DWORD dwMID) +inline PVMMLOG_CONTEXT_MODULEINFO VmmLog_GetModuleInfo(_In_ VMM_HANDLE H, _In_ VMM_MODULE_ID MID) { PVMMLOG_CONTEXT ctxLog = H->log; if(!ctxLog) { return NULL; } - if(dwMID & 0x80000000) { - return (dwMID <= MID_MAX) ? &ctxLog->CoreInfo[dwMID & 0x7fffffff] : NULL; + if(MID & 0x80000000) { + return (MID <= MID_MAX) ? &ctxLog->CoreInfo[MID & 0x7fffffff] : NULL; } else { - return (dwMID < ctxLog->iNextMID) ? &ctxLog->ModuleInfo[dwMID] : NULL; + return (MID < ctxLog->iNextMID) ? &ctxLog->ModuleInfo[MID] : NULL; } } @@ -59,14 +59,14 @@ inline PVMMLOG_CONTEXT_MODULEINFO VmmLog_GetModuleInfo(_In_ VMM_HANDLE H, _In_ D VOID VmmLog_Close(_In_ VMM_HANDLE H) { PVMMLOG_CONTEXT_MODULEINFO pmi; - DWORD dwMID; + VMM_MODULE_ID MID; H->logfilter = (DWORD)LOGLEVEL_NONE; if(H->log) { if(H->log->pFile) { fclose(H->log->pFile); } - for(dwMID = 0; dwMID < H->log->iNextMID; dwMID++) { - if((pmi = VmmLog_GetModuleInfo(H, dwMID))) { + for(MID = 0; MID < H->log->iNextMID; MID++) { + if((pmi = VmmLog_GetModuleInfo(H, MID))) { LocalFree(pmi->uszName); } } @@ -78,17 +78,17 @@ VOID VmmLog_Close(_In_ VMM_HANDLE H) /* * Get the log level for either display (on-screen) or file. * -- H -* -- dwMID = specify MID to get specific level override (i.e. not default MID) +* -- MID = specify MID to get specific level override (i.e. not default MID) * -- fDisplay * -- return */ -VMMLOG_LEVEL VmmLog_LevelGet(_In_ VMM_HANDLE H, _In_opt_ DWORD dwMID, _In_ BOOL fDisplay) +VMMLOG_LEVEL VmmLog_LevelGet(_In_ VMM_HANDLE H, _In_opt_ VMM_MODULE_ID MID, _In_ BOOL fDisplay) { PVMMLOG_CONTEXT ctxLog = H->log; PVMMLOG_CONTEXT_MODULEINFO pmi; if(!ctxLog) { return LOGLEVEL_NONE; } - if(dwMID) { - pmi = VmmLog_GetModuleInfo(H, dwMID); + if(MID) { + pmi = VmmLog_GetModuleInfo(H, MID); if(!pmi) { return LOGLEVEL_NONE; } return fDisplay ? pmi->dwLevelD : pmi->dwLevelF; } else { @@ -99,18 +99,18 @@ VMMLOG_LEVEL VmmLog_LevelGet(_In_ VMM_HANDLE H, _In_opt_ DWORD dwMID, _In_ BOOL /* * Set the log level for either display (on-screen) or file. * -- H -* -- dwMID = specify MID (other than 0) to set specific module level override. +* -- MID = specify MID (other than 0) to set specific module level override. * -- dwLogLevel * -- fDisplay = TRUE(display), FALSE(file) * -- fSetOrIncrease = TRUE(set), FALSE(increase) */ -VOID VmmLog_LevelSet(_In_ VMM_HANDLE H, _In_opt_ DWORD dwMID, _In_ VMMLOG_LEVEL dwLogLevel, _In_ BOOL fDisplay, _In_ BOOL fSetOrIncrease) +VOID VmmLog_LevelSet(_In_ VMM_HANDLE H, _In_opt_ VMM_MODULE_ID MID, _In_ VMMLOG_LEVEL dwLogLevel, _In_ BOOL fDisplay, _In_ BOOL fSetOrIncrease) { PVMMLOG_CONTEXT ctxLog = H->log; PVMMLOG_CONTEXT_MODULEINFO pmi = NULL; if(!ctxLog || (dwLogLevel < 0) || (dwLogLevel > LOGLEVEL_ALL)) { return; } - if(dwMID) { - if(!(pmi = VmmLog_GetModuleInfo(H, dwMID))) { return; } + if(MID) { + if(!(pmi = VmmLog_GetModuleInfo(H, MID))) { return; } if(fDisplay) { pmi->dwLevelD = fSetOrIncrease ? dwLogLevel : max(dwLogLevel, pmi->dwLevelD); } else { @@ -121,12 +121,12 @@ VOID VmmLog_LevelSet(_In_ VMM_HANDLE H, _In_opt_ DWORD dwMID, _In_ VMMLOG_LEVEL ctxLog->dwLevelMID = max(ctxLog->dwLevelMID, dwLogLevel); } else { ctxLog->dwLevelMID = 0; - for(dwMID = 1; dwMID < ctxLog->iNextMID; dwMID++) { - pmi = VmmLog_GetModuleInfo(H, dwMID); + for(MID = 1; MID < ctxLog->iNextMID; MID++) { + pmi = VmmLog_GetModuleInfo(H, MID); ctxLog->dwLevelMID = max(ctxLog->dwLevelMID, max(pmi->dwLevelD, pmi->dwLevelF)); } - for(dwMID = MID_NA; dwMID <= MID_MAX; dwMID++) { - pmi = VmmLog_GetModuleInfo(H, dwMID); + for(MID = MID_NA; MID <= MID_MAX; MID++) { + pmi = VmmLog_GetModuleInfo(H, MID); ctxLog->dwLevelMID = max(ctxLog->dwLevelMID, max(pmi->dwLevelD, pmi->dwLevelF)); } } @@ -146,7 +146,8 @@ VOID VmmLog_LevelSet(_In_ VMM_HANDLE H, _In_opt_ DWORD dwMID, _In_ VMMLOG_LEVEL VOID VmmLog_LevelRefresh(_In_ VMM_HANDLE H) { PVMMLOG_CONTEXT ctxLog = H->log; - DWORD i, dwMID, dwTokenMID; + VMM_MODULE_ID MID; + DWORD i, dwTokenMID; PVMMLOG_CONTEXT_MODULEINFO pmi; CHAR ch, szModuleName[9], szTokenBuffer[MAX_PATH]; LPSTR szToken, szTokenInitial, szTokenContext = NULL; @@ -156,20 +157,20 @@ VOID VmmLog_LevelRefresh(_In_ VMM_HANDLE H) if(!ctxLog) { H->log = LocalAlloc(LMEM_ZEROINIT, sizeof(VMMLOG_CONTEXT)); if(!(ctxLog = H->log)) { return; } - for(dwMID = MID_NA; dwMID <= MID_MAX; dwMID++) { - pmi = VmmLog_GetModuleInfo(H, dwMID); - pmi->dwMID = dwMID; - pmi->uszName = (LPSTR)VMMLOG_MID_STR[dwMID & 0x7fffffff]; + for(MID = MID_NA; MID <= MID_MAX; MID++) { + pmi = VmmLog_GetModuleInfo(H, MID); + pmi->MID = MID; + pmi->uszName = (LPSTR)VMMLOG_MID_STR[MID & 0x7fffffff]; } } // clear module overrides (if any): - for(dwMID = 1; dwMID < ctxLog->iNextMID; dwMID++) { - pmi = VmmLog_GetModuleInfo(H, dwMID); + for(MID = 1; MID < ctxLog->iNextMID; MID++) { + pmi = VmmLog_GetModuleInfo(H, MID); pmi->dwLevelD = LOGLEVEL_NONE; pmi->dwLevelF = LOGLEVEL_NONE; } - for(dwMID = MID_NA; dwMID <= MID_MAX; dwMID++) { - pmi = VmmLog_GetModuleInfo(H, dwMID); + for(MID = MID_NA; MID <= MID_MAX; MID++) { + pmi = VmmLog_GetModuleInfo(H, MID); pmi->dwLevelD = LOGLEVEL_NONE; pmi->dwLevelF = LOGLEVEL_NONE; } @@ -220,17 +221,17 @@ VOID VmmLog_LevelRefresh(_In_ VMM_HANDLE H) i++; } if(fModuleName) { - for(dwMID = 1; dwMID < ctxLog->iNextMID; dwMID++) { - pmi = VmmLog_GetModuleInfo(H, dwMID); + for(MID = 1; MID < ctxLog->iNextMID; MID++) { + pmi = VmmLog_GetModuleInfo(H, MID); if(pmi->uszName && !_stricmp(pmi->uszName, szModuleName)) { - dwTokenMID = dwMID; + dwTokenMID = MID; break; } } - for(dwMID = MID_NA; dwMID <= MID_MAX; dwMID++) { - pmi = VmmLog_GetModuleInfo(H, dwMID); + for(MID = MID_NA; MID <= MID_MAX; MID++) { + pmi = VmmLog_GetModuleInfo(H, MID); if(pmi->uszName && !_stricmp(pmi->uszName, szModuleName)) { - dwTokenMID = dwMID; + dwTokenMID = MID; break; } } @@ -248,47 +249,47 @@ VOID VmmLog_LevelRefresh(_In_ VMM_HANDLE H) * Register a new module ID (MID) with the log database. * This function should be called in a single-threaded context by the plugin manager. * -- H -* -- dwMID = the module ID (MID) to register +* -- MID = the module ID (MID) to register * -- uszModuleName * -- fExternal = externally loaded module (dll/so). */ -VOID VmmLog_RegisterModule(_In_ VMM_HANDLE H, _In_ DWORD dwMID, _In_ LPSTR uszModuleName, _In_ BOOL fExternal) +VOID VmmLog_RegisterModule(_In_ VMM_HANDLE H, _In_ VMM_MODULE_ID MID, _In_ LPSTR uszModuleName, _In_ BOOL fExternal) { PVMMLOG_CONTEXT_MODULEINFO pmi; if(!H->log) { return; } - if(dwMID >= VMMLOG_MID_MODULE_MAX) { return; } - pmi = H->log->ModuleInfo + dwMID; + if(MID >= VMMLOG_MID_MODULE_MAX) { return; } + pmi = H->log->ModuleInfo + MID; if(pmi->uszName) { LocalFree(pmi->uszName); ZeroMemory(pmi, sizeof(VMMLOG_CONTEXT_MODULEINFO)); } if(CharUtil_UtoU(uszModuleName, 8, NULL, 0, &pmi->uszName, NULL, CHARUTIL_FLAG_ALLOC)) { } - pmi->dwMID = dwMID; - H->log->iNextMID = dwMID + 1; + pmi->MID = MID; + H->log->iNextMID = MID + 1; } /* * Log a message "printf" style. Whether the message is displayed and/or saved * to log file depends on the internal logging setup. * -- H -* -- dwMID = module ID (MID) +* -- MID = module ID (MID) * -- dwLogLevel = log level as defined by LOGLEVEL_* * -- uszFormat * -- ... */ -VOID VmmLogEx(_In_ VMM_HANDLE H, _In_ DWORD dwMID, _In_ VMMLOG_LEVEL dwLogLevel, _In_z_ _Printf_format_string_ LPSTR uszFormat, ...) +VOID VmmLogEx(_In_ VMM_HANDLE H, _In_ VMM_MODULE_ID MID, _In_ VMMLOG_LEVEL dwLogLevel, _In_z_ _Printf_format_string_ LPSTR uszFormat, ...) { va_list arglist; va_start(arglist, uszFormat); - VmmLogEx2(H, dwMID, dwLogLevel, uszFormat, arglist); + VmmLogEx2(H, MID, dwLogLevel, uszFormat, arglist); va_end(arglist); } /* * Log a message "printf" style followed by a hexascii printout. * -- H -* -- dwMID = module ID (MID) +* -- MID = module ID (MID) * -- dwLogLevel = log level as defined by LOGLEVEL_* * -- pb = binary to log * -- cb = size of binary to log @@ -296,7 +297,7 @@ VOID VmmLogEx(_In_ VMM_HANDLE H, _In_ DWORD dwMID, _In_ VMMLOG_LEVEL dwLogLevel, * -- uszFormat * -- ... */ -VOID VmmLogHexAsciiEx(_In_ VMM_HANDLE H, _In_ DWORD dwMID, _In_ VMMLOG_LEVEL dwLogLevel, _In_reads_(cb) PBYTE pb, _In_ DWORD cb, _In_ DWORD cbInitialOffset, _In_z_ _Printf_format_string_ LPSTR uszFormat, ...) +VOID VmmLogHexAsciiEx(_In_ VMM_HANDLE H, _In_ VMM_MODULE_ID MID, _In_ VMMLOG_LEVEL dwLogLevel, _In_reads_(cb) PBYTE pb, _In_ DWORD cb, _In_ DWORD cbInitialOffset, _In_z_ _Printf_format_string_ LPSTR uszFormat, ...) { LPSTR usz; va_list arglist; @@ -320,7 +321,7 @@ VOID VmmLogHexAsciiEx(_In_ VMM_HANDLE H, _In_ DWORD dwMID, _In_ VMMLOG_LEVEL dwL } // 4: log va_start(arglist, uszFormat); - VmmLogEx2(H, dwMID, dwLogLevel, usz, arglist); + VmmLogEx2(H, MID, dwLogLevel, usz, arglist); va_end(arglist); LocalFree(usz); } @@ -328,11 +329,11 @@ VOID VmmLogHexAsciiEx(_In_ VMM_HANDLE H, _In_ DWORD dwMID, _In_ VMMLOG_LEVEL dwL /* * Check whether the MID/LogLevel will log to any output. * -- H -* -- dwMID = module ID (MID) +* -- MID = module ID (MID) * -- dwLogLevel = log level as defined by LOGLEVEL_* * -- return = TRUE(will log), FALSE(will NOT log). */ -BOOL VmmLogIsActive(_In_ VMM_HANDLE H, _In_ DWORD dwMID, _In_ VMMLOG_LEVEL dwLogLevel) +BOOL VmmLogIsActive(_In_ VMM_HANDLE H, _In_ VMM_MODULE_ID MID, _In_ VMMLOG_LEVEL dwLogLevel) { PVMMLOG_CONTEXT ctxLog = H->log; PVMMLOG_CONTEXT_MODULEINFO pmi; @@ -340,7 +341,7 @@ BOOL VmmLogIsActive(_In_ VMM_HANDLE H, _In_ DWORD dwMID, _In_ VMMLOG_LEVEL dwLog if(!ctxLog) { return FALSE; } // sanity checks, get module info object and check if logs should happen to display/file: if((dwLogLevel < LOGLEVEL_NONE) || (dwLogLevel > LOGLEVEL_ALL) || (dwLogLevel > (VMMLOG_LEVEL)H->logfilter)) { return FALSE; } - if(!(pmi = VmmLog_GetModuleInfo(H, dwMID))) { return FALSE; } + if(!(pmi = VmmLog_GetModuleInfo(H, MID))) { return FALSE; } fD = ((dwLogLevel <= ctxLog->dwLevelD) || (dwLogLevel <= pmi->dwLevelD)); // log to display fF = ((dwLogLevel <= ctxLog->dwLevelF) || (dwLogLevel <= pmi->dwLevelF)) && ctxLog->pFile; // log to file return fD || fF; @@ -350,12 +351,12 @@ BOOL VmmLogIsActive(_In_ VMM_HANDLE H, _In_ DWORD dwMID, _In_ VMMLOG_LEVEL dwLog * Log a message using a va_list. Whether the message is displayed and/or saved * to log file depends on the internal logging setup. * -- H -* -- dwMID = module ID (MID) +* -- MID = module ID (MID) * -- dwLogLevel = log level as defined by LOGLEVEL_* * -- uszFormat * -- arglist */ -VOID VmmLogEx2(_In_ VMM_HANDLE H, _In_ DWORD dwMID, _In_ VMMLOG_LEVEL dwLogLevel, _In_z_ _Printf_format_string_ LPSTR uszFormat, va_list arglist) +VOID VmmLogEx2(_In_ VMM_HANDLE H, _In_ VMM_MODULE_ID MID, _In_ VMMLOG_LEVEL dwLogLevel, _In_z_ _Printf_format_string_ LPSTR uszFormat, va_list arglist) { PVMMLOG_CONTEXT ctxLog = H->log; PVMMLOG_CONTEXT_MODULEINFO pmi; @@ -369,7 +370,7 @@ VOID VmmLogEx2(_In_ VMM_HANDLE H, _In_ DWORD dwMID, _In_ VMMLOG_LEVEL dwLogLevel if(!ctxLog) { return; } // sanity checks, get module info object and check if logs should happen to display/file: if((dwLogLevel < LOGLEVEL_NONE) || (dwLogLevel > LOGLEVEL_ALL) || (dwLogLevel > (VMMLOG_LEVEL)H->logfilter)) { return; } - if(!(pmi = VmmLog_GetModuleInfo(H, dwMID))) { return; } + if(!(pmi = VmmLog_GetModuleInfo(H, MID))) { return; } fD = ((dwLogLevel <= ctxLog->dwLevelD) || (dwLogLevel <= pmi->dwLevelD)); // log to display fF = ((dwLogLevel <= ctxLog->dwLevelF) || (dwLogLevel <= pmi->dwLevelF)) && ctxLog->pFile; // log to file if(!fD && !fF) { return; } diff --git a/vmm/vmmlog.h b/vmm/vmmlog.h index dffa104..3484e77 100644 --- a/vmm/vmmlog.h +++ b/vmm/vmmlog.h @@ -7,6 +7,8 @@ #define __VMMLOG_H__ #include "vmm.h" +typedef DWORD VMM_MODULE_ID; + typedef enum tdVMMLOG_LEVEL { LOGLEVEL_NONE = 0, // do not use! LOGLEVEL_0_NONE = 0, @@ -31,6 +33,7 @@ typedef enum tdVMMLOG_LEVEL { #define MID_MAIN 0x80000001 #define MID_PYTHON 0x80000002 #define MID_DEBUG 0x80000003 +#define MID_RUST 0x80000004 #define MID_CORE 0x80000010 #define MID_API 0x80000011 #define MID_VMM 0x80000012 @@ -57,7 +60,8 @@ static LPCSTR VMMLOG_MID_STR[] = { "MAIN", "PYTHON", "DEBUG", - "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", + "RUST", + "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", // vmm internal built-in module: "CORE", "API", @@ -95,45 +99,45 @@ VOID VmmLog_Close(_In_ VMM_HANDLE H); /* * Get the log level for either display (on-screen) or file. * -- H -* -- dwMID = specify MID (other than 0) to get specific module level override. +* -- MID = specify MID (other than 0) to get specific module level override. * -- fDisplay * -- return */ -VMMLOG_LEVEL VmmLog_LevelGet(_In_ VMM_HANDLE H, _In_opt_ DWORD dwMID, _In_ BOOL fDisplay); +VMMLOG_LEVEL VmmLog_LevelGet(_In_ VMM_HANDLE H, _In_opt_ VMM_MODULE_ID MID, _In_ BOOL fDisplay); /* * Set the log level for either display (on-screen) or file. * -- H -* -- dwMID = specify MID (other than 0) to set specific module level override. +* -- MID = specify MID (other than 0) to set specific module level override. * -- dwLogLevel * -- fDisplay = TRUE(display), FALSE(file) * -- fSetOrIncrease = TRUE(set), FALSE(increase) */ -VOID VmmLog_LevelSet(_In_ VMM_HANDLE H, _In_opt_ DWORD dwMID, _In_ VMMLOG_LEVEL dwLogLevel, _In_ BOOL fDisplay, _In_ BOOL fSetOrIncrease); +VOID VmmLog_LevelSet(_In_ VMM_HANDLE H, _In_opt_ VMM_MODULE_ID MID, _In_ VMMLOG_LEVEL dwLogLevel, _In_ BOOL fDisplay, _In_ BOOL fSetOrIncrease); /* * Register a new module ID (MID) with the log database. * This function should be called in a single-threaded context by the plugin manager. * -- H -* -- dwMID = the module ID (MID) to register +* -- MID = the module ID (MID) to register * -- uszModuleName * -- fExternal = externally loaded module (dll/so). */ -VOID VmmLog_RegisterModule(_In_ VMM_HANDLE H, _In_ DWORD dwMID, _In_ LPSTR uszModuleName, _In_ BOOL fExternal); +VOID VmmLog_RegisterModule(_In_ VMM_HANDLE H, _In_ VMM_MODULE_ID MID, _In_ LPSTR uszModuleName, _In_ BOOL fExternal); /* * Check whether the MID/LogLevel will log to any output. * -- H -* -- dwMID = module ID (MID) +* -- MID = module ID (MID) * -- dwLogLevel = log level as defined by LOGLEVEL_* * -- return = TRUE(will log), FALSE(will NOT log). */ -BOOL VmmLogIsActive(_In_ VMM_HANDLE H, _In_ DWORD dwMID, _In_ VMMLOG_LEVEL dwLogLevel); +BOOL VmmLogIsActive(_In_ VMM_HANDLE H, _In_ VMM_MODULE_ID MID, _In_ VMMLOG_LEVEL dwLogLevel); /* * Log a message "printf" style followed by a hexascii printout. * -- H -* -- dwMID = module ID (MID) +* -- MID = module ID (MID) * -- dwLogLevel = log level as defined by LOGLEVEL_* * -- pb = binary to log * -- cb = size of binary to log @@ -143,7 +147,7 @@ BOOL VmmLogIsActive(_In_ VMM_HANDLE H, _In_ DWORD dwMID, _In_ VMMLOG_LEVEL dwLog */ VOID VmmLogHexAsciiEx( _In_ VMM_HANDLE H, - _In_ DWORD dwMID, + _In_ VMM_MODULE_ID MID, _In_ VMMLOG_LEVEL dwLogLevel, _In_reads_(cb) PBYTE pb, _In_ DWORD cb, @@ -156,33 +160,33 @@ VOID VmmLogHexAsciiEx( * Log a message "printf" style. Whether the message is displayed and/or saved * to log file depends on the internal logging setup. * -- H -* -- dwMID = module ID (MID) +* -- MID = module ID (MID) * -- dwLogLevel = log level as defined by LOGLEVEL_* * -- uszFormat * -- ... */ -VOID VmmLogEx(_In_ VMM_HANDLE H, _In_ DWORD dwMID, _In_ VMMLOG_LEVEL dwLogLevel, _In_z_ _Printf_format_string_ LPSTR uszFormat, ...); +VOID VmmLogEx(_In_ VMM_HANDLE H, _In_ VMM_MODULE_ID MID, _In_ VMMLOG_LEVEL dwLogLevel, _In_z_ _Printf_format_string_ LPSTR uszFormat, ...); /* * Log a message using a va_list. Whether the message is displayed and/or saved * to log file depends on the internal logging setup. * -- H -* -- dwMID = module ID (MID) +* -- MID = module ID (MID) * -- dwLogLevel = log level as defined by LOGLEVEL_* * -- uszFormat * -- arglist */ -VOID VmmLogEx2(_In_ VMM_HANDLE H, _In_ DWORD dwMID, _In_ VMMLOG_LEVEL dwLogLevel, _In_z_ _Printf_format_string_ LPSTR uszFormat, va_list arglist); +VOID VmmLogEx2(_In_ VMM_HANDLE H, _In_ VMM_MODULE_ID MID, _In_ VMMLOG_LEVEL dwLogLevel, _In_z_ _Printf_format_string_ LPSTR uszFormat, va_list arglist); /* * Log amessage "printf" style. * -- H -* -- dwMID +* -- MID * -- dwLogLevel * -- format * -- ... */ -#define VmmLog(H, dwMID, dwLogLevel, format, ...) { if(dwLogLevel <= (VMMLOG_LEVEL)H->logfilter) { VmmLogEx(H, dwMID, dwLogLevel, format, ##__VA_ARGS__); } } +#define VmmLog(H, MID, dwLogLevel, format, ...) { if(dwLogLevel <= (VMMLOG_LEVEL)H->logfilter) { VmmLogEx(H, MID, dwLogLevel, format, ##__VA_ARGS__); } } /* * printf a message to the console if allowed (i.e. not suppressed in a dll context). diff --git a/vmm/vmmproc.c b/vmm/vmmproc.c index d72e9c5..7609ada 100644 --- a/vmm/vmmproc.c +++ b/vmm/vmmproc.c @@ -15,7 +15,7 @@ #include "vmmwinpool.h" #include "vmmwinreg.h" #include "vmmwinsvc.h" -#include "mm_pfn.h" +#include "mm/mm_pfn.h" #include "pluginmanager.h" #include "statistics.h" #include "util.h" diff --git a/vmm/vmmwin.c b/vmm/vmmwin.c index b383987..4d6925f 100644 --- a/vmm/vmmwin.c +++ b/vmm/vmmwin.c @@ -13,7 +13,7 @@ #include "util.h" #include "pdb.h" #include "pe.h" -#include "mm.h" +#include "mm/mm.h" #include "infodb.h" #include "statistics.h" #ifdef _WIN32 diff --git a/vmm/vmmwininit.c b/vmm/vmmwininit.c index 5307fe3..90fd734 100644 --- a/vmm/vmmwininit.c +++ b/vmm/vmmwininit.c @@ -7,7 +7,6 @@ // #include "vmm.h" -#include "mm.h" #include "pe.h" #include "pdb.h" #include "util.h" @@ -16,6 +15,7 @@ #include "vmmwinobj.h" #include "vmmwinreg.h" #include "infodb.h" +#include "mm/mm.h" /* * Try initialize threading - this is dependent on available PDB symbols. diff --git a/vmmpyc/version.h b/vmmpyc/version.h index 371cc79..55fe987 100644 --- a/vmmpyc/version.h +++ b/vmmpyc/version.h @@ -2,9 +2,9 @@ #define STRINGIZE(s) STRINGIZE2(s) #define VERSION_MAJOR 5 -#define VERSION_MINOR 3 -#define VERSION_REVISION 1 -#define VERSION_BUILD 100 +#define VERSION_MINOR 4 +#define VERSION_REVISION 0 +#define VERSION_BUILD 102 #define VER_FILE_DESCRIPTION_STR "MemProcFS : Python API" #define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD diff --git a/vmmpyc/vmmpyc_vmm.c b/vmmpyc/vmmpyc_vmm.c index 507133f..ef13a7e 100644 --- a/vmmpyc/vmmpyc_vmm.c +++ b/vmmpyc/vmmpyc_vmm.c @@ -143,7 +143,6 @@ VmmPycVmm_reg_hive_list(PyObj_Vmm *self, PyObject *args) if(!self->fValid) { return PyErr_Format(PyExc_RuntimeError, "Vmm.reg_hive_list(): Not initialized."); } if(!(pyList = PyList_New(0))) { return PyErr_NoMemory(); } Py_BEGIN_ALLOW_THREADS; - VMMDLL_WinReg_HiveList(self->hVMM, NULL, 0, &cHives); result = VMMDLL_WinReg_HiveList(self->hVMM, NULL, 0, &cHives) && cHives && diff --git a/vmmrust/.vscode/launch.json b/vmmrust/.vscode/launch.json new file mode 100644 index 0000000..0c6df32 --- /dev/null +++ b/vmmrust/.vscode/launch.json @@ -0,0 +1,28 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "VmmRust API BINARY EXAMPLE (Debug)", + "type": "lldb", + "request": "launch", + "cargo": { + "args": ["build", "--manifest-path", "${workspaceRoot}/memprocfs_example/Cargo.toml"] + }, + "program": "${workspaceRoot}/memprocfs_example/target/debug/memprocfs_example", + "args": [], + "cwd": "${workspaceRoot}", + }, + { + "name": "VmmRust PLUGIN LIBRARY EXAMPLE (Debug)", + "type": "lldb", + "request": "launch", + "preLaunchTask": "build_debug_m_example_plugin", + "program": "${workspaceRoot}/../files/memprocfs", + "args": ["-device", "Z:\\x64\\WIN10-X64-1909-18363-1.core"], + "cwd": "${workspaceRoot}", + } + ] +} diff --git a/vmmrust/.vscode/tasks.json b/vmmrust/.vscode/tasks.json new file mode 100644 index 0000000..f1cb566 --- /dev/null +++ b/vmmrust/.vscode/tasks.json @@ -0,0 +1,24 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "cargo_debug_m_example_plugin", + "type": "shell", + "command": "cargo", + "args": ["build", "--manifest-path", "${workspaceRoot}/m_example_plugin/Cargo.toml"] + }, + { + "label": "build_debug_m_example_plugin", + "dependsOn": ["cargo_debug_m_example_plugin"], + "type": "shell", + "windows": { + "command": "copy", // Could be any other shell command + "args": ["${workspaceRoot}\\m_example_plugin\\target\\debug\\m_example_plugin.dll", "${workspaceRoot}\\..\\files\\plugins\\m_vmmrust_plugin.dll"], + }, + "linux": { + "command": "cp", // Could be any other shell command + "args": ["-f", "${workspaceRoot}/m_example_plugin/target/debug/libm_example_plugin.so", "${workspaceRoot}/../files/plugins/m_vmmrust_plugin.so", "/y"], + } + } + ] +} diff --git a/vmmrust/m_example_plugin/Cargo.lock b/vmmrust/m_example_plugin/Cargo.lock new file mode 100644 index 0000000..9497520 --- /dev/null +++ b/vmmrust/m_example_plugin/Cargo.lock @@ -0,0 +1,111 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "m_example_plugin" +version = "5.4.0" +dependencies = [ + "memprocfs", +] + +[[package]] +name = "memprocfs" +version = "5.4.0" +dependencies = [ + "libloading", + "serde", +] + +[[package]] +name = "proc-macro2" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/vmmrust/m_example_plugin/Cargo.toml b/vmmrust/m_example_plugin/Cargo.toml new file mode 100644 index 0000000..722d50a --- /dev/null +++ b/vmmrust/m_example_plugin/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "m_example_plugin" +version = "5.4.0" +edition = "2021" +publish = false + +[lib] +name = "m_example_plugin" +path = "src/lib.rs" +crate-type = ["cdylib"] + +[profile.release] +codegen-units = 1 +opt-level = "z" +panic = "abort" +strip = true +lto = true + +[dependencies] +memprocfs = { path = "../memprocfs" } diff --git a/vmmrust/m_example_plugin/src/lib.rs b/vmmrust/m_example_plugin/src/lib.rs new file mode 100644 index 0000000..103783d --- /dev/null +++ b/vmmrust/m_example_plugin/src/lib.rs @@ -0,0 +1,265 @@ +// lib.rs - MemProcFS Plugin Example +// +// The example will show up under /rust/example both at the root and per-process. +// The process views are dependent on the process PID and type. +// +// The plugin will not show up in kernel-mode processes. +// The plugin will show the 024 directory for PIDs ending in those numbers. +// The plugin will show the 68 directory for PIDs ending in those numbers. +// The root folder have a readme.txt as a read-only file. +// The sub-folders will have a writeme.txt as a read/write file. +// +// (c) Ulf Frisk, 2023 +// Author: Ulf Frisk, pcileech@frizk.net +// https://github.com/ufrisk/MemProcFS +// + +use memprocfs::*; + +const PLUGIN_README : &str = + "This is a MemProcFS example plugin for Rust.\n\ + --------------------------------------------\n\ + Please check the plugin source code for info\n\ + Or visit https://github.com/ufrisk/MemProcFS\n"; + +const PLUGIN_README_024_WRITABLE : &str = + "This is a writable file in the MemProcFS Rust example plugin.\n\ + The file should _not_ show up in processes which pid doesn't \n\ + end with the numbers 6 or 8. \n"; + +const PLUGIN_README_68_WRITABLE : &str = + "This is a writable file in the MemProcFS Rust example plugin. \n\ + The file should show up in processes which pids ends with 6 or 8.\n"; + + + +// User-defined plugin context. +struct PluginContext { + id : u32, + file_024 : Vec, + file_68 : Vec, +} + + + +/// MemProcFS plugin main entry point: +/// +/// The native VMM library will call this function when the plugin should be +/// initialized. The function should start by making a call to the plugin API +/// initializing the MemProcFS rust sub-system before doing anything else. +#[no_mangle] +pub extern "C" fn InitializeVmmPlugin(native_h : usize, native_reginfo : usize) { + // First line of initialization function: + // Retrieve system information and the plugin init context from native layer. + // Note the generic type T which allows the init function to initialize a + // plugin specific context which will be shared between callbacks. + let (system_info, mut plugin_init_ctx) = + match new_plugin_initialization::(native_h, native_reginfo) { + Ok(r) => r, + Err(_) => return, + }; + + // The system_info may be used to check whether the plugin should run or not. + // It's important that this function is fast! General API usage is possibele + // to further check whether the plugin should be run or not, but is discouraged + // due to added overhead. Ideally only the system_info should be used. + // In this example do not allow plugin to run on Windows Vista and older. + if system_info.version_build < 7600 { + return; + } + + // Initialize the plugin by setting the custom plugin context and also + // callback functions. Callback functions may be called in multi-threaded + // mode and the custom plugin context will be serialized behind a RwLock to + // avoid race conditions. + // This module is a root module which will show up under /rust/example in + // the MemProcFS virtual file system. + let ctx = PluginContext { + id : 1337, + file_024 : PLUGIN_README_024_WRITABLE.as_bytes().to_vec(), + file_68 : PLUGIN_README_68_WRITABLE.as_bytes().to_vec(), + }; + plugin_init_ctx.ctx = Some(ctx); + plugin_init_ctx.is_root_module = true; + plugin_init_ctx.is_process_module = true; + plugin_init_ctx.path_name = String::from("/rust/example"); + + // Populate the plugin_init_ctx with implemented functions. + // list/read/write is commonly implemented (even if no requirement) + // while notify/visible is more rare and used in special circumstances. + plugin_init_ctx.fn_list = Some(plugin_list_cb); + plugin_init_ctx.fn_read = Some(plugin_read_cb); + plugin_init_ctx.fn_write = Some(plugin_write_cb); + plugin_init_ctx.fn_notify = Some(plugin_notify_cb); + plugin_init_ctx.fn_visible = Some(plugin_visible_cb); + + // Register the plugin with MemProcFS. This will consume the context which + // should not be possible to use after this. + let _r = plugin_init_ctx.register(); + + // Sometimes one may wish to register additional plugins. This is possible. + // Simply call new_plugin_initialization() to retrieve a new + // plugin initialzation context and start anew. + // + // let (system_info, mut plugin_init_ctx) = + // match new_plugin_initialization::(native_h, native_reginfo) { + // Ok(r) => r, + // Err(_) => return, + // }; + // ... + // let _r = plugin_init_ctx.register(); +} + + + +// Example: Plugin VFS list Callback: +// +// The list callback should populate the directory that is queried by path. +// +// It is important that the list callback is fast. Any longer running tasks +// should be spawn into a separate thread so that the file system doesn't +// freeze waiting for the list callback to complete processing. +fn plugin_list_cb(ctxp : &VmmPluginContext, process : Option, path : &str, file_list : &VmmPluginFileList) -> ResultEx<()> { + // The user-defined is stored behind a RwLock that may be locked for either read() or write(). + // All callbacks may happen in multi-threaded mode so locking is important! + let mut ctx_user = ctxp.ctxlock.write().unwrap(); + let mut is_pid_024 = true; + let mut is_pid_68 = true; + if let Some(process) = process { + is_pid_024 = ((process.pid % 10) == 0) || ((process.pid % 10) == 2) || ((process.pid % 10) == 4); + is_pid_68 = ((process.pid % 10) == 6) || ((process.pid % 10) == 8); + } + // root folder: + if path.len() == 0 { + ctx_user.id += 1; + let id_string = ctx_user.id.to_string(); + file_list.add_file("readme.txt", PLUGIN_README.len() as u64); + file_list.add_file("numeric_list_counter.txt", id_string.len() as u64); + if is_pid_024 { + file_list.add_directory("024"); + } + if is_pid_68 { + file_list.add_directory("68"); + } + return Ok(()); + } + // 024 folder: + if path.eq_ignore_ascii_case("024") { + file_list.add_file("writeme_024.txt", ctx_user.file_024.len() as u64); + return Ok(()); + } + // 68 folder: + if path.eq_ignore_ascii_case("68") { + file_list.add_file("writeme_68.txt", ctx_user.file_68.len() as u64); + return Ok(()); + } + return Ok(()); +} + + + +// Example: Plugin VFS read Callback: +// +// The read callback should return the read data as a vectorized byte-array. +// If the read is past the file size an empty vector should be returned. +// If the file does not exist an error should be returned. +fn plugin_read_cb(ctxp : &VmmPluginContext, _process : Option, file_name : &str, cb : u32, cb_offset : u64) -> ResultEx> { + let ctx_user = ctxp.ctxlock.read().unwrap(); + let data_vec; + let data; + // check which file to read: + if file_name.eq_ignore_ascii_case("readme.txt") { + data_vec = Vec::from(PLUGIN_README); + data = &data_vec; + } else if file_name.eq_ignore_ascii_case("numeric_list_counter.txt") { + data_vec = Vec::from(ctx_user.id.to_string()); + data = &data_vec; + } else if file_name.eq_ignore_ascii_case("024/writeme_024.txt") { + data = &ctx_user.file_024; + } else if file_name.eq_ignore_ascii_case("68/writeme_68.txt") { + data = &ctx_user.file_68; + } else { + return Err("[err]".into()); + } + // read from file: + let file_offset_base = usize::try_from(cb_offset)?; + let file_offset_top = std::cmp::min(data.len(), file_offset_base + usize::try_from(cb)?); + if file_offset_base > data.len() { + return Ok(Vec::new()); + } + let r = (&data[file_offset_base..file_offset_top]).to_vec(); + return Ok(r); +} + + + +// Example: Plugin VFS write Callback: +// +// The write callback should return success always even if no data is written. +// Errors may be returned when files are missing and in rare error propagation cases. +fn plugin_write_cb(ctxp : &VmmPluginContext, _process : Option, file_name : &str, data : Vec, cb_offset : u64) -> ResultEx<()> { + let mut ctx_user = ctxp.ctxlock.write().unwrap(); + let file_offset_base = usize::try_from(cb_offset)?; + // check which file to write: + if file_name.eq_ignore_ascii_case("024/writeme_024.txt") { + let file_offset_top = std::cmp::min(ctx_user.file_024.len(), file_offset_base + data.len()); + let file_copy_len = file_offset_top - file_offset_base; + if (file_offset_base > data.len()) || (file_copy_len == 0) { + return Ok(()); + } + for i in 0..file_copy_len { + ctx_user.file_024[file_offset_base + i] = data[i]; + } + } else if file_name.eq_ignore_ascii_case("68/writeme_68.txt") { + let file_offset_top = std::cmp::min(ctx_user.file_68.len(), file_offset_base + data.len()); + let file_copy_len = file_offset_top - file_offset_base; + if (file_offset_base > data.len()) || (file_copy_len == 0) { + return Ok(()); + } + for i in 0..file_copy_len { + ctx_user.file_68[file_offset_base + i] = data[i]; + } + } else { + return Err("[err]".into()); + } + return Ok(()); +} + + + +// Example: Plugin VFS dynamic visibility Callback: +// +// This should almost never be used! It is only used in the cases when it's +// desirable to have dynamic plugin visiblity in select processes. +// +// In this example the plugin is shown in the root and in user-mode processes - +// but it should be hidden in kernel mode processes. +fn plugin_visible_cb(_ctxp : &VmmPluginContext, process : Option) -> ResultEx { + if process.is_some() { + if let Ok(info) = process.unwrap().info() { + return Ok(info.is_user_mode); + } + } + return Ok(true); +} + + + +// Example: Plugin Notification Callback: +// +// MemProcFS will send notification at some events to subscribers. +// These notifications are stored in constants: PLUGIN_NOTIFY_* +// +// Some times it's desirable to take an action on a notification, +// such as changing plugin visibility when forensic mode is complete, +// or throw away cached data at PLUGIN_NOTIFY_REFRESH_SLOW. +fn plugin_notify_cb(ctxp : &VmmPluginContext, event_id : u32) -> ResultEx<()> { + // Throw away changes to files if there is a slow (complete) refresh. + // NB! this does only happen on dynamic memory (not on memory dump files). + if event_id == PLUGIN_NOTIFY_REFRESH_SLOW { + let mut ctx_user = ctxp.ctxlock.write().unwrap(); + ctx_user.file_024 = PLUGIN_README_024_WRITABLE.as_bytes().to_vec(); + ctx_user.file_68 = PLUGIN_README_68_WRITABLE.as_bytes().to_vec(); + } + return Ok(()); +} diff --git a/vmmrust/memprocfs/Cargo.lock b/vmmrust/memprocfs/Cargo.lock new file mode 100644 index 0000000..580ab10 --- /dev/null +++ b/vmmrust/memprocfs/Cargo.lock @@ -0,0 +1,104 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "memprocfs" +version = "5.4.0" +dependencies = [ + "libloading", + "serde", +] + +[[package]] +name = "proc-macro2" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/vmmrust/memprocfs/Cargo.toml b/vmmrust/memprocfs/Cargo.toml new file mode 100644 index 0000000..15163ff --- /dev/null +++ b/vmmrust/memprocfs/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "memprocfs" +version = "5.4.0" +edition = "2021" +description = "MemProcFS - Windows Physical Memory Analysis Framwork" +homepage = "https://github.com/ufrisk/MemProcFS" +repository = "https://github.com/ufrisk/MemProcFS" +categories = ["external-ffi-bindings"] +keywords = ["memory", "forensics", "dma", "pcileech", "memprocfs"] +readme = "README.md" +license = "AGPL-3.0-or-later" + +[lib] +name = "memprocfs" +path = "src/lib_memprocfs.rs" + +[dependencies] +serde = { version = "1.0", features = ["derive"] } +libloading = "0.7.4" diff --git a/vmmrust/memprocfs/README.md b/vmmrust/memprocfs/README.md new file mode 100644 index 0000000..cf6327a --- /dev/null +++ b/vmmrust/memprocfs/README.md @@ -0,0 +1,92 @@ +# MemProcFS Rust API + +The MemProcFS crate contains a wrapper API around the [MemProcFS physical +memory analysis framework](https://github.com/ufrisk/MemProcFS). The native +libray in the form of `vmm.dll` or `vmm.so` must be downloaded or compiled +in order to make use of the memprocfs rust crate. + +The aim of the MemProcFS rust crate and rust API is to make MemProcFS usage +as easy and smooth as possible on Rust! Please let me know what you think +or if you have any improvement suggestions! + +Physical memory analysis may take place on memory dump files for forensic +purposes. Analysis may also take place on live memory - either captured by +using [PCILeech PCIe DMA devices](https://github.com/ufrisk/pcileech-fpga) +or by using a driver - such as WinPMEM, LiveCloudKd, VMware or similar. + +The base of the MemProcFS API is the `Vmm` struct. Once the native vmm +has been initialized it's possible to retrieve processes in the form of +the `VmmProcess` struct. Using the `Vmm` and `VmmProcess` it's +possible to undertake a wide range of actions - such as reading/writing +memory or retrieve various information. + +Access the VFS (Virtual File System) via the Rust API to get access +to the full range of built-in and external plugins. + +Read and write memory by using the methods `mem_read()`, +`mem_read_ex()`, `mem_write()` of the `Vmm` and `VmmProcess` structs. + +Efficiently read and write memory using the `VmmScatterMemory` +struct. + +The MemProcFS rust API supports creation of native MemProcFS plugins in +the form of a library `.dll` or `.so` for the more advanced user. + + +## Example + +```Rust +let mut args = Vec::new(); +args.push("-printf"); +args.push("-device"); +args.push("-FPGA"); +let vmm = Vmm::new("/home/user/memprocfs/vmm.so", &args)? +if let Ok(process_all) = vmm.process_list() { + for process in &*process_all { + println!("{} : {}", process.pid, process.info()?.name); + } +} +``` + + +## Example projects +Check out the example documentation, both in the form of the [example +project](https://github.com/ufrisk/MemProcFS/tree/master/vmmrust/memprocfs_example) +and the [example MemProcFS plugin](https://github.com/ufrisk/MemProcFS/tree/master/vmmrust/m_example_plugin) + + +## Project documentation +Check out the project documentation for MemProcFS, LeechCore and pcileech-fpga: +* [MemProcFS](https://github.com/ufrisk/MemProcFS) - [Documentation](https://github.com/ufrisk/MemProcFS/wiki). +* [LeechCore](https://github.com/ufrisk/LeechCore/) - [Documentation](https://github.com/ufrisk/LeechCore/wiki). +* [PCILeech](https://github.com/ufrisk/pcileech) - [Documentation](https://github.com/ufrisk/pcileech/wiki). +* [PCILeech-FPGA](https://github.com/ufrisk/pcileech-fpga). + + +## License +MemProcFS and its rust API is open source under the [AGPL-3.0](https://github.com/ufrisk/MemProcFS/blob/master/LICENSE) license. + + +# Support PCILeech/MemProcFS development: +PCILeech and MemProcFS is free and open source! + +I put a lot of time and energy into PCILeech and MemProcFS and related research to make this happen. Some aspects of the projects relate to hardware and I put quite some money into my projects and related research. If you think PCILeech and/or MemProcFS are awesome tools and/or if you had a use for them it's now possible to contribute by becoming a sponsor! + +If you like what I've created with PCIleech and MemProcFS with regards to DMA, Memory Analysis and Memory Forensics and would like to give something back to support future development please consider becoming a sponsor at: + +To all my sponsors, Thank You 💖 + + +## Questions and Comments +Please feel free to contact me! +* Github: +* Discord: UlfFrisk#5780 +* Discord #pcileech channel at the [Porchetta](https://discord.gg/sEkn3aa) server. +* Twitter: +* Email: pcileech@frizk.net + + +## Get Started! +Check out the `Vmm` documentation and the [example project](https://github.com/ufrisk/MemProcFS/tree/master/vmmrust/memprocfs_example)! + +Best wishes with your memory analysis Rust project! diff --git a/vmmrust/memprocfs/src/lib_memprocfs.rs b/vmmrust/memprocfs/src/lib_memprocfs.rs new file mode 100644 index 0000000..1987536 --- /dev/null +++ b/vmmrust/memprocfs/src/lib_memprocfs.rs @@ -0,0 +1,5662 @@ +//! # The MemProcFS Rust API Documentation +//! +//! The MemProcFS crate contains a wrapper API around the [MemProcFS physical +//! memory analysis framework](https://github.com/ufrisk/MemProcFS). The native +//! libray in the form of `vmm.dll` or `vmm.so` must be downloaded or compiled +//! in order to make use of the memprocfs rust crate. +//! +//! The aim of the MemProcFS rust crate and rust API is to make MemProcFS usage +//! as easy and smooth as possible on Rust! Please let me know what you think +//! or if you have any improvement suggestions! +//! +//! Physical memory analysis may take place on memory dump files for forensic +//! purposes. Analysis may also take place on live memory - either captured by +//! using [PCILeech PCIe DMA devices](https://github.com/ufrisk/pcileech-fpga) +//! or by using a driver - such as WinPMEM, LiveCloudKd, VMware or similar. +//! +//! The base of the MemProcFS API is the [`Vmm`] struct. Once the native vmm +//! has been initialized it's possible to retrieve processes in the form of +//! the [`VmmProcess`] struct. Using the [`Vmm`] and [`VmmProcess`] it's +//! possible to undertake a wide range of actions - such as reading/writing +//! memory or retrieve various information. +//! +//! +//! Read and write memory by using the methods `mem_read()`, +//! `mem_read_ex()`, `mem_write()` of the [`Vmm`] and [`VmmProcess`] structs. +//! Virtual memory is read from individual processes. Physical memory is read +//! from the base `vmm`. +//! +//! Efficiently read and write memory using the [`VmmScatterMemory`] +//! struct. The scatter struct is retrieved by calling `mem_scatter()` on +//! either the base [`Vmm`] struct or the individual [`VmmProcess`] structs. +//! +//! Access the Virtual File System (VFS) using the Rust API to get access +//! to the full range of built-in and external plugins. The VFS is accessed by +//! using the methods `vmm.vfs_list()`, `vmm.vfs_read()`, `vmm.vfs_write()` +//! on the [`Vmm`] struct. +//! +//! The MemProcFS rust API supports creation of native MemProcFS plugins in +//! the form of a library `.dll` or `.so` for the more advanced user. +//! +//! +//! ## Example documentation +//! Check out the example documentation, both in the form of the [example +//! project](https://github.com/ufrisk/MemProcFS/tree/master/vmmrust/memprocfs_example) +//! and the [example MemProcFS plugin](https://github.com/ufrisk/MemProcFS/tree/master/vmmrust/m_example_plugin) +//! +//! +//! ## Project documentation +//! Check out the project documentation for MemProcFS, LeechCore and pcileech-fpga: +//! * [MemProcFS](https://github.com/ufrisk/MemProcFS) - [Documentation](https://github.com/ufrisk/MemProcFS/wiki). +//! * [LeechCore](https://github.com/ufrisk/LeechCore/) - [Documentation](https://github.com/ufrisk/LeechCore/wiki). +//! * [PCILeech](https://github.com/ufrisk/pcileech) - [Documentation](https://github.com/ufrisk/pcileech/wiki). +//! * [PCILeech-FPGA](https://github.com/ufrisk/pcileech-fpga). +//! +//! +//! ## License +//! MemProcFS and its rust API is open source under the [AGPL-3.0](https://github.com/ufrisk/MemProcFS/blob/master/LICENSE) license. +//! +//! +//! # Support PCILeech/MemProcFS development: +//! PCILeech and MemProcFS is free and open source! +//! +//! I put a lot of time and energy into PCILeech and MemProcFS and related research to make this happen. Some aspects of the projects relate to hardware and I put quite some money into my projects and related research. If you think PCILeech and/or MemProcFS are awesome tools and/or if you had a use for them it's now possible to contribute by becoming a sponsor! +//! +//! If you like what I've created with PCIleech and MemProcFS with regards to DMA, Memory Analysis and Memory Forensics and would like to give something back to support future development please consider becoming a sponsor at: +//! +//! To all my sponsors, Thank You 💖 +//! +//! +//! ## Questions and Comments +//! Please feel free to contact me! +//! * Github: +//! * Discord #pcileech channel at the [Porchetta](https://discord.gg/sEkn3aa) server. +//! * Twitter: +//! * Email: pcileech@frizk.net +//! +//! +//! ## Future work +//! Functionality haven't yet made it into the API, but will be added in the near future! +//! * Completed Registry API +//! * Search API +//! +//! +//! ## Get Started! +//! Check out the [`Vmm`] documentation and the [example project](https://github.com/ufrisk/MemProcFS/tree/master/vmmrust/memprocfs_example)! +//! +//! Best wishes with your memory analysis Rust project! + +use std::ffi::{CStr, CString, c_char, c_int}; +use std::fmt; +use serde::{Serialize, Deserialize}; + + + +/// Result type for MemProcFS API. +/// +/// The MemProcFS result type contains a function-defined return type and +/// a String error type. +pub type ResultEx = std::result::Result>; + + + +// MemProcFS memory read/write flags: +/// Do not use internal data cache. +pub const FLAG_NOCACHE : u64 = 0x0001; +/// Zero pad failed memory reads and report success. +pub const FLAG_ZEROPAD_ON_FAIL : u64 = 0x0002; +/// Force use of data cache - fail non-cached pages. +/// +/// Flag is only valid for reads, invalid with VMM_FLAG_NOCACHE/VMM_FLAG_ZEROPAD_ON_FAIL. +pub const FLAG_FORCECACHE_READ : u64 = 0x0008; +/// Do not retrieve memory from paged out memory. +/// +/// Paged out memory may be from pagefile/compressed (even if possible). +/// If slow I/O accesses are the concern the flag `FLAG_NOPAGING_IO` may be a better choice. +pub const FLAG_NOPAGING : u64 = 0x0010; +/// Do not retrieve memory from paged out memory***. +/// +/// ***) If the read would incur additional I/O (even if possible). +pub const FLAG_NOPAGING_IO : u64 = 0x0020; +/// Do not populate the data cache on a successful read. +pub const FLAG_NOCACHEPUT : u64 = 0x0100; +/// Only fetch from the most recent active cache region when reading. +pub const FLAG_CACHE_RECENT_ONLY : u64 = 0x0200; +/// Do not perform additional predictive page reads. +/// +/// This is default on smaller requests. +pub const FLAG_NO_PREDICTIVE_READ : u64 = 0x0400; +/// Disable/override any use of VMM_FLAG_FORCECACHE_READ. +/// +/// This flag is only recommended for local files. improves forensic artifact order. +pub const FLAG_FORCECACHE_READ_DISABLE : u64 = 0x0800; +/// Get/Set library console printouts. +pub const CONFIG_OPT_CORE_PRINTF_ENABLE : u64 = 0x4000000100000000; +/// Get/Set standard verbosity. +pub const CONFIG_OPT_CORE_VERBOSE : u64 = 0x4000000200000000; +/// Get/Set extra verbosity. +pub const CONFIG_OPT_CORE_VERBOSE_EXTRA : u64 = 0x4000000300000000; +/// Get/Set super extra verbosity and PCIe TLP debug. +pub const CONFIG_OPT_CORE_VERBOSE_EXTRA_TLP : u64 = 0x4000000400000000; +/// Get max native physical memory address. +pub const CONFIG_OPT_CORE_MAX_NATIVE_ADDRESS : u64 = 0x4000000800000000; +/// Get the numeric system type according to VMM C-API. +pub const CONFIG_OPT_CORE_SYSTEM : u64 = 0x2000000100000000; +/// Get the numeric memory model type according to the VMM C-API. +pub const CONFIG_OPT_CORE_MEMORYMODEL : u64 = 0x2000000200000000; +/// Get whether the refresh is enabled or not (1/0). +pub const CONFIG_OPT_CONFIG_IS_REFRESH_ENABLED : u64 = 0x2000000300000000; +/// Get/Set base tick period in ms. +pub const CONFIG_OPT_CONFIG_TICK_PERIOD : u64 = 0x2000000400000000; +/// Get/Set memory cache validity period (in ticks). +pub const CONFIG_OPT_CONFIG_READCACHE_TICKS : u64 = 0x2000000500000000; +/// Get/Set page table (tlb) cache validity period (in ticks). +pub const CONFIG_OPT_CONFIG_TLBCACHE_TICKS : u64 = 0x2000000600000000; +/// Get/Set process refresh (partial) period (in ticks). +pub const CONFIG_OPT_CONFIG_PROCCACHE_TICKS_PARTIAL : u64 = 0x2000000700000000; +/// Get/Set process refresh (full) period (in ticks). +pub const CONFIG_OPT_CONFIG_PROCCACHE_TICKS_TOTAL : u64 = 0x2000000800000000; +/// Get MemProcFS major version. +pub const CONFIG_OPT_CONFIG_VMM_VERSION_MAJOR : u64 = 0x2000000900000000; +/// Get MemProcFS minor version. +pub const CONFIG_OPT_CONFIG_VMM_VERSION_MINOR : u64 = 0x2000000A00000000; +/// Get MemProcFS revision version. +pub const CONFIG_OPT_CONFIG_VMM_VERSION_REVISION : u64 = 0x2000000B00000000; +/// Get/Set enable function call statistics (.status/statistics_fncall file) +pub const CONFIG_OPT_CONFIG_STATISTICS_FUNCTIONCALL : u64 = 0x2000000C00000000; +/// Get/Set enable paging support 1/0. +pub const CONFIG_OPT_CONFIG_IS_PAGING_ENABLED : u64 = 0x2000000D00000000; +/// Set native library internal custom debug. +pub const CONFIG_OPT_CONFIG_DEBUG : u64 = 0x2000000E00000000; +/// Get OS version major. +pub const CONFIG_OPT_WIN_VERSION_MAJOR : u64 = 0x2000010100000000; +/// Get OS version minor. +pub const CONFIG_OPT_WIN_VERSION_MINOR : u64 = 0x2000010200000000; +/// Get OS version build. +pub const CONFIG_OPT_WIN_VERSION_BUILD : u64 = 0x2000010300000000; +/// Get MemProcFS unique system id. +pub const CONFIG_OPT_WIN_SYSTEM_UNIQUE_ID : u64 = 0x2000010400000000; +/// Get/Set enable/retrieve forensic mode type [0-4]. +pub const CONFIG_OPT_FORENSIC_MODE : u64 = 0x2000020100000000; + +// REFRESH OPTIONS: +/// Set - trigger refresh all caches. +pub const CONFIG_OPT_REFRESH_ALL : u64 = 0x2001ffff00000000; +/// Set - refresh memory cache (excl. TLB) (fully) +pub const CONFIG_OPT_REFRESH_FREQ_MEM : u64 = 0x2001100000000000; +/// Set - refresh memory cache (excl. TLB) [partial 33%/call] +pub const CONFIG_OPT_REFRESH_FREQ_MEM_PARTIAL : u64 = 0x2001000200000000; +/// Set - refresh page table (TLB) cache (fully) +pub const CONFIG_OPT_REFRESH_FREQ_TLB : u64 = 0x2001080000000000; +/// Set - refresh page table (TLB) cache [partial 33%/call] +pub const CONFIG_OPT_REFRESH_FREQ_TLB_PARTIAL : u64 = 0x2001000400000000; +/// Set - refresh fast frequency - incl. partial process refresh. +pub const CONFIG_OPT_REFRESH_FREQ_FAST : u64 = 0x2001040000000000; +/// Set - refresh medium frequency - incl. full process refresh. +pub const CONFIG_OPT_REFRESH_FREQ_MEDIUM : u64 = 0x2001000100000000; +/// Set - refresh slow frequency. +pub const CONFIG_OPT_REFRESH_FREQ_SLOW : u64 = 0x2001001000000000; +/// Set custom process directory table base. [LO-DWORD: Process PID]. +pub const CONFIG_OPT_PROCESS_DTB : u64 = 0x2002000100000000; + +// PLUGIN NOTIFICATIONS: +/// Verbosity change. Query new verbosity with: `vmm.get_config()`. +pub const PLUGIN_NOTIFY_VERBOSITYCHANGE : u32 = 0x01; +/// Fast refresh. Partial process refresh. +pub const PLUGIN_NOTIFY_REFRESH_FAST : u32 = 0x05; +/// Medium refresh. Full process refresh and other refresh tasks. +pub const PLUGIN_NOTIFY_REFRESH_MEDIUM : u32 = 0x02; +/// Slow refresh. Total refresh of as much as possible. +pub const PLUGIN_NOTIFY_REFRESH_SLOW : u32 = 0x04; +/// Forensic mode initialization start. +pub const PLUGIN_NOTIFY_FORENSIC_INIT : u32 = 0x01000100; +/// Forensic mode processing is completed. +pub const PLUGIN_NOTIFY_FORENSIC_INIT_COMPLETE : u32 = 0x01000200; +/// A child VM was attached or detached. Query new state with API. +pub const PLUGIN_NOTIFY_VM_ATTACH_DETACH : u32 = 0x01000400; + + + +/// MemProcFS API Base Struct. +/// +/// The [`Vmm`] struct is the base of the MemProcFS API. All API accesses +/// takes place from the [`Vmm`] struct and its sub-structs. +/// +/// The [`Vmm`] struct acts as a wrapper around the native MemProcFS VMM API. +/// +/// Check out the example project for more detailed API usage and +/// additional examples! +/// +/// +/// # Created By +/// - [`Vmm::new()`] +/// - [`Vmm::new_from_virtual_machine()`] +/// - `plugin sub-system` +/// +/// The [`Vmm`] is normally created by [`Vmm::new()`] (see example below). +/// +/// The [`Vmm`] object represents memory analysis of a target system. If the +/// target system contains virtual machines additional child `Vmm` objects +/// representing the individual VMs may be retrieved by calling the +/// function [`Vmm::new_from_virtual_machine()`]. +/// +/// The [`Vmm`] object is also supplied by the plugin API to any plugins created. +/// +/// +/// # Examples +/// +/// ``` +/// // Initialize MemProcFS VMM on a Windows system parsing a +/// // memory dump and virtual machines inside it. +/// let mut args = Vec::new(); +/// args.push("-printf"); +/// args.push("-v"); +/// args.push("-waitinitialize"); +/// args.push("-device"); +/// args.push("C:\\Dumps\\myfullmemorydump.dmp"); +/// args.push("-vm"); +/// if let Ok(vmm) = Vmm::new("C:\\MemProcFS\\vmm.dll", &args) { +/// ... +/// // The underlying native vmm is automatically closed +/// // when the vmm object goes out of scope. +/// }; +/// ``` +/// +/// ``` +/// // Initialize MemProcFS VMM on a Linux system parsing live memory +/// // retrieved from a PCILeech FPGA hardware device. +/// let mut args = Vec::new(); +/// args.push("-device"); +/// args.push("fpga"); +/// if let Ok(vmm) = Vmm::new("/home/user/memprocfs/vmm.so", &args) { +/// ... +/// // The underlying native vmm is automatically closed +/// // when the vmm object goes out of scope. +/// }; +/// ``` +#[allow(dead_code)] +#[derive(Debug)] +pub struct Vmm<'a> { + native : VmmNative, + parent_vmm : Option<&'a Vmm<'a>>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum VmmLogLevel { + _1Critical, + _2Warning, + _3Info, + _4Verbose, + _5Debug, + _6Trace, + _7None, +} + +/// Info: Network connections. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmMapNetEntry { + pub pid : u32, + pub state : u32, + pub address_family : u16, + pub src_is_valid : bool, + pub src_port : u16, + pub src_addr_raw : [u8; 16], + pub src_str : String, + pub dst_is_valid : bool, + pub dst_port : u16, + pub dst_addr_raw : [u8; 16], + pub dst_str : String, + pub va_object : u64, + pub filetime : u64, + pub pool_tag : u32, + pub desc : String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum VmmMapPfnType { + Zero, + Free, + Standby, + Modified, + ModifiedNoWrite, + Bad, + Active, + Transition, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum VmmMapPfnTypeExtended { + Unknown, + Unused, + ProcessPrivate, + PageTable, + LargePage, + DriverLocked, + Shareable, + File, +} + +/// Info: Memory PFN (Page Frame Number). +/// +/// # Created By +/// - `vmmprocess.map_pfn()` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmMapPfnEntry { + pub pfn : u32, + pub location : VmmMapPfnType, + pub is_prototype : bool, + pub color : u32, + // extended attributes below - only valid if is_extended == true + pub is_extended : bool, + pub tp_ex : VmmMapPfnTypeExtended, + pub pid : u32, + pub ptes : [u32; 5], // 1 = pfn:PTE, .. 4 = pfn:PML4E + pub va : u64, + pub va_pte : u64, + pub pte_original : u64, +} + +/// Info: Kernel pool entries. +/// +/// # Created By +/// - `vmm.map_pool()` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmMapPoolEntry { + pub va : u64, + pub cb : u32, + pub tag : u32, + pub is_alloc : bool, + pub tp_pool : u8, // VMMDLL_MAP_POOL_TYPE + pub tp_subsegment : u8, // VMMDLL_MAP_POOL_TYPE_SUBSEGMENT +} + +/// Info: Physical memory map entries. +/// +/// # Created By +/// - `vmm.map_memory()` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmMapMemoryEntry { + pub pa : u64, + pub cb : u64 +} + +/// Info: Services. +/// +/// # Created By +/// - `vmm.map_service()` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmMapServiceEntry { + pub ordinal : u32, + pub va_object : u64, + pub pid : u32, + pub start_type : u32, + pub service_type : u32, + pub current_state : u32, + pub controls_accepted : u32, + pub win32_exit_code : u32, + pub service_specific_exit_code : u32, + pub check_point : u32, + pub wait_hint : u32, + pub name : String, + pub name_display : String, + pub path : String, + pub user_type : String, + pub user_account : String, + pub image_path : String, +} + +/// Info: Users. +/// +/// # Created By +/// - `vmm.map_user()` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmMapUserEntry { + pub user : String, + pub sid : String, + pub va_reg_hive : u64, +} + +/// Info: Virtual Machines (VMs). +/// +/// # Created By +/// - `vmm.map_virtual_machine()` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmMapVirtualMachineEntry { + h_vmm : usize, + h_vm : usize, + pub name : String, + pub gpa_max : u64, + pub tp_vm : u32, + pub is_active : bool, + pub is_readonly : bool, + pub is_physicalonly : bool, + pub partition_id : u32, + pub guest_os_version_build : u32, + pub guest_tp_system : u32, + pub parent_mount_id : u32, + pub vmmem_pid : u32, +} + +/// VFS (Virtual File System) entry information - file or directory. +/// +/// # Created By +/// - `vmm.vfs_list()` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmVfsEntry { + pub name : String, + pub is_directory : bool, + pub size : u64, +} + +impl Vmm<'_> { + /// MemProcFS Initialization Function. + /// + /// The [`Vmm`] struct is the base of the MemProcFS API. All API accesses + /// takes place from the [`Vmm`] struct and its sub-structs. + /// + /// The [`Vmm`] struct acts as a wrapper around the native MemProcFS VMM API. + /// + /// + /// # Arguments + /// * `vmm_lib_path` - The full path to the native vmm library - i.e. `vmm.dll` or `vmm.so`. + /// * `args` - MemProcFS command line arguments as a Vec<&str>. + /// + /// MemProcFS command line argument documentation is found on the [MemProcFS wiki](https://github.com/ufrisk/MemProcFS/wiki/_CommandLine). + /// + /// + /// # Examples + /// + /// ``` + /// // Initialize MemProcFS VMM on a Windows system parsing a + /// // memory dump and virtual machines inside it. + /// let mut args = Vec::new(); + /// args.push("-printf"); + /// args.push("-v"); + /// args.push("-waitinitialize"); + /// args.push("-device"); + /// args.push("C:\\Dumps\\myfullmemorydump.dmp"); + /// args.push("-vm"); + /// if let Ok(vmm) = Vmm::new("C:\\MemProcFS\\vmm.dll", &args) { + /// ... + /// // The underlying native vmm is automatically closed + /// // when the vmm object goes out of scope. + /// }; + /// ``` + /// + /// ``` + /// // Initialize MemProcFS VMM on a Linux system parsing live memory + /// // retrieved from a PCILeech FPGA hardware device. + /// let mut args = Vec::new(); + /// args.push("-device"); + /// args.push("fpga"); + /// if let Ok(vmm) = Vmm::new("/home/user/memprocfs/vmm.so", &args) { + /// ... + /// // The underlying native vmm is automatically closed + /// // when the vmm object goes out of scope. + /// }; + /// ``` + pub fn new<'a>(vmm_lib_path : &str, args: &Vec<&str>) -> ResultEx> { + return crate::impl_new(vmm_lib_path, 0, args); + } + + /// Initialize MemProcFS from a host VMM and a child VM. + /// + /// Initialize a MemProcFS VMM object representing a child virtual machine (VM). + /// + /// # Arguments + /// * `vmm_parent` - The host (parent) [`Vmm`]. + /// * `vm_entry` - The [`VmmMapVirtualMachineEntry`] to initialize as a [`Vmm`]. + /// + /// # Examples + /// ``` + /// if let Ok(virtualmachine_all) = vmm.map_virtual_machine() { + /// for virtualmachine in &*virtualmachine_all { + /// println!("{virtualmachine}"); + /// if virtualmachine.is_active { + /// // for active vms it's possible to create a new vmm object for + /// // the vm. it's possible to treat this as any other vmm object + /// // to read memory, query processes etc. + /// let vmm_vm = match Vmm::new_from_virtual_machine(&vmm, &virtualmachine) { + /// Err(_) => continue, + /// Ok(r) => r, + /// }; + /// let max_addr = vmm_vm.get_config(CONFIG_OPT_CORE_MAX_NATIVE_ADDRESS).unwrap_or(0); + /// println!("vm max native address: {:#x}", max_addr); + /// } + /// } + /// } + /// ``` + pub fn new_from_virtual_machine<'a>(vmm_parent : &'a Vmm, vm_entry : &VmmMapVirtualMachineEntry) -> ResultEx> { + return crate::impl_new_from_virtual_machine(vmm_parent, vm_entry); + } + + /// Retrieve a single process by PID. + /// + /// # Arguments + /// * `pid` - The process id (PID) of the process to retrieve. + /// + /// # Examples + /// ``` + /// if let Ok(process) = vmm.process_from_pid(4) { + /// println!("{}", process); + /// } + /// ``` + pub fn process_from_pid(&self, pid : u32) -> ResultEx { + return self.impl_process_from_pid(pid); + } + + /// Retrieve a single process by name. + /// + /// If multiple processes have the same name the first process located by + /// MemProcFS will be returned. If it is important to fetch the correct + /// process retrieve the process list from `vmm.list()` and iterate. + /// + /// # Arguments + /// * `process_name` - Name of the process to retrieve. + /// + /// # Examples + /// ``` + /// if let Ok(process) = vmm.process_from_name("System") { + /// println!("{}", process); + /// } + /// ``` + pub fn process_from_name(&self, process_name : &str) -> ResultEx { + return self.impl_process_from_name(process_name); + } + + /// Retrieve all processes. + /// + /// # Examples + /// ``` + /// if let Ok(process_all) = vmm.process_list() { + /// for process in &*process_all { + /// println!("{process} "); + /// } + /// } + /// ``` + pub fn process_list(&self) -> ResultEx> { + return self.impl_process_list(); + } + + /// Get a numeric configuration value. + /// + /// # Arguments + /// * `config_id` - As specified by one CONFIG_OPT_* constant marked as `Get`. (Optionally or'ed | with process pid for select options). + /// + /// # Examples + /// ``` + /// println!("max addr: {:#x}", vmm.get_config(CONFIG_OPT_CORE_MAX_NATIVE_ADDRESS).unwrap_or(0)); + /// ``` + pub fn get_config(&self, config_id : u64) -> ResultEx { + return self.impl_get_config(config_id); + } + + /// Set a numeric configuration value. + /// + /// # Arguments + /// * `config_id` - As specified by one CONFIG_OPT_* constant marked as `Set`. (Optionally or'ed | with process pid for select options). + /// * `config_value` - The config value to set. + /// + /// # Examples + /// ``` + /// // The below force MemProcFS to undertake a full refresh - refresing + /// // processes, memory and other general data structures completely. + /// let _r = vmm.set_config(CONFIG_OPT_REFRESH_ALL, 1); + /// ``` + pub fn set_config(&self, config_id : u64, config_value : u64) -> ResultEx<()> { + return self.impl_set_config(config_id, config_value); + } + + /// Retrieve the kernel convenience struct. + /// + /// The kernel struct provides easy access to kernel build number, + /// the system process (pid 4) and kernel (nt) debug symbols. + /// + /// # Examples + /// ``` + /// // Retrieve and print the kernel build number. + /// println!("{}", vmm.kernel().build()); + /// ``` + pub fn kernel(&self) -> VmmKernel { + return VmmKernel { vmm : &self }; + } + + /// Log a message to the MemProcFS logging system. + /// + /// # Arguments + /// * `log_level` + /// * `log_message` + /// + /// # Examples + /// ``` + /// vmm.log(&VmmLogLevel::_1Critical, "Test Message Critical!"); + /// ``` + pub fn log(&self, log_level : &VmmLogLevel, log_message : &str) { + self.impl_log(VMMDLL_MID_RUST, log_level, log_message); + } + + /// Retrieve the physical memory info map. + /// + /// # Examples + /// ``` + /// if let Ok(memory_range_all) = vmm.map_memory() { + /// for memory_range in &*memory_range_all { + /// println!("{memory_range} \t pa={:x} cb={:x}", memory_range.pa, memory_range.cb); + /// } + /// } + /// ``` + pub fn map_memory(&self) -> ResultEx> { + return self.impl_map_memory(); + } + + /// Retrieve the network connection info map. + /// + /// # Examples + /// ``` + /// if let Ok(net_all) = vmm.map_net() { + /// for net in &*net_all { + /// println!("{net}"); + /// } + /// } + /// ``` + pub fn map_net(&self) -> ResultEx> { + return self.impl_map_net(); + } + + /// Retrieve the page frame number (PFN) info map. + /// + /// # Arguments + /// * `pfns` - The PFNs to retrieve. + /// * `is_extended` - Retrieve extended information (more resource intense). + /// + /// # Examples + /// ``` + /// let pfns: Vec = (1..=10).collect(); + /// if let Ok(pfn_all) = vmm.map_pfn(&pfns, true) { + /// for pfn in &*pfn_all { + /// println!("{pfn} \t location={} tp_ex={} pid={:x} va={:x} color={}", + /// pfn.location, pfn.tp_ex, pfn.pid, pfn.va, pfn.color); + /// } + /// } + /// ``` + pub fn map_pfn(&self, pfns : &Vec, is_extended : bool) -> ResultEx> { + return self.impl_map_pfn(pfns, is_extended); + } + + /// Retrieve the kernel pool allocation info map. + /// + /// # Arguments + /// * `is_bigpool_only` - xxxx. + /// + /// # Examples + /// ``` + /// if let Ok(pool_all) = vmm.map_pool(false) { + /// println!("Number of pool allocations: {}.", pool_all.len()); + /// let pool_proc_all : Vec<&VmmMapPoolEntry> = + /// pool_all.iter().filter(|e| e.tag == 0x636f7250 /* 'Proc' backwards */).collect(); + /// println!("Number of pool 'Proc' allocations: {}.", pool_all.len()); + /// for pool_proc in &*pool_proc_all { + /// print!("{pool_proc} "); + /// } + /// println!(""); + /// } + /// ``` + pub fn map_pool(&self, is_bigpool_only : bool) -> ResultEx> { + return self.impl_map_pool(is_bigpool_only); + } + + /// Retrieve the servives info map. + /// + /// # Examples + /// ``` + /// if let Ok(service_all) = vmm.map_service() { + /// for service in &*service_all { + /// println!("{service} "); + /// } + /// } + /// ``` + pub fn map_service(&self) -> ResultEx> { + return self.impl_map_service(); + } + + /// Retrieve the user map. + /// + /// # Examples + /// ``` + /// if let Ok(user_all) = vmm.map_user() { + /// for user in &*user_all { + /// println!("{:x}:: {} :: {} :: {user}", user.va_reg_hive, user.sid, user.user); + /// } + /// } + /// ``` + pub fn map_user(&self) -> ResultEx> { + return self.impl_map_user(); + } + + /// Retrieve the virtual machines info map. + /// + /// # Examples + /// ``` + /// if let Ok(user_all) = vmm.map_user() { + /// for user in &*user_all { + /// println!("{:x}:: {} :: {} :: {user}", user.va_reg_hive, user.sid, user.user); + /// } + /// } + /// ``` + pub fn map_virtual_machine(&self) -> ResultEx> { + return self.impl_map_virtual_machine(); + } + + /// Read a contigious physical memory chunk. + /// + /// The physical memory is read without any special flags. The whole chunk + /// must be read successfully for the method to succeed. + /// + /// If deseriable to provide flags modifying the behavior (such as skipping + /// the built-in data cache or slower paging access) use the method + /// `mem_read_ex()` instead. + /// + /// Reading many memory chunks individually may be slow, especially if + /// reading takes place using hardware FPGA devices. In that case it's + /// better to use the `mem_scatter()` functionality for better performance. + /// + /// + /// # Arguments + /// * `pa` - Physical address to start reading from. + /// * `size` - The number of bytes to read. + /// + /// # Examples + /// ``` + /// // Read 0x100 bytes of data starting at address 0x1000. + /// // Example assumes: use pretty_hex::*; + /// if let Ok(data_read) = vmm.mem_read(0x1000, 0x100) { + /// println!("{:?}", data_read.hex_dump()); + /// } + /// ``` + pub fn mem_read(&self, pa : u64, size : usize) -> ResultEx> { + return self.impl_mem_read(u32::MAX, pa, size, 0); + } + + /// Read a contigious physical memory chunk with flags. + /// + /// Flags are constants named `FLAG_*` + /// + /// Reading many memory chunks individually may be slow, especially if + /// reading takes place using hardware FPGA devices. In that case it's + /// better to use the `mem_scatter()` functionality for better performance. + /// + /// + /// # Arguments + /// * `pa` - Physical address to start reading from. + /// * `size` - The number of bytes to read. + /// * `flags` - Any combination of FLAG_*. + /// + /// # Examples + /// ``` + /// // Read 0x100 bytes of data starting at address 0x1000. + /// // Force reading the underlying memory device (skip data cache) and + /// // Zero-Pad if parts of the memory read fail instead of failing. + /// // Example assumes: use pretty_hex::*; + /// if let Ok(data_read) = vmm.mem_read_ex(0x1000, 0x100, FLAG_NOCACHE | FLAG_ZEROPAD_ON_FAIL) { + /// println!("{:?}", data_read.hex_dump()); + /// } + /// ``` + pub fn mem_read_ex(&self, pa : u64, size : usize, flags : u64) -> ResultEx> { + return self.impl_mem_read(u32::MAX, pa, size, flags); + } + + /// Create a scatter memory object for efficient physical memory reads. + /// + /// Check out the [`VmmScatterMemory`] struct for more detailed information. + /// + /// # Arguments + /// * `flags` - Any combination of FLAG_*. + /// + /// # Examples + /// ``` + /// let mem_scatter_physical = vmm.mem_scatter(FLAG_NOCACHE | FLAG_ZEROPAD_ON_FAIL)?; + /// ``` + pub fn mem_scatter(&self, flags : u64) -> ResultEx { + return self.impl_mem_scatter(u32::MAX, flags); + } + + /// Write physical memory. + /// + /// The write is a best effort. Even of the write should fail it's not + /// certain that an error will be returned. To be absolutely certain that + /// a write has taken place follow up with a read. + /// + /// # Arguments + /// * `pa` - Physical address to start writing from. + /// * `data` - Byte data to write. + /// + /// # Examples + /// ``` + /// let data_to_write = [0x56u8, 0x4d, 0x4d, 0x52, 0x55, 0x53, 0x54].to_vec(); + /// let _r = vmm.mem_write(0x1000, &data_to_write); + /// ``` + pub fn mem_write(&self, pa : u64, data : &Vec) -> ResultEx<()> { + return self.impl_mem_write(u32::MAX, pa, data); + } + + /// List a VFS (Virtual File System) directory. + /// + /// Returns a result containing the individual directory entries - + /// which may be files or directories. + /// + /// # Arguments + /// * `path` - Vfs path to list directory contents in. Ex: /sys/ + /// + /// # Examples + /// ``` + /// let vfs_list_path = "/sys/"; + /// if let Ok(vfs_all) = vmm.vfs_list(vfs_list_path) { + /// println!("VFS directory listing for directory: {vfs_list_path}"); + /// println!("Number of file/directory entries: {}.", vfs_all.len()); + /// for vfs in &*vfs_all { + /// println!("{vfs}"); + /// } + /// } + /// ``` + pub fn vfs_list(&self, path : &str) -> ResultEx> { + return self.impl_vfs_list(path); + } + + /// Read a VFS (Virtual File System) file. + /// + /// The read contents are returned as a Vec containing the byte results. + /// If the end of the file is reached the number of read bytes may be + /// shorter than the requested read size. + /// + /// # Arguments + /// * `filename` - Full vfs path of the file to read. Ex: /sys/version.txt + /// * `size` - The number of bytes to read. + /// * `offset` - File offset. + /// + /// # Examples + /// ``` + /// if let Ok(vfs_file_data) = vmm.vfs_read("/sys/memory/physmemmap.txt", 0x2000, 0) { + /// println!("Bytes read from file '/sys/memory/physmemmap.txt': {}.", vfs_file_data.len()); + /// println!("{:?}", vfs_file_data.hex_dump()); + /// } + /// ``` + pub fn vfs_read(&self, filename : &str, size : u32, offset : u64) -> ResultEx> { + return self.impl_vfs_read(filename, size, offset); + } + + /// Write a VFS (Virtual File System) file. + /// + /// Writes are undertaken on a best-effort basis. Writing to read-only + /// files will have no meaning. Writing to memory may or may not be + /// possible depending on various factors. If important, it's recommended + /// to verify the `vfs_write()` with a `vfs_read()`. + /// + /// # Arguments + /// * `filename` - Full vfs path of the file to write. Ex: /conf/config_printf_enable.txt + /// * `data` - The byte data to write. + /// * `offset` - File offset. + /// + /// # Examples + /// ``` + /// let vfs_write_data = vec![1u8; 1]; + /// vmm.vfs_write("/conf/config_process_show_terminated.txt", vfs_write_data, 0); + /// ``` + pub fn vfs_write(&self, filename : &str, data : Vec, offset : u64) { + return self.impl_vfs_write(filename, data, offset); + } + + /// Retrieve all registry hives. + /// + /// # Examples + /// ``` + /// let hive_all = vmm.reg_hive_list()?; + /// for hive in hive_all { + /// println!("{hive} size={} path={}", hive.size, hive.path); + /// } + /// ``` + pub fn reg_hive_list(&self) -> ResultEx> { + return self.impl_reg_hive_list(); + } +} + + + + + + +/// Kernel information. +/// +/// The kernel struct gives easy access to: +/// * The system process (pid 4). +/// * Kernel build number. +/// * Kernel debug symbols (nt). +/// +/// +/// # Created By +/// - `vmm.kernel()` +/// +/// # Examples +/// ``` +/// println!("{}", vmm.kernel().process()); +/// println!("{}", vmm.kernel().build()); +/// let kernel = vmm.kernel(); +/// let pdb = kernel.pdb(); +/// println!("{pdb}"); +/// ``` +#[derive(Debug)] +pub struct VmmKernel<'a> { + vmm : &'a Vmm<'a>, +} + +impl VmmKernel<'_> { + /// Get the kernel build numer. + /// + /// # Examples + /// ``` + /// // Retrieve and print the kernel build number. + /// println!("{}", vmm.kernel().build()); + /// ``` + pub fn build(&self) -> u32 { + return self.vmm.get_config(CONFIG_OPT_WIN_VERSION_BUILD).unwrap_or_default().try_into().unwrap_or_default(); + } + + /// Get the System process (pid 4). + /// + /// # Examples + /// ``` + /// // Retrieve and print the kernel build number. + /// let systemprocess = vmm.kernel().process(); + /// ``` + pub fn process(&self) -> VmmProcess { + return VmmProcess { vmm : self.vmm, pid : 4 }; + } + + /// Get kernel debug information (nt). + /// + /// For additional information about debug symbols check out the [`VmmPdb`] struct. + /// + /// # Examples + /// ``` + /// // Retrieve and print the kernel build number. + /// let pdb_nt = vmm.kernel().pdb(); + /// ``` + pub fn pdb(&self) -> VmmPdb { + return VmmPdb { vmm : self.vmm, module : String::from("nt") }; + } +} + + + + + +/// Debug Symbol API. +/// +/// The PDB sub-system requires that MemProcFS supporting DLLs/.SO's for +/// debugging and symbol server are put alongside `vmm.dll`. Also it's +/// recommended that the file `info.db` is put alongside `vmm.dll`. +/// +/// +/// # Created By +/// - `vmmprocess.pdb_from_module_address()` +/// - `vmm.kernel().pdb()` +/// +/// # Examples +/// ``` +/// // Retrieve the PDB struct associated with the kernel (nt). +/// let kernel = vmm.kernel(); +/// let pdb = kernel.pdb(); +/// ``` +/// +/// ``` +/// // Retrieve the PDB struct associated with a process module. +/// let pdb = vmmprocess.pdb("ntdll.dll")?; +/// ``` +#[derive(Debug)] +pub struct VmmPdb<'a> { + vmm : &'a Vmm<'a>, + pub module : String, +} + +impl VmmPdb<'_> { + /// Retrieve a symbol name and a displacement given a module offset or virtual address. + /// + /// # Arguments + /// * `va_or_offset` - Virtual address or offset from module base. + /// + /// # Examples + /// ``` + /// if let Ok(r) = pdb.symbol_name_from_address(va_symbol) { + /// println!("va_o: {:x} name: '{}' displacement: {:x}", va_symbol, r.0, r.1); + /// } + /// ``` + pub fn symbol_name_from_address(&self, va_or_offset : u64) -> ResultEx<(String, u32)> { + return self.impl_symbol_name_from_address(va_or_offset); + } + + /// Lookup a symbol address given its name. + /// + /// # Arguments + /// * `symbol_name` + /// + /// # Examples + /// ``` + /// let va = pdb_nt.symbol_address_from_name("MiMapContiguousMemory")?; + /// ``` + pub fn symbol_address_from_name(&self, symbol_name : &str) -> ResultEx { + return self.impl_symbol_address_from_name(symbol_name); + } + + /// Retrieve the size of a struct/type. + /// + /// # Arguments + /// * `type_name` + /// + /// # Examples + /// ``` + /// let size_eprocess = pdb_nt.type_size("_EPROCESS")?; + /// ``` + pub fn type_size(&self, type_name : &str) -> ResultEx { + return self.impl_type_size(type_name); + } + + /// Retrieve offset of a struct child member. + /// + /// # Arguments + /// * `type_name` + /// * `type_child_name` + /// + /// # Examples + /// ``` + /// let offet_vadroot = pdb_nt.type_child_offset("_EPROCESS", "VadRoot")? + /// ``` + pub fn type_child_offset(&self, type_name : &str, type_child_name : &str) -> ResultEx { + return self.impl_type_child_offset(type_name, type_child_name); + } +} + + + + + + +/// Efficient Memory Reading API. +/// +/// The Scatter Memory API allows reading several scattered memory regions at +/// the same time in one pass - greatly improving efficiency over multiple +/// normal memory reads. +/// +/// The Rust Scatter API may be used in two different ways, both are displayed +/// below in the examples section. +/// +/// +/// # Created By +/// - `vmm.mem_scatter()` +/// - `vmmprocess.mem_scatter()` +/// +/// # Example #1 +/// ``` +/// // Example: vmmprocess.mem_scatter() #1: +/// // This example will show how it's possible to use VmmScatterMemory to +/// // more efficiently read memory from the underlying device. +/// { +/// // Example: vmmprocess.mem_scatter(): +/// // Retrieve a scatter memory read object that may be used to batch +/// // several reads/writes into one efficient call to the memory device. +/// println!("========================================"); +/// println!("vmmprocess.mem_scatter() #1:"); +/// let mem_scatter = vmmprocess.mem_scatter(FLAG_NOCACHE | FLAG_ZEROPAD_ON_FAIL).unwrap(); +/// println!("mem_scatter = {mem_scatter}"); +/// // Prepare three memory ranges to read. +/// let _r = mem_scatter.prepare(kernel32.va_base + 0x0000, 0x100); +/// let _r = mem_scatter.prepare(kernel32.va_base + 0x1000, 0x100); +/// let _r = mem_scatter.prepare(kernel32.va_base + 0x2000, 0x100); +/// // Perform the actual read (and writes) by calling the execute() function. +/// let _r = mem_scatter.execute(); +/// // Fetch data read. It's possible (but wasteful) to read less data than was prepared. +/// if let Ok(data_read) = mem_scatter.read(kernel32.va_base + 0x0000, 0x80) { +/// println!("memory range: va={:x} cb={:x} cb_read={:x}", kernel32.va_base + 0x0000, 0x80, data_read.len()); +/// println!("{:?}", data_read.hex_dump()); +/// println!("-----------------------"); +/// } +/// if let Ok(data_read) = mem_scatter.read(kernel32.va_base + 0x1000, 0x100) { +/// println!("memory range: va={:x} cb={:x} cb_read={:x}", kernel32.va_base + 0x1000, 0x100, data_read.len()); +/// println!("{:?}", data_read.hex_dump()); +/// println!("-----------------------"); +/// } +/// // It's possible to do a re-read of the ranges by calling execute again! +/// let _r = mem_scatter.execute(); +/// if let Ok(data_read) = mem_scatter.read(kernel32.va_base + 0x0000, 0x80) { +/// println!("memory range: va={:x} cb={:x} cb_read={:x}", kernel32.va_base + 0x0000, 0x80, data_read.len()); +/// println!("{:?}", data_read.hex_dump()); +/// println!("-----------------------"); +/// } +/// // It's also possible to clear the VmmScatterMemory to start anew. +/// // Clearing is slightly more efficient than creating a new object. +/// // let _r = mem_scatter.clear(); +/// +/// // NB! the VmmScatterMemory struct will be automatically free'd +/// // on the native backend when it goes out of scope. +/// } +/// ``` +/// +/// # Example #2 +/// ``` +/// // Example: vmmprocess.mem_scatter() #2: +/// // This example demo how it's possible to use the prepare_ex function +/// // which will populate the prepared data regions automatically when the +/// // VmmScatterMemory is dropped. +/// // It's not recommended to mix the #1 and #2 syntaxes. +/// { +/// // memory ranges to read are tuples: +/// // .0 = the virtual address to read. +/// // .1 = vector of u8 which memory should be read into. +/// // .2 = u32 receiving the bytes successfully read data. +/// let mut memory_range_1 = (kernel32.va_base + 0x0000, vec![0u8; 0x100], 0u32); +/// let mut memory_range_2 = (kernel32.va_base + 0x1000, vec![0u8; 0x100], 0u32); +/// let mut memory_range_3 = (kernel32.va_base + 0x2000, vec![0u8; 0x100], 0u32); +/// // Feed the ranges into a mutable VmmScatterMemory inside a +/// // separate scope. The actual memory read will take place when +/// // the VmmScatterMemory goes out of scope and are dropped. +/// println!("========================================"); +/// println!("vmmprocess.mem_scatter() #2:"); +/// if let Ok(mut mem_scatter) = vmmprocess.mem_scatter(FLAG_NOCACHE | FLAG_ZEROPAD_ON_FAIL) { +/// let _r = mem_scatter.prepare_ex(&mut memory_range_1); +/// let _r = mem_scatter.prepare_ex(&mut memory_range_2); +/// let _r = mem_scatter.prepare_ex(&mut memory_range_3); +/// } +/// // Results should now be available in the memory ranges if the read +/// // was successful. Note that there is no guarantee that memory is +/// // read - make sure to check the .2 item - number of bytes read. +/// for memory_range in [memory_range_1, memory_range_2, memory_range_3] { +/// println!("memory range: va={:x} cb={:x} cb_read={:x}", memory_range.0, memory_range.1.len(), memory_range.2); +/// println!("{:?}", memory_range.1.hex_dump()); +/// println!("-----------------------"); +/// } +/// } +/// ``` +#[derive(Debug)] +pub struct VmmScatterMemory<'a> { + vmm : &'a Vmm<'a>, + hs : usize, + pid : u32, + flags : u32, + is_scatter_ex : bool, +} + +impl <'a> VmmScatterMemory<'a> { + /// Prepare a memory range for reading according to method #2. + /// + /// Once the `mem_scatter.execute()` call has been made the memory + /// read should (if successful) be found in the prepared tuple. + /// + /// See the [`VmmScatterMemory`] struct for an example. + /// + /// # Arguments + /// * `data_to_read` - Tuple with data to prepare as below: + /// * `data_to_read.0` - Address to start read from. + /// * `data_to_read.1` - Byte Vec with space to fill with read data on success. + /// * `data_to_read.2` - Bytes actually read on `mem_scatter.execute()` call. Should be zero at call to `mem_scatter.prepare_ex()`. + pub fn prepare_ex(&mut self, data_to_read : &'a mut (u64, Vec, u32)) -> ResultEx<()> { + return self.impl_prepare_ex(data_to_read); + } +} + +impl VmmScatterMemory<'_> { + /// Prepare a memory range for reading according to method #1. + /// + /// Once the `mem_scatter.execute()` call has been made it's possible + /// to read the memory by calling `mem_scatter.read()`. + /// + /// See the [`VmmScatterMemory`] struct for an example. + /// + /// # Arguments + /// * `va` - Address to prepare to read from. + /// * `size` - Number of bytes to read. + pub fn prepare(&self, va : u64, size : usize) -> ResultEx<()> { + return self.impl_prepare(va, size); + } + + /// Prepare a memory range for writing. + /// + /// Writing takes place on the call to `mem_scatter.execute()`. + /// + /// # Arguments + /// * `va` - Address to prepare to write to. + /// * `data` - Data to write. + pub fn prepare_write(&self, va : u64, data : &Vec) -> ResultEx<()> { + return self.impl_prepare_write(va, data); + } + + /// Execute the scatter call to the underlying memory device. + /// + /// This function takes care of all reading and writing. After + /// this function is called it's possible to read memory, or if + /// approach #2 is used the memory should already be read into + /// buffers prepared with the call to `mem_scatter.prepare_ex()`. + pub fn execute(&self) -> ResultEx<()> { + return self.impl_execute(); + } + + /// Read memory prepared after the `execute()` call. + pub fn read(&self, va : u64, size : usize) -> ResultEx> { + return self.impl_read(va, size); + } + + /// Clear the scatter memory for additional read/writes. + pub fn clear(&self) -> ResultEx<()> { + return self.impl_clear(); + } +} + + + + + + +/// Process API Base Struct. +/// +/// The [`VmmProcess`] struct is the base of the per-process related +/// functionality of the MemProcFS API. The [`VmmProcess`] struct should +/// be considered a child to the main [`Vmm`] struct. +/// +/// Check out the example project for more detailed API usage and +/// additional examples! +/// +/// +/// # Created By +/// - `vmm.process_from_pid()` +/// - `vmm.process_from_name()` +/// - `vmm.process_list()` +/// - `vmm.kernel().process()` +/// - `plugin sub-system` +/// +/// +/// # Examples +/// +/// ``` +/// // Retrieve all processes: +/// if let Ok(process_all) = vmm.process_list() { +/// for process in &*process_all { +/// print!("{process} "); +/// } +/// } +/// ``` +/// +/// ``` +/// // Retrieve a process by its name. If more than one process share the +/// // same name the first found will be returned. +/// if let Ok(systemprocess) = vmm.process_from_name("System") { +/// print!("{process} "); +/// }; +/// ``` +/// +/// ``` +/// // Retrieve a process by its PID. +/// if let Ok(systemprocess) = vmm.process_from_pid(4) { +/// print!("{process} "); +/// }; +/// ``` +#[derive(Debug)] +pub struct VmmProcess<'a> { + vmm : &'a Vmm<'a>, + pub pid : u32, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum VmmIntegrityLevelType { + Unknown, + Untrusted, + Low, + Medium, + MediumPlus, + High, + System, + Protected, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum VmmMemoryModelType { + NA, + X86, + X86PAE, + X64, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum VmmSystemType { + UnknownPhysical, + UnknownX64, + WindowsX64, + UnknownX86, + WindowsX86, +} + +/// Process Information. +/// +/// # Created By +/// - `vmmprocess.info()` +/// +/// # Examples +/// ``` +/// // Retrieve the VmmProcess info struct from a process. +/// // It's better to retrieve this struct once and query its fields rather +/// // than calling `vmmprocess.info()` repetedly since there is a small +/// // native overhead doing so. +/// if let Ok(procinfo) = vmmprocess.info() { +/// println!("struct -> {procinfo}"); +/// println!("pid -> {}", procinfo.pid); +/// println!("ppid -> {}", procinfo.pid); +/// println!("peb -> {:x}", procinfo.va_peb); +/// println!("eprocess -> {:x}", procinfo.va_eprocess); +/// println!("name -> {}", procinfo.name); +/// println!("longname -> {}", procinfo.name_long); +/// println!("SID -> {}", procinfo.sid); +/// } +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmProcessInfo { + pub pid : u32, + pub ppid : u32, + pub name : String, + pub name_long : String, + pub tp_system : VmmSystemType, + pub tp_memorymodel : VmmMemoryModelType, + pub is_user_mode : bool, + pub state : u32, + pub pa_dtb : u64, + pub pa_dtb_user : u64, + pub va_eprocess : u64, + pub va_peb : u64, + pub is_wow64 : bool, + pub va_peb32 : u32, + pub session_id : u32, + pub luid : u64, + pub sid : String, + pub integrity_level : VmmIntegrityLevelType, +} + +/// Info: Process Module: PE data directories. +/// +/// # Created By +/// - `vmmprocess.map_module_data_directory()` +/// +/// # Examples +/// ``` +/// if let Ok(data_directory_all) = vmmprocess.map_module_data_directory("kernel32.dll") { +/// println!("Number of module data directories: {}.", data_directory_all.len()); +/// for data_directory in &*data_directory_all { +/// println!("{data_directory}"); +/// } +/// } +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmProcessMapDirectoryEntry { + pub pid : u32, + pub name : &'static str, + pub virtual_address : u32, + pub size : u32, +} + +/// Info: Process Module: PE exported entries. +/// +/// # Created By +/// - `vmmprocess.map_module_eat()` +/// +/// # Examples +/// ``` +/// if let Ok(eat_all) = vmmprocess.map_module_eat("kernel32.dll") { +/// println!("Number of module exported functions: {}.", eat_all.len()); +/// for eat in &*eat_all { +/// println!("{eat} :: {}", eat.forwarded_function); +/// } +/// } +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmProcessMapEatEntry { + pub pid : u32, + pub va_function : u64, + pub ordinal : u32, + pub function : String, + pub forwarded_function : String, +} + +/// Info: Process: Handles. +/// +/// # Created By +/// - `vmmprocess.map_handle()` +/// +/// # Examples +/// ``` +/// if let Ok(handle_all) = vmmprocess.map_handle() { +/// println!("Number of handle entries: {}.", handle_all.len()); +/// for handle in &*handle_all { +/// println!("{handle}"); +/// } +/// } +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmProcessMapHandleEntry { + pub pid : u32, + pub va_object : u64, + pub handle_id : u32, + pub granted_access : u32, + pub type_index : u32, + pub handle_count : u64, + pub pointer_count : u64, + pub va_object_create_info : u64, + pub va_security_descriptor : u64, + pub handle_pid : u32, + pub pool_tag : u32, + pub info : String, + pub tp : String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum VmmProcessMapHeapType { + NA, + NtHeap, + SegmentHeap, +} + +/// Info: Process: Heaps. +/// +/// # Created By +/// - `vmmprocess.map_heap()` +/// +/// # Examples +/// ``` +/// if let Ok(heap_all) = vmmprocess.map_heap() { +/// println!("Number of heap entries: {}.", heap_all.len()); +/// for heap in &*heap_all { +/// println!("{heap}"); +/// } +/// } +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmProcessMapHeapEntry { + pub pid : u32, + pub tp : VmmProcessMapHeapType, + pub is_32 : bool, + pub index : u32, + pub number : u32, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum VmmProcessMapHeapAllocType { + NA, + NtHeap, + NtLFH, + NtLarge, + NtNA, + SegVS, + SegLFH, + SegLarge, + SegNA, +} + +/// Info: Process: Heap allocations. +/// +/// # Created By +/// - `vmmprocess.map_heapalloc()` +/// +/// # Examples +/// ``` +/// if let Ok(heapalloc_all) = vmmprocess.map_heapalloc(0) { +/// println!("Number of allocated heap entries: {}.", heapalloc_all.len()); +/// for heapalloc in &*heapalloc_all { +/// print!("{heapalloc} "); +/// } +/// println!(""); +/// } +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmProcessMapHeapAllocEntry { + pub pid : u32, + pub va : u64, + pub size : u32, + pub tp : VmmProcessMapHeapAllocType, +} + +/// Info: Process Module: PE imported entries. +/// +/// # Created By +/// - `vmmprocess.map_module_iat()` +/// +/// # Examples +/// ``` +/// if let Ok(iat_all) = vmmprocess.map_module_iat("kernel32.dll") { +/// println!("Number of module imported functions: {}.", iat_all.len()); +/// for iat in &*iat_all { +/// println!("{iat}"); +/// } +/// } +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmProcessMapIatEntry { + pub pid : u32, + pub va_function : u64, + pub function : String, + pub module : String, +} + +/// Info: Process: Modules (loaded DLLs) debug information. +/// +/// # Created By +/// - `vmmprocess.map_module()` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmProcessMapModuleDebugEntry { + pub pid : u32, + pub age : u32, + pub raw_guid : [u8; 16], + pub guid : String, + pub pdb_filename : String, +} + +/// Info: Process: Modules (loaded DLLs) version information. +/// +/// # Created By +/// - `vmmprocess.map_module()` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmProcessMapModuleVersionEntry { + pub pid : u32, + pub company_name : String, + pub file_description : String, + pub file_version : String, + pub internal_name : String, + pub legal_copyright : String, + pub original_file_name : String, + pub product_name : String, + pub product_version : String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum VmmProcessMapModuleType { + Normal, + Data, + NotLinked, + Injected, +} + +/// Info: Process: Modules (loaded DLLs). +/// +/// # Created By +/// - `vmmprocess.map_module()` +/// +/// # Examples +/// ``` +/// if let Ok(module_all) = vmmprocess.map_module(true, true) { +/// println!("Number of process modules: {}.", module_all.len()); +/// for module in &*module_all { +/// println!("{module}"); +/// } +/// } +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmProcessMapModuleEntry { + pub pid : u32, + pub va_base : u64, + pub va_entry : u64, + pub image_size : u32, + pub is_wow64 : bool, + pub tp : VmmProcessMapModuleType, + pub name : String, + pub full_name : String, + pub file_size_raw : u32, + pub section_count : u32, + pub eat_count : u32, + pub iat_count : u32, + pub debug_info : Option, + pub version_info : Option, +} + +/// Info: Process: PTE memory map entries. +/// +/// # Created By +/// - `vmmprocess.map_pte()` +/// +/// # Examples +/// ``` +/// if let Ok(pte_all) = vmmprocess.map_pte(true) { +/// println!("Number of pte entries: {}.", pte_all.len()); +/// for pte in &*pte_all { +/// let s = if pte.is_s { 's' } else { '-' }; +/// let r = if pte.is_r { 'r' } else { '-' }; +/// let w = if pte.is_w { 'w' } else { '-' }; +/// let x = if pte.is_x { 'x' } else { '-' }; +/// println!("{pte} :: {s}{r}{w}{x} :: {}", pte.info); +/// } +/// } +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmProcessMapPteEntry { + pub pid : u32, + pub va_base : u64, + pub page_count : u64, + pub page_software_count : u32, + pub is_wow64 : bool, + pub info : String, + pub is_r : bool, + pub is_w : bool, + pub is_x : bool, + pub is_s : bool, +} + +/// Info: Process Module: PE sections. +/// +/// # Created By +/// - `vmmprocess.map_module_section()` +/// +/// # Examples +/// ``` +/// if let Ok(section_all) = vmmprocess.map_module_section("kernel32.dll") { +/// println!("Number of module sections: {}.", section_all.len()); +/// for section in &*section_all { +/// println!("{section}"); +/// } +/// } +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmProcessSectionEntry { + pub pid : u32, + pub index : u32, + pub name : String, + pub name_raw : [u8; 8], + pub misc_virtual_size : u32, + pub virtual_address : u32, + pub size_of_raw_data : u32, + pub pointer_to_raw_data : u32, + pub pointer_to_relocations : u32, + pub pointer_to_linenumbers : u32, + pub number_of_relocations : u16, + pub number_of_linenumbers : u16, + pub characteristics : u32, +} + +/// Info: Process: Threads. +/// +/// # Created By +/// - `vmmprocess.map_thread()` +/// +/// # Examples +/// ``` +/// if let Ok(thread_all) = vmmprocess.map_thread() { +/// println!("Number of process threads: {}.", thread_all.len()); +/// for thread in &*thread_all { +/// println!("{thread}"); +/// } +/// } +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmProcessMapThreadEntry { + pub pid : u32, + pub thread_id : u32, + pub thread_pid : u32, + pub exit_status : u32, + pub state : u8, + pub running : u8, + pub priority : u8, + pub priority_base : u8, + pub va_ethread : u64, + pub va_teb : u64, + pub ft_create_time : u64, + pub ft_exit_time : u64, + pub va_start_address : u64, + pub va_stack_user_base : u64, + pub va_stack_user_limit : u64, + pub va_stack_kernel_base : u64, + pub va_stack_kernel_limit : u64, + pub va_trap_frame : u64, + pub va_rip : u64, + pub va_rsp : u64, + pub affinity : u64, + pub user_time : u32, + pub kernel_time : u32, + pub suspend_count : u8, + pub wait_reason : u8 +} + +/// Info: Process: Unloaded modules. +/// +/// # Created By +/// - `vmmprocess.map_unloaded_module()` +/// +/// # Examples +/// ``` +/// if let Ok(unloaded_all) = vmmprocess.map_unloaded_module() { +/// println!("Number of process unloaded modules: {}.", unloaded_all.len()); +/// for unloaded in &*unloaded_all { +/// println!("{unloaded}"); +/// } +/// } +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmProcessMapUnloadedModuleEntry { + pub pid : u32, + pub va_base : u64, + pub image_size : u32, + pub is_wow64 : bool, + pub name : String, + pub checksum : u32, // user-mode only + pub timedatestamp : u32, // user-mode only + pub ft_unload : u64, // kernel-mode only +} + +/// Info: Process: VAD (Virtual Address Descriptor) memory map entries. +/// +/// # Created By +/// - `vmmprocess.map_vad()` +/// +/// # Examples +/// ``` +/// if let Ok(vad_all) = vmmprocess.map_vad(true) { +/// println!("Number of vad entries: {}.", vad_all.len()); +/// for vad in &*vad_all { +/// println!("{vad} :: {}", vad.info); +/// } +/// } +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmProcessMapVadEntry { + pub pid : u32, + pub va_start : u64, + pub va_end : u64, + pub va_vad : u64, + pub u0 : u32, + pub u1 : u32, + pub u2 : u32, + pub commit_charge : u32, + pub is_mem_commit : bool, + pub cb_prototype_pte : u32, + pub va_prototype_pte : u64, + pub va_subsection : u64, + pub va_file_object : u64, + pub info : String, + pub vadex_page_base : u32, + pub vadex_page_count : u32, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum VmmProcessMapVadExType { + NA, + Hardware, + Transition, + Prototype, + DemandZero, + Compressed, + Pagefile, + File, +} + +/// Info: Process: Extended VAD memory map entries. +/// +/// # Created By +/// - `vmmprocess.map_vadex()` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmProcessMapVadExEntry { + pub pid : u32, + pub tp : VmmProcessMapVadExType, + pub i_pml : u32, + pub va : u64, + pub pa : u64, + pub pte : u64, + pub proto_tp : VmmProcessMapVadExType, + pub proto_pa : u64, + pub proto_pte : u64, + pub va_vad_base : u64, +} + +impl VmmProcess<'_> { + /// Get the base virtual address for a loaded module. + /// + /// # Arguments + /// * `module_name` + /// + /// # Examples + /// ``` + /// if let Ok(module_base_kernel32) = vmmprocess.get_module_base("kernel32.dll") { + /// println!("kernel32.dll -> {:x}", module_base_kernel32); + /// } + /// ``` + pub fn get_module_base(&self, module_name : &str) -> ResultEx { + return self.impl_get_module_base(module_name); + } + + /// Get the address of an exported function or symbol. + /// + /// This is similar to the Windows function GetProcAddress. + /// + /// # Arguments + /// * `module_name` + /// * `function_name` + /// + /// # Examples + /// ``` + /// if let Ok(procaddress) = vmmprocess.get_proc_address("kernel32.dll", "GetProcAddress") { + /// println!("kernel32.dll!GetProcAddress -> {:x}", procaddress); + /// } + /// ``` + pub fn get_proc_address(&self, module_name : &str, function_name : &str) -> ResultEx { + return self.impl_get_proc_address(module_name, function_name); + } + + /// Get the process path (retrieved fom kernel mode). + /// + /// # Examples + /// ``` + /// if let Ok(path) = vmmprocess.get_path_kernel() { + /// println!("-> {path}"); + /// } + /// ``` + pub fn get_path_kernel(&self) -> ResultEx { + return self.impl_get_information_string(VMMDLL_PROCESS_INFORMATION_OPT_STRING_PATH_KERNEL); + } + + /// Get the process path (retrieved from user-mode). + /// + /// # Examples + /// ``` + /// if let Ok(path) = vmmprocess.get_path_user() { + /// println!("-> {path}"); + /// } + /// ``` + pub fn get_path_user(&self) -> ResultEx { + return self.impl_get_information_string(VMMDLL_PROCESS_INFORMATION_OPT_STRING_PATH_USER_IMAGE); + } + + /// Get the process command line. + /// + /// # Examples + /// ``` + /// if let Ok(s_cmdline) = vmmprocess.get_cmdline() { + /// println!("-> {s_cmdline}"); + /// } + /// ``` + pub fn get_cmdline(&self) -> ResultEx { + return self.impl_get_information_string(VMMDLL_PROCESS_INFORMATION_OPT_STRING_CMDLINE); + } + + /// Get process information - such as name, ppid, state, etc. + /// + /// If retrieving multiple values from the [`VmmProcessInfo`] struct it's + /// recommended to retrieve the info object once instead of repetedly + /// calling the info() method. + /// + /// # Examples + /// ``` + /// if let Ok(procinfo) = vmmprocess.info() { + /// println!("struct -> {procinfo}"); + /// println!("pid -> {}", procinfo.pid); + /// println!("ppid -> {}", procinfo.pid); + /// println!("peb -> {:x}", procinfo.va_peb); + /// println!("eprocess -> {:x}", procinfo.va_eprocess); + /// println!("name -> {}", procinfo.name); + /// println!("longname -> {}", procinfo.name_long); + /// println!("SID -> {}", procinfo.sid); + /// } + /// ``` + pub fn info(&self) -> ResultEx { + return self.impl_info(); + } + + /// Retrieve the handles info map. + /// + /// For additional information see the [`VmmProcessMapHandleEntry`] struct. + /// + /// # Examples + /// ``` + /// if let Ok(handle_all) = vmmprocess.map_handle() { + /// println!("Number of handle entries: {}.", handle_all.len()); + /// for handle in &*handle_all { + /// println!("{handle}"); + /// } + /// } + /// ``` + pub fn map_handle(&self) -> ResultEx> { + return self.impl_map_handle(); + } + + /// Retrieve the heaps info map. + /// + /// For additional information see the [`VmmProcessMapHeapEntry`] struct. + /// + /// # Examples + /// ``` + /// if let Ok(heap_all) = vmmprocess.map_heap() { + /// println!("Number of heap entries: {}.", heap_all.len()); + /// for heap in &*heap_all { + /// println!("{heap}"); + /// } + /// } + /// ``` + pub fn map_heap(&self) -> ResultEx> { + return self.impl_map_heap(); + } + + /// Retrieve the heap allocations info map. + /// + /// For additional information see the [`VmmProcessMapHeapAllocEntry`] struct. + /// + /// # Arguments + /// * `heap_number_or_address` - Heap number as given by [`VmmProcessMapHeapEntry`] or the heap base address. + /// + /// # Examples + /// ``` + /// if let Ok(heapalloc_all) = vmmprocess.map_heapalloc(0) { + /// println!("Number of allocated heap entries: {}.", heapalloc_all.len()); + /// for heapalloc in &*heapalloc_all { + /// print!("{heapalloc} "); + /// } + /// println!(""); + /// } + /// ``` + pub fn map_heapalloc(&self, heap_number_or_address : u64) -> ResultEx> { + return self.impl_map_heapalloc(heap_number_or_address); + } + + /// Retrieve the loaded modules map. + /// + /// For additional information see the [`VmmProcessMapModuleEntry`] struct. + /// + /// # Arguments + /// * `is_info_debug` - Also retrieve debug information. + /// * `is_info_version` - Also version information. + /// + /// # Examples + /// ``` + /// if let Ok(module_all) = vmmprocess.map_module(true, true) { + /// println!("Number of process modules: {}.", module_all.len()); + /// for module in &*module_all { + /// println!("{module}"); + /// } + /// } + /// ``` + pub fn map_module(&self, is_info_debug : bool, is_info_version : bool) -> ResultEx> { + return self.impl_map_module(is_info_debug, is_info_version); + } + + /// Retrieve PE data directories associated with a module. + /// + /// For additional information see the [`VmmProcessMapDirectoryEntry`] struct. + /// + /// # Arguments + /// * `module_name` + /// + /// # Examples + /// ``` + /// if let Ok(data_directory_all) = vmmprocess.map_module_data_directory("kernel32.dll") { + /// println!("Number of module data directories: {}.", data_directory_all.len()); + /// for data_directory in &*data_directory_all { + /// println!("{data_directory}"); + /// } + /// } + /// ``` + pub fn map_module_data_directory(&self, module_name : &str) -> ResultEx> { + return self.impl_map_module_data_directory(module_name); + } + + /// Retrieve exported functions and symbols associated with a module. + /// + /// For additional information see the [`VmmProcessMapEatEntry`] struct. + /// + /// # Arguments + /// * `module_name` + /// + /// # Examples + /// ``` + /// if let Ok(eat_all) = vmmprocess.map_module_eat("kernel32.dll") { + /// println!("Number of module exported functions: {}.", eat_all.len()); + /// for eat in &*eat_all { + /// println!("{eat} :: {}", eat.forwarded_function); + /// } + /// } + /// ``` + pub fn map_module_eat(&self, module_name : &str) -> ResultEx> { + return self.impl_map_module_eat(module_name); + } + + /// Retrieve imported functions associated with a module. + /// + /// For additional information see the [`VmmProcessMapIatEntry`] struct. + /// + /// # Arguments + /// * `module_name` + /// + /// # Examples + /// ``` + /// if let Ok(iat_all) = vmmprocess.map_module_iat("kernel32.dll") { + /// println!("Number of module imported functions: {}.", iat_all.len()); + /// for iat in &*iat_all { + /// println!("{iat}"); + /// } + /// } + /// ``` + pub fn map_module_iat(&self, module_name : &str) -> ResultEx> { + return self.impl_map_module_iat(module_name); + } + + /// Retrieve PE sections associated with a module. + /// + /// For additional information see the [`VmmProcessSectionEntry`] struct. + /// + /// # Arguments + /// * `module_name` + /// + /// # Examples + /// ``` + /// if let Ok(section_all) = vmmprocess.map_module_section("kernel32.dll") { + /// println!("Number of module sections: {}.", section_all.len()); + /// for section in &*section_all { + /// println!("{section}"); + /// } + /// } + /// ``` + pub fn map_module_section(&self, module_name : &str) -> ResultEx> { + return self.impl_map_module_section(module_name); + } + + /// Retrieve the PTE memory info map. + /// + /// For additional information see the [`VmmProcessMapPteEntry`] struct. + /// + /// # Examples + /// ``` + /// if let Ok(pte_all) = vmmprocess.map_pte(true) { + /// println!("Number of pte entries: {}.", pte_all.len()); + /// for pte in &*pte_all { + /// let s = if pte.is_s { 's' } else { '-' }; + /// let r = if pte.is_r { 'r' } else { '-' }; + /// let w = if pte.is_w { 'w' } else { '-' }; + /// let x = if pte.is_x { 'x' } else { '-' }; + /// println!("{pte} :: {s}{r}{w}{x} :: {}", pte.info); + /// } + /// } + /// ``` + pub fn map_pte(&self, is_identify_modules : bool) -> ResultEx> { + return self.impl_map_pte(is_identify_modules); + } + + /// Retrieve the thread info map. + /// + /// For additional information see the [`VmmProcessMapThreadEntry`] struct. + /// + /// # Examples + /// ``` + /// if let Ok(thread_all) = vmmprocess.map_thread() { + /// println!("Number of process threads: {}.", thread_all.len()); + /// for thread in &*thread_all { + /// println!("{thread}"); + /// } + /// } + /// ``` + pub fn map_thread(&self) -> ResultEx> { + return self.impl_map_thread(); + } + + /// Retrieve the unloaded module info map. + /// + /// For additional information see the [`VmmProcessMapUnloadedModuleEntry`] struct. + /// + /// # Examples + /// ``` + /// if let Ok(unloaded_all) = vmmprocess.map_unloaded_module() { + /// println!("Number of process unloaded modules: {}.", unloaded_all.len()); + /// for unloaded in &*unloaded_all { + /// println!("{unloaded}"); + /// } + /// } + /// ``` + pub fn map_unloaded_module(&self) -> ResultEx> { + return self.impl_map_unloaded_module(); + } + + /// Retrieve the VAD (virtual address descriptor) memory info map. + /// + /// For additional information see the [`VmmProcessMapVadEntry`] struct. + /// + /// # Examples + /// ``` + /// if let Ok(vad_all) = vmmprocess.map_vad(true) { + /// println!("Number of vad entries: {}.", vad_all.len()); + /// for vad in &*vad_all { + /// println!("{vad} :: {}", vad.info); + /// } + /// } + /// ``` + pub fn map_vad(&self, is_identify_modules : bool) -> ResultEx> { + return self.impl_map_vad(is_identify_modules); + } + + /// Retrieve the extended VAD info map. + /// + /// For additional information see the [`VmmProcessMapVadExEntry`] struct. + pub fn map_vadex(&self, offset_pages : u32, count_pages : u32) -> ResultEx> { + return self.impl_map_vadex(offset_pages, count_pages); + } + + /// Read a contigious virtual memory chunk. + /// + /// The virtual memory is read without any special flags. The whole chunk + /// must be read successfully for the method to succeed. + /// + /// If deseriable to provide flags modifying the behavior (such as skipping + /// the built-in data cache or slower paging access) use the method + /// `mem_read_ex()` instead. + /// + /// Reading many memory chunks individually may be slow, especially if + /// reading takes place using hardware FPGA devices. In that case it's + /// better to use the `mem_scatter()` functionality for better performance. + /// + /// + /// # Arguments + /// * `va` - Virtual address to start reading from. + /// * `size` - The number of bytes to read. + /// + /// # Examples + /// ``` + /// // Read 0x100 bytes of data from the base of kernel32. + /// // Example assumes: use pretty_hex::*; + /// if let Ok(data_read) = vmmprocess.mem_read(va_kernel32, 0x100) { + /// println!("{:?}", data_read.hex_dump()); + /// } + /// ``` + pub fn mem_read(&self, va : u64, size : usize) -> ResultEx> { + return self.vmm.impl_mem_read(self.pid, va, size, 0); + } + + /// Read a contigious virtual memory chunk with flags. + /// + /// Flags are constants named `FLAG_*` + /// + /// Reading many memory chunks individually may be slow, especially if + /// reading takes place using hardware FPGA devices. In that case it's + /// better to use the `mem_scatter()` functionality for better performance. + /// + /// + /// # Arguments + /// * `va` - Virtual address to start reading from. + /// * `size` - The number of bytes to read. + /// * `flags` - Any combination of FLAG_*. + /// + /// # Examples + /// ``` + /// // Read 0x100 bytes of data from the base of kernel32. + /// // Force reading the underlying memory device (skip data cache) and + /// // Zero-Pad if parts of the memory read fail instead of failing. + /// // Example assumes: use pretty_hex::*; + /// let r = vmmprocess.mem_read_ex(va_kernel32, 0x100, FLAG_NOCACHE | FLAG_ZEROPAD_ON_FAIL); + /// let Ok(data_read) = r { + /// println!("{:?}", data_read.hex_dump()); + /// } + /// ``` + pub fn mem_read_ex(&self, va : u64, size : usize, flags : u64) -> ResultEx> { + return self.vmm.impl_mem_read(self.pid, va, size, flags); + } + + /// Create a scatter memory object for efficient virtual memory reads. + /// + /// Check out the [`VmmScatterMemory`] struct for more detailed information. + /// + /// # Arguments + /// * `flags` - Any combination of FLAG_*. + /// + /// # Examples + /// ``` + /// let mem_scatter = vmmprocess.mem_scatter(FLAG_NOCACHE | FLAG_ZEROPAD_ON_FAIL)?; + /// ``` + pub fn mem_scatter(&self, flags : u64) -> ResultEx { + return self.vmm.impl_mem_scatter(self.pid, flags); + } + + /// Translate a virtual address to a physical address. + /// + /// It's not always possible to translate a virtual address to a physical + /// address. This is the case when memory is "paged out". + /// + /// # Arguments + /// * `va` - Virtual address to translate. + /// + /// # Examples + /// ``` + /// let pa_kernel32 = vmmprocess.mem_virt2phys(va_kernel32)?; + /// ``` + pub fn mem_virt2phys(&self, va : u64) -> ResultEx { + return self.vmm.impl_mem_virt2phys(self.pid, va); + } + + /// Write virtual memory. + /// + /// The write is a best effort. Even of the write should fail it's not + /// certain that an error will be returned. To be absolutely certain that + /// a write has taken place follow up with a read. + /// + /// # Arguments + /// * `va` - Virtual address to start writing from. + /// * `data` - Byte data to write. + /// + /// # Examples + /// ``` + /// // Write data starting at the base of kernel32 (in the pe header). + /// let data_to_write = [0x56u8, 0x4d, 0x4d, 0x52, 0x55, 0x53, 0x54].to_vec(); + /// let _r = vmmprocess.mem_write(va_kernel32, &data_to_write); + /// ``` + pub fn mem_write(&self, va : u64, data : &Vec) -> ResultEx<()> { + return self.vmm.impl_mem_write(self.pid, va, data); + } + + /// Retrieve PDB debugging for the module. + /// + /// PDB debugging most often only work on modules by Microsoft. + /// See [`VmmPdb`] documentation for additional information. + /// + /// # Arguments + /// * `va_module_base` + /// + /// # Examples + /// ``` + /// if let Ok(pdb_kernel32) = vmmprocess.pdb_from_module_address(kernel32.va_base) { + /// println!("-> {pdb_kernel32}"); + /// } + /// ``` + pub fn pdb_from_module_address(&self, va_module_base : u64) -> ResultEx { + return self.impl_pdb_from_module_address(va_module_base); + } +} + + + + +/// Registry Hive API. +/// +/// The [`VmmRegHive`] info struct allows for access to the registry hive by +/// exposed fields and various methods. +/// +/// # Created By +/// - `vmm.reg_hive_list()` +/// +/// # Examples +/// ``` +/// let hive_all = vmm.reg_hive_list()?; +/// for hive in hive_all { +/// println!("{hive} size={} path={}", hive.size, hive.path); +/// } +/// ``` +#[derive(Debug)] +pub struct VmmRegHive<'a> { + vmm : &'a Vmm<'a>, + pub va : u64, + pub va_baseblock : u64, + pub size : u32, + pub name : String, + pub name_short : String, + pub path : String, +} + +impl VmmRegHive<'_> { + /// Read registry hive data. + /// + /// # Arguments + /// * `ra` - Registry hive address to start reading from. + /// * `size` - The number of bytes to read. + /// * `flags` - Any combination of FLAG_*. + /// + /// # Examples + /// ``` + /// if let Ok(data) = hive.reg_hive_read(0x1000, 0x100, FLAG_NOCACHE | FLAG_ZEROPAD_ON_FAIL) { + /// println!("{:?}", data.hex_dump()); + /// } + /// ``` + pub fn reg_hive_read(&self, ra : u32, size : usize, flags : u64) -> ResultEx> { + return self.impl_reg_hive_read(ra, size, flags); + } + + /// Write registry hive data. + /// + /// Writing to registry hives is extemely unsafe and may lead to + /// registry corruption and unusable systems. Use with extreme care! + /// + /// # Arguments + /// * `ra` - Registry hive address to start writing from. + /// * `data` - Byte data to write. + /// + /// # Examples + /// ``` + /// let data_to_write = [0x56u8, 0x4d, 0x4d, 0x52, 0x55, 0x53, 0x54].to_vec(); + /// let _r = hive.reg_hive_write(0x1000, &data_to_write); + /// ``` + pub fn reg_hive_write(&self, ra : u32, data : &Vec) -> ResultEx<()> { + return self.impl_reg_hive_write(ra, data); + } +} + + + + + + +/// Initialize plugin information and initialization context. +/// +/// This should usually be the first call in a `InitializeVmmPlugin()` export. +/// +/// See the plugin example for additional documentation. +pub fn new_plugin_initialization(native_h : usize, native_reginfo : usize) -> ResultEx<(VmmPluginInitializationInfo, VmmPluginInitializationContext)> { + return impl_new_plugin_initialization::(native_h, native_reginfo); +} + + + +/// Plugin Context: Supplied by MemProcFS to plugin callback functions. +/// +/// Contains the `vmm` field which gives access to the general API. +/// +/// Contains the `ctxlock` field which gives access to the user-defined generic +/// struct set at plugin initialization. +/// +/// The `ctxlock` field is a `std::sync::RwLock` and the inner user-defined +/// generic struct may be accessed in either multi-threaded read-mode or +/// single-threaded mutable write-mode. Read mode is more efficient. +/// +/// See the plugin example for additional use cases and documentation. +/// +/// +/// # Created By +/// - `plugin sub-system` +/// +/// +/// # Examples +/// +/// ``` +/// // Access the `vmm` field to retrieve a process for pid 768. +/// // Some `vmm` calls such as `vmm.process(pid)` may fail. In this case if +/// // the process does not exist. It is recommended to handle these errors +/// // gracefully as per below. +/// if let Ok(systemprocess) = plugin_ctx.vmm.process(768) { +/// // ... +/// } +/// ``` +/// +/// ``` +/// // Access the `vmm` field to retrieve a process for pid 768. +/// // Some `vmm` calls such as `vmm.process(pid)` may fail. It is possible to +/// // use error propagation for simplicity. Errors will be handled by upper +/// // plugin layers. If this is preferred error propagation may be simpler. +/// let systemprocess = plugin_ctx.vmm.process(768)?; +/// ``` +/// +/// ``` +/// // Access the ctxlock in multi-threaded read-mode: +/// // The lock should always contain a generic so unwrap() should be safe. +/// let user_ctx = plugin_ctx.ctxlock.read().unwrap(); +/// ``` +/// +/// ``` +/// // Access the ctxlock in single-threaded mutable write-mode: +/// // The lock should always contain a generic so unwrap() should be safe. +/// let mut user_ctx = plugin_ctx.ctxlock.write().unwrap(); +/// ``` +/// +/// +/// See the plugin example about usage of the ctxlock field. +pub struct VmmPluginContext<'a, T> { + /// Access the general MemProcFS API through the `vmm` field. + pub vmm : Vmm<'a>, + /// Access generic user-set plugin context in a thread-safe way. + pub ctxlock : std::sync::RwLock, + fn_list : Option, process : Option, path : &str, file_list : &VmmPluginFileList) -> ResultEx<()>>, + fn_read : Option, process : Option, file_name : &str, cb : u32, cb_offset : u64) -> ResultEx>>, + fn_write : Option, process : Option, file_name : &str, data : Vec, cb_offset : u64) -> ResultEx<()>>, + fn_visible : Option, process : Option) -> ResultEx>, + fn_notify : Option, event_id : u32) -> ResultEx<()>>, +} + + + +/// Plugin File List: Supplied by MemProcFS to plugin list callback function. +/// +/// The `VmmPluginFileList` struct contains the methods `add_file()` and +/// `add_directory()` which will allow the plugin list callback function +/// to populate files & directories given the specified path and process. +/// +/// # Created By +/// - `plugin sub-system` +#[derive(Debug)] +pub struct VmmPluginFileList<'a> { + vmm : &'a Vmm<'a>, + h_file_list : usize, +} + +impl VmmPluginFileList<'_> { + /// Add a file to the plugin directory indicated by path and process. + /// + /// For additional information check the `plugin_list_cb()` function in the + /// plugin example project. + /// + /// # Examples + /// + /// ``` + /// // Add a directory named readme.txt with size 4kB to the plugin path. + /// file_list.impl_add_file("readme.txt", 4096); + /// ``` + pub fn add_file(&self, name : &str, size : u64) { + self.impl_add_file(name, size); + } + + /// Add a directory to the plugin directory indicated by path and process. + /// + /// For additional information check the `plugin_list_cb()` function in the + /// plugin example project. + /// + /// # Examples + /// + /// ``` + /// // Add a directory named subdir33 to the plugin path. + /// file_list.add_directory("subdir33"); + /// ``` + pub fn add_directory(&self, name : &str) { + self.impl_add_directory(name); + } +} + + + +/// Plugin Initialization System Information. +/// +/// The `VmmPluginInitializationInfo` is used in the plugin module entry point +/// (the exported `InitializeVmmPlugin()` function). +/// +/// The `InitializeVmmPlugin()` function must be fast for the user experience +/// and the initialization function may query this info struct to decide if +/// the current system is supported or not before registering the plugin. +/// +/// Contains information about the: system type, memory model and OS version +/// (in the form of build, major and minor). +/// +/// For additional information check the `InitializeVmmPlugin()` function in +/// the plugin example project. +/// +/// +/// # Created By +/// - [`new_plugin_initialization()`] +/// +/// +/// # Examples +/// +/// ``` +/// // Retrieve the system_info and plugin_init_ctx in InitializeVmmPlugin() +/// let (system_info, mut plugin_init_ctx) = match new_plugin_initialization::(native_h, native_reginfo) { +/// Ok(r) => r, +/// Err(_) => return, +/// }; +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VmmPluginInitializationInfo { + /// The system type - i.e. 32-bit or 64-bit Windows. + pub tp_system : VmmSystemType, + /// The memory model type - i.e. X86, X86PAE, X64. + pub tp_memorymodel : VmmMemoryModelType, + /// The OS major version. Use version_build instead if possible. + pub version_major : u32, + /// The OS minor version. Use version_build instead if possible. + pub version_minor : u32, + /// The build version number. + pub version_build : u32, +} + + + +/// Plugin Initialization Context. +/// +/// The `VmmPluginInitializationContext` is used in the plugin module entry +/// point (the exported `InitializeVmmPlugin()` function). +/// +/// The context is to be populated by the user with information such as name, +/// callback functions and plugin visibility. +/// +/// The flow usually follows the below structure: +/// +/// 1: Call: memprocfs::new_plugin_initialization(native_h, native_reginfo) to +/// create the plugin init context inside the InitializeVmmPlugin() function. +/// +/// 2: Fill out the required ctx and path_name struct members. +/// +/// 3: Fill out the module type in the is* struct members. +/// +/// 4: Fill out the optional pfn* callback functions. +/// +/// 5: Register the plugin with the VMM by calling the register() method. +/// +/// For additional information check the `InitializeVmmPlugin()` function in +/// the plugin example project. +/// +/// +/// # Created By +/// - [`new_plugin_initialization()`] +/// +/// +/// # Examples +/// +/// ``` +/// // Retrieve the system_info and plugin_init_ctx in InitializeVmmPlugin() +/// let (system_info, mut plugin_init_ctx) = match new_plugin_initialization::(native_h, native_reginfo) { +/// Ok(r) => r, +/// Err(_) => return, +/// }; +/// // set plugin name: +/// plugin_init_ctx.path_name = String::from("/rust/example"); +/// // Set user-defined generic plugin context: +/// let ctx = PluginContext { +/// ... +/// }; +/// plugin_init_ctx.ctx = Some(ctx); +/// // Set visiblity: +/// plugin_init_ctx.is_root_module = true; +/// plugin_init_ctx.is_process_module = true; +/// // Set callback functions: +/// plugin_init_ctx.fn_list = Some(plugin_list_cb); +/// plugin_init_ctx.fn_read = Some(plugin_read_cb); +/// plugin_init_ctx.fn_write = Some(plugin_write_cb); +/// // Register the plugin with the MemProcFS plugin manager: +/// let _r = plugin_init_ctx.register(); +/// ``` +pub struct VmmPluginInitializationContext { + h_vmm : usize, + h_reginfo : usize, + /// user-defined generic plugin context. + pub ctx : Option, + /// Plugin path and name. + pub path_name : String, + /// Plugin shows up in the file system root. + pub is_root_module : bool, + /// Plugin is hidden in the file system root. + pub is_root_module_hidden : bool, + /// Plugin shows up on a per-process basis. + pub is_process_module : bool, + /// Plugin is hidden on a per-process basis. + pub is_process_module_hidden : bool, + /// Callback function - VFS list directory. This callback used in most cases. + pub fn_list : Option, process : Option, path : &str, file_list : &VmmPluginFileList) -> ResultEx<()>>, + /// Callback function - VFS read file. This callback is used in most cases. + pub fn_read : Option, process : Option, file_name : &str, cb : u32, cb_offset : u64) -> ResultEx>>, + /// Callback function - VFS write file. + pub fn_write : Option, process : Option, file_name : &str, data : Vec, cb_offset : u64) -> ResultEx<()>>, + /// Callback function - plugin dynamic visiblity. This callback is rarely used, and in special circumstances only. + pub fn_visible : Option, process : Option) -> ResultEx>, + /// Callback function - notification on an event defined by: `PLUGIN_NOTIFY_*` constants. + pub fn_notify : Option, event_id : u32) -> ResultEx<()>>, +} + +impl VmmPluginInitializationContext { + /// Register the plugin with the MemProcFS plugin sub-system. + /// + /// The initialiation context may not be used after the `register()` call. + /// + /// It is possible to register additional plugins in the same plugin + /// initialization function if a new `VmmPluginInitializationContext` + /// is retrieved from the `new_plugin_initialization()` function. + /// + /// # Examples + /// + /// ``` + /// // Register the plugin with MemProcFS. This will consume the context + /// // which should not be possible to use after this. + /// let _r = plugin_init_ctx.register(); + /// ``` + /// + pub fn register(self) -> ResultEx<()> { + return self.impl_register(); + } +} + + + + + + + + + + + + + + + + + + + + + +//============================================================================= +// INTERNAL: VMM CORE: +//============================================================================= + +#[allow(dead_code)] +#[allow(non_snake_case)] +#[derive(Debug)] +struct VmmNative { + h : usize, + is_close_h : bool, + library_lc : Option, + library_vmm : Option, + VMMDLL_Initialize : extern "C" fn(argc: c_int, argv: *const *const c_char) -> usize, + VMMDLL_InitializePlugins : extern "C" fn(hVMM : usize) -> bool, + VMMDLL_Close : extern "C" fn(hVMM : usize), + VMMDLL_ConfigGet : extern "C" fn(hVMM : usize, fOption : u64, pqwValue : *mut u64) -> bool, + VMMDLL_ConfigSet : extern "C" fn(hVMM : usize, fOption : u64, qwValue : u64) -> bool, + VMMDLL_MemFree : extern "C" fn(pvMem : usize), + + VMMDLL_Log : extern "C" fn(hVMM : usize, MID : u32, dwLogLevel : u32, uszFormat : *const c_char, uszParam : *const c_char), + + VMMDLL_MemReadEx : extern "C" fn(hVMM : usize, pid : u32, qwA : u64, pb : *mut u8, cb : u32, pcbReadOpt : *mut u32, flags : u64) -> bool, + VMMDLL_MemWrite : extern "C" fn(hVMM : usize, pid : u32, qwA : u64, pb : *const u8, cb : u32) -> bool, + VMMDLL_MemVirt2Phys : extern "C" fn(hVMM : usize, pid : u32, qwA : u64, pqwPA : *mut u64) -> bool, + + VMMDLL_Scatter_Initialize : extern "C" fn(hVMM : usize, pid : u32, flags : u32) -> usize, + VMMDLL_Scatter_Prepare : extern "C" fn(hS : usize, va : u64, cb : u32) -> bool, + VMMDLL_Scatter_PrepareEx : extern "C" fn(hS : usize, va : u64, cb : u32, pb : *mut u8, pcbRead : *mut u32) -> bool, + VMMDLL_Scatter_PrepareWrite : extern "C" fn(hS : usize, va : u64, pb : *const u8, cb : u32) -> bool, + VMMDLL_Scatter_Execute : extern "C" fn(hS : usize) -> bool, + VMMDLL_Scatter_Read : extern "C" fn(hS : usize, va : u64, cb : u32, pb : *mut u8, pcbRead : *mut u32) -> bool, + VMMDLL_Scatter_Clear : extern "C" fn(hS : usize, pid : u32, flags : u32) -> bool, + VMMDLL_Scatter_CloseHandle : extern "C" fn(hS : usize), + + VMMDLL_PidGetFromName : extern "C" fn(hVMM : usize, szProcName : *const c_char, pdwPID : *mut u32) -> bool, + VMMDLL_PidList : extern "C" fn(hVMM : usize, pPIDs : *mut u32, pcPIDs : *mut usize) -> bool, + + VMMDLL_WinReg_HiveList : extern "C" fn(hVMM : usize, pHives : *mut CRegHive, cHives : u32, pcHives : *mut u32) -> bool, + VMMDLL_WinReg_HiveReadEx : extern "C" fn(hVMM : usize, vaCMHive : u64, ra : u32, pb : *mut u8, cb : u32, pcbReadOpt : *mut u32, flags : u64) -> bool, + VMMDLL_WinReg_HiveWrite : extern "C" fn(hVMM : usize, vaCMHive : u64, ra : u32, pb : *const u8, cb : u32) -> bool, + + VMMDLL_ProcessGetModuleBaseU : extern "C" fn(hVMM : usize, pid : u32, uszModuleName : *const c_char) -> u64, + VMMDLL_ProcessGetProcAddressU : extern "C" fn(hVMM : usize, pid : u32, uszModuleName : *const c_char, szFunctionName : *const c_char) -> u64, + VMMDLL_ProcessGetInformation : extern "C" fn(hVMM : usize, pid : u32, pProcessInformation : *mut CProcessInformation, pcbProcessInformation : *mut usize) -> bool, + VMMDLL_ProcessGetInformationString : extern "C" fn(hVMM : usize, pid : u32, fOptionString : u32) -> *const c_char, + + VMMDLL_Map_GetNetU : extern "C" fn(hVMM : usize, ppNetMap : *mut *mut CNetMap) -> bool, + VMMDLL_Map_GetPfnEx : extern "C" fn(hVMM : usize, pPfns : *const u32, cPfns : u32, ppPfnMap : *mut *mut CPfnMap, flags : u32) -> bool, + VMMDLL_Map_GetPhysMem : extern "C" fn(hVMM : usize, ppPhysMemMap : *mut *mut CMemoryMap) -> bool, + VMMDLL_Map_GetPool : extern "C" fn(hVMM : usize, ppPoolMap : *mut *mut CPoolMap, flags : u32) -> bool, + VMMDLL_Map_GetServicesU : extern "C" fn(hVMM : usize, ppServiceMap : *mut *mut CServiceMap) -> bool, + VMMDLL_Map_GetUsersU : extern "C" fn(hVMM : usize, ppUserMap : *mut *mut CUserMap) -> bool, + VMMDLL_Map_GetVMU : extern "C" fn(hVMM : usize, ppVmMap : *mut *mut CVmMap) -> bool, + + VMMDLL_PdbLoad : extern "C" fn(hVMM : usize, dwPID : u32, vaModuleBase : u64, szModuleName : *mut c_char) -> bool, + VMMDLL_PdbSymbolName : extern "C" fn(hVMM : usize, szModule : *const c_char, cbSymbolAddressOrOffset : u64, szSymbolName : *mut c_char, pdwSymbolDisplacement : *mut u32) -> bool, + VMMDLL_PdbSymbolAddress : extern "C" fn(hVMM : usize, szModule : *const c_char, szSymbolName : *const c_char, pvaSymbolAddress : *mut u64) -> bool, + VMMDLL_PdbTypeSize : extern "C" fn(hVMM : usize, szModule : *const c_char, szTypeName : *const c_char, pcbTypeSize : *mut u32) -> bool, + VMMDLL_PdbTypeChildOffset : extern "C" fn(hVMM : usize, szModule : *const c_char, uszTypeName : *const c_char, uszTypeChildName : *const c_char, pcbTypeChildOffset : *mut u32) -> bool, + + VMMDLL_Map_GetEATU : extern "C" fn(hVMM : usize, pid : u32, uszModuleName : *const c_char, ppEatMap : *mut *mut CEatMap) -> bool, + VMMDLL_Map_GetHandleU : extern "C" fn(hVMM : usize, pid : u32, ppHandleMap : *mut *mut CHandleMap) -> bool, + VMMDLL_Map_GetHeap : extern "C" fn(hVMM : usize, pid : u32, ppHeapMap : *mut *mut CHeapMap) -> bool, + VMMDLL_Map_GetHeapAlloc : extern "C" fn(hVMM : usize, pid : u32, qwHeapNumOrAddress : u64, ppHeapAllocMap : *mut *mut CHeapAllocMap) -> bool, + VMMDLL_Map_GetIATU : extern "C" fn(hVMM : usize, pid : u32, uszModuleName : *const c_char, ppIatMap : *mut *mut CIatMap) -> bool, + VMMDLL_Map_GetModuleU : extern "C" fn(hVMM : usize, pid : u32, ppModuleMap : *mut *mut CModuleMap, flags : u32) -> bool, + VMMDLL_Map_GetPteU : extern "C" fn(hVMM : usize, pid : u32, fIdentifyModules : bool, ppPteMap : *mut *mut CPteMap) -> bool, + VMMDLL_Map_GetThread : extern "C" fn(hVMM : usize, pid : u32, ppThreadMap : *mut *mut CThreadMap) -> bool, + VMMDLL_Map_GetUnloadedModuleU : extern "C" fn(hVMM : usize, pid : u32, ppUnloadedModuleMap : *mut *mut CUnloadedModuleMap) -> bool, + VMMDLL_Map_GetVadU : extern "C" fn(hVMM : usize, pid : u32, fIdentifyModules : bool, ppVadMap : *mut *mut CVadMap) -> bool, + VMMDLL_Map_GetVadEx : extern "C" fn(hVMM : usize, pid : u32, oPage : u32, cPage : u32, ppVadExMap : *mut *mut CVadExMap) -> bool, + VMMDLL_ProcessGetDirectoriesU : extern "C" fn(hVMM : usize, pid : u32, uszModule : *const c_char, pDataDirectories : *mut CIMAGE_DATA_DIRECTORY) -> bool, + VMMDLL_ProcessGetSectionsU : extern "C" fn(hVMM : usize, pid : u32, uszModule : *const c_char, pSections : *mut CIMAGE_SECTION_HEADER, cSections : u32, pcSections : *mut u32) -> bool, + + VMMDLL_VfsListU : extern "C" fn(hVMM : usize, uszPath : *const c_char, pFileList : *mut CVMMDLL_VFS_FILELIST2) -> bool, + VMMDLL_VfsReadU : extern "C" fn(hVMM : usize, uszFileName : *const c_char, pb : *mut u8, cb : u32, pcbRead : *mut u32, cbOffset : u64) -> u32, + VMMDLL_VfsWriteU : extern "C" fn(hVMM : usize, uszFileName : *const c_char, pb : *const u8, cb : u32, pcbWrite : *mut u32, cbOffset : u64) -> u32, + + VMMDLL_VmGetVmmHandle : extern "C" fn(hVMM : usize, hVM : usize) -> usize, + + // Plugin related info below: + VMMDLL_VfsList_AddFile : extern "C" fn(pFileList : usize, uszName : *const c_char, cb : u64, pExInfo : usize), + VMMDLL_VfsList_AddDirectory : extern "C" fn(pFileList : usize, uszName : *const c_char, pExInfo : usize), + +} + +#[allow(non_snake_case)] +fn impl_new<'a>(vmm_lib_path : &str, h_vmm_existing_opt : usize, args: &Vec<&str>) -> ResultEx> { + unsafe { + // load MemProcFS native library (vmm.dll / vmm.so): + // vmm is however dependant on leechcore which must be loaded first... + let path_vmm = std::path::Path::new(vmm_lib_path).canonicalize()?; + let mut path_lc = path_vmm.parent().unwrap().canonicalize()?; + if cfg!(windows) { + path_lc = path_lc.join("leechcore.dll"); + } else { + path_lc = path_lc.join("leechcore.so"); + } + let lib_lc : libloading::Library = libloading::Library::new(path_lc.to_str().unwrap_or(""))?; + let lib : libloading::Library = libloading::Library::new(path_vmm.to_str().unwrap_or(""))?; + // fetch function references: + let VMMDLL_Initialize : extern "C" fn(argc: c_int, argv: *const *const c_char) -> usize = *lib.get(b"VMMDLL_Initialize")?; + let VMMDLL_InitializePlugins : extern "C" fn(usize) -> bool = *lib.get(b"VMMDLL_InitializePlugins")?; + let VMMDLL_Close = *lib.get(b"VMMDLL_Close")?; + let VMMDLL_ConfigGet = *lib.get(b"VMMDLL_ConfigGet")?; + let VMMDLL_ConfigSet = *lib.get(b"VMMDLL_ConfigSet")?; + let VMMDLL_MemFree = *lib.get(b"VMMDLL_MemFree")?; + let VMMDLL_Log = *lib.get(b"VMMDLL_Log")?; + let VMMDLL_MemReadEx = *lib.get(b"VMMDLL_MemReadEx")?; + let VMMDLL_MemWrite = *lib.get(b"VMMDLL_MemWrite")?; + let VMMDLL_MemVirt2Phys = *lib.get(b"VMMDLL_MemVirt2Phys")?; + let VMMDLL_Scatter_Initialize = *lib.get(b"VMMDLL_Scatter_Initialize")?; + let VMMDLL_Scatter_Prepare = *lib.get(b"VMMDLL_Scatter_Prepare")?; + let VMMDLL_Scatter_PrepareEx = *lib.get(b"VMMDLL_Scatter_PrepareEx")?; + let VMMDLL_Scatter_PrepareWrite = *lib.get(b"VMMDLL_Scatter_PrepareWrite")?; + let VMMDLL_Scatter_Execute = *lib.get(b"VMMDLL_Scatter_Execute")?; + let VMMDLL_Scatter_Read = *lib.get(b"VMMDLL_Scatter_Read")?; + let VMMDLL_Scatter_Clear = *lib.get(b"VMMDLL_Scatter_Clear")?; + let VMMDLL_Scatter_CloseHandle = *lib.get(b"VMMDLL_Scatter_CloseHandle")?; + let VMMDLL_PidGetFromName = *lib.get(b"VMMDLL_PidGetFromName")?; + let VMMDLL_PidList = *lib.get(b"VMMDLL_PidList")?; + let VMMDLL_WinReg_HiveList = *lib.get(b"VMMDLL_WinReg_HiveList")?; + let VMMDLL_WinReg_HiveReadEx = *lib.get(b"VMMDLL_WinReg_HiveReadEx")?; + let VMMDLL_WinReg_HiveWrite = *lib.get(b"VMMDLL_WinReg_HiveWrite")?; + let VMMDLL_ProcessGetModuleBaseU = *lib.get(b"VMMDLL_ProcessGetModuleBaseU")?; + let VMMDLL_ProcessGetProcAddressU = *lib.get(b"VMMDLL_ProcessGetProcAddressU")?; + let VMMDLL_ProcessGetInformation = *lib.get(b"VMMDLL_ProcessGetInformation")?; + let VMMDLL_ProcessGetInformationString = *lib.get(b"VMMDLL_ProcessGetInformationString")?; + let VMMDLL_Map_GetNetU = *lib.get(b"VMMDLL_Map_GetNetU")?; + let VMMDLL_Map_GetPfnEx = *lib.get(b"VMMDLL_Map_GetPfnEx")?; + let VMMDLL_Map_GetPhysMem = *lib.get(b"VMMDLL_Map_GetPhysMem")?; + let VMMDLL_Map_GetPool = *lib.get(b"VMMDLL_Map_GetPool")?; + let VMMDLL_Map_GetUsersU = *lib.get(b"VMMDLL_Map_GetUsersU")?; + let VMMDLL_Map_GetServicesU = *lib.get(b"VMMDLL_Map_GetServicesU")?; + let VMMDLL_Map_GetVMU = *lib.get(b"VMMDLL_Map_GetVMU")?; + let VMMDLL_PdbLoad = *lib.get(b"VMMDLL_PdbLoad")?; + let VMMDLL_PdbSymbolName = *lib.get(b"VMMDLL_PdbSymbolName")?; + let VMMDLL_PdbSymbolAddress = *lib.get(b"VMMDLL_PdbSymbolAddress")?; + let VMMDLL_PdbTypeSize = *lib.get(b"VMMDLL_PdbTypeSize")?; + let VMMDLL_PdbTypeChildOffset = *lib.get(b"VMMDLL_PdbTypeChildOffset")?; + let VMMDLL_Map_GetEATU = *lib.get(b"VMMDLL_Map_GetEATU")?; + let VMMDLL_Map_GetHandleU = *lib.get(b"VMMDLL_Map_GetHandleU")?; + let VMMDLL_Map_GetHeap = *lib.get(b"VMMDLL_Map_GetHeap")?; + let VMMDLL_Map_GetHeapAlloc = *lib.get(b"VMMDLL_Map_GetHeapAlloc")?; + let VMMDLL_Map_GetIATU = *lib.get(b"VMMDLL_Map_GetIATU")?; + let VMMDLL_Map_GetModuleU = *lib.get(b"VMMDLL_Map_GetModuleU")?; + let VMMDLL_Map_GetPteU = *lib.get(b"VMMDLL_Map_GetPteU")?; + let VMMDLL_Map_GetThread = *lib.get(b"VMMDLL_Map_GetThread")?; + let VMMDLL_Map_GetUnloadedModuleU = *lib.get(b"VMMDLL_Map_GetUnloadedModuleU")?; + let VMMDLL_Map_GetVadU = *lib.get(b"VMMDLL_Map_GetVadU")?; + let VMMDLL_Map_GetVadEx = *lib.get(b"VMMDLL_Map_GetVadEx")?; + let VMMDLL_ProcessGetDirectoriesU = *lib.get(b"VMMDLL_ProcessGetDirectoriesU")?; + let VMMDLL_ProcessGetSectionsU = *lib.get(b"VMMDLL_ProcessGetSectionsU")?; + let VMMDLL_VfsListU = *lib.get(b"VMMDLL_VfsListU")?; + let VMMDLL_VfsReadU = *lib.get(b"VMMDLL_VfsReadU")?; + let VMMDLL_VfsWriteU = *lib.get(b"VMMDLL_VfsWriteU")?; + let VMMDLL_VmGetVmmHandle = *lib.get(b"VMMDLL_VmGetVmmHandle")?; + let VMMDLL_VfsList_AddFile = *lib.get(b"VMMDLL_VfsList_AddFile")?; + let VMMDLL_VfsList_AddDirectory = *lib.get(b"VMMDLL_VfsList_AddDirectory")?; + // initialize MemProcFS + let h; + if h_vmm_existing_opt != 0 { + h = h_vmm_existing_opt; + } else { + let args = args.iter().map(|arg| CString::new(*arg).unwrap()).collect::>(); + let argv: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect(); + let argc: c_int = args.len() as c_int; + h = (VMMDLL_Initialize)(argc, argv.as_ptr()); + if h == 0 { + return Err("VMMDLL_Initialize: fail".into()); + } + let r = (VMMDLL_InitializePlugins)(h); + if !r { + return Err("VMMDLL_InitializePlugins: fail".into()); + } + } + // return Vmm struct: + let native = VmmNative { + h, + is_close_h : h_vmm_existing_opt == 0, + library_lc : Some(lib_lc), + library_vmm : Some(lib), + VMMDLL_Initialize, + VMMDLL_InitializePlugins, + VMMDLL_Close, + VMMDLL_ConfigGet, + VMMDLL_ConfigSet, + VMMDLL_MemFree, + VMMDLL_Log, + VMMDLL_MemReadEx, + VMMDLL_MemWrite, + VMMDLL_MemVirt2Phys, + VMMDLL_Scatter_Initialize, + VMMDLL_Scatter_Prepare, + VMMDLL_Scatter_PrepareEx, + VMMDLL_Scatter_PrepareWrite, + VMMDLL_Scatter_Execute, + VMMDLL_Scatter_Read, + VMMDLL_Scatter_Clear, + VMMDLL_Scatter_CloseHandle, + VMMDLL_PidGetFromName, + VMMDLL_PidList, + VMMDLL_WinReg_HiveList, + VMMDLL_WinReg_HiveReadEx, + VMMDLL_WinReg_HiveWrite, + VMMDLL_ProcessGetModuleBaseU, + VMMDLL_ProcessGetProcAddressU, + VMMDLL_ProcessGetInformation, + VMMDLL_ProcessGetInformationString, + VMMDLL_Map_GetNetU, + VMMDLL_Map_GetPfnEx, + VMMDLL_Map_GetPhysMem, + VMMDLL_Map_GetPool, + VMMDLL_Map_GetUsersU, + VMMDLL_Map_GetServicesU, + VMMDLL_Map_GetVMU, + VMMDLL_PdbLoad, + VMMDLL_PdbSymbolName, + VMMDLL_PdbSymbolAddress, + VMMDLL_PdbTypeSize, + VMMDLL_PdbTypeChildOffset, + VMMDLL_Map_GetEATU, + VMMDLL_Map_GetHandleU, + VMMDLL_Map_GetHeap, + VMMDLL_Map_GetHeapAlloc, + VMMDLL_Map_GetIATU, + VMMDLL_Map_GetModuleU, + VMMDLL_Map_GetPteU, + VMMDLL_Map_GetThread, + VMMDLL_Map_GetUnloadedModuleU, + VMMDLL_Map_GetVadU, + VMMDLL_Map_GetVadEx, + VMMDLL_ProcessGetDirectoriesU, + VMMDLL_ProcessGetSectionsU, + VMMDLL_VfsListU, + VMMDLL_VfsReadU, + VMMDLL_VfsWriteU, + VMMDLL_VmGetVmmHandle, + VMMDLL_VfsList_AddFile, + VMMDLL_VfsList_AddDirectory, + }; + let vmm = Vmm { + native, + parent_vmm : None, + }; + return Ok(vmm); + } +} + +#[allow(non_snake_case)] +fn impl_new_from_virtual_machine<'a>(vmm_parent : &'a Vmm, vm_entry : &VmmMapVirtualMachineEntry) -> ResultEx> { + if vmm_parent.native.h != vm_entry.h_vmm { + return Err("Invalid parent/vm relationship.".into()); + } + let h_vmm_vm = (vmm_parent.native.VMMDLL_VmGetVmmHandle)(vmm_parent.native.h, vm_entry.h_vm); + if h_vmm_vm == 0 { + return Err("VMMDLL_VmGetVmmHandle: fail.".into()); + } + let native = VmmNative { + h: vmm_parent.native.h, + library_lc : None, + library_vmm : None, + ..vmm_parent.native + }; + let vmm = Vmm { + native : native, + parent_vmm : Some(vmm_parent), + }; + return Ok(vmm); +} + + + + + + +//============================================================================= +// INTERNAL: VMM: +//============================================================================= + +const MAX_PATH : usize = 260; +const VMMDLL_VFS_FILELIST_VERSION : u32 = 2; +const VMMDLL_MAP_EAT_VERSION : u32 = 3; +const VMMDLL_MAP_HANDLE_VERSION : u32 = 3; +const VMMDLL_MAP_HEAP_VERSION : u32 = 4; +const VMMDLL_MAP_HEAPALLOC_VERSION : u32 = 1; +const VMMDLL_MAP_IAT_VERSION : u32 = 2; +const VMMDLL_MAP_POOL_VERSION : u32 = 2; +const VMMDLL_MAP_PTE_VERSION : u32 = 2; +const VMMDLL_MAP_MODULE_VERSION : u32 = 6; +const VMMDLL_MAP_NET_VERSION : u32 = 3; +const VMMDLL_MAP_PFN_VERSION : u32 = 1; +const VMMDLL_MAP_PHYSMEM_VERSION : u32 = 2; +const VMMDLL_MAP_SERVICE_VERSION : u32 = 3; +const VMMDLL_MAP_THREAD_VERSION : u32 = 4; +const VMMDLL_MAP_UNLOADEDMODULE_VERSION : u32 = 2; +const VMMDLL_MAP_USER_VERSION : u32 = 2; +const VMMDLL_MAP_VAD_VERSION : u32 = 6; +const VMMDLL_MAP_VADEX_VERSION : u32 = 3; +const VMMDLL_MAP_VM_VERSION : u32 = 2; + +const VMMDLL_MID_RUST : u32 = 0x80000004; + +const VMMDLL_PLUGIN_CONTEXT_MAGIC : u64 = 0xc0ffee663df9301c; +const VMMDLL_PLUGIN_CONTEXT_VERSION : u16 = 5; +const VMMDLL_PLUGIN_REGINFO_MAGIC : u64 = 0xc0ffee663df9301d; +const VMMDLL_PLUGIN_REGINFO_VERSION : u16 = 16; +const VMMDLL_STATUS_SUCCESS : u32 = 0x00000000; +const VMMDLL_STATUS_END_OF_FILE : u32 = 0xC0000011; +const VMMDLL_STATUS_FILE_INVALID : u32 = 0xC0000098; + +const VMMDLL_PROCESS_INFORMATION_MAGIC : u64 = 0xc0ffee663df9301e; +const VMMDLL_PROCESS_INFORMATION_VERSION : u16 = 7; +const VMMDLL_REGISTRY_HIVE_INFORMATION_MAGIC : u64 = 0xc0ffee653df8d01e; +const VMMDLL_REGISTRY_HIVE_INFORMATION_VERSION : u16 = 4; + +const VMMDLL_PROCESS_INFORMATION_OPT_STRING_PATH_KERNEL : u32 = 1; +const VMMDLL_PROCESS_INFORMATION_OPT_STRING_PATH_USER_IMAGE : u32 = 2; +const VMMDLL_PROCESS_INFORMATION_OPT_STRING_CMDLINE : u32 = 3; + +const DIRECTORY_NAMES : [&str; 16] = ["EXPORT", "IMPORT", "RESOURCE", "EXCEPTION", "SECURITY", "BASERELOC", "DEBUG", "ARCHITECTURE", "GLOBALPTR", "TLS", "LOAD_CONFIG", "BOUND_IMPORT", "IAT", "DELAY_IMPORT", "COM_DESCRIPTOR", "RESERVED"]; + +impl Drop for Vmm<'_> { + fn drop(&mut self) { + if self.native.is_close_h { + (self.native.VMMDLL_Close)(self.native.h); + } + } +} + +impl fmt::Display for Vmm<'_> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Vmm") + } +} + +impl fmt::Display for VmmLogLevel { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let v = match self { + VmmLogLevel::_1Critical => "Critical(1)", + VmmLogLevel::_2Warning => "Warning(2)", + VmmLogLevel::_3Info => "Info(3)", + VmmLogLevel::_4Verbose => "Verbose(4)", + VmmLogLevel::_5Debug => "Debug(5)", + VmmLogLevel::_6Trace => "Trace(6)", + VmmLogLevel::_7None => "None(7)", + }; + write!(f, "{v}") + } +} + +impl From for VmmMemoryModelType { + fn from(v : u32) -> Self { + return match v { + 1 => VmmMemoryModelType::X86, + 2 => VmmMemoryModelType::X86PAE, + 3 => VmmMemoryModelType::X64, + _ => VmmMemoryModelType::NA, + }; + } +} + +impl fmt::Display for VmmMemoryModelType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let v = match self { + VmmMemoryModelType::NA => "NA", + VmmMemoryModelType::X86 => "X86", + VmmMemoryModelType::X86PAE => "X86PAE", + VmmMemoryModelType::X64 => "X64", + }; + write!(f, "{v}") + } +} + +impl From for VmmSystemType { + fn from(v : u32) -> Self { + return match v { + 1 => VmmSystemType::UnknownX64, + 2 => VmmSystemType::WindowsX64, + 3 => VmmSystemType::UnknownX86, + 4 => VmmSystemType::WindowsX86, + _ => VmmSystemType::UnknownPhysical, + }; + } +} + +impl fmt::Display for VmmSystemType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let v = match self { + VmmSystemType::UnknownPhysical => "UnknownPhysical", + VmmSystemType::UnknownX64 => "UnknownX64", + VmmSystemType::WindowsX64 => "WindowsX64", + VmmSystemType::UnknownX86 => "UnknownX86", + VmmSystemType::WindowsX86 => "WindowsX86", + }; + write!(f, "{v}") + } +} + +impl From for VmmIntegrityLevelType { + fn from(v : u32) -> Self { + return match v { + 1 => VmmIntegrityLevelType::Untrusted, + 2 => VmmIntegrityLevelType::Low, + 3 => VmmIntegrityLevelType::Medium, + 4 => VmmIntegrityLevelType::MediumPlus, + 5 => VmmIntegrityLevelType::High, + 6 => VmmIntegrityLevelType::System, + 7 => VmmIntegrityLevelType::Protected, + _ => VmmIntegrityLevelType::Unknown, + }; + } +} + +impl fmt::Display for VmmIntegrityLevelType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let v = match self { + VmmIntegrityLevelType::Untrusted => "Untrusted", + VmmIntegrityLevelType::Low => "Low", + VmmIntegrityLevelType::Medium => "Medium", + VmmIntegrityLevelType::MediumPlus => "MediumPlus", + VmmIntegrityLevelType::High => "High", + VmmIntegrityLevelType::System => "System", + VmmIntegrityLevelType::Protected => "Protected", + VmmIntegrityLevelType::Unknown => "Unknown", + }; + write!(f, "{v}") + } +} + +impl From for VmmMapPfnType { + fn from(v : u32) -> Self { + return match v { + 0 => VmmMapPfnType::Zero, + 1 => VmmMapPfnType::Free, + 2 => VmmMapPfnType::Standby, + 3 => VmmMapPfnType::Modified, + 4 => VmmMapPfnType::ModifiedNoWrite, + 5 => VmmMapPfnType::Bad, + 6 => VmmMapPfnType::Active, + _ => VmmMapPfnType::Transition, + }; + } +} + +impl fmt::Display for VmmMapPfnType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let v = match self { + VmmMapPfnType::Zero => "Zero", + VmmMapPfnType::Free => "Free", + VmmMapPfnType::Standby => "Standby", + VmmMapPfnType::Modified => "Modified", + VmmMapPfnType::ModifiedNoWrite => "ModifiedNoWrite", + VmmMapPfnType::Bad => "Bad", + VmmMapPfnType::Active => "Active", + VmmMapPfnType::Transition => "Transition", + }; + write!(f, "{v}") + } +} + +impl From for VmmMapPfnTypeExtended { + fn from(v : u32) -> Self { + return match v { + 1 => VmmMapPfnTypeExtended::Unused, + 2 => VmmMapPfnTypeExtended::ProcessPrivate, + 3 => VmmMapPfnTypeExtended::PageTable, + 4 => VmmMapPfnTypeExtended::LargePage, + 5 => VmmMapPfnTypeExtended::DriverLocked, + 6 => VmmMapPfnTypeExtended::Shareable, + 7 => VmmMapPfnTypeExtended::File, + _ => VmmMapPfnTypeExtended::Unknown, + }; + } +} + +impl fmt::Display for VmmMapPfnTypeExtended { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let v = match self { + VmmMapPfnTypeExtended::Unused => "Unused", + VmmMapPfnTypeExtended::ProcessPrivate => "ProcessPrivate", + VmmMapPfnTypeExtended::PageTable => "PageTable", + VmmMapPfnTypeExtended::LargePage => "LargePage", + VmmMapPfnTypeExtended::DriverLocked => "DriverLocked", + VmmMapPfnTypeExtended::Shareable => "Shareable", + VmmMapPfnTypeExtended::File => "File", + VmmMapPfnTypeExtended::Unknown => "Unknown", + }; + write!(f, "{v}") + } +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CVmEntry { + hVM : usize, + uszName : *const c_char, + gpaMax : u64, + tp : u32, + fActive : bool, + fReadOnly : bool, + fPhysicalOnly : bool, + dwPartitionID : u32, + dwVersionBuild : u32, + tpSystem : u32, + dwParentVmmMountID : u32, + dwVmMemPID : u32, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CVmMap { + dwVersion : u32, + _Reserved1 : [u32; 5], + pbMultiText : *const c_char, + cbMultiText : u32, + cMap : u32, + pMap : CVmEntry, +} + +impl fmt::Display for VmmMapPfnEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmMapPfnEntry:{}", self.pfn) + } +} + +impl fmt::Display for VmmMapMemoryEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmMapMemoryEntry:{:x}->{:x}", self.pa, self.pa + self.cb - 1) + } +} + +impl fmt::Display for VmmMapNetEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmMapNetEntry:'{}'", self.desc) + } +} + +impl PartialEq for VmmMapNetEntry { + fn eq(&self, other: &Self) -> bool { + self.va_object == other.va_object + } +} + +impl fmt::Display for VmmMapPoolEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmMapPoolEntry:'{}':{:x}", self.tag_to_string(), self.va) + } +} + +impl PartialEq for VmmMapPoolEntry { + fn eq(&self, other: &Self) -> bool { + self.va == other.va + } +} + +impl fmt::Display for VmmMapServiceEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmMapServiceEntry:{}", self.name) + } +} + +impl fmt::Display for VmmMapUserEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmMapUserEntry:[{}]", self.user) + } +} + +impl fmt::Display for VmmMapVirtualMachineEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmMapVirtualMachineEntry:[{}]", self.name) + } +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CPfnEntry { + dwPfn : u32, + tpExtended : u32, + dwPfnPte : [u32; 5], + va : u64, + vaPte : u64, + OriginalPte : u64, + u3 : u32, + u4 : u64, + _FutureUse : [u32; 6], +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CPfnMap { + dwVersion : u32, + _Reserved1 : [u32; 5], + cMap : u32, + pMap : CPfnEntry, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CNetMapEntry { + dwPID : u32, + dwState : u32, + _FutureUse3 : [u16; 3], + AF : u16, + src_fValid : bool, + src__Reserved : u16, + src_port : u16, + src_pbAddr : [u8; 16], + src_uszText : *const c_char, + dst_fValid : bool, + dst__Reserved : u16, + dst_port : u16, + dst_pbAddr : [u8; 16], + dst_uszText : *const c_char, + vaObj : u64, + ftTime : u64, + dwPoolTag : u32, + _FutureUse4 : u32, + uszText : *const c_char, + _FutureUse2 : [u32; 4], +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CNetMap { + dwVersion : u32, + _Reserved1 : u32, + pbMultiText : *const c_char, + cbMultiText : u32, + cMap : u32, + pMap : CNetMapEntry, +} + +#[repr(C)] +struct CMemoryMapEntry { + pa : u64, + cb : u64, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CMemoryMap { + dwVersion : u32, + _Reserved1 : [u32; 5], + cMap : u32, + _Reserved2 : u32, + pMap : CMemoryMapEntry, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CPoolEntry { + va : u64, + dwTag : u32, + _ReservedZero : u8, + fAlloc : u8, + tpPool : u8, + tpSS : u8, + cb : u32, + _Filler : u32, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CPoolMap { + dwVersion : u32, + _Reserved1 : [u32; 6], + cbTotal : u32, + piTag2Map : usize, // ptr + pTag : usize, // ptr + cTag : u32, + cMap : u32, + pMap : CPoolEntry, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CServiceEntry { + vaObj : u64, + dwOrdinal : u32, + dwStartType : u32, + dwServiceType : u32, + dwCurrentState : u32, + dwControlsAccepted : u32, + dwWin32ExitCode : u32, + dwServiceSpecificExitCode : u32, + dwCheckPoint : u32, + wWaitHint : u32, + uszServiceName : *const c_char, + uszDisplayName : *const c_char, + uszPath : *const c_char, + uszUserTp : *const c_char, + uszUserAcct : *const c_char, + uszImagePath : *const c_char, + dwPID : u32, + _FutureUse1 : u32, + _FutureUse2 : u64, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CServiceMap { + dwVersion : u32, + _Reserved1 : [u32; 5], + pbMultiText : *const c_char, + cbMultiText : u32, + cMap : u32, + pMap : CServiceEntry, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CUserEntry { + _FutureUse1 : [u32; 2], + uszText : *const c_char, + vaRegHive : u64, + uszSID : *const c_char, + _FutureUse2 : [u32; 2], +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CUserMap { + dwVersion : u32, + _Reserved1 : [u32; 5], + pbMultiText : *const c_char, + cbMultiText : u32, + cMap : u32, + pMap : CUserEntry, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CRegHive { + magic : u64, + wVersion : u16, + wSize : u16, + _FutureReserved1 : [u8; 0x34], + vaCMHIVE : u64, + vaHBASE_BLOCK : u64, + cbLength : u32, + uszName : [i8; 128], + uszNameShort : [i8; 32 + 1], + uszHiveRootPath : [i8; 260], + _FutureReserved : [u64; 0x10], +} + +impl fmt::Display for VmmVfsEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.is_directory { + write!(f, "VmmVfsEntry:D:'{}'", self.name,) + } else { + write!(f, "VmmVfsEntry:F:'{}':0x{:x}", self.name, self.size) + } + } +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CVMMDLL_VFS_FILELIST2 { + dwVersion : u32, + pfnAddFile : extern "C" fn(h : &mut Vec, uszName : *const c_char, cb : u64, pExInfo : usize), + pfnAddDirectory : extern "C" fn(h : &mut Vec, uszName : *const c_char, pExInfo : usize), + h : *mut Vec, +} + +extern "C" fn vfs_list_addfile_cb(h : &mut Vec, name : *const c_char, cb : u64, _p_ex_info : usize) { + unsafe { + if let Ok(name) = CStr::from_ptr(name).to_str() { + let e = VmmVfsEntry { + name : name.to_string(), + is_directory : false, + size : cb, + }; + h.push(e); + } + } +} + +extern "C" fn vfs_list_adddirectory_cb(h : &mut Vec, name : *const c_char, _p_ex_info : usize) { + unsafe { + if let Ok(name) = CStr::from_ptr(name).to_str() { + let e = VmmVfsEntry { + name : name.to_string(), + is_directory : true, + size : 0, + }; + h.push(e); + } + } +} + +#[allow(non_snake_case)] +impl Vmm<'_> { + fn impl_log(&self, log_mid : u32, log_level : &VmmLogLevel, log_message : &str) { + let c_loglevel : u32 = match log_level { + VmmLogLevel::_1Critical => 1, + VmmLogLevel::_2Warning => 2, + VmmLogLevel::_3Info => 3, + VmmLogLevel::_4Verbose => 4, + VmmLogLevel::_5Debug => 5, + VmmLogLevel::_6Trace => 6, + VmmLogLevel::_7None => 7, + }; + let sz_log_fmt = CString::new("%s").unwrap(); + let sz_log_message = CString::new(log_message).unwrap(); + let _r = (self.native.VMMDLL_Log)(self.native.h, log_mid, c_loglevel, sz_log_fmt.as_ptr(), sz_log_message.as_ptr()); + } + + fn impl_get_config(&self, config_id : u64) -> ResultEx { + let mut v = 0; + let f = (self.native.VMMDLL_ConfigGet)(self.native.h, config_id, &mut v); + return if f { Ok(v) } else { Err("VMMDLL_ConfigGet: fail".into()) }; + } + + fn impl_set_config(&self, config_id : u64, config_value : u64) -> ResultEx<()> { + let f = (self.native.VMMDLL_ConfigSet)(self.native.h, config_id, config_value); + return if f { Ok(()) } else { Err("VMMDLL_ConfigSet: fail".into()) }; + } + + fn impl_process_from_pid(&self, pid : u32) -> ResultEx { + let process_list = self.process_list()?; + let process = VmmProcess { + vmm : &self, + pid : pid, + }; + if process_list.contains(&process) { + return Ok(process); + } + return Err(format!("VMMDLL_PidGetFromName: fail. PID '{pid}' does not exist.").into()); + } + + fn impl_process_from_name(&self, process_name : &str) -> ResultEx { + let mut pid = 0; + let sz_process_name = CString::new(process_name).unwrap(); + let r = (self.native.VMMDLL_PidGetFromName)(self.native.h, sz_process_name.as_ptr(), &mut pid); + if !r { + return Err(format!("VMMDLL_PidGetFromName: fail. Process '{process_name}' does not exist.").into()); + } + return Ok(VmmProcess { + vmm : &self, + pid : pid, + }); + } + + fn impl_process_list(&self) -> ResultEx> { + let mut cpids : usize = 0; + let r = (self.native.VMMDLL_PidList)(self.native.h, std::ptr::null_mut(), &mut cpids); + if !r || cpids > 0x00100000 { + return Err("VMMDLL_PidList: fail.".into()); + } + let mut pids = vec![0u32; cpids]; + let r = (self.native.VMMDLL_PidList)(self.native.h, pids.as_mut_ptr(), &mut cpids); + if !r || cpids > 0x00100000 { + return Err("VMMDLL_PidList: fail.".into()); + } + let mut proclist = Vec::new(); + for i in 0..cpids { + let proc = VmmProcess { + vmm : self, + pid : *pids.get(i).unwrap(), + }; + proclist.push(proc); + } + return Ok(proclist); + } + fn impl_map_pfn(&self, pfns : &Vec, is_extended : bool) -> ResultEx> { + unsafe { + let mut structs = std::ptr::null_mut(); + let flags = if is_extended { 1 } else { 0 }; + let r = (self.native.VMMDLL_Map_GetPfnEx)(self.native.h, pfns.as_ptr(), u32::try_from(pfns.len())?, &mut structs, flags); + if !r { + return Err("VMMDLL_Map_GetPfnEx: fail.".into()); + } + if (*structs).dwVersion != VMMDLL_MAP_PFN_VERSION { + (self.native.VMMDLL_MemFree)(structs as usize); + return Err("VMMDLL_Map_GetPfnEx: bad version.".into()); + } + let mut result = Vec::new(); + if (*structs).cMap == 0 { + (self.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + let cMap : usize = (*structs).cMap.try_into()?; + let pMap = std::slice::from_raw_parts(&(*structs).pMap, cMap); + for i in 0..cMap { + let ne = &pMap[i]; + let e = VmmMapPfnEntry { + pfn : ne.dwPfn, + location : VmmMapPfnType::from((ne.u3 >> 16) & 7), + is_prototype : if ne.u4 & 0x0200000000000000 > 0 { true } else { false }, + color : u32::try_from(ne.u4 >> 58)?, + is_extended : is_extended, + tp_ex : VmmMapPfnTypeExtended::from(ne.tpExtended), + pid : ne.dwPfnPte[0], + ptes : [0, ne.dwPfnPte[1], ne.dwPfnPte[2], ne.dwPfnPte[3], ne.dwPfnPte[4]], + va : ne.va, + va_pte : ne.vaPte, + pte_original : ne.OriginalPte, + }; + result.push(e); + } + (self.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + } + + fn impl_map_memory(&self) -> ResultEx> { + unsafe { + let mut structs = std::ptr::null_mut(); + let r = (self.native.VMMDLL_Map_GetPhysMem)(self.native.h, &mut structs); + if !r { + return Err("VMMDLL_Map_GetPhysMem: fail.".into()); + } + if (*structs).dwVersion != VMMDLL_MAP_PHYSMEM_VERSION { + (self.native.VMMDLL_MemFree)(structs as usize); + return Err("VMMDLL_Map_GetPhysMem: bad version.".into()); + } + let mut result = Vec::new(); + if (*structs).cMap == 0 { + (self.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + let cMap : usize = (*structs).cMap.try_into()?; + let pMap = std::slice::from_raw_parts(&(*structs).pMap, cMap); + for i in 0..cMap { + let ne = &pMap[i]; + let e = VmmMapMemoryEntry { + pa : ne.pa, + cb : ne.cb, + }; + result.push(e); + } + (self.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + } + + fn impl_map_net(&self) -> ResultEx> { + unsafe { + let mut structs = std::ptr::null_mut(); + let r = (self.native.VMMDLL_Map_GetNetU)(self.native.h, &mut structs); + if !r { + return Err("VMMDLL_Map_GetNetU: fail.".into()); + } + if (*structs).dwVersion != VMMDLL_MAP_NET_VERSION { + (self.native.VMMDLL_MemFree)(structs as usize); + return Err("VMMDLL_Map_GetNetU: bad version.".into()); + } + let mut result = Vec::new(); + if (*structs).cMap == 0 { + (self.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + let cMap : usize = (*structs).cMap.try_into()?; + let pMap = std::slice::from_raw_parts(&(*structs).pMap, cMap); + for i in 0..cMap { + let ne = &pMap[i]; + let e = VmmMapNetEntry { + pid : ne.dwPID, + state : ne.dwState, + address_family : ne.AF, + src_is_valid : ne.src_fValid, + src_port : ne.src_port, + src_addr_raw : ne.src_pbAddr, + src_str : String::from(CStr::from_ptr(ne.src_uszText).to_str().unwrap_or("")), + dst_is_valid : ne.dst_fValid, + dst_port : ne.dst_port, + dst_addr_raw : ne.dst_pbAddr, + dst_str : String::from(CStr::from_ptr(ne.dst_uszText).to_str().unwrap_or("")), + va_object : ne.vaObj, + filetime : ne.ftTime, + pool_tag : ne.dwPoolTag, + desc : String::from(CStr::from_ptr(ne.uszText).to_str().unwrap_or("")), + }; + result.push(e); + } + (self.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + } + + fn impl_map_pool(&self, is_bigpool_only : bool) -> ResultEx> { + unsafe { + let mut structs = std::ptr::null_mut(); + let flags = if is_bigpool_only { 1 } else { 0 }; + let r = (self.native.VMMDLL_Map_GetPool)(self.native.h, &mut structs, flags); + if !r { + return Err("VMMDLL_Map_GetPool: fail.".into()); + } + if (*structs).dwVersion != VMMDLL_MAP_POOL_VERSION { + (self.native.VMMDLL_MemFree)(structs as usize); + return Err("VMMDLL_Map_GetPool: bad version.".into()); + } + let mut result = Vec::new(); + if (*structs).cMap == 0 { + (self.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + let cMap : usize = (*structs).cMap.try_into()?; + let pMap = std::slice::from_raw_parts(&(*structs).pMap, cMap); + for i in 0..cMap { + let ne = &pMap[i]; + let e = VmmMapPoolEntry { + va : ne.va, + cb : ne.cb, + tag : ne.dwTag, + is_alloc : ne.fAlloc != 0, + tp_pool : ne.tpPool, + tp_subsegment : ne.tpSS, + }; + result.push(e); + } + (self.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + } + + fn impl_map_service(&self) -> ResultEx> { + unsafe { + let mut structs = std::ptr::null_mut(); + let r = (self.native.VMMDLL_Map_GetServicesU)(self.native.h, &mut structs); + if !r { + return Err("VMMDLL_Map_GetServicesU: fail.".into()); + } + if (*structs).dwVersion != VMMDLL_MAP_SERVICE_VERSION { + (self.native.VMMDLL_MemFree)(structs as usize); + return Err("VMMDLL_Map_GetServicesU: bad version.".into()); + } + let mut result = Vec::new(); + if (*structs).cMap == 0 { + (self.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + let cMap : usize = (*structs).cMap.try_into()?; + let pMap = std::slice::from_raw_parts(&(*structs).pMap, cMap); + for i in 0..cMap { + let ne = &pMap[i]; + let e = VmmMapServiceEntry { + ordinal : ne.dwOrdinal, + va_object : ne.vaObj, + pid : ne.dwPID, + start_type : ne.dwStartType, + service_type : ne.dwServiceType, + current_state : ne.dwCurrentState, + controls_accepted : ne.dwControlsAccepted, + win32_exit_code : ne.dwWin32ExitCode, + service_specific_exit_code : ne.dwServiceSpecificExitCode, + check_point : ne.dwCheckPoint, + wait_hint : ne.wWaitHint, + name : String::from(CStr::from_ptr(ne.uszServiceName).to_str().unwrap_or("")), + name_display : String::from(CStr::from_ptr(ne.uszDisplayName).to_str().unwrap_or("")), + path : String::from(CStr::from_ptr(ne.uszPath).to_str().unwrap_or("")), + user_type : String::from(CStr::from_ptr(ne.uszUserTp).to_str().unwrap_or("")), + user_account : String::from(CStr::from_ptr(ne.uszUserAcct).to_str().unwrap_or("")), + image_path : String::from(CStr::from_ptr(ne.uszImagePath).to_str().unwrap_or("")), + }; + result.push(e); + } + (self.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + } + + fn impl_map_user(&self) -> ResultEx> { + unsafe { + let mut structs = std::ptr::null_mut(); + let r = (self.native.VMMDLL_Map_GetUsersU)(self.native.h, &mut structs); + if !r { + return Err("VMMDLL_Map_GetUsersU: fail.".into()); + } + if (*structs).dwVersion != VMMDLL_MAP_USER_VERSION { + (self.native.VMMDLL_MemFree)(structs as usize); + return Err("VMMDLL_Map_GetUsersU: bad version.".into()); + } + let mut result = Vec::new(); + if (*structs).cMap == 0 { + (self.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + let cMap : usize = (*structs).cMap.try_into()?; + let pMap = std::slice::from_raw_parts(&(*structs).pMap, cMap); + for i in 0..cMap { + let ne = &pMap[i]; + let e = VmmMapUserEntry { + user : String::from(CStr::from_ptr(ne.uszText).to_str().unwrap_or("")), + sid : String::from(CStr::from_ptr(ne.uszSID).to_str().unwrap_or("")), + va_reg_hive : ne.vaRegHive, + }; + result.push(e); + } + (self.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + } + + fn impl_map_virtual_machine(&self) -> ResultEx> { + unsafe { + let mut structs = std::ptr::null_mut(); + let r = (self.native.VMMDLL_Map_GetVMU)(self.native.h, &mut structs); + if !r { + return Err("VMMDLL_Map_GetVMU: fail.".into()); + } + if (*structs).dwVersion != VMMDLL_MAP_VM_VERSION { + (self.native.VMMDLL_MemFree)(structs as usize); + return Err("VMMDLL_Map_GetVMU: bad version.".into()); + } + let mut result = Vec::new(); + if (*structs).cMap == 0 { + (self.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + let cMap : usize = (*structs).cMap.try_into()?; + let pMap = std::slice::from_raw_parts(&(*structs).pMap, cMap); + for i in 0..cMap { + let ne = &pMap[i]; + let e = VmmMapVirtualMachineEntry { + h_vmm : self.native.h, + h_vm : ne.hVM, + name : String::from(CStr::from_ptr(ne.uszName).to_str().unwrap_or("")), + gpa_max : ne.gpaMax, + tp_vm : ne.tp, + is_active : ne.fActive, + is_readonly : ne.fReadOnly, + is_physicalonly : ne.fPhysicalOnly, + partition_id : ne.dwPartitionID, + guest_os_version_build : ne.dwVersionBuild, + guest_tp_system : ne.tpSystem, + parent_mount_id : ne.dwParentVmmMountID, + vmmem_pid : ne.dwVmMemPID, + }; + result.push(e); + } + (self.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + } + + fn impl_mem_read(&self, pid : u32, va : u64, size : usize, flags : u64) -> ResultEx> { + let cb = u32::try_from(size)?; + let mut cb_read = 0; + let mut pb_result = vec![0u8; size]; + let r = (self.native.VMMDLL_MemReadEx)(self.native.h, pid, va, pb_result.as_mut_ptr(), cb, &mut cb_read, flags); + if !r { + return Err("VMMDLL_MemReadEx: fail.".into()); + } + return Ok(pb_result); + } + + fn impl_mem_scatter(&self, pid : u32, flags : u64) -> ResultEx { + let flags = u32::try_from(flags)?; + let r = (self.native.VMMDLL_Scatter_Initialize)(self.native.h, pid, flags); + if r == 0 { + return Err("VMMDLL_Scatter_Initialize: fail.".into()); + } + return Ok(VmmScatterMemory { + vmm : &self, + hs : r, + pid, + flags, + is_scatter_ex : false, + }); + } + + fn impl_mem_virt2phys(&self, pid : u32, va : u64) -> ResultEx { + let mut pa : u64 = 0; + let r = (self.native.VMMDLL_MemVirt2Phys)(self.native.h, pid, va, &mut pa); + if !r { + return Err("VMMDLL_MemVirt2Phys: fail.".into()); + } + return Ok(pa); + } + + fn impl_mem_write(&self, pid : u32, va : u64, data : &Vec) -> ResultEx<()> { + let cb = u32::try_from(data.len())?; + let pb = data.as_ptr(); + let r = (self.native.VMMDLL_MemWrite)(self.native.h, pid, va, pb, cb); + if !r { + return Err("VMMDLL_MemWrite: fail.".into()); + } + return Ok(()); + } + + fn impl_vfs_list(&self, path : &str) -> ResultEx> { + let c_path = CString::new(str::replace(path, "/", "\\"))?; + let mut vec_result : Vec = Vec::new(); + let ptr_result : *mut Vec = &mut vec_result; + let mut filelist2 = CVMMDLL_VFS_FILELIST2 { + dwVersion : VMMDLL_VFS_FILELIST_VERSION, + pfnAddFile : vfs_list_addfile_cb, + pfnAddDirectory : vfs_list_adddirectory_cb, + h : ptr_result, + }; + let r = (self.native.VMMDLL_VfsListU)(self.native.h, c_path.as_ptr(), &mut filelist2); + if !r { + return Err("VMMDLL_VfsListU: fail.".into()); + } + return Ok(vec_result); + } + + fn impl_vfs_read(&self, filename : &str, size : u32, offset : u64) -> ResultEx> { + let c_filename = CString::new(str::replace(filename, "/", "\\"))?; + let mut cb_read = 0u32; + let mut data = vec![0u8; size as usize]; + let ntstatus = (self.native.VMMDLL_VfsReadU)(self.native.h, c_filename.as_ptr(), data.as_mut_ptr(), size, &mut cb_read, offset); + if ntstatus != 0 && ntstatus != 0xC0000011 { + return Err("VMMDLL_VfsReadU: fail.".into()); + } + if cb_read < size { + data.resize(cb_read as usize, 0); + } + return Ok(data); + } + + fn impl_vfs_write(&self, filename : &str, data : Vec, offset : u64) { + if data.len() < u32::MAX as usize { + let c_filename = CString::new(str::replace(filename, "/", "\\")).unwrap(); + let mut cb_write = 0u32; + let _ntstatus = (self.native.VMMDLL_VfsWriteU)(self.native.h, c_filename.as_ptr(), data.as_ptr(), data.len() as u32, &mut cb_write, offset); + } + } + + pub fn impl_reg_hive_list(&self) -> ResultEx> { + unsafe { + let mut cHives = 0; + let r = (self.native.VMMDLL_WinReg_HiveList)(self.native.h, std::ptr::null_mut(), 0, &mut cHives); + if !r { + return Err("VMMDLL_WinReg_HiveList: fail.".into()); + } + if cHives == 0 { + return Ok(Vec::new()); + } + let size = std::mem::size_of::(); + let mut bytes = vec![0u8; size * cHives as usize]; + let ptr = bytes.as_mut_ptr() as *mut CRegHive; + let r = (self.native.VMMDLL_WinReg_HiveList)(self.native.h, ptr, cHives, &mut cHives); + if !r { + return Err("VMMDLL_WinReg_HiveList: fail.".into()); + } + if cHives == 0 { + return Ok(Vec::new()); + } + let mut result = Vec::new(); + let pMap = std::slice::from_raw_parts(ptr, cHives as usize); + for i in 0..cHives as usize { + let ne = &pMap[i]; + if (ne.magic != VMMDLL_REGISTRY_HIVE_INFORMATION_MAGIC) || (ne.wVersion != VMMDLL_REGISTRY_HIVE_INFORMATION_VERSION) { + return Err("Hive Bad Version.".into()); + } + let e = VmmRegHive { + vmm : &self, + va : ne.vaCMHIVE, + va_baseblock : ne.vaHBASE_BLOCK, + size : ne.cbLength, + name : String::from_utf8_lossy(CStr::from_ptr(ne.uszName.as_ptr()).to_bytes()).to_string(), + name_short : String::from_utf8_lossy(CStr::from_ptr(ne.uszNameShort.as_ptr()).to_bytes()).to_string(), + path : String::from_utf8_lossy(CStr::from_ptr(ne.uszHiveRootPath.as_ptr()).to_bytes()).to_string(), + }; + result.push(e); + } + return Ok(result); + } + } +} + +impl VmmMapPoolEntry { + pub fn tag_to_string(&self) -> String { + let tag_chars = [((self.tag >> 0) & 0xff) as u8, ((self.tag >> 8) & 0xff) as u8, ((self.tag >> 16) & 0xff) as u8, ((self.tag >> 24) & 0xff) as u8]; + return String::from_utf8_lossy(&tag_chars).to_string(); + } +} + + + + + + +//============================================================================= +// INTERNAL: VMM.KERNEL: +//============================================================================= + +impl fmt::Display for VmmKernel<'_> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmKernel") + } +} + + + + + + +//============================================================================= +// INTERNAL: VMM.PDB: +//============================================================================= + +impl fmt::Display for VmmPdb<'_> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmPdb:{}", self.module) + } +} + +impl VmmPdb<'_> { + fn impl_symbol_name_from_address(&self, va_or_offset : u64) -> ResultEx<(String, u32)> { + let c_module = CString::new(self.module.as_str())?; + let mut c_symbol_name = [0 as c_char; MAX_PATH]; + let mut result_symbol_displacement = 0; + let r = (self.vmm.native.VMMDLL_PdbSymbolName)(self.vmm.native.h, c_module.as_ptr(), va_or_offset, c_symbol_name.as_mut_ptr(), &mut result_symbol_displacement); + if !r { + return Err("VMMDLL_PdbSymbolName: fail.".into()); + } + let cstr_symbol_name = unsafe { CStr::from_ptr(c_symbol_name.as_ptr()) }; + let string_symbol_name = String::from_utf8_lossy(cstr_symbol_name.to_bytes()).to_string(); + return Ok((string_symbol_name, result_symbol_displacement)); + } + + fn impl_symbol_address_from_name(&self, symbol_name : &str) -> ResultEx { + let c_module = CString::new(self.module.as_str())?; + let c_symbol_name = CString::new(symbol_name)?; + let mut result = 0; + let r = (self.vmm.native.VMMDLL_PdbSymbolAddress)(self.vmm.native.h, c_module.as_ptr(), c_symbol_name.as_ptr(), &mut result); + if !r { + return Err("VMMDLL_PdbSymbolAddress: fail.".into()); + } + return Ok(result); + } + + fn impl_type_size(&self, type_name : &str) -> ResultEx { + let c_module = CString::new(self.module.as_str())?; + let c_type_name = CString::new(type_name)?; + let mut result = 0; + let r = (self.vmm.native.VMMDLL_PdbTypeSize)(self.vmm.native.h, c_module.as_ptr(), c_type_name.as_ptr(), &mut result); + if !r { + return Err("VMMDLL_PdbTypeSize: fail.".into()); + } + return Ok(result); + } + + fn impl_type_child_offset(&self, type_name : &str, type_child_name : &str) -> ResultEx { + let c_module = CString::new(self.module.as_str())?; + let c_type_name = CString::new(type_name)?; + let c_type_child_name = CString::new(type_child_name)?; + let mut result = 0; + let r = (self.vmm.native.VMMDLL_PdbTypeChildOffset)(self.vmm.native.h, c_module.as_ptr(), c_type_name.as_ptr(), c_type_child_name.as_ptr(), &mut result); + if !r { + return Err("VMMDLL_PdbTypeChildOffset: fail.".into()); + } + return Ok(result); + } +} + + + + + + +//============================================================================= +// INTERNAL: VMM.REGISTRY: +//============================================================================= + +impl fmt::Display for VmmRegHive<'_> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmRegHive:{:x}", self.va) + } +} + +impl PartialEq for VmmRegHive<'_> { + fn eq(&self, other: &Self) -> bool { + self.va == other.va + } +} + +impl VmmRegHive<'_> { + fn impl_reg_hive_read(&self, ra : u32, size : usize, flags : u64) -> ResultEx> { + let cb = u32::try_from(size)?; + let mut cb_read = 0; + let mut pb_result = vec![0u8; size]; + let r = (self.vmm.native.VMMDLL_WinReg_HiveReadEx)(self.vmm.native.h, self.va, ra, pb_result.as_mut_ptr(), cb, &mut cb_read, flags); + if !r { + return Err("VMMDLL_WinReg_HiveReadEx: fail.".into()); + } + return Ok(pb_result); + } + + fn impl_reg_hive_write(&self, ra : u32, data : &Vec) -> ResultEx<()> { + let cb = u32::try_from(data.len())?; + let pb = data.as_ptr(); + let r = (self.vmm.native.VMMDLL_WinReg_HiveWrite)(self.vmm.native.h, self.va, ra, pb, cb); + if !r { + return Err("VMMDLL_WinReg_HiveWrite: fail.".into()); + } + return Ok(()); + } +} + + + + + + +//============================================================================= +// INTERNAL: VMM.PROCESS: +//============================================================================= + +impl fmt::Display for VmmProcess<'_> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmProcess:{}", self.pid) + } +} + +impl PartialEq for VmmProcess<'_> { + fn eq(&self, other: &Self) -> bool { + self.pid == other.pid + } +} + +impl fmt::Display for VmmProcessInfo { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmProcessInfo:{}:{}", self.pid, self.name) + } +} + +impl fmt::Display for VmmProcessMapEatEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmProcessMapEatEntry:{:x}:{}", self.va_function, self.function) + } +} + +impl fmt::Display for VmmProcessMapHandleEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmProcessMapHandleEntry:{}:{:x}:{}:[{}]", self.pid, self.handle_id, self.tp, self.info) + } +} + +impl From for VmmProcessMapHeapType { + fn from(v : u32) -> Self { + return match v { + 1 => VmmProcessMapHeapType::NtHeap, + 2 => VmmProcessMapHeapType::SegmentHeap, + _ => VmmProcessMapHeapType::NA, + }; + } +} + +impl fmt::Display for VmmProcessMapHeapType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let v = match self { + VmmProcessMapHeapType::NA => "NA", + VmmProcessMapHeapType::NtHeap => "NtHeap", + VmmProcessMapHeapType::SegmentHeap => "SegmentHeap", + }; + write!(f, "{v}") + } +} + +impl fmt::Display for VmmProcessMapHeapEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmProcessMapHeapAllocEntry:{}:{}:{}", self.pid, self.number, self.tp) + } +} + +impl From for VmmProcessMapHeapAllocType { + fn from(v : u32) -> Self { + return match v { + 1 => VmmProcessMapHeapAllocType::NtHeap, + 2 => VmmProcessMapHeapAllocType::NtLFH, + 3 => VmmProcessMapHeapAllocType::NtLarge, + 4 => VmmProcessMapHeapAllocType::NtNA, + 5 => VmmProcessMapHeapAllocType::SegVS, + 6 => VmmProcessMapHeapAllocType::SegLFH, + 7 => VmmProcessMapHeapAllocType::SegLarge, + 8 => VmmProcessMapHeapAllocType::SegNA, + _ => VmmProcessMapHeapAllocType::NA, + }; + } +} + +impl fmt::Display for VmmProcessMapHeapAllocType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let v = match self { + VmmProcessMapHeapAllocType::NA => "NA", + VmmProcessMapHeapAllocType::NtHeap => "NtHeap", + VmmProcessMapHeapAllocType::NtLFH => "NtLFH", + VmmProcessMapHeapAllocType::NtLarge => "NtLarge", + VmmProcessMapHeapAllocType::NtNA => "NtNA", + VmmProcessMapHeapAllocType::SegVS => "SegVS", + VmmProcessMapHeapAllocType::SegLFH => "SegLFH", + VmmProcessMapHeapAllocType::SegLarge => "SegLarge", + VmmProcessMapHeapAllocType::SegNA => "SegNA", + }; + write!(f, "{v}") + } +} + +impl fmt::Display for VmmProcessMapHeapAllocEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmProcessMapHeapAllocEntry:{}:{}:{:x}", self.pid, self.tp, self.va) + } +} + +impl fmt::Display for VmmProcessMapIatEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmProcessMapIatEntry:{:x}:{}", self.va_function, self.function) + } +} + +impl fmt::Display for VmmProcessMapPteEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmProcessMapPteEntry:{}:{:x}->{:x}", self.pid, self.va_base, self.va_base + self.page_count * 0x1000 - 1) + } +} + +impl fmt::Display for VmmProcessMapModuleEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmProcessMapModuleEntry:{}:{:x}:[{}]", self.pid, self.va_base, self.name) + } +} + +impl fmt::Display for VmmProcessMapModuleDebugEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmProcessMapModuleDebugEntry:[{}]", self.pdb_filename) + } +} + +impl fmt::Display for VmmProcessMapModuleVersionEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmProcessMapModuleVersionEntry") + } +} + +impl fmt::Display for VmmProcessMapThreadEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmProcessMapThreadEntry:{}:{:x}", self.pid, self.thread_id) + } +} + +impl fmt::Display for VmmProcessMapUnloadedModuleEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmProcessMapUnloadedModuleEntry:{}:{:x}:[{}]", self.pid, self.va_base, self.name) + } +} + +impl fmt::Display for VmmProcessMapVadEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmProcessMapVadEntry:{}:{:x}->{}", self.pid, self.va_start, self.va_end) + } +} + +impl From for VmmProcessMapVadExType { + fn from(v : u32) -> Self { + return match v { + 1 => VmmProcessMapVadExType::Hardware, + 2 => VmmProcessMapVadExType::Transition, + 3 => VmmProcessMapVadExType::Prototype, + 4 => VmmProcessMapVadExType::DemandZero, + 5 => VmmProcessMapVadExType::Compressed, + 6 => VmmProcessMapVadExType::Pagefile, + 7 => VmmProcessMapVadExType::File, + _ => VmmProcessMapVadExType::NA, + }; + } +} + +impl fmt::Display for VmmProcessMapVadExType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let v = match self { + VmmProcessMapVadExType::NA => "NA", + VmmProcessMapVadExType::Hardware => "Hardware", + VmmProcessMapVadExType::Transition => "Transition", + VmmProcessMapVadExType::Prototype => "Prototype", + VmmProcessMapVadExType::DemandZero => "DemandZero", + VmmProcessMapVadExType::Compressed => "Compressed", + VmmProcessMapVadExType::Pagefile => "Pagefile", + VmmProcessMapVadExType::File => "File", + }; + write!(f, "{v}") + } +} + +impl fmt::Display for VmmProcessMapVadExEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmProcessMapVadExEntry:{}:{:x}:{}", self.pid, self.va, self.tp) + } +} + +impl fmt::Display for VmmProcessMapDirectoryEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmProcessMapDirectoryEntry:{}:{}:{:x}:{:x}", self.pid, self.name, self.virtual_address, self.size) + } +} + +impl fmt::Display for VmmProcessSectionEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmProcessSectionEntry:{}:[{}]:{:x}:{:x}", self.pid, self.name, self.virtual_address, self.misc_virtual_size) + } +} + +impl From for VmmProcessMapModuleType { + fn from(v : u32) -> Self { + return match v { + 1 => VmmProcessMapModuleType::Data, + 2 => VmmProcessMapModuleType::NotLinked, + 3 => VmmProcessMapModuleType::Injected, + _ => VmmProcessMapModuleType::Normal, + }; + } +} + +impl fmt::Display for VmmProcessMapModuleType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let v = match self { + VmmProcessMapModuleType::Data => "Data", + VmmProcessMapModuleType::NotLinked => "NotLinked", + VmmProcessMapModuleType::Injected => "Injected", + VmmProcessMapModuleType::Normal => "Normal", + }; + write!(f, "{v}") + } +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CProcessInformation { + magic : u64, + wVersion : u16, + wSize : u16, + tpMemoryModel : u32, + tpSystem : u32, + fUserOnly : bool, + dwPID : u32, + dwPPID : u32, + dwState : u32, + szName : [i8; 16], + szNameLong : [i8; 64], + paDTB : u64, + paDTB_UserOpt : u64, + vaEPROCESS : u64, + vaPEB : u64, + _Reserved1 : u64, + fWow64 : bool, + vaPEB32 : u32, + dwSessionId : u32, + qwLUID : u64, + szSID : [i8; 260], + IntegrityLevel : u32, +} + +#[repr(C)] +#[allow(non_snake_case)] +#[derive(Clone, Default)] +struct CIMAGE_SECTION_HEADER { + Name : [u8; 8], + Misc_VirtualAddress : u32, + VirtualAddress : u32, + SizeOfRawData : u32, + PointerToRawData : u32, + PointerToRelocations : u32, + PointerToLinenumbers : u32, + NumberOfRelocations : u16, + NumberOfLinenumbers : u16, + Characteristics : u32, +} + +#[repr(C)] +#[allow(non_snake_case)] +#[derive(Clone, Default)] +struct CIMAGE_DATA_DIRECTORY { + VirtualAddress : u32, + Size : u32, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CEatEntry { + vaFunction : u64, + dwOrdinal : u32, + oFunctionsArray : u32, + oNamesArray : u32, + _FutureUse1 : u32, + uszFunction : *const c_char, + uszForwardedFunction : *const c_char, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CEatMap { + dwVersion : u32, + dwOrdinalBase : u32, + cNumberOfNames : u32, + cNumberOfFunctions : u32, + cNumberOfForwardedFunctions : u32, + _Reserved1 : [u32; 3], + vaModuleBase : u64, + vaAddressOfFunctions : u64, + vaAddressOfNames : u64, + pbMultiText : *const c_char, + cbMultiText : u32, + cMap : u32, + pMap : CEatEntry, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CHandleEntry { + vaObject : u64, + dwHandle : u32, + dwGrantedAccess_Tp : u32, + qwHandleCount : u64, + qwPointerCount : u64, + vaObjectCreateInfo : u64, + vaSecurityDescriptor : u64, + uszText : *const c_char, + _FutureUse2 : u32, + dwPID : u32, + dwPoolTag : u32, + _FutureUse : [u32; 7], + uszType : *const c_char, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CHandleMap { + dwVersion : u32, + _Reserved1 : [u32; 5], + pbMultiText : *const c_char, + cbMultiText : u32, + cMap : u32, + pMap : CHandleEntry, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CHeapEntry { + va : u64, + tp : u32, + f32 : bool, + iHeap : u32, + dwHeapNum : u32, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CHeapMap { + dwVersion : u32, + _Reserved1 : [u32; 7], + pSegments : usize, + cSegments : u32, + cMap : u32, + pMap : CHeapEntry, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CHeapAllocEntry { + va : u64, + cb : u32, + tp : u32, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CHeapAllocMap { + dwVersion : u32, + _Reserved1 : [u32; 7], + _Reserved2 : [usize; 2], + cMap : u32, + pMap : CHeapAllocEntry, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CIatEntry { + vaFunction : u64, + uszFunction : *const c_char, + _FutureUse1 : u32, + _FutureUse2 : u32, + uszModule : *const c_char, + thunk_f32 : bool, + thunk_wHint : u16, + thunk__Reserved1 : u16, + thunk_rvaFirstThunk : u32, + thunk_rvaOriginalFirstThunk : u32, + thunk_rvaNameModule : u32, + thunk_rvaNameFunction : u32, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CIatMap { + dwVersion : u32, + _Reserved1 : [u32; 5], + vaModuleBase : u64, + pbMultiText : *const c_char, + cbMultiText : u32, + cMap : u32, + pMap : CIatEntry, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CDebugInfo { + dwAge : u32, + _Reserved : u32, + Guid : [u8; 16], + uszGuid : *const c_char, + uszPdbFilename : *const c_char, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CVersionInfo { + uszCompanyName : *const c_char, + uszFileDescription : *const c_char, + uszFileVersion : *const c_char, + uszInternalName : *const c_char, + uszLegalCopyright : *const c_char, + uszOriginalFilename : *const c_char, + uszProductName : *const c_char, + uszProductVersion : *const c_char, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CModuleEntry { + vaBase : u64, + vaEntry : u64, + cbImageSize : u32, + fWoW64 : bool, + uszText : *const c_char, + _Reserved3 : u32, + _Reserved4 : u32, + uszFullName : *const c_char, + tp : u32, + cbFileSizeRaw : u32, + cSection : u32, + cEAT : u32, + cIAT : u32, + _Reserved2 : u32, + _Reserved1 : [u64; 3], + pExDebugInfo : *const CDebugInfo, + pExVersionInfo : *const CVersionInfo, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CModuleMap { + dwVersion : u32, + _Reserved1 : [u32; 5], + pbMultiText : *const c_char, + cbMultiText : u32, + cMap : u32, + pMap : CModuleEntry, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CPteEntry { + vaBase : u64, + cPages : u64, + fPage : u64, + fWoW64 : bool, + _FutureUse1 : u32, + uszText : *const c_char, + _Reserved1 : u32, + cSoftware : u32, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CPteMap { + dwVersion : u32, + _Reserved1 : [u32; 5], + pbMultiText : *const c_char, + cbMultiText : u32, + cMap : u32, + pMap : CPteEntry, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CThreadEntry { + dwTID : u32, + dwPID : u32, + dwExitStatus : u32, + bState : u8, + bRunning : u8, + bPriority : u8, + bBasePriority : u8, + vaETHREAD : u64, + vaTeb : u64, + ftCreateTime : u64, + ftExitTime : u64, + vaStartAddress : u64, + vaStackBaseUser : u64, + vaStackLimitUser : u64, + vaStackBaseKernel : u64, + vaStackLimitKernel : u64, + vaTrapFrame : u64, + vaRIP : u64, + vaRSP : u64, + qwAffinity : u64, + dwUserTime : u32, + dwKernelTime : u32, + bSuspendCount : u8, + bWaitReason : u8, + _FutureUse1 : [u8; 2], + _FutureUse2 : [u32; 15], +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CThreadMap { + dwVersion : u32, + _Reserved1 : [u32; 8], + cMap : u32, + pMap : CThreadEntry, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CUnloadedModuleEntry { + vaBase : u64, + cbImageSize : u32, + fWoW64 : bool, + uszText : *const c_char, + _FutureUse1 : u32, + dwCheckSum : u32, + dwTimeDateStamp : u32, + _Reserved1 : u32, + ftUnload : u64, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CUnloadedModuleMap { + dwVersion : u32, + _Reserved1 : [u32; 5], + pbMultiText : *const c_char, + cbMultiText : u32, + cMap : u32, + pMap : CUnloadedModuleEntry, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CVadEntry { + vaStart : u64, + vaEnd : u64, + vaVad : u64, + u0 : u32, + u1 : u32, + u2 : u32, + cbPrototypePte : u32, + vaPrototypePte : u64, + vaSubsection : u64, + uszText : *const c_char, + _FutureUse1 : u32, + _Reserved1 : u32, + vaFileObject : u64, + cVadExPages : u32, + cVadExPagesBase : u32, + _Reserved2 : u64, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CVadMap { + dwVersion : u32, + _Reserved1 : [u32; 4], + cPage : u32, + pbMultiText : *const c_char, + cbMultiText : u32, + cMap : u32, + pMap : CVadEntry, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CVadExEntry { + tp : u32, + iPML : u32, + va : u64, + pa : u64, + pte : u64, + proto__Reserved1 : u32, + proto_tp : u32, + proto_pa : u64, + proto_va : u64, + vaVadBase : u64, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CVadExMap { + dwVersion : u32, + _Reserved1 : [u32; 4], + cMap : u32, + pMap : CVadExEntry, +} + +#[allow(non_snake_case)] +impl VmmProcess<'_> { + fn impl_info(&self) -> ResultEx { + let mut cb_pi = std::mem::size_of::(); + let mut pi = CProcessInformation { + magic : VMMDLL_PROCESS_INFORMATION_MAGIC, + wVersion : VMMDLL_PROCESS_INFORMATION_VERSION, + wSize : u16::try_from(cb_pi)?, + tpMemoryModel : 0, + tpSystem : 0, + fUserOnly : false, + dwPID : 0, + dwPPID : 0, + dwState : 0, + szName : [0i8; 16], + szNameLong : [0i8; 64], + paDTB : 0, + paDTB_UserOpt : 0, + vaEPROCESS : 0, + vaPEB : 0, + _Reserved1 : 0, + fWow64 : false, + vaPEB32 : 0, + dwSessionId : 0, + qwLUID : 0, + szSID : [0i8; 260], + IntegrityLevel : 0, + }; + let raw_pi = &mut pi as *mut CProcessInformation; + let r = (self.vmm.native.VMMDLL_ProcessGetInformation)(self.vmm.native.h, self.pid, raw_pi, &mut cb_pi); + if !r { + return Err("VMMDLL_ProcessGetInformation: fail.".into()); + } + let result = VmmProcessInfo { + tp_system : VmmSystemType::from(pi.tpSystem), + tp_memorymodel : VmmMemoryModelType::from(pi.tpMemoryModel), + is_user_mode : pi.fUserOnly, + pid : pi.dwPID, + ppid : pi.dwPPID, + state : pi.dwState, + name : unsafe { CStr::from_ptr(&pi.szName as *const c_char).to_string_lossy().to_string() }, + name_long : unsafe { CStr::from_ptr(&pi.szNameLong as *const c_char).to_string_lossy().to_string() }, + pa_dtb : pi.paDTB, + pa_dtb_user : pi.paDTB_UserOpt, + va_eprocess : pi.vaEPROCESS, + va_peb : pi.vaPEB, + is_wow64 : pi.fWow64, + va_peb32 : pi.vaPEB32, + session_id : pi.dwSessionId, + luid : pi.qwLUID, + sid : unsafe { CStr::from_ptr(&pi.szSID as *const c_char).to_string_lossy().to_string() }, + integrity_level : VmmIntegrityLevelType::from(pi.IntegrityLevel), + }; + return Ok(result); + } + + fn impl_get_information_string(&self, option : u32) -> ResultEx { + let r = (self.vmm.native.VMMDLL_ProcessGetInformationString)(self.vmm.native.h, self.pid, option); + if r.is_null() { + return Err("VMMDLL_ProcessGetInformationString: fail.".into()); + } + let cstr = unsafe { CStr::from_ptr(r) }; + let result = cstr.to_string_lossy().to_string(); + (self.vmm.native.VMMDLL_MemFree)(r as usize); + return Ok(result); + } + + fn impl_get_module_base(&self, module_name : &str) -> ResultEx { + let sz_module_name = CString::new(module_name).unwrap(); + let r = (self.vmm.native.VMMDLL_ProcessGetModuleBaseU)(self.vmm.native.h, self.pid, sz_module_name.as_ptr()); + if r == 0 { + return Err("VMMDLL_ProcessGetModuleBaseU: fail.".into()); + } + return Ok(r); + } + + fn impl_get_proc_address(&self, module_name : &str, function_name : &str) -> ResultEx { + let sz_module_name = CString::new(module_name).unwrap(); + let sz_function_name = CString::new(function_name).unwrap(); + let r = (self.vmm.native.VMMDLL_ProcessGetProcAddressU)(self.vmm.native.h, self.pid, sz_module_name.as_ptr(), sz_function_name.as_ptr()); + if r == 0 { + return Err("VMMDLL_ProcessGetProcAddressU: fail.".into()); + } + return Ok(r); + } + + fn impl_pdb_from_module_address(&self, va_module_base : u64) -> ResultEx { + let mut szModuleName = [0i8; MAX_PATH + 1]; + let r = (self.vmm.native.VMMDLL_PdbLoad)(self.vmm.native.h, self.pid, va_module_base, szModuleName.as_mut_ptr()); + if !r { + return Err("VMMDLL_PdbLoad: fail.".into()); + } + let cstr = unsafe { CStr::from_ptr(szModuleName.as_ptr()) }; + let module = cstr.to_string_lossy().to_string(); + let pdb = VmmPdb { + vmm : self.vmm, + module, + }; + return Ok(pdb); + } + + fn impl_map_handle(&self) -> ResultEx> { + unsafe { + let mut structs = std::ptr::null_mut(); + let r = (self.vmm.native.VMMDLL_Map_GetHandleU)(self.vmm.native.h, self.pid, &mut structs); + if !r { + return Err("VMMDLL_Map_GetHandleU: fail.".into()); + } + if (*structs).dwVersion != VMMDLL_MAP_HANDLE_VERSION { + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Err("VMMDLL_Map_GetHandleU: bad version.".into()); + } + let mut result = Vec::new(); + if (*structs).cMap == 0 { + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + let cMap : usize = (*structs).cMap.try_into()?; + let pMap = std::slice::from_raw_parts(&(*structs).pMap, cMap); + for i in 0..cMap { + let ne = &pMap[i]; + let e = VmmProcessMapHandleEntry { + pid : self.pid, + va_object : ne.vaObject, + handle_id : ne.dwHandle, + granted_access : ne.dwGrantedAccess_Tp & 0x00ffffff, + type_index : (ne.dwGrantedAccess_Tp >> 24) & 0xff, + handle_count : ne.qwHandleCount, + pointer_count : ne.qwPointerCount, + va_object_create_info : ne.vaObjectCreateInfo, + va_security_descriptor : ne.vaSecurityDescriptor, + handle_pid : ne.dwPID, + pool_tag : ne.dwPoolTag, + info : String::from(CStr::from_ptr(ne.uszText).to_str().unwrap_or("")), + tp : String::from(CStr::from_ptr(ne.uszType).to_str().unwrap_or("")), + }; + result.push(e); + } + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + } + + fn impl_map_heap(&self) -> ResultEx> { + unsafe { + let mut structs = std::ptr::null_mut(); + let r = (self.vmm.native.VMMDLL_Map_GetHeap)(self.vmm.native.h, self.pid, &mut structs); + if !r { + return Err("VMMDLL_Map_GetHeap: fail.".into()); + } + if (*structs).dwVersion != VMMDLL_MAP_HEAP_VERSION { + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Err("VMMDLL_Map_GetHeap: bad version.".into()); + } + let mut result = Vec::new(); + if (*structs).cMap == 0 { + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + let cMap : usize = (*structs).cMap.try_into()?; + let pMap = std::slice::from_raw_parts(&(*structs).pMap, cMap); + for i in 0..cMap { + let ne = &pMap[i]; + let e = VmmProcessMapHeapEntry { + pid : self.pid, + tp : VmmProcessMapHeapType::from(ne.tp), + is_32 : ne.f32, + index : ne.iHeap, + number : ne.dwHeapNum, + }; + result.push(e); + } + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + } + + fn impl_map_heapalloc(&self, heap_number_or_address : u64) -> ResultEx> { + unsafe { + let mut structs = std::ptr::null_mut(); + let r = (self.vmm.native.VMMDLL_Map_GetHeapAlloc)(self.vmm.native.h, self.pid, heap_number_or_address, &mut structs); + if !r { + return Err("VMMDLL_Map_GetHeapAlloc: fail.".into()); + } + if (*structs).dwVersion != VMMDLL_MAP_HEAPALLOC_VERSION { + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Err("VMMDLL_Map_GetHeapAlloc: bad version.".into()); + } + let mut result = Vec::new(); + if (*structs).cMap == 0 { + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + let cMap : usize = (*structs).cMap.try_into()?; + let pMap = std::slice::from_raw_parts(&(*structs).pMap, cMap); + for i in 0..cMap { + let ne = &pMap[i]; + let e = VmmProcessMapHeapAllocEntry { + pid : self.pid, + va : ne.va, + size : ne.cb, + tp : VmmProcessMapHeapAllocType::from(ne.tp), + }; + result.push(e); + } + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + } + + fn impl_map_module(&self, is_info_debug : bool, is_info_version : bool) -> ResultEx> { + unsafe { + let mut structs = std::ptr::null_mut(); + let flags = 0 + if is_info_debug { 1 } else { 0 } + if is_info_version { 2 } else { 0 }; + let r = (self.vmm.native.VMMDLL_Map_GetModuleU)(self.vmm.native.h, self.pid, &mut structs, flags); + if !r { + return Err("VMMDLL_Map_GetModuleU: fail.".into()); + } + if (*structs).dwVersion != VMMDLL_MAP_MODULE_VERSION { + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Err("VMMDLL_Map_GetModuleU: bad version.".into()); + } + let mut result = Vec::new(); + if (*structs).cMap == 0 { + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + let cMap : usize = (*structs).cMap.try_into()?; + let pMap = std::slice::from_raw_parts(&(*structs).pMap, cMap); + for i in 0..cMap { + let ne = &pMap[i]; + let mut debug_info = None; + if !ne.pExDebugInfo.is_null() { + let nei = &*ne.pExDebugInfo; + debug_info = Some(VmmProcessMapModuleDebugEntry { + pid : self.pid, + age : nei.dwAge, + raw_guid : nei.Guid, + guid : String::from(CStr::from_ptr(nei.uszGuid).to_str().unwrap_or("")), + pdb_filename : String::from(CStr::from_ptr(nei.uszPdbFilename).to_str().unwrap_or("")), + }); + } + let mut version_info = None; + if !ne.pExVersionInfo.is_null() { + let nei = &*ne.pExVersionInfo; + version_info = Some(VmmProcessMapModuleVersionEntry { + pid : self.pid, + company_name : String::from(CStr::from_ptr(nei.uszCompanyName).to_str().unwrap_or("")), + file_description : String::from(CStr::from_ptr(nei.uszFileDescription).to_str().unwrap_or("")), + file_version : String::from(CStr::from_ptr(nei.uszFileVersion).to_str().unwrap_or("")), + internal_name : String::from(CStr::from_ptr(nei.uszInternalName).to_str().unwrap_or("")), + legal_copyright : String::from(CStr::from_ptr(nei.uszLegalCopyright).to_str().unwrap_or("")), + original_file_name : String::from(CStr::from_ptr(nei.uszOriginalFilename).to_str().unwrap_or("")), + product_name : String::from(CStr::from_ptr(nei.uszProductName).to_str().unwrap_or("")), + product_version : String::from(CStr::from_ptr(nei.uszProductVersion).to_str().unwrap_or("")), + }); + } + let e = VmmProcessMapModuleEntry { + pid : self.pid, + va_base : ne.vaBase, + va_entry : ne.vaEntry, + image_size : ne.cbImageSize, + is_wow64 : ne.fWoW64, + tp : VmmProcessMapModuleType::from(ne.tp), + name : String::from(CStr::from_ptr(ne.uszText).to_str().unwrap_or("")), + full_name : String::from(CStr::from_ptr(ne.uszFullName).to_str().unwrap_or("")), + file_size_raw : ne.cbFileSizeRaw, + section_count : ne.cSection, + eat_count : ne.cEAT, + iat_count : ne.cIAT, + debug_info : debug_info, + version_info : version_info, + }; + result.push(e); + } + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + } + + fn impl_map_module_eat(&self, module_name : &str) -> ResultEx> { + unsafe { + let mut structs = std::ptr::null_mut(); + let sz_module_name = CString::new(module_name).unwrap(); + let r = (self.vmm.native.VMMDLL_Map_GetEATU)(self.vmm.native.h, self.pid, sz_module_name.as_ptr(), &mut structs); + if !r { + return Err("VMMDLL_Map_GetEATU: fail.".into()); + } + if (*structs).dwVersion != VMMDLL_MAP_EAT_VERSION { + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Err("VMMDLL_Map_GetEATU: bad version.".into()); + } + let mut result = Vec::new(); + if (*structs).cMap == 0 { + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + let cMap : usize = (*structs).cMap.try_into()?; + let pMap = std::slice::from_raw_parts(&(*structs).pMap, cMap); + for i in 0..cMap { + let ne = &pMap[i]; + let e = VmmProcessMapEatEntry { + pid : self.pid, + va_function : ne.vaFunction, + ordinal : ne.dwOrdinal, + function : String::from(CStr::from_ptr(ne.uszFunction).to_str().unwrap_or("")), + forwarded_function : String::from(CStr::from_ptr(ne.uszForwardedFunction).to_str().unwrap_or("")), + }; + result.push(e); + } + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + } + + fn impl_map_module_iat(&self, module_name : &str) -> ResultEx> { + unsafe { + let mut structs = std::ptr::null_mut(); + let sz_module_name = CString::new(module_name).unwrap(); + let r = (self.vmm.native.VMMDLL_Map_GetIATU)(self.vmm.native.h, self.pid, sz_module_name.as_ptr(), &mut structs); + if !r { + return Err("VMMDLL_Map_GetIATU: fail.".into()); + } + if (*structs).dwVersion != VMMDLL_MAP_IAT_VERSION { + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Err("VMMDLL_Map_GetIATU: bad version.".into()); + } + let mut result = Vec::new(); + if (*structs).cMap == 0 { + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + let cMap : usize = (*structs).cMap.try_into()?; + let pMap = std::slice::from_raw_parts(&(*structs).pMap, cMap); + for i in 0..cMap { + let ne = &pMap[i]; + let e = VmmProcessMapIatEntry { + pid : self.pid, + va_function : ne.vaFunction, + function : String::from(CStr::from_ptr(ne.uszFunction).to_str().unwrap_or("")), + module : String::from(CStr::from_ptr(ne.uszModule).to_str().unwrap_or("")), + }; + result.push(e); + } + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + } + + fn impl_map_pte(&self, is_identify_modules : bool) -> ResultEx> { + unsafe { + let mut structs = std::ptr::null_mut(); + let r = (self.vmm.native.VMMDLL_Map_GetPteU)(self.vmm.native.h, self.pid, is_identify_modules, &mut structs); + if !r { + return Err("VMMDLL_Map_GetPteU: fail.".into()); + } + if (*structs).dwVersion != VMMDLL_MAP_PTE_VERSION { + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Err("VMMDLL_Map_GetPteU: bad version.".into()); + } + let mut result = Vec::new(); + if (*structs).cMap == 0 { + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + let cMap : usize = (*structs).cMap.try_into()?; + let pMap = std::slice::from_raw_parts(&(*structs).pMap, cMap); + for i in 0..cMap { + let ne = &pMap[i]; + let e = VmmProcessMapPteEntry { + pid : self.pid, + va_base : ne.vaBase, + page_count : ne.cPages, + page_software_count : ne.cSoftware, + is_r : true, + is_w : (ne.fPage & 0x0000000000000002) != 0, + is_x : (ne.fPage & 0x8000000000000000) == 0, + is_s : (ne.fPage & 0x0000000000000004) == 0, + is_wow64 : ne.fWoW64, + info : String::from(CStr::from_ptr(ne.uszText).to_str().unwrap_or("")), + }; + result.push(e); + } + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + } + + fn impl_map_thread(&self) -> ResultEx> { + unsafe { + let mut structs = std::ptr::null_mut(); + let r = (self.vmm.native.VMMDLL_Map_GetThread)(self.vmm.native.h, self.pid, &mut structs); + if !r { + return Err("VMMDLL_Map_GetThread: fail.".into()); + } + if (*structs).dwVersion != VMMDLL_MAP_THREAD_VERSION { + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Err("VMMDLL_Map_GetThread: bad version.".into()); + } + let mut result = Vec::new(); + if (*structs).cMap == 0 { + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + let cMap : usize = (*structs).cMap.try_into()?; + let pMap = std::slice::from_raw_parts(&(*structs).pMap, cMap); + for i in 0..cMap { + let ne = &pMap[i]; + let e = VmmProcessMapThreadEntry { + pid : self.pid, + thread_id : ne.dwTID, + thread_pid : ne.dwPID, + exit_status : ne.dwExitStatus, + state : ne.bState, + running : ne.bRunning, + priority : ne.bPriority, + priority_base : ne.bBasePriority, + va_ethread : ne.vaETHREAD, + va_teb : ne.vaTeb, + ft_create_time : ne.ftCreateTime, + ft_exit_time : ne.ftExitTime, + va_start_address : ne.vaStartAddress, + va_stack_user_base : ne.vaStackBaseUser, + va_stack_user_limit : ne.vaStackLimitUser, + va_stack_kernel_base : ne.vaStackBaseKernel, + va_stack_kernel_limit : ne.vaStackLimitKernel, + va_trap_frame : ne.vaTrapFrame, + va_rip : ne.vaRIP, + va_rsp : ne.vaRSP, + affinity : ne.qwAffinity, + user_time : ne.dwUserTime, + kernel_time : ne.dwKernelTime, + suspend_count : ne.bSuspendCount, + wait_reason : ne.bWaitReason + }; + result.push(e); + } + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + } + + fn impl_map_unloaded_module(&self) -> ResultEx> { + unsafe { + let mut structs = std::ptr::null_mut(); + let r = (self.vmm.native.VMMDLL_Map_GetUnloadedModuleU)(self.vmm.native.h, self.pid, &mut structs); + if !r { + return Err("VMMDLL_Map_GetUnloadedModuleU: fail.".into()); + } + if (*structs).dwVersion != VMMDLL_MAP_UNLOADEDMODULE_VERSION { + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Err("VMMDLL_Map_GetUnloadedModuleU: bad version.".into()); + } + let mut result = Vec::new(); + if (*structs).cMap == 0 { + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + let cMap : usize = (*structs).cMap.try_into()?; + let pMap = std::slice::from_raw_parts(&(*structs).pMap, cMap); + for i in 0..cMap { + let ne = &pMap[i]; + let e = VmmProcessMapUnloadedModuleEntry { + pid : self.pid, + va_base : ne.vaBase, + image_size : ne.cbImageSize, + is_wow64 : ne.fWoW64, + name : String::from(CStr::from_ptr(ne.uszText).to_str().unwrap_or("")), + checksum : ne.dwCheckSum, + timedatestamp : ne.dwTimeDateStamp, + ft_unload : ne.ftUnload, + }; + result.push(e); + } + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + } + + fn impl_map_vad(&self, is_identify_modules : bool) -> ResultEx> { + unsafe { + let mut structs = std::ptr::null_mut(); + let r = (self.vmm.native.VMMDLL_Map_GetVadU)(self.vmm.native.h, self.pid, is_identify_modules, &mut structs); + if !r { + return Err("VMMDLL_Map_GetVadU: fail.".into()); + } + if (*structs).dwVersion != VMMDLL_MAP_VAD_VERSION { + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Err("VMMDLL_Map_GetVadU: bad version.".into()); + } + let mut result = Vec::new(); + if (*structs).cMap == 0 { + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + let cMap : usize = (*structs).cMap.try_into()?; + let pMap = std::slice::from_raw_parts(&(*structs).pMap, cMap); + for i in 0..cMap { + let ne = &pMap[i]; + let e = VmmProcessMapVadEntry { + pid : self.pid, + va_start : ne.vaStart, + va_end : ne.vaEnd, + va_vad : ne.vaVad, + u0 : ne.u0, + u1 : ne.u1, + u2 : ne.u2, + commit_charge : ne.u1 & 0x7fffffff, + is_mem_commit : (ne.u1 & 0x80000000) != 0, + cb_prototype_pte : ne.cbPrototypePte, + va_prototype_pte : ne.vaPrototypePte, + va_subsection : ne.vaSubsection, + va_file_object : ne.vaFileObject, + info : String::from(CStr::from_ptr(ne.uszText).to_str().unwrap_or("")), + vadex_page_base : ne.cVadExPagesBase, + vadex_page_count : ne.cVadExPages, + }; + result.push(e); + } + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + } + + fn impl_map_vadex(&self, offset_pages : u32, count_pages : u32) -> ResultEx> { + unsafe { + let mut structs = std::ptr::null_mut(); + let r = (self.vmm.native.VMMDLL_Map_GetVadEx)(self.vmm.native.h, self.pid, offset_pages, count_pages, &mut structs); + if !r { + return Err("VMMDLL_Map_GetVadEx: fail.".into()); + } + if (*structs).dwVersion != VMMDLL_MAP_VADEX_VERSION { + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Err("VMMDLL_Map_GetVadEx: bad version.".into()); + } + let mut result = Vec::new(); + if (*structs).cMap == 0 { + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + let cMap : usize = (*structs).cMap.try_into()?; + let pMap = std::slice::from_raw_parts(&(*structs).pMap, cMap); + for i in 0..cMap { + let ne = &pMap[i]; + let e = VmmProcessMapVadExEntry { + pid : self.pid, + tp : VmmProcessMapVadExType::from(ne.tp), + i_pml : ne.iPML, + va : ne.va, + pa : ne.pa, + pte : ne.pte, + proto_tp : VmmProcessMapVadExType::from(ne.proto_tp), + proto_pa : ne.proto_pa, + proto_pte : ne.proto_va, + va_vad_base : ne.vaVadBase, + }; + result.push(e); + } + (self.vmm.native.VMMDLL_MemFree)(structs as usize); + return Ok(result); + } + } + + fn impl_map_module_data_directory(&self, module_name : &str) -> ResultEx> { + let sz_module_name = CString::new(module_name).unwrap(); + let mut data_directories = vec![CIMAGE_DATA_DIRECTORY::default(); 16]; + let r = (self.vmm.native.VMMDLL_ProcessGetDirectoriesU)(self.vmm.native.h, self.pid, sz_module_name.as_ptr(), data_directories.as_mut_ptr()); + if !r { + return Err("VMMDLL_ProcessGetDirectoriesU: fail.".into()); + } + let mut result = Vec::new(); + for i in 0..16 { + let src : &CIMAGE_DATA_DIRECTORY = data_directories.get(i).unwrap(); + let dst = VmmProcessMapDirectoryEntry { + pid : self.pid, + name : DIRECTORY_NAMES[i], + virtual_address : src.VirtualAddress, + size : src.Size, + }; + result.push(dst); + } + return Ok(result); + } + + fn impl_map_module_section(&self, module_name : &str) -> ResultEx> { + let sz_module_name = CString::new(module_name).unwrap(); + let mut section_count = 0u32; + let r = (self.vmm.native.VMMDLL_ProcessGetSectionsU)(self.vmm.native.h, self.pid, sz_module_name.as_ptr(), std::ptr::null_mut(), 0, &mut section_count); + if !r { + return Err("VMMDLL_ProcessGetSectionsU: fail.".into()); + } + let mut sections = vec![CIMAGE_SECTION_HEADER::default(); section_count.try_into()?]; + let mut result = Vec::new(); + if section_count == 0 { + return Ok(result); + } + let r = (self.vmm.native.VMMDLL_ProcessGetSectionsU)(self.vmm.native.h, self.pid, sz_module_name.as_ptr(), sections.as_mut_ptr(), section_count, &mut section_count); + if !r { + return Err("VMMDLL_ProcessGetSectionsU: fail.".into()); + } + for i in 0..(section_count as usize) { + let src : &CIMAGE_SECTION_HEADER = sections.get(i).unwrap(); + let dst = VmmProcessSectionEntry { + pid : self.pid, + index : i as u32, + name : std::str::from_utf8(&src.Name).unwrap_or_default().to_string(), + name_raw : src.Name, + misc_virtual_size : src.Misc_VirtualAddress, + virtual_address : src.VirtualAddress, + size_of_raw_data : src.SizeOfRawData, + pointer_to_raw_data : src.PointerToRawData, + pointer_to_relocations : src.PointerToRelocations, + pointer_to_linenumbers : src.PointerToLinenumbers, + number_of_relocations : src.NumberOfRelocations, + number_of_linenumbers : src.NumberOfLinenumbers, + characteristics : src.Characteristics, + }; + result.push(dst); + } + return Ok(result); + } + +} + + + + + + +//============================================================================= +// INTERNAL: VMM.SCATTERMEMORY: +//============================================================================= + +impl fmt::Display for VmmScatterMemory<'_> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.pid == u32::MAX { write!(f, "VmmScatterMemory:physical") } else { write!(f, "VmmScatterMemory:virtual:{}", self.pid) } + } +} + +impl Drop for VmmScatterMemory<'_> { + fn drop(&mut self) { + if self.is_scatter_ex { + let _r = self.impl_execute(); + } + (self.vmm.native.VMMDLL_Scatter_CloseHandle)(self.hs); + } +} + +impl <'a> VmmScatterMemory<'a> { + pub fn impl_prepare_ex(&mut self, data_to_read : &'a mut (u64, Vec, u32)) -> ResultEx<()> { + if data_to_read.2 != 0 { + return Err("data_to_read.2 not set to zero".into()); + } + let cb = u32::try_from(data_to_read.1.len())?; + let r = (self.vmm.native.VMMDLL_Scatter_PrepareEx)(self.hs, data_to_read.0, cb, data_to_read.1.as_mut_ptr(), &mut data_to_read.2); + if !r { + return Err("VMMDLL_Scatter_PrepareEx: fail.".into()); + } + self.is_scatter_ex = true; + return Ok(()); + } +} + +impl VmmScatterMemory<'_> { + fn impl_prepare(&self, va : u64, size : usize) -> ResultEx<()> { + let cb = u32::try_from(size)?; + let r = (self.vmm.native.VMMDLL_Scatter_Prepare)(self.hs, va, cb); + if !r { + return Err("VMMDLL_Scatter_Prepare: fail.".into()); + } + return Ok(()); + } + + fn impl_prepare_write(&self, va : u64, data : &Vec) -> ResultEx<()> { + let cb = u32::try_from(data.len())?; + let pb = data.as_ptr(); + let r = (self.vmm.native.VMMDLL_Scatter_PrepareWrite)(self.hs, va, pb, cb); + if !r { + return Err("VMMDLL_Scatter_PrepareWrite: fail.".into()); + } + return Ok(()); + } + + fn impl_execute(&self) -> ResultEx<()> { + let r = (self.vmm.native.VMMDLL_Scatter_Execute)(self.hs); + if !r { + return Err("VMMDLL_Scatter_Execute: fail.".into()); + } + return Ok(()); + } + + fn impl_read(&self, va : u64, size : usize) -> ResultEx> { + let cb = u32::try_from(size)?; + let mut cb_read = 0; + let mut pb_result = vec![0u8; size]; + let r = (self.vmm.native.VMMDLL_Scatter_Read)(self.hs, va, cb, pb_result.as_mut_ptr(), &mut cb_read); + if !r { + return Err("VMMDLL_Scatter_Read: fail.".into()); + } + return Ok(pb_result); + } + + fn impl_clear(&self) -> ResultEx<()> { + let r = (self.vmm.native.VMMDLL_Scatter_Clear)(self.hs, self.pid, self.flags); + if !r { + return Err("VMMDLL_Scatter_Clear: fail.".into()); + } + return Ok(()); + } +} + + + + + + +//============================================================================= +// INTERNAL: VMM.PLUGINS: +//============================================================================= + +impl fmt::Display for VmmPluginContext<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmPluginContext") + } +} + +impl fmt::Display for VmmPluginFileList<'_> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmPluginFileList") + } +} + +impl fmt::Display for VmmPluginInitializationContext { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmPluginInitializationContext") + } +} + +impl fmt::Display for VmmPluginInitializationInfo { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VmmPluginInitializationInfo") + } +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CVMMDLL_PLUGIN_CONTEXT<'a, T> { + magic : u64, + wVersion : u16, + wSize : u16, + pid : u32, + pProcess : usize, + uszModule : *const c_char, + uszPath : *const c_char, + pvReserved1 : usize, + ctxM : *const VmmPluginContext<'a, T>, + MID : u32, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct CVMMDLL_PLUGIN_REGINFO { + magic : u64, + wVersion : u16, + wSize : u16, + tpMemoryModel : u32, + tpSystem : u32, + hDLL : usize, + pfnPluginManager_Register : extern "C" fn(H : usize, pPluginRegInfo : *mut CVMMDLL_PLUGIN_REGINFO) -> bool, + uszPathVmmDLL : *const c_char, + _Reserved : [u32; 30], + Py_fPythonStandalone : bool, + Py__Reserved : u32, + Py_hReservedDllPython3 : usize, + Py_hReservedDllPython3X : usize, + // reg_info: + reg_info_ctxM : usize, + reg_info_uszPathName : [u8; 128], + reg_info_fRootModule : u32, // bool + reg_info_fProcessModule : u32, // bool + reg_info_fRootModuleHidden : u32, // bool + reg_info_fProcessModuleHidden : u32, // bool + reg_info_sTimelineNameShort : [u8; 6], + reg_info__Reserved : [u8; 2], + reg_info_uszTimelineFile : [u8; 32], + reg_info__Reserved2 : [u8; 32], + // reg_fn: + reg_fn_pfnList : extern "C" fn(H : usize, ctxP : *const CVMMDLL_PLUGIN_CONTEXT, pFileList : usize) -> bool, + reg_fn_pfnRead : extern "C" fn(H : usize, ctxP : *const CVMMDLL_PLUGIN_CONTEXT, pb : *mut u8, cb : u32, pcbRead : *mut u32, cbOffset : u64) -> u32, + reg_fn_pfnWrite : extern "C" fn(H : usize, ctxP : *const CVMMDLL_PLUGIN_CONTEXT, pb : *const u8, cb : u32, pcbWrite : *mut u32, cbOffset : u64) -> u32, + reg_fn_pfnNotify : extern "C" fn(H : usize, ctxP : *const CVMMDLL_PLUGIN_CONTEXT, fEvent : u32, pvEvent : usize, cbEvent : usize), + reg_fn_pfnClose : extern "C" fn(H : usize, ctxP : *const CVMMDLL_PLUGIN_CONTEXT), + reg_fn_pfnVisibleModule : extern "C" fn(H : usize, ctxP : *const CVMMDLL_PLUGIN_CONTEXT) -> bool, + reg_fn_pvReserved : [usize; 10], + // reg_fnfc: // TODO: + reg_fnfc_pfnInitialize : usize, + reg_fnfc_pfnFinalize : usize, + reg_fnfc_pfnTimeline : usize, + reg_fnfc_pfnIngestPhysmem : usize, + reg_fnfc_pfnIngestVirtmem : usize, + reg_fnfc_pfnIngestFinalize : usize, + reg_fnfc_pvReserved : [usize; 8], + reg_fnfc_pfnLogCSV : usize, + reg_fnfc_pfnLogJSON : usize, + // sysinfo: + sysinfo_f32 : u32, + sysinfo_dwVersionMajor : u32, + sysinfo_dwVersionMinor : u32, + sysinfo_dwVersionBuild : u32, + sysinfo__Reserved : [u32; 32], +} + +fn impl_new_plugin_initialization(native_h : usize, native_reginfo : usize) -> ResultEx<(VmmPluginInitializationInfo, VmmPluginInitializationContext)> { + unsafe { + let reginfo = native_reginfo as *mut CVMMDLL_PLUGIN_REGINFO; + if (*reginfo).magic != VMMDLL_PLUGIN_REGINFO_MAGIC || (*reginfo).wVersion != VMMDLL_PLUGIN_REGINFO_VERSION { + return Err("Bad reginfo magic/version.".into()); + } + let info = VmmPluginInitializationInfo { + tp_system : VmmSystemType::from((*reginfo).tpSystem), + tp_memorymodel : VmmMemoryModelType::from((*reginfo).tpMemoryModel), + version_major : (*reginfo).sysinfo_dwVersionMajor, + version_minor : (*reginfo).sysinfo_dwVersionMinor, + version_build : (*reginfo).sysinfo_dwVersionBuild, + }; + let ctx = VmmPluginInitializationContext { + h_vmm : native_h, + h_reginfo : native_reginfo, + ctx : None, + path_name : String::from(""), + is_root_module : false, + is_root_module_hidden : false, + is_process_module : false, + is_process_module_hidden : false, + fn_list : None, + fn_read : None, + fn_write : None, + fn_notify : None, + fn_visible : None, + }; + return Ok((info, ctx)); + } +} + +impl VmmPluginInitializationContext { + fn impl_register(self) -> ResultEx<()> { + unsafe { + let mut reginfo = self.h_reginfo as *mut CVMMDLL_PLUGIN_REGINFO; + if (*reginfo).magic != VMMDLL_PLUGIN_REGINFO_MAGIC || (*reginfo).wVersion != VMMDLL_PLUGIN_REGINFO_VERSION { + return Err("Bad reginfo magic/version.".into()); + } + if self.ctx.is_none() { + return Err("User context ctx is missing. User context cannot be None.".into()); + } + let pathname_str = str::replace(&self.path_name, "/", "\\"); + let pathname_cstring = CString::new(pathname_str)?; + let pathname_bytes = pathname_cstring.to_bytes_with_nul(); + if pathname_bytes.len() > (*reginfo).reg_info_uszPathName.len() { + return Err("Plugin path/name too long.".into()); + } + let pathname_len = std::cmp::min(pathname_bytes.len(), (*reginfo).reg_info_uszPathName.len()); + // "initialize" rust vmm context from handle and create rust plugin native context: + let c_path_vmm = CStr::from_ptr((*reginfo).uszPathVmmDLL); + let vmm = impl_new(c_path_vmm.to_str()?, self.h_vmm, &Vec::new())?; + let ctx_user = self.ctx.unwrap(); + let ctx_rust = VmmPluginContext { + vmm : vmm, + ctxlock : std::sync::RwLock::new(ctx_user), + fn_list : self.fn_list, + fn_read : self.fn_read, + fn_write : self.fn_write, + fn_notify : self.fn_notify, + fn_visible : self.fn_visible, + }; + let ctx_rust_box = Box::new(ctx_rust); + let ctx_native = Box::into_raw(ctx_rust_box); + // prepare native registration context and register: + for i in 0..pathname_len { + (*reginfo).reg_info_uszPathName[i] = pathname_bytes[i]; + } + (*reginfo).reg_info_ctxM = ctx_native as usize; + (*reginfo).reg_info_fProcessModule = if self.is_process_module { 1 } else { 0 }; + (*reginfo).reg_info_fProcessModuleHidden = if self.is_process_module_hidden { 1 } else { 0 }; + (*reginfo).reg_info_fRootModule = if self.is_root_module { 1 } else { 0 }; + (*reginfo).reg_info_fRootModuleHidden = if self.is_root_module_hidden { 1 } else { 0 }; + // native callback registration: + (*reginfo).reg_fn_pfnClose = impl_plugin_close_cb; + if self.fn_list.is_some() { + (*reginfo).reg_fn_pfnList = impl_plugin_list_cb; + } + if self.fn_read.is_some() { + (*reginfo).reg_fn_pfnRead = impl_plugin_read_cb; + } + if self.fn_write.is_some() { + (*reginfo).reg_fn_pfnWrite = impl_plugin_write_cb; + } + if self.fn_visible.is_some() { + (*reginfo).reg_fn_pfnVisibleModule = impl_plugin_visible_cb; + } + if self.fn_notify.is_some() { + (*reginfo).reg_fn_pfnNotify = impl_plugin_notify_cb; + } + let r = ((*reginfo).pfnPluginManager_Register)(self.h_vmm, reginfo); + if !r { + return Err("Failed registering plugin.".into()); + } + return Ok(()); + } + } +} + +impl VmmPluginFileList<'_> { + fn impl_add_file(&self, name : &str, size : u64) { + let sz_name = CString::new(name).unwrap(); + (self.vmm.native.VMMDLL_VfsList_AddFile)(self.h_file_list, sz_name.as_ptr(), size, 0); + } + + fn impl_add_directory(&self, name : &str) { + let sz_name = CString::new(name).unwrap(); + (self.vmm.native.VMMDLL_VfsList_AddDirectory)(self.h_file_list, sz_name.as_ptr(), 0); + } +} + +extern "C" fn impl_plugin_close_cb(_h : usize, ctxp : *const CVMMDLL_PLUGIN_CONTEXT) { + unsafe { + drop(Box::from_raw((*ctxp).ctxM as *mut VmmPluginContext)); + } + println!("RUST: PLUGIN CLOSE"); +} + +extern "C" fn impl_plugin_list_cb(_h : usize, ctxp : *const CVMMDLL_PLUGIN_CONTEXT, h_pfilelist : usize) -> bool { + unsafe { + let ctx = &*(*ctxp).ctxM; + if ((*ctxp).magic != VMMDLL_PLUGIN_CONTEXT_MAGIC) || ((*ctxp).wVersion != VMMDLL_PLUGIN_CONTEXT_VERSION) { + return true; + } + let callback = ctx.fn_list.unwrap(); + let process = if (*ctxp).pid > 0 { Some(VmmProcess{ vmm : &ctx.vmm, pid : (*ctxp).pid }) } else { None }; + let path_string = str::replace(CStr::from_ptr((*ctxp).uszPath).to_str().unwrap_or("[err]"), "\\", "/"); + let path = path_string.as_str(); + if path == "[err]" { + return true; + } + let filelist = VmmPluginFileList { + vmm : &ctx.vmm, + h_file_list : h_pfilelist, + }; + let _r = (callback)(ctx, process, path, &filelist); + return true; + } +} + +extern "C" fn impl_plugin_read_cb(_h : usize, ctxp : *const CVMMDLL_PLUGIN_CONTEXT, pb : *mut u8, cb : u32, pcb_read : *mut u32, cb_offset : u64) -> u32 { + unsafe { + *pcb_read = 0; + let ctx = &*(*ctxp).ctxM; + if ((*ctxp).magic != VMMDLL_PLUGIN_CONTEXT_MAGIC) || ((*ctxp).wVersion != VMMDLL_PLUGIN_CONTEXT_VERSION) { + return VMMDLL_STATUS_FILE_INVALID; + } + let callback = ctx.fn_read.unwrap(); + let process = if (*ctxp).pid > 0 { Some(VmmProcess{ vmm : &ctx.vmm, pid : (*ctxp).pid }) } else { None }; + let path_string = str::replace(CStr::from_ptr((*ctxp).uszPath).to_str().unwrap_or("[err]"), "\\", "/"); + let path = path_string.as_str(); + if path == "[err]" { + return VMMDLL_STATUS_FILE_INVALID; + } + let r = match (callback)(ctx, process, path, cb, cb_offset) { + Err(_) => return VMMDLL_STATUS_FILE_INVALID, + Ok(r) => r, + }; + if r.len() == 0 { + return VMMDLL_STATUS_END_OF_FILE; + } + if r.len() > u32::MAX as usize { + return VMMDLL_STATUS_FILE_INVALID; + } + *pcb_read = r.len() as u32; + std::ptr::copy_nonoverlapping(r.as_ptr(), pb, r.len()); + return VMMDLL_STATUS_SUCCESS; + } +} + +extern "C" fn impl_plugin_write_cb(_h : usize, ctxp : *const CVMMDLL_PLUGIN_CONTEXT, pb : *const u8, cb : u32, pcb_write : *mut u32, cb_offset : u64) -> u32 { + unsafe { + *pcb_write = 0; + let ctx = &*(*ctxp).ctxM; + if ((*ctxp).magic != VMMDLL_PLUGIN_CONTEXT_MAGIC) || ((*ctxp).wVersion != VMMDLL_PLUGIN_CONTEXT_VERSION) { + return VMMDLL_STATUS_FILE_INVALID; + } + let callback = ctx.fn_write.unwrap(); + let process = if (*ctxp).pid > 0 { Some(VmmProcess{ vmm : &ctx.vmm, pid : (*ctxp).pid }) } else { None }; + let path_string = str::replace(CStr::from_ptr((*ctxp).uszPath).to_str().unwrap_or("[err]"), "\\", "/"); + let path = path_string.as_str(); + if path == "[err]" { + return VMMDLL_STATUS_FILE_INVALID; + } + let size = cb as usize; + let mut data = vec![0u8; size]; + std::ptr::copy_nonoverlapping(pb, data.as_mut_ptr(), size); + if (callback)(ctx, process, path, data, cb_offset).is_err() { + return VMMDLL_STATUS_FILE_INVALID; + }; + *pcb_write = cb; + return VMMDLL_STATUS_SUCCESS; + } +} + +extern "C" fn impl_plugin_visible_cb(_h : usize, ctxp : *const CVMMDLL_PLUGIN_CONTEXT) -> bool { + unsafe { + let ctx = &*(*ctxp).ctxM; + if ((*ctxp).magic != VMMDLL_PLUGIN_CONTEXT_MAGIC) || ((*ctxp).wVersion != VMMDLL_PLUGIN_CONTEXT_VERSION) { + return false; + } + let callback = ctx.fn_visible.unwrap(); + let process = if (*ctxp).pid > 0 { Some(VmmProcess{ vmm : &ctx.vmm, pid : (*ctxp).pid }) } else { None }; + let path_string = str::replace(CStr::from_ptr((*ctxp).uszPath).to_str().unwrap_or("[err]"), "\\", "/"); + let path = path_string.as_str(); + if path == "[err]" { + return false; + } + return (callback)(ctx, process).unwrap_or(false); + } +} + +extern "C" fn impl_plugin_notify_cb(_h : usize, ctxp : *const CVMMDLL_PLUGIN_CONTEXT, f_event : u32, _pv_event : usize, _cb_event : usize) { + unsafe { + let ctx = &*(*ctxp).ctxM; + if ((*ctxp).magic != VMMDLL_PLUGIN_CONTEXT_MAGIC) || ((*ctxp).wVersion != VMMDLL_PLUGIN_CONTEXT_VERSION) { + return; + } + let callback = ctx.fn_notify.unwrap(); + let _r = (callback)(ctx, f_event); + } +} diff --git a/vmmrust/memprocfs_example/Cargo.lock b/vmmrust/memprocfs_example/Cargo.lock new file mode 100644 index 0000000..bd952a8 --- /dev/null +++ b/vmmrust/memprocfs_example/Cargo.lock @@ -0,0 +1,118 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "memprocfs" +version = "5.4.0" +dependencies = [ + "libloading", + "serde", +] + +[[package]] +name = "memprocfs_example" +version = "5.4.0" +dependencies = [ + "memprocfs", + "pretty-hex", +] + +[[package]] +name = "pretty-hex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5" + +[[package]] +name = "proc-macro2" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/vmmrust/memprocfs_example/Cargo.toml b/vmmrust/memprocfs_example/Cargo.toml new file mode 100644 index 0000000..3706040 --- /dev/null +++ b/vmmrust/memprocfs_example/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "memprocfs_example" +version = "5.4.0" +edition = "2021" +publish = false + +[dependencies] +pretty-hex = "0.3.0" +memprocfs = { path = "../memprocfs" } diff --git a/vmmrust/memprocfs_example/src/main.rs b/vmmrust/memprocfs_example/src/main.rs new file mode 100644 index 0000000..3523ef2 --- /dev/null +++ b/vmmrust/memprocfs_example/src/main.rs @@ -0,0 +1,824 @@ +// main.rs - MemProcFS Rust VMM API usage examples +// +// (c) Ulf Frisk, 2023 +// Author: Ulf Frisk, pcileech@frizk.net +// https://github.com/ufrisk/MemProcFS +// + +use memprocfs::*; +use pretty_hex::*; + +pub fn main() { + let vmm_lib_path; + let memdump_path; + if cfg!(windows) { + vmm_lib_path = "C:\\Github\\MemProcFS-dev\\files\\vmm.dll"; + memdump_path = "C:\\Dumps\\trickbot-ram.pmem"; + } else { + vmm_lib_path = "/home/user/memprocfs/vmm.so"; + memdump_path = "/dumps/warren.mem"; + } + + println!("MemProcFS Rust API Example - START"); + + // Example arguments to initialize MemProcFS VMM.DLL/VMM.SO with: + // For complete argument list please see MemProcFS command line documentation. + let vmm_args = ["-printf", "-v", "-waitinitialize", "-device", memdump_path, "-vm"].to_vec(); + { + // Example: Vmm::new + // Instantiate a new Vmm object/struct both on the rust layer and the + // underlying native layer. + // The first argument is the path to the native vmm.dll or vmm.so. + // The second argument is a Vec with MemProcFS command line arguments. + // NB! For example simplicity we'll use unwrap() here which will panic + // when instantiation fails. + let vmm = Vmm::new(vmm_lib_path, &vmm_args).unwrap(); + println!("vmm result = ok!"); + + + // Example: vmm.get_config(): + // Retrieve max native address and print it on the screen. + println!("========================================"); + println!("Vmm.get_config():"); + println!("max native address: {:#x} -> {:#x}", CONFIG_OPT_CORE_MAX_NATIVE_ADDRESS, vmm.get_config(CONFIG_OPT_CORE_MAX_NATIVE_ADDRESS).unwrap_or(0)); + + + // Example: vmm.set_config(): + // For a full refresh of internal data caches. + println!("========================================"); + println!("Vmm.set_config():"); + let _r = vmm.set_config(CONFIG_OPT_REFRESH_ALL, 1); + println!("full refresh: -> Ok"); + + + // Example: vmm.mem_write(): + // Write to physical memory at address 0x1000 + // (Writes are only possible if underlying layers are write-capable.) + println!("========================================"); + println!("Vmm.mem_write():"); + let data_to_write = [0x56u8, 0x4d, 0x4d, 0x52, 0x55, 0x53, 0x54].to_vec(); + match vmm.mem_write(0x1000, &data_to_write) { + Ok(()) => println!("Vmm.mem_write(): success"), + Err(e) => println!("Vmm.mem_write(): fail [{}]", e), + } + + + // Example: vmm.mem_read(): + // Read 0x100 bytes from physical address 0x1000. + println!("========================================"); + println!("Vmm.mem_read():"); + if let Ok(data_read) = vmm.mem_read(0x1000, 0x100) { + println!("{:?}", data_read.hex_dump()); + } + + + // Example: vmm.mem_read_ex(): + // Read 0x100 bytes from physical address 0x1000 with vmm flags. + println!("========================================"); + println!("Vmm.mem_read_ex():"); + if let Ok(data_read) = vmm.mem_read_ex(0x1000, 0x100, FLAG_NOCACHE | FLAG_ZEROPAD_ON_FAIL) { + println!("{:?}", data_read.hex_dump()); + } + + + // Example: vmm.log(): + // Log a message to VMM/MemProcFS + println!("========================================"); + println!("Vmm.log():"); + vmm.log(&VmmLogLevel::_1Critical, "Test Message Critical!"); + + + // Example: vmm.process_from_pid(): + // Retrieve the 'System' process by its PID. + println!("========================================"); + println!("Vmm.process_from_pid():"); + if let Ok(process) = vmm.process_from_pid(4) { + println!("{}", process); + } + + + // Example: vmm.process_from_name(): + // Retrieve the 'System' process by its name. + println!("========================================"); + println!("Vmm.process_from_name():"); + if let Ok(process) = vmm.process_from_name("System") { + println!("{}", process); + } + + + // Example: vmm.process_list(): + // Retrieve all processes of the running system. + println!("========================================"); + println!("Vmm.process_list():"); + if let Ok(process_all) = vmm.process_list() { + for process in &*process_all { + print!("{process} "); + } + println!(""); + } + + + // Example: vmm.map_pfn(): + // Retrieve the first 10 page frame numbers PFNs and display extended info about them. + // NB! extended PFN info is rather expensive so use with caution. + println!("========================================"); + println!("vmm.map_pfn():"); + let pfns: Vec = (1..=10).collect(); + if let Ok(pfn_all) = vmm.map_pfn(&pfns, true) { + for pfn in &*pfn_all { + println!("{pfn} \t location={} tp_ex={} pid={:x} va={:x} color={}", pfn.location, pfn.tp_ex, pfn.pid, pfn.va, pfn.color); + } + } + + + // Example: vmm.map_memory(): + // Retrieve the physical memory map as seen by the operating system: + println!("========================================"); + println!("vmm.map_memory():"); + if let Ok(memory_range_all) = vmm.map_memory() { + for memory_range in &*memory_range_all { + println!("{memory_range} \t pa={:x} cb={:x}", memory_range.pa, memory_range.cb); + } + } + + + // Example: vmm.map_net(): + // Retrieve the network connection information: + println!("========================================"); + println!("vmm.map_net():"); + if let Ok(net_all) = vmm.map_net() { + for net in &*net_all { + println!("{net}"); + } + } else { + println!("Error retrieving network information."); + } + + + // Example: vmm.map_pool(): + // Retrieve kernel pool allocations and display the 'Proc' allocations. + // NB! here we retrieve all pool allocations which is substantially + // slower than retrieving the big pool only. + println!("========================================"); + println!("vmm.map_pool():"); + if let Ok(pool_all) = vmm.map_pool(false) { + println!("Number of pool allocations: {}.", pool_all.len()); + let pool_proc_all : Vec<&VmmMapPoolEntry> = pool_all.iter().filter(|e| e.tag == 0x636f7250 /* 'Proc' backwards */).collect(); + println!("Number of pool 'Proc' allocations: {}.", pool_all.len()); + for pool_proc in &*pool_proc_all { + print!("{pool_proc} "); + } + println!(""); + } else { + println!("Error retrieving pool allocations."); + } + + + // Example: vmm.map_service(): + // Retrieve all services in the system: + println!("========================================"); + println!("vmm.map_service():"); + if let Ok(service_all) = vmm.map_service() { + for service in &*service_all { + print!("{service} "); + } + println!(""); + } + + + // Example: vmm.map_user(): + // Retrieve the detected users in the system: + println!("========================================"); + println!("vmm.map_user():"); + if let Ok(user_all) = vmm.map_user() { + for user in &*user_all { + println!("{:x}:: {} :: {} :: {user}", user.va_reg_hive, user.sid, user.user); + } + } + + + // Example: vmm.map_virtual_machine(): + // Retrieve any virtual machines detected: + // NB! vm parsing must be enabled (-vm startup option). + println!("========================================"); + println!("vmm.map_virtual_machine():"); + if let Ok(virtualmachine_all) = vmm.map_virtual_machine() { + for virtualmachine in &*virtualmachine_all { + println!("{virtualmachine}"); + if virtualmachine.is_active { + // for active vms it's possible to create a new vmm object for + // the vm. it's possible to treat this as any other vmm object + // to read memory, query processes etc. + let vmm_vm = match Vmm::new_from_virtual_machine(&vmm, &virtualmachine) { + Err(_) => continue, + Ok(r) => r, + }; + println!("vm max native address: {:#x} -> {:#x}", CONFIG_OPT_CORE_MAX_NATIVE_ADDRESS, vmm_vm.get_config(CONFIG_OPT_CORE_MAX_NATIVE_ADDRESS).unwrap_or(0)); + } + } + } + + + // Example: vmm.kernel().process(): + // Retrieve the system process (PID 4). + // NB! vmm.kernel() is a lightweight operation so ok to call multiple times... + println!("========================================"); + println!("vmm.kernel().process():"); + println!("{}", vmm.kernel().process()); + + + // Example: vmm.kernel().build(): + // Retrieve the kernel build number. + println!("========================================"); + println!("vmm.kernel().build():"); + println!("{}", vmm.kernel().build()); + + + // Example: vmm.kernel().pdb(): + // Retrieve the VmmPdb object containg debug symbols for ntoskrnl. + // NB! This call will always succeed even if the symbols aren't loaded. + // Subsequent calls to the pdb methods may however fail. + println!("========================================"); + println!("vmm.kernel().pdb():"); + let kernel = vmm.kernel(); + let pdb = kernel.pdb(); + println!("{pdb}"); + + + // Example: pdb.symbol_address_from_name(): + // Retrieve the address of the symbol nt!MiMapContiguousMemory + // NB! this requires that the MemProcFS symbol-subsystem is working. + println!("========================================"); + println!("pdb.symbol_address_from_name():"); + let mut va_kernel_symbol = 0u64; + if let Ok(va) = pdb.symbol_address_from_name("MiMapContiguousMemory") { + va_kernel_symbol = va; + println!("Address of 'MiMapContiguousMemory' = {:x}", va_kernel_symbol); + } else { + println!("Error retrieving symbol address for 'MiMapContiguousMemory'"); + } + + + // Example: pdb.symbol_name_from_address(): + // Retrieve the symbol name from an address. + // Use the already retrieved address of nt!MiMapContiguousMemory. + if va_kernel_symbol != 0 { + println!("========================================"); + println!("pdb.symbol_name_from_address():"); + if let Ok(r) = pdb.symbol_name_from_address(va_kernel_symbol) { + println!("Address: {:x} Name: '{}' Displacement: {:x}", va_kernel_symbol, r.0, r.1); + } else { + println!("Error retrieving symbol name for address {:x}", va_kernel_symbol); + } + } + + + // Example: pdb.type_size(): + // Retrieve the size of a type. In this example use _EPROCESS. + println!("========================================"); + println!("pdb.type_size:"); + match pdb.type_size("_EPROCESS") { + Err(_) => println!("Error retrieving type size."), + Ok(type_size) => println!("Type Size of _EPROCESS: {:x}", type_size), + } + + + // Example: pdb.type_child_offset(): + // Retrieve the offset of a type child. + // In this example use _EPROCESS.VadRoot + println!("========================================"); + println!("pdb.type_size:"); + match pdb.type_child_offset("_EPROCESS", "VadRoot") { + Err(_) => println!("Error retrieving type child offset."), + Ok(offset_child) => println!("Offset of _EPROCESS.VadRoot: {:x}", offset_child), + } + + + + + + + // Examples below deal with virtual file system (vfs) access. + // Example: vmm.vfs_list(): + // Retrieve a directory listing of the /sys/ folder. + // NB! forward-slash '/' and back-slash '\\' both work fine! + println!("========================================"); + println!("vmm.vfs_list():"); + let vfs_list_path = "/sys/"; + if let Ok(vfs_all) = vmm.vfs_list(vfs_list_path) { + println!("VFS directory listing for directory: {vfs_list_path}"); + println!("Number of file/directory entries: {}.", vfs_all.len()); + for vfs in &*vfs_all { + println!("{vfs}"); + } + } + + + // Example: vmm.vfs_read(): + // Read (a part) of a file in the virtual file system. + // In this case try reading the file /sys/memory/physmemmap.txt + println!("========================================"); + println!("vmm.vfs_read():"); + if let Ok(vfs_file_data) = vmm.vfs_read("/sys/memory/physmemmap.txt", 0x2000, 0) { + println!("Number of bytes file contents read from file '/sys/memory/physmemmap.txt': {}.", vfs_file_data.len()); + println!("{:?}", vfs_file_data.hex_dump()); + } + + + // Example: vmm.vfs_write(): + // Write (to a part) of a file in the virtual file system. + // In this case write '1' to /conf/config_process_show_terminated.txt + // to enable listings of terminated processes in the filesystem. + // NB! vfs_write() writes are undertaken on a best-effort! + // please verify with vfs_read() (if this is possible). + println!("========================================"); + println!("vmm.vfs_write():"); + let vfs_write_data = vec![1u8; 1]; + vmm.vfs_write("/conf/config_process_show_terminated.txt", vfs_write_data, 0); + + + // Example: vmm.vfs_read(): + // Read (a part) of a file in the virtual file system. + // In this case try reading the file /sys/memory/physmemmap.txt + println!("========================================"); + println!("vmm.vfs_read():"); + if let Ok(vfs_file_data) = vmm.vfs_read("/conf/config_process_show_terminated.txt", 0x2000, 0) { + println!("Number of bytes file contents read from file '/conf/config_process_show_terminated.txt': {}.", vfs_file_data.len()); + println!("{:?}", vfs_file_data.hex_dump()); + } + + + + + + + // Example vmm.reg_hive_list(): + // List the registry hives of the system: + println!("========================================"); + println!("vmm.reg_hive_list():"); + let mut hive_software : Option = None; + let hive_all = vmm.reg_hive_list(); + if hive_all.is_ok() { + for hive in hive_all.unwrap() { + println!("{hive} size={} path={}", hive.size, hive.path); + if hive.path.contains("SOFTWARE") { + hive_software = Some(hive); + } + } + } + + + // Example: vmm.reg_hive_read(): + // Read 0x100 bytes from the address 0x1000 in the software registry hive. + println!("========================================"); + println!("Vmm.reg_hive_read():"); + if let Some(hive_software) = &hive_software { + if let Ok(data_read) = hive_software.reg_hive_read(0x1000, 0x100, FLAG_NOCACHE | FLAG_ZEROPAD_ON_FAIL) { + println!("{:?}", data_read.hex_dump()); + } + } + + + // Example: vmm.mem_write(): + // Write to the registry address 0x1000. + // This have been commented out since this is extremely dangerous on live + // systems and is likely to bluescreen / cause registry corruption. + println!("========================================"); + println!("Vmm.reg_hive_write():"); + if let Some(hive_software) = &hive_software { + let data_to_write = [0x56u8, 0x4d, 0x4d, 0x52, 0x55, 0x53, 0x54].to_vec(); + let _r = hive_software.reg_hive_write(0x1000, &data_to_write); + } + + + + + + + // Example: vmm.process_from_name(): + // Retrieve the process object for 'explorer.exe'. + // If explorer.exe does not exist just panic since the remainder of the + // examples forward on are process related. + println!("========================================"); + println!("vmm.process_from_name():"); + let vmmprocess = vmm.process_from_name("explorer.exe").unwrap(); + println!("PID of explorer.exe: {}", vmmprocess.pid); + + + // Example: vmmprocess.info(): + // Retrieve common process info such as process pid, ppid and name. + println!("========================================"); + println!("vmmprocess.info():"); + if let Ok(procinfo) = vmmprocess.info() { + println!("struct -> {procinfo}"); + println!("pid -> {}", procinfo.pid); + println!("ppid -> {}", procinfo.pid); + println!("peb -> {:x}", procinfo.va_peb); + println!("eprocess -> {:x}", procinfo.va_eprocess); + println!("name -> {}", procinfo.name); + println!("longname -> {}", procinfo.name_long); + println!("SID -> {}", procinfo.sid); + } + + + // Example: vmmprocess.get_module_base(): + // Retrieve the base address of a module. + println!("========================================"); + println!("vmmprocess.get_module_base():"); + if let Ok(modulebaseaddress) = vmmprocess.get_module_base("kernel32.dll") { + println!("kernel32.dll -> {:x}", modulebaseaddress); + } + + + // Example: vmmprocess.get_proc_address(): + // Retrieve the function address inside a module i.e. GetProcAddress(). + println!("========================================"); + println!("vmmprocess.get_proc_address():"); + if let Ok(procaddress) = vmmprocess.get_proc_address("kernel32.dll", "GetProcAddress") { + println!("kernel32.dll!GetProcAddress -> {:x}", procaddress); + } + + + // Example: vmmprocess.get_cmdline(): + // Retrieve the process commandline. + println!("========================================"); + println!("vmmprocess.get_cmdline():"); + if let Ok(s_cmdline) = vmmprocess.get_cmdline() { + println!("-> {s_cmdline}"); + } + + + // Example: vmmprocess.get_path_user(): + // Retrieve the process image path in user-mode (derived from PEB). + println!("========================================"); + println!("vmmprocess.get_path_user():"); + if let Ok(s) = vmmprocess.get_path_user() { + println!("-> {s}"); + } + + + // Example: vmmprocess.get_path_kernel(): + // Retrieve the process image path in user-mode (derived from EPROCESS). + println!("========================================"); + println!("vmmprocess.get_path_kernel():"); + if let Ok(s) = vmmprocess.get_path_kernel() { + println!("-> {s}"); + } + + + // Example: vmmprocess.map_pte(): + // Retrieve the page table entry (PTE) map for explorer. + println!("========================================"); + println!("vmmprocess.map_pte():"); + if let Ok(pte_all) = vmmprocess.map_pte(true) { + println!("Number of pte entries: {}.", pte_all.len()); + for pte in &*pte_all { + let s = if pte.is_s { 's' } else { '-' }; + let r = if pte.is_r { 'r' } else { '-' }; + let w = if pte.is_w { 'w' } else { '-' }; + let x = if pte.is_x { 'x' } else { '-' }; + println!("{pte} :: {s}{r}{w}{x} :: {}", pte.info); + } + } else { + println!("Error retrieving process pte map."); + } + + + // Example: vmmprocess.map_vad(): + // Retrieve the virtual address descriptor (VAD) map for explorer. + println!("========================================"); + println!("vmmprocess.map_vad():"); + if let Ok(vad_all) = vmmprocess.map_vad(true) { + println!("Number of vad entries: {}.", vad_all.len()); + for vad in &*vad_all { + println!("{vad} :: {}", vad.info); + } + } else { + println!("Error retrieving process vad map."); + } + + + // Example: vmmprocess.map_handle(): + // Retrieve the open handles associated with the process. + println!("========================================"); + println!("vmmprocess.map_handle():"); + if let Ok(handle_all) = vmmprocess.map_handle() { + println!("Number of handle entries: {}.", handle_all.len()); + for handle in &*handle_all { + println!("{handle}"); + } + } else { + println!("Error retrieving process handle map."); + } + + + // Example: vmmprocess.map_heap(): + // Retrieve info about the process heaps: + println!("========================================"); + println!("vmmprocess.map_heap():"); + if let Ok(heap_all) = vmmprocess.map_heap() { + println!("Number of heap entries: {}.", heap_all.len()); + for heap in &*heap_all { + println!("{heap}"); + } + } else { + println!("Error retrieving process heap map."); + } + + + // Example: vmmprocess.map_heapalloc(): + // Retrieve info about the allocated heap entries for heap 0. + println!("========================================"); + println!("vmmprocess.map_heapalloc():"); + if let Ok(heapalloc_all) = vmmprocess.map_heapalloc(0) { + println!("Number of allocated heap entries: {}.", heapalloc_all.len()); + for heapalloc in &*heapalloc_all { + print!("{heapalloc} "); + } + println!(""); + } else { + println!("Error retrieving process heap allocation map."); + } + + + // Example: vmmprocess.map_thread(): + // Retrieve information about the process threads. + println!("========================================"); + println!("vmmprocess.map_thread():"); + if let Ok(thread_all) = vmmprocess.map_thread() { + println!("Number of process threads: {}.", thread_all.len()); + for thread in &*thread_all { + println!("{thread}"); + } + } else { + println!("Error retrieving process thread map."); + } + + + // Example: vmmprocess.map_unloaded_module(): + // Retrieve information about unloaded modules (if any). + println!("========================================"); + println!("vmmprocess.map_unloaded_module():"); + if let Ok(unloaded_all) = vmmprocess.map_unloaded_module() { + println!("Number of process unloaded modules: {}.", unloaded_all.len()); + for unloaded in &*unloaded_all { + println!("{unloaded}"); + } + } else { + println!("Error retrieving process unloaded modules map."); + } + + + // Example: vmmprocess.map_module(): + // Retrieve information about process modules. + // NB! process map is used in subsequent example calls so panic if + // there is some error for example simplicity. + // NB! Debug Info and Version Info will slow down parsing somewhat. + println!("========================================"); + println!("vmmprocess.map_module():"); + let module_all = vmmprocess.map_module(true, true).unwrap(); + println!("Number of process modules: {}.", module_all.len()); + for module in &*module_all { + println!("{module}"); + } + + + // Example: locate the module of kernel32 within the modules. + // NB! this will panic if kernel32 is not found, but use this + // for simplicity below. + println!("========================================"); + println!("locate module of kernel32.dll:"); + let kernel32 : &VmmProcessMapModuleEntry = (&*module_all).into_iter().find(| m| m.name.to_lowercase() == "kernel32.dll").unwrap(); + println!("module kernel32 located: {kernel32}"); + + + // Show debug information related to kernel32. + // NB! debug info is retrieved on a best-effort way if the flag + // is_info_debug is set to true in the vmmprocess.map_module() call. + println!("========================================"); + println!("kernel32 debug info:"); + if let Some(debug_info) = &kernel32.debug_info { + println!("kernel32 debug info found. {}", debug_info); + println!("kernel32 debug guid={}, pdb={}", debug_info.guid, debug_info.pdb_filename); + } else { + println!("kernel32 debug info not found."); + } + + + // Show version information related to kernel32. + // NB! version info is retrieved on a best-effort way if the flag + // is_info_version is set to true in the vmmprocess.map_module() call. + println!("========================================"); + println!("kernel32 version info:"); + if let Some(ver_info) = &kernel32.version_info { + println!("kernel32 version info found. {}", ver_info); + println!("kernel32 company_name: {}", ver_info.company_name); + println!("kernel32 file_description: {}", ver_info.file_description); + println!("kernel32 file_version: {}", ver_info.file_version); + println!("kernel32 internal_name: {}", ver_info.internal_name); + println!("kernel32 legal_copyright: {}", ver_info.legal_copyright); + println!("kernel32 original_file_name: {}", ver_info.original_file_name); + println!("kernel32 product_name: {}", ver_info.product_name); + println!("kernel32 product_version: {}", ver_info.product_version); + } else { + println!("kernel32 version info not found."); + } + + + // Example: vmmprocess.pdb_from_module_address(): + // Retrieve debugging information (for microsoft modules) given + // the module base address. + println!("========================================"); + println!("vmmprocess.pdb_from_module_address():"); + if let Ok(pdb) = vmmprocess.pdb_from_module_address(kernel32.va_base) { + println!("-> {pdb}"); + } + + + // Example: vmmprocess.map_module_eat(): + // Retrieve exported functions in the export address table (EAT) of a + // module (kernel32 in this case). + println!("========================================"); + println!("vmmprocess.map_module_eat():"); + if let Ok(eat_all) = vmmprocess.map_module_eat("kernel32.dll") { + println!("Number of module exported functions: {}.", eat_all.len()); + for eat in &*eat_all { + println!("{eat} :: {}", eat.forwarded_function); + } + } else { + println!("Error retrieving module exported functions (EAT)."); + } + + + // Example: vmmprocess.map_module_iat(): + // Retrieve imported functions in the import address table (IAT) of a + // module (kernel32 in this case). + println!("========================================"); + println!("vmmprocess.map_module_iat():"); + if let Ok(iat_all) = vmmprocess.map_module_iat("kernel32.dll") { + println!("Number of module imported functions: {}.", iat_all.len()); + for iat in &*iat_all { + println!("{iat}"); + } + } else { + println!("Error retrieving module imported functions (IAT)."); + } + + // Example: vmmprocess.map_module_data_directory(): + // Retrieve info about the PE data directories, which always equal 16. + println!("========================================"); + println!("vmmprocess.map_module_data_directory():"); + if let Ok(data_directory_all) = vmmprocess.map_module_data_directory("kernel32.dll") { + println!("Number of module data directories: {}.", data_directory_all.len()); + for data_directory in &*data_directory_all { + println!("{data_directory}"); + } + } + + // Example: vmmprocess.map_module_section(): + // Retrieve info about the PE sections. + println!("========================================"); + println!("vmmprocess.map_module_section():"); + if let Ok(section_all) = vmmprocess.map_module_section("kernel32.dll") { + println!("Number of module sections: {}.", section_all.len()); + for section in &*section_all { + println!("{section}"); + } + } + + + + + + + // Example: vmmprocess.mem_write(): + // Write to virtual memory of kernel32 PE header (dangerous) + // (Writes are only possible if underlying layers are write-capable.) + println!("========================================"); + println!("vmmprocess.mem_write():"); + let data_to_write = [0x56u8, 0x4d, 0x4d, 0x52, 0x55, 0x53, 0x54].to_vec(); + match vmmprocess.mem_write(kernel32.va_base + 8, &data_to_write) { + Err(e) => println!("vmmprocess.mem_write(): fail [{}]", e), + Ok(()) => println!("vmmprocess.mem_write(): success"), + } + + + // Example: vmmprocess.mem_read(): + // Read 0x100 bytes from beginning of explorer.exe!kernel32.dll + println!("========================================"); + println!("vmmprocess.mem_read():"); + match vmmprocess.mem_read(kernel32.va_base, 0x100) { + Err(e) => println!("vmmprocess.mem_read(): fail [{}]", e), + Ok(data_read) => println!("{:?}", data_read.hex_dump()), + } + + + // Example: vmmprocess.mem_read_ex(): + // Read 0x100 bytes from beginning of explorer.exe!kernel32.dll with vmm flags. + println!("========================================"); + println!("vmmprocess.mem_read_ex():"); + match vmmprocess.mem_read_ex(kernel32.va_base, 0x100, FLAG_NOCACHE | FLAG_ZEROPAD_ON_FAIL) { + Err(e) => println!("vmmprocess.mem_read_ex(): fail [{}]", e), + Ok(data_read) => println!("{:?}", data_read.hex_dump()), + } + + + // Example: vmmprocess.mem_virt2phys(): + // Retrieve the physical base address of explorer.exe!kernel32.dll. + println!("========================================"); + println!("vmmprocess.mem_virt2phys():"); + match vmmprocess.mem_virt2phys(kernel32.va_base) { + Err(e) => println!("vmmprocess.mem_virt2phys(): fail [{}]", e), + Ok(pa_kernel32) => println!("explorer.exe!kernel32 va={:x} pa={:x}", kernel32.va_base, pa_kernel32), + } + + + // Example: vmmprocess.mem_scatter() #1: + // This example will show how it's possible to use VmmScatterMemory to + // more efficiently read memory from the underlying device. + { + // Example: vmmprocess.mem_scatter(): + // Retrieve a scatter memory read object that may be used to batch + // several reads/writes into one efficient call to the memory device. + println!("========================================"); + println!("vmmprocess.mem_scatter() #1:"); + let mem_scatter = vmmprocess.mem_scatter(FLAG_NOCACHE | FLAG_ZEROPAD_ON_FAIL).unwrap(); + println!("mem_scatter = {mem_scatter}"); + // Prepare three memory ranges to read. + let _r = mem_scatter.prepare(kernel32.va_base + 0x0000, 0x100); + let _r = mem_scatter.prepare(kernel32.va_base + 0x1000, 0x100); + let _r = mem_scatter.prepare(kernel32.va_base + 0x2000, 0x100); + // Perform the actual read (and writes) by calling the execute() function. + let _r = mem_scatter.execute(); + // Fetch data read. It's possible (but wasteful) to read less data than was prepared. + if let Ok(data_read) = mem_scatter.read(kernel32.va_base + 0x0000, 0x80) { + println!("memory range: va={:x} cb={:x} cb_read={:x}", kernel32.va_base + 0x0000, 0x80, data_read.len()); + println!("{:?}", data_read.hex_dump()); + println!("-----------------------"); + } + if let Ok(data_read) = mem_scatter.read(kernel32.va_base + 0x1000, 0x100) { + println!("memory range: va={:x} cb={:x} cb_read={:x}", kernel32.va_base + 0x1000, 0x100, data_read.len()); + println!("{:?}", data_read.hex_dump()); + println!("-----------------------"); + } + // It's possible to do a re-read of the ranges by calling execute again! + let _r = mem_scatter.execute(); + if let Ok(data_read) = mem_scatter.read(kernel32.va_base + 0x0000, 0x80) { + println!("memory range: va={:x} cb={:x} cb_read={:x}", kernel32.va_base + 0x0000, 0x80, data_read.len()); + println!("{:?}", data_read.hex_dump()); + println!("-----------------------"); + } + // It's also possible to clear the VmmScatterMemory to start anew. + // Clearing is slightly more efficient than creating a new object. + // let _r = mem_scatter.clear(); + + // NB! the VmmScatterMemory struct will be automatically free'd + // on the native backend when it goes out of scope. + } + + + // Example: vmmprocess.mem_scatter() #2: + // This example demo how it's possible to use the prepare_ex function + // which will populate the prepared data regions automatically when the + // VmmScatterMemory is dropped. + // It's not recommended to mix the #1 and #2 syntaxes. + { + // memory ranges to read are tuples: + // .0 = the virtual address to read. + // .1 = vector of u8 which memory should be read into. + // .2 = u32 receiving the bytes successfully read data. + let mut memory_range_1 = (kernel32.va_base + 0x0000, vec![0u8; 0x100], 0u32); + let mut memory_range_2 = (kernel32.va_base + 0x1000, vec![0u8; 0x100], 0u32); + let mut memory_range_3 = (kernel32.va_base + 0x2000, vec![0u8; 0x100], 0u32); + // Feed the ranges into a mutable VmmScatterMemory inside a + // separate scope. The actual memory read will take place when + // the VmmScatterMemory goes out of scope and are dropped. + println!("========================================"); + println!("vmmprocess.mem_scatter() #2:"); + if let Ok(mut mem_scatter) = vmmprocess.mem_scatter(FLAG_NOCACHE | FLAG_ZEROPAD_ON_FAIL) { + let _r = mem_scatter.prepare_ex(&mut memory_range_1); + let _r = mem_scatter.prepare_ex(&mut memory_range_2); + let _r = mem_scatter.prepare_ex(&mut memory_range_3); + } + // Results should now be available in the memory ranges if the read + // was successful. Note that there is no guarantee that memory is + // read - make sure to check the .2 item - number of bytes read. + for memory_range in [memory_range_1, memory_range_2, memory_range_3] { + println!("memory range: va={:x} cb={:x} cb_read={:x}", memory_range.0, memory_range.1.len(), memory_range.2); + println!("{:?}", memory_range.1.hex_dump()); + println!("-----------------------"); + } + } + + + + + + + // Example: close: + // The underlying native VMM instance will be automatically dropped + // when the Rust Vmm struct goes out of scope and is dropped. + } + + println!("MemProcFS Rust API Example - COMPLETED"); +} \ No newline at end of file diff --git a/vmmrust/vmmrust.code-workspace b/vmmrust/vmmrust.code-workspace new file mode 100644 index 0000000..876a149 --- /dev/null +++ b/vmmrust/vmmrust.code-workspace @@ -0,0 +1,8 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": {} +} \ No newline at end of file diff --git a/vmmsharp/vmmsharp.cs b/vmmsharp/vmmsharp.cs index b66a18e..f7b7713 100644 --- a/vmmsharp/vmmsharp.cs +++ b/vmmsharp/vmmsharp.cs @@ -14,7 +14,7 @@ using System.Collections.Generic; * (c) Ulf Frisk, 2020-2023 * Author: Ulf Frisk, pcileech@frizk.net * - * Version 5.3 + * Version 5.4 * */ @@ -2175,7 +2175,7 @@ namespace vmmsharp internal static uint VMMDLL_MAP_HEAP_VERSION = 4; internal static uint VMMDLL_MAP_HEAPALLOC_VERSION = 1; internal static uint VMMDLL_MAP_THREAD_VERSION = 4; - internal static uint VMMDLL_MAP_HANDLE_VERSION = 2; + internal static uint VMMDLL_MAP_HANDLE_VERSION = 3; internal static uint VMMDLL_MAP_NET_VERSION = 3; internal static uint VMMDLL_MAP_PHYSMEM_VERSION = 2; internal static uint VMMDLL_MAP_POOL_VERSION = 2; @@ -2931,7 +2931,7 @@ namespace vmmsharp internal uint _FutureUse2; internal uint dwPID; internal uint dwPoolTag; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] internal uint[] _FutureUse; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] internal uint[] _FutureUse; [MarshalAs(UnmanagedType.LPWStr)] internal string wszType; }