mirror of
https://github.com/ufrisk/MemProcFS.git
synced 2026-05-31 07:42:37 +08:00
288 lines
11 KiB
C
288 lines
11 KiB
C
// m_phys2virt.c : implementation of the phys2virt built-in module.
|
|
//
|
|
// (c) Ulf Frisk, 2019-2026
|
|
// Author: Ulf Frisk, pcileech@frizk.net
|
|
//
|
|
|
|
#include "modules.h"
|
|
|
|
LPCSTR szMPHYS2VIRT_README =
|
|
"Information about the phys2virt module \n" \
|
|
"====================================== \n" \
|
|
"Write a physical address (in hex) to the file 'phys'. Writing will trigger a\n" \
|
|
"scan of process page tables for corresponding virtual addresses. Up to four\n" \
|
|
"virtual addresses which map to physical address will be presented in the file\n" \
|
|
"'virt' (per process). \n" \
|
|
"The phys2virt module may take time to execute - especially if using the root\n" \
|
|
"module (scan all process page tables) instead of individual processes. \n" \
|
|
"--- \n" \
|
|
"Documentation: https://github.com/ufrisk/MemProcFS/wiki/FS_Phys2Virt \n";
|
|
|
|
typedef struct tdM_PHYS2VIRT_MULTIENTRY {
|
|
DWORD dwPID;
|
|
QWORD va;
|
|
} M_PHYS2VIRT_MULTIENTRY, *PM_PHYS2VIRT_MULTIENTRY;
|
|
|
|
typedef struct tdM_PHYS2VIRT_MULTIENTRY_CONTEXT {
|
|
QWORD pa;
|
|
DWORD c;
|
|
DWORD cMax;
|
|
M_PHYS2VIRT_MULTIENTRY e[0];
|
|
} M_PHYS2VIRT_MULTIENTRY_CONTEXT, *PM_PHYS2VIRT_MULTIENTRY_CONTEXT;
|
|
|
|
VOID Phys2Virt_GetUpdateAll_CallbackAction(_In_ VMM_HANDLE H, _In_ PVMM_PROCESS pProcess, _In_opt_ PVOID ctxIn)
|
|
{
|
|
PM_PHYS2VIRT_MULTIENTRY_CONTEXT ctx = (PM_PHYS2VIRT_MULTIENTRY_CONTEXT)ctxIn;
|
|
DWORD i, j;
|
|
PVMMOB_PHYS2VIRT_INFORMATION pObPhys2Virt = NULL;
|
|
if(!ctx) { return; }
|
|
pObPhys2Virt = VmmPhys2VirtGetInformation(H, pProcess, ctx->pa);
|
|
if(!pObPhys2Virt) { return; }
|
|
for(j = 0; j < pObPhys2Virt->cvaList; j++) {
|
|
if(pObPhys2Virt->pvaList[j]) {
|
|
i = InterlockedIncrement(&ctx->c);
|
|
if(i < ctx->cMax) {
|
|
ctx->e[i].dwPID = pProcess->dwPID;
|
|
ctx->e[i].va = pObPhys2Virt->pvaList[j];
|
|
}
|
|
}
|
|
}
|
|
Ob_DECREF(pObPhys2Virt);
|
|
}
|
|
|
|
/*
|
|
* CALLER LocalFree: ppMultiEntry
|
|
*/
|
|
_Success_(return)
|
|
BOOL Phys2Virt_GetUpdateAll(_In_ VMM_HANDLE H, _Out_opt_ PM_PHYS2VIRT_MULTIENTRY_CONTEXT *ppMultiEntry, _Out_opt_ PDWORD pcMultiEntry)
|
|
{
|
|
PM_PHYS2VIRT_MULTIENTRY_CONTEXT ctx = NULL;
|
|
SIZE_T cPIDs = 0;
|
|
VmmProcessListPIDs(H, NULL, &cPIDs, 0);
|
|
ctx = LocalAlloc(LMEM_ZEROINIT, sizeof(M_PHYS2VIRT_MULTIENTRY_CONTEXT) + cPIDs * 4 * sizeof(M_PHYS2VIRT_MULTIENTRY));
|
|
if(!ctx) { return FALSE; }
|
|
ctx->pa = H->vmm.paPluginPhys2VirtRoot;
|
|
ctx->cMax = (DWORD)cPIDs * 4;
|
|
if(!VmmWork_ProcessActionForeachParallel_Void(H, 0, ctx, VmmWork_ProcessActionForeachParallel_CriteriaActiveOnly, Phys2Virt_GetUpdateAll_CallbackAction)) {
|
|
LocalFree(ctx);
|
|
return FALSE;
|
|
}
|
|
ctx->c = min(ctx->c, ctx->cMax - 1);
|
|
if(pcMultiEntry) { *pcMultiEntry = ctx->c; }
|
|
if(ppMultiEntry) {
|
|
*ppMultiEntry = ctx;
|
|
} else {
|
|
LocalFree(ctx);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* qsort compare function for sorting the resulting Phys2Virt virtual addresses and PIDs
|
|
* after a scan of all process virtual address spaces from the root module.
|
|
*/
|
|
int Phys2Virt_ReadVirtRoot_CmpSort(PM_PHYS2VIRT_MULTIENTRY a, PM_PHYS2VIRT_MULTIENTRY b)
|
|
{
|
|
if(a->dwPID == b->dwPID) {
|
|
return (a->va > b->va) ? 1 : -1;
|
|
}
|
|
return a->dwPID - b->dwPID;
|
|
}
|
|
|
|
NTSTATUS Phys2Virt_ReadVirtRoot(_In_ VMM_HANDLE H, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset)
|
|
{
|
|
NTSTATUS nt = VMMDLL_STATUS_FILE_INVALID;
|
|
DWORD i, cbBuffer = 0, cbBufferMax;
|
|
PBYTE pbBuffer = NULL;
|
|
PM_PHYS2VIRT_MULTIENTRY_CONTEXT ctx = NULL;
|
|
if(Phys2Virt_GetUpdateAll(H, &ctx, NULL)) {
|
|
cbBufferMax = 24 * ctx->c + 0x10;
|
|
pbBuffer = LocalAlloc(0, cbBufferMax);
|
|
if(pbBuffer) {
|
|
if(ctx->c > 1) {
|
|
qsort(ctx->e + 1, ctx->c - 1, sizeof(M_PHYS2VIRT_MULTIENTRY), (_CoreCrtNonSecureSearchSortCompareFunction)Phys2Virt_ReadVirtRoot_CmpSort);
|
|
}
|
|
for(i = 1; i <= ctx->c; i++) {
|
|
cbBuffer += snprintf(
|
|
pbBuffer + cbBuffer,
|
|
cbBufferMax - cbBuffer,
|
|
H->vmm.f32 ? "%6i %08llx\n" : "%6i %016llx\n",
|
|
ctx->e[i].dwPID,
|
|
ctx->e[i].va
|
|
);
|
|
}
|
|
nt = Util_VfsReadFile_FromPBYTE(pbBuffer, cbBuffer, pb, cb, pcbRead, cbOffset);
|
|
}
|
|
}
|
|
LocalFree(ctx);
|
|
LocalFree(pbBuffer);
|
|
return nt;
|
|
}
|
|
|
|
/*
|
|
* Read the virtual address file with up to PHYS2VIRT_MAX_NUMBER_ADDRESS entries.
|
|
* -- H
|
|
* -- pProcess
|
|
* -- pb
|
|
* -- cb
|
|
* -- pcbRead
|
|
* -- cbOffset
|
|
* -- return
|
|
*/
|
|
NTSTATUS Phys2Virt_ReadVirtProcess(_In_ VMM_HANDLE H, _In_ PVMM_PROCESS pProcess, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset)
|
|
{
|
|
DWORD i;
|
|
DWORD cbBuffer = 0, cbBufferMax = 0x10 + VMM_PHYS2VIRT_INFORMATION_MAX_PROCESS_RESULT * 17;
|
|
BYTE pbBuffer[0x10 + VMM_PHYS2VIRT_INFORMATION_MAX_PROCESS_RESULT * 17] = { 0 };
|
|
PVMMOB_PHYS2VIRT_INFORMATION pObPhys2Virt = NULL;
|
|
// 1: retrieve / start update of PVMMOB_PHYS2VIRT_INFORMATION in a thread-safe way.
|
|
pObPhys2Virt = VmmPhys2VirtGetInformation(H, pProcess, 0);
|
|
// 2: show result
|
|
if(pObPhys2Virt) {
|
|
for(i = 0; i < pObPhys2Virt->cvaList; i++) {
|
|
cbBuffer += snprintf(
|
|
pbBuffer + cbBuffer,
|
|
cbBufferMax - cbBuffer,
|
|
H->vmm.f32 ? "%08llx\n" : "%016llx\n",
|
|
pObPhys2Virt->pvaList[i]
|
|
);
|
|
}
|
|
}
|
|
Ob_DECREF_NULL(&pObPhys2Virt);
|
|
return Util_VfsReadFile_FromPBYTE(pbBuffer, cbBuffer, pb, cb, pcbRead, cbOffset);
|
|
}
|
|
|
|
/*
|
|
* Read : function as specified by the module manager. The module manager will
|
|
* call into this callback function whenever a read shall occur from a "file".
|
|
* -- H
|
|
* -- ctxP
|
|
* -- pb
|
|
* -- cb
|
|
* -- pcbRead
|
|
* -- cbOffset
|
|
* -- return
|
|
*/
|
|
NTSTATUS Phys2Virt_Read(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, _Out_writes_to_(cb, *pcbRead) PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset)
|
|
{
|
|
NTSTATUS nt;
|
|
PVMMOB_PHYS2VIRT_INFORMATION pObPhys2Virt = NULL;
|
|
PVMM_PROCESS pProcess = (PVMM_PROCESS)ctxP->pProcess;
|
|
if(!_stricmp(ctxP->uszPath, "readme.txt")) {
|
|
return Util_VfsReadFile_FromStrA(szMPHYS2VIRT_README, pb, cb, pcbRead, cbOffset);
|
|
}
|
|
if(pProcess) {
|
|
if(!_stricmp(ctxP->uszPath, "phys.txt")) {
|
|
pObPhys2Virt = VmmPhys2VirtGetInformation(H, pProcess, 0);
|
|
nt = Util_VfsReadFile_FromQWORD((pObPhys2Virt ? pObPhys2Virt->paTarget : 0), pb, cb, pcbRead, cbOffset, FALSE);
|
|
Ob_DECREF_NULL(&pObPhys2Virt);
|
|
return nt;
|
|
}
|
|
if(!_stricmp(ctxP->uszPath, "virt.txt")) {
|
|
return Phys2Virt_ReadVirtProcess(H, pProcess, pb, cb, pcbRead, cbOffset);
|
|
}
|
|
} else {
|
|
if(!_stricmp(ctxP->uszPath, "phys.txt")) {
|
|
return Util_VfsReadFile_FromQWORD(H->vmm.paPluginPhys2VirtRoot, pb, cb, pcbRead, cbOffset, FALSE);
|
|
}
|
|
if(!_stricmp(ctxP->uszPath, "virt.txt")) {
|
|
return Phys2Virt_ReadVirtRoot(H, pb, cb, pcbRead, cbOffset);
|
|
}
|
|
}
|
|
return VMMDLL_STATUS_FILE_INVALID;
|
|
}
|
|
|
|
/*
|
|
* Write : function as specified by the module manager. The module manager will
|
|
* call into this callback function whenever a write shall occur from a "file".
|
|
* -- H
|
|
* -- ctxP
|
|
* -- pb
|
|
* -- cb
|
|
* -- pcbWrite
|
|
* -- cbOffset
|
|
* -- return
|
|
*/
|
|
NTSTATUS Phys2Virt_Write(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, _In_reads_(cb) PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbWrite, _In_ QWORD cbOffset)
|
|
{
|
|
BYTE pbBuffer[17];
|
|
PVMM_PROCESS pProcess = (PVMM_PROCESS)ctxP->pProcess;
|
|
*pcbWrite = 0;
|
|
if(!_stricmp(ctxP->uszPath, "phys.txt")) {
|
|
if(cbOffset < 16) {
|
|
*pcbWrite = cb;
|
|
memcpy(pbBuffer, "0000000000000000", 16);
|
|
cb = (DWORD)min(16 - cbOffset, cb);
|
|
memcpy(pbBuffer + cbOffset, pb, cb);
|
|
pbBuffer[16] = 0;
|
|
if(pProcess) {
|
|
Ob_DECREF(VmmPhys2VirtGetInformation(H, pProcess, strtoull(pbBuffer, NULL, 16)));
|
|
} else {
|
|
H->vmm.paPluginPhys2VirtRoot = strtoull(pbBuffer, NULL, 16);
|
|
Phys2Virt_GetUpdateAll(H, NULL, NULL);
|
|
}
|
|
}
|
|
return *pcbWrite ? VMMDLL_STATUS_SUCCESS : VMMDLL_STATUS_END_OF_FILE;
|
|
}
|
|
return VMMDLL_STATUS_FILE_INVALID; // only 'phys' file is writable
|
|
}
|
|
|
|
/*
|
|
* List : function as specified by the module manager. The module manager will
|
|
* call into this callback function whenever a list directory shall occur from
|
|
* the given module.
|
|
* -- H
|
|
* -- ctxP
|
|
* -- pFileList
|
|
* -- return
|
|
*/
|
|
BOOL Phys2Virt_List(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, _Inout_ PHANDLE pFileList)
|
|
{
|
|
DWORD c = 0, cb;
|
|
PVMMOB_PHYS2VIRT_INFORMATION pObPhys2Virt = NULL;
|
|
PVMM_PROCESS pProcess = (PVMM_PROCESS)ctxP->pProcess;
|
|
if(ctxP->uszPath[0]) { return FALSE; }
|
|
if(pProcess) {
|
|
// process context
|
|
pObPhys2Virt = VmmPhys2VirtGetInformation(H, pProcess, 0);
|
|
cb = !pObPhys2Virt ? 0 : pObPhys2Virt->cvaList * (1 + (H->vmm.f32 ? 8 : 16));
|
|
Ob_DECREF(pObPhys2Virt);
|
|
} else {
|
|
// root context
|
|
Phys2Virt_GetUpdateAll(H, NULL, &c);
|
|
cb = c * (8 + (H->vmm.f32 ? 8 : 16));
|
|
}
|
|
VMMDLL_VfsList_AddFile(pFileList, "readme.txt", strlen(szMPHYS2VIRT_README), NULL);
|
|
VMMDLL_VfsList_AddFile(pFileList, "phys.txt", 16, NULL);
|
|
VMMDLL_VfsList_AddFile(pFileList, "virt.txt", cb, NULL);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Initialization function. The module manager shall call into this function
|
|
* when the module shall be initialized. If the module wish to initialize it
|
|
* shall call the supplied pfnPluginManager_Register function.
|
|
* NB! the module does not have to register itself - for example if the target
|
|
* operating system or architecture is unsupported.
|
|
* -- H
|
|
* -- pRI
|
|
*/
|
|
VOID M_Phys2Virt_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->tpMemoryModel == VMMDLL_MEMORYMODEL_X64) || (pRI->tpMemoryModel == VMMDLL_MEMORYMODEL_ARM64) || (pRI->tpMemoryModel == VMMDLL_MEMORYMODEL_X86) || (pRI->tpMemoryModel == VMMDLL_MEMORYMODEL_X86PAE))) { return; }
|
|
pRI->reg_fn.pfnList = Phys2Virt_List; // List function supported
|
|
pRI->reg_fn.pfnRead = Phys2Virt_Read; // Read function supported
|
|
pRI->reg_fn.pfnWrite = Phys2Virt_Write; // Write function supported
|
|
// register process plugin
|
|
strcpy_s(pRI->reg_info.uszPathName, 128, "\\phys2virt");
|
|
pRI->reg_info.fRootModule = FALSE;
|
|
pRI->reg_info.fProcessModule = TRUE;
|
|
pRI->pfnPluginManager_Register(H, pRI);
|
|
// register root plugin
|
|
strcpy_s(pRI->reg_info.uszPathName, 128, "\\misc\\phys2virt");
|
|
pRI->reg_info.fRootModule = TRUE;
|
|
pRI->reg_info.fProcessModule = FALSE;
|
|
pRI->pfnPluginManager_Register(H, pRI);
|
|
}
|