Files
MemProcFS/vmm/modules/m_sys_syscall.c
2026-01-04 15:00:03 +01:00

199 lines
9.6 KiB
C

// m_sys_syscall.c : implementation related to the Sys/Syscall built-in module.
//
// The '/sys/syscall' module is responsible for displaying the Windows syscall table
// a.k.a. the System Service Dispatch Table (SSDT).
//
// (c) Ulf Frisk, 2020-2026
// Author: Ulf Frisk, pcileech@frizk.net
//
#include "modules.h"
typedef struct tdKSERVICE_DESCRIPTOR_TABLE32 {
DWORD ServiceTableBase;
DWORD ServiceCounterTableBase;
DWORD NumberOfServices;
DWORD ParamTableBase;
} KSERVICE_DESCRIPTOR_TABLE32, *PKSERVICE_DESCRIPTOR_TABLE32;
typedef struct tdKSERVICE_DESCRIPTOR_TABLE64 {
QWORD ServiceTableBase;
QWORD ServiceCounterTableBase;
QWORD NumberOfServices;
QWORD ParamTableBase;
} KSERVICE_DESCRIPTOR_TABLE64, *PKSERVICE_DESCRIPTOR_TABLE64;
typedef struct tdMSYSCALL_CONTEXT {
BOOL fInit;
QWORD vaKeServiceDescriptorTable;
QWORD vaKeServiceDescriptorTableShadow;
union {
BYTE pbServiceDescriptorTable[1];
// [0] = TableNt, [1] = TableNtShadow, [2] = TableWin32kShadow
KSERVICE_DESCRIPTOR_TABLE32 ServiceDescriptorTable32[3];
KSERVICE_DESCRIPTOR_TABLE64 ServiceDescriptorTable64[3];
};
POB_COMPRESSED pCompressedData[3];
} MSYSCALL_CONTEXT, *PMSYSCALL_CONTEXT;
/*
* Retrieve csrss.exe process. It's a specially treated process with both
* user/kernel space mapped for win32k reasons.
* CALLER DECREF: return
* -- H
* -- return
*/
PVMM_PROCESS MSyscall_GetProcessCsrss(_In_ VMM_HANDLE H)
{
PVMM_PROCESS pObProcess = NULL;
while((pObProcess = VmmProcessGetNext(H, pObProcess, 0))) {
if(!strcmp(pObProcess->szName, "csrss.exe")) {
return pObProcess;
}
}
return NULL;
}
VOID MSyscall_Initialize_BuildText(_In_ VMM_HANDLE H, PMSYSCALL_CONTEXT ctxM, PVMM_PROCESS pProcess, DWORD iTable, PDB_HANDLE hPDB, QWORD vaBase)
{
BOOL f, f32 = H->vmm.f32;
DWORD i, c, dwSymbolDisplacement, oBuffer = 0, cbBuffer;
PBYTE pbBuffer = NULL;
PDWORD pdwTable = NULL;
QWORD va, vaServiceTableBase;
CHAR szSymbolName[MAX_PATH];
c = f32 ? ctxM->ServiceDescriptorTable32[iTable].NumberOfServices : (DWORD)ctxM->ServiceDescriptorTable64[iTable].NumberOfServices;
vaServiceTableBase = f32 ? ctxM->ServiceDescriptorTable32[iTable].ServiceTableBase : ctxM->ServiceDescriptorTable64[iTable].ServiceTableBase;
if(!(pdwTable = LocalAlloc(0, c * sizeof(DWORD)))) { goto fail; }
if(!VmmRead(H, pProcess, vaServiceTableBase, (PBYTE)pdwTable, c * sizeof(DWORD))) { goto fail; }
cbBuffer = c * (64 + MAX_PATH);
if(!(pbBuffer = LocalAlloc(0, cbBuffer))) { goto fail; }
for(i = 0; i < c; i++) {
va = 0;
f = pdwTable[i] &&
(va = f32 ? pdwTable[i] : vaServiceTableBase + (((LONG)pdwTable[i]) >> 4)) &&
PDB_GetSymbolFromOffset(H, hPDB, (DWORD)(va - vaBase), szSymbolName, &dwSymbolDisplacement) &&
(dwSymbolDisplacement == 0);
oBuffer += f ?
snprintf(pbBuffer + oBuffer, cbBuffer - oBuffer, "%04x %08x +%06x %llx %s %s\n", (i + 0x1000 * iTable), pdwTable[i], (DWORD)(va - vaBase), va, (iTable == 2 ? "win32k" : "nt "), szSymbolName) :
snprintf(pbBuffer + oBuffer, cbBuffer - oBuffer, "%04x %08x +%06x %*llx %s %s\n", (i + 0x1000 * iTable), pdwTable[i], 0, (f32 ? 8 : 16), va, (iTable == 2 ? "win32k" : "nt "), "---");
}
ctxM->pCompressedData[iTable] = ObCompressed_NewFromByte(H, H->vmm.pObCacheMapObCompressedShared, pbBuffer, oBuffer);
fail:
LocalFree(pbBuffer);
LocalFree(pdwTable);
}
VOID MSyscall_Initialize(_In_ VMM_HANDLE H, PMSYSCALL_CONTEXT ctxM)
{
BOOL f, f32 = H->vmm.f32;
DWORD i, cbRead;
PDB_HANDLE hPdbWin32k;
PVMM_MAP_MODULEENTRY peModuleWin32k;
PVMMOB_MAP_MODULE pObModuleMap = NULL;
PE_CODEVIEW_INFO CVInfoWin32k = { 0 };
PVMM_PROCESS pObProcessCsrss = NULL, pObSystemProcess = NULL;
if(!(pObSystemProcess = VmmProcessGet(H, 4))) { goto fail; }
// retrieve nt!KeServiceDescriptorTable & nt!KeServiceDescriptorTableShadow
if(!PDB_GetSymbolAddress(H, PDB_HANDLE_KERNEL, "KeServiceDescriptorTable", &ctxM->vaKeServiceDescriptorTable)) { goto fail; }
if(!PDB_GetSymbolAddress(H, PDB_HANDLE_KERNEL, "KeServiceDescriptorTableShadow", &ctxM->vaKeServiceDescriptorTableShadow)) { goto fail; }
if(!VMM_KADDR_8_16(f32, ctxM->vaKeServiceDescriptorTable)) { goto fail; }
if(!VMM_KADDR_8_16(f32, ctxM->vaKeServiceDescriptorTableShadow)) { goto fail; }
cbRead = (f32 ? sizeof(KSERVICE_DESCRIPTOR_TABLE32) : sizeof(KSERVICE_DESCRIPTOR_TABLE64));
if(!VmmRead(H, pObSystemProcess, ctxM->vaKeServiceDescriptorTable, ctxM->pbServiceDescriptorTable, cbRead)) { goto fail; } // Table
if(!VmmRead(H, pObSystemProcess, ctxM->vaKeServiceDescriptorTableShadow, ctxM->pbServiceDescriptorTable + cbRead, 2 * cbRead)) { goto fail; } // TableShadow
// validate nt!KeServiceDescriptorTable / nt!KeServiceDescriptorTableShadow
for(i = 0; i < 3; i++) {
if(f32) {
f = VMM_KADDR32_4(ctxM->ServiceDescriptorTable32[i].ServiceTableBase) &&
(ctxM->ServiceDescriptorTable32[i].ServiceCounterTableBase == 0) &&
(ctxM->ServiceDescriptorTable32[i].NumberOfServices < 0x800) &&
VMM_KADDR32(ctxM->ServiceDescriptorTable32[i].ParamTableBase) &&
(ctxM->ServiceDescriptorTable32[i].ServiceTableBase < ctxM->ServiceDescriptorTable32[i].ParamTableBase);
if(!f) { return; }
} else {
f = VMM_KADDR64_8(ctxM->ServiceDescriptorTable64[i].ServiceTableBase) &&
(ctxM->ServiceDescriptorTable64[i].ServiceCounterTableBase == 0) &&
(ctxM->ServiceDescriptorTable64[i].NumberOfServices < 0x800) &&
VMM_KADDR64(ctxM->ServiceDescriptorTable64[i].ParamTableBase) &&
(ctxM->ServiceDescriptorTable64[i].ServiceTableBase < ctxM->ServiceDescriptorTable64[i].ParamTableBase);
if(!f) { return; }
}
}
// fetch win32k infos
if(!(pObProcessCsrss = MSyscall_GetProcessCsrss(H))) { goto fail_win32k; }
if(!VmmMap_GetModuleEntryEx(H, pObSystemProcess, 0, "win32k.sys", 0, &pObModuleMap, &peModuleWin32k)) { goto fail_win32k; }
if(!(hPdbWin32k = PDB_GetHandleFromModuleAddress(H, pObProcessCsrss, peModuleWin32k->vaBase))) { goto fail_win32k; }
// build text files in-memory (win32k)
MSyscall_Initialize_BuildText(H, ctxM, pObProcessCsrss, 2, hPdbWin32k, peModuleWin32k->vaBase);
fail_win32k:
// build text files in-memory (nt)
MSyscall_Initialize_BuildText(H, ctxM, pObSystemProcess, 0, PDB_HANDLE_KERNEL, H->vmm.kernel.vaBase);
MSyscall_Initialize_BuildText(H, ctxM, pObSystemProcess, 1, PDB_HANDLE_KERNEL, H->vmm.kernel.vaBase);
fail:
Ob_DECREF(pObModuleMap);
Ob_DECREF(pObSystemProcess);
Ob_DECREF(pObProcessCsrss);
ctxM->fInit = TRUE;
}
PMSYSCALL_CONTEXT MSyscall_GetContext(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP)
{
PMSYSCALL_CONTEXT ctxM = (PMSYSCALL_CONTEXT)ctxP->ctxM;
if(ctxM->fInit) { return ctxM; }
EnterCriticalSection(&H->vmm.LockPlugin);
if(ctxM->fInit) { goto finish; }
PDB_Initialize_WaitComplete(H);
MSyscall_Initialize(H, ctxM);
finish:
LeaveCriticalSection(&H->vmm.LockPlugin);
return ctxM;
}
NTSTATUS MSyscall_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)
{
PMSYSCALL_CONTEXT ctxM = MSyscall_GetContext(H, ctxP);
if(!_stricmp(ctxP->uszPath, "syscall_nt.txt")) {
return Util_VfsReadFile_FromObCompressed(ctxM->pCompressedData[0], pb, cb, pcbRead, cbOffset);
}
if(!_stricmp(ctxP->uszPath, "syscall_nt_shadow.txt")) {
return Util_VfsReadFile_FromObCompressed(ctxM->pCompressedData[1], pb, cb, pcbRead, cbOffset);
}
if(!_stricmp(ctxP->uszPath, "syscall_win32k.txt")) {
return Util_VfsReadFile_FromObCompressed(ctxM->pCompressedData[2], pb, cb, pcbRead, cbOffset);
}
return VMMDLL_STATUS_FILE_INVALID;
}
BOOL MSyscall_List(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, _Inout_ PHANDLE pFileList)
{
PMSYSCALL_CONTEXT ctxM = MSyscall_GetContext(H, ctxP);
if(ctxP->uszPath[0]) { return FALSE; }
VMMDLL_VfsList_AddFile(pFileList, "syscall_nt.txt", ObCompress_Size(ctxM->pCompressedData[0]), NULL);
VMMDLL_VfsList_AddFile(pFileList, "syscall_nt_shadow.txt", ObCompress_Size(ctxM->pCompressedData[1]), NULL);
VMMDLL_VfsList_AddFile(pFileList, "syscall_win32k.txt", ObCompress_Size(ctxM->pCompressedData[2]), NULL);
return TRUE;
}
VOID MSyscall_Close(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP)
{
PMSYSCALL_CONTEXT ctxM = (PMSYSCALL_CONTEXT)ctxP->ctxM;
Ob_DECREF(ctxM->pCompressedData[0]);
Ob_DECREF(ctxM->pCompressedData[1]);
Ob_DECREF(ctxM->pCompressedData[2]);
LocalFree(ctxM);
}
VOID M_SysSyscall_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 != VMMDLL_SYSTEM_WINDOWS_64) && (pRI->tpSystem != VMMDLL_SYSTEM_WINDOWS_32)) { return; }
if(!(pRI->reg_info.ctxM = LocalAlloc(LMEM_ZEROINIT, sizeof(MSYSCALL_CONTEXT)))) { return; } // internal module context
strcpy_s(pRI->reg_info.uszPathName, 128, "\\sys\\syscall"); // module name
pRI->reg_info.fRootModule = TRUE; // module shows in root directory
pRI->reg_fn.pfnList = MSyscall_List; // List function supported
pRI->reg_fn.pfnRead = MSyscall_Read; // Read function supported
pRI->reg_fn.pfnClose = MSyscall_Close; // Close function supported
pRI->pfnPluginManager_Register(H, pRI);
}