mirror of
https://github.com/ufrisk/MemProcFS.git
synced 2026-06-03 09:14:19 +08:00
260 lines
12 KiB
C
260 lines
12 KiB
C
// m_fc_proc.c : process forensic module.
|
|
//
|
|
// REQUIRE: FORENSIC SUB-SYSTEM INIT.
|
|
//
|
|
// NB! module generate forensic data only - no file system presence!
|
|
//
|
|
// (c) Ulf Frisk, 2020-2026
|
|
// Author: Ulf Frisk, pcileech@frizk.net
|
|
//
|
|
|
|
#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,Flag\n";
|
|
|
|
static LPSTR FC_SQL_SCHEMA_PROCESS =
|
|
"DROP TABLE IF EXISTS process; " \
|
|
"CREATE TABLE process(id INTEGER PRIMARY KEY AUTOINCREMENT, id_str_name INTEGER, id_str_path INTEGER, id_str_user INTEGER, id_str_all INTEGER, pid INT, ppid INT, eprocess INTEGER, dtb INTEGER, dtb_user INTEGER, state INTEGER, wow64 INT, peb INTEGER, peb32 INTEGER, time_create INTEGER, time_exit INTEGER); " \
|
|
"DROP VIEW IF EXISTS v_process; " \
|
|
"CREATE VIEW v_process AS SELECT p.*, sn.cbu AS cbu_name, sn.cbj AS cbj_name, sn.sz AS sz_name, sp.cbu AS cbu_path, sp.cbj AS cbj_path, sp.sz AS sz_path, su.cbu AS cbu_user, su.cbj AS cbj_user, su.sz AS sz_user, sa.cbu AS cbu_all, sa.cbj AS cbj_all, sa.sz AS sz_all FROM process p, str sn, str sp, str su, str sa WHERE p.id_str_name = sn.id AND p.id_str_path = sp.id AND p.id_str_user = su.id AND p.id_str_all = sa.id; ";
|
|
|
|
/*
|
|
* Forensic initialization function called when the forensic sub-system is initializing.
|
|
*/
|
|
PVOID MFcProc_FcInitialize(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP)
|
|
{
|
|
int rc;
|
|
BOOL fWellKnownAccount = FALSE;
|
|
PVMM_PROCESS pObProcess = NULL;
|
|
sqlite3 *hSql = NULL;
|
|
sqlite3_stmt *hStmt = NULL, *hStmtStr = NULL;
|
|
FCSQL_INSERTSTRTABLE SqlStrInsert[4];
|
|
CHAR uszUserName[MAX_PATH], uszFullInfo[2048];
|
|
// 1: initialize csv
|
|
FcFileAppend(H, "process.csv", MFCPROC_CSV_PROCESS);
|
|
// 2: initialize timelining (sql)
|
|
if(SQLITE_OK != Fc_SqlExec(H, FC_SQL_SCHEMA_PROCESS)) { goto fail; }
|
|
if(!(hSql = Fc_SqlReserve(H))) { goto fail; }
|
|
if(SQLITE_OK != sqlite3_prepare_v2(hSql, "INSERT INTO process (id_str_name, id_str_path, id_str_user, id_str_all, pid, ppid, eprocess, dtb, dtb_user, state, wow64, peb, peb32, time_create, time_exit) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);", -1, &hStmt, NULL)) { goto fail; }
|
|
if(SQLITE_OK != sqlite3_prepare_v2(hSql, szFC_SQL_STR_INSERT, -1, &hStmtStr, NULL)) { goto fail; }
|
|
sqlite3_exec(hSql, "BEGIN TRANSACTION", NULL, NULL, NULL);
|
|
while((pObProcess = VmmProcessGetNext(H, pObProcess, VMM_FLAG_PROCESS_TOKEN | VMM_FLAG_PROCESS_SHOW_TERMINATED))) {
|
|
// build and insert string data into 'str' table.
|
|
if(!Fc_SqlInsertStr(H, hStmtStr, pObProcess->pObPersistent->uszNameLong, &SqlStrInsert[0])) { goto fail_transact; }
|
|
if(!Fc_SqlInsertStr(H, hStmtStr, pObProcess->pObPersistent->uszPathKernel, &SqlStrInsert[1])) { goto fail_transact; }
|
|
if(!pObProcess->win.Token || !pObProcess->win.Token->fSidUserValid || !VmmWinUser_GetName(H, &pObProcess->win.Token->SidUser.SID, uszUserName, MAX_PATH, &fWellKnownAccount)) { uszUserName[0] = 0; }
|
|
if(!Fc_SqlInsertStr(H, hStmtStr, uszUserName, &SqlStrInsert[2])) { goto fail_transact; }
|
|
_snprintf_s(uszFullInfo, 2048 - 2, 2048 - 3, "%s [%s%s] %s", pObProcess->pObPersistent->uszNameLong, (fWellKnownAccount ? "*" : ""), uszUserName, pObProcess->pObPersistent->uszPathKernel);
|
|
if(!Fc_SqlInsertStr(H, hStmtStr, uszFullInfo, &SqlStrInsert[3])) { goto fail_transact; }
|
|
// insert into 'process' table.
|
|
sqlite3_reset(hStmt);
|
|
rc = Fc_SqlBindMultiInt64(hStmt, 1, 15,
|
|
SqlStrInsert[0].id,
|
|
SqlStrInsert[1].id,
|
|
SqlStrInsert[2].id,
|
|
SqlStrInsert[3].id,
|
|
(QWORD)pObProcess->dwPID,
|
|
(QWORD)pObProcess->dwPPID,
|
|
pObProcess->win.EPROCESS.va,
|
|
pObProcess->paDTB,
|
|
pObProcess->paDTB_UserOpt,
|
|
(QWORD)pObProcess->dwState,
|
|
(QWORD)(pObProcess->win.fWow64 ? 1 : 0),
|
|
pObProcess->win.vaPEB,
|
|
(QWORD)pObProcess->win.vaPEB32,
|
|
VmmProcess_GetCreateTimeOpt(H, pObProcess),
|
|
VmmProcess_GetExitTimeOpt(H, pObProcess)
|
|
);
|
|
if(SQLITE_OK != rc) { goto fail_transact; }
|
|
sqlite3_step(hStmt);
|
|
}
|
|
sqlite3_exec(hSql, "COMMIT TRANSACTION", NULL, NULL, NULL);
|
|
fail:
|
|
Ob_DECREF(pObProcess);
|
|
sqlite3_finalize(hStmt);
|
|
sqlite3_finalize(hStmtStr);
|
|
Fc_SqlReserveReturn(H, hSql);
|
|
return NULL;
|
|
fail_transact:
|
|
sqlite3_exec(hSql, "COMMIT TRANSACTION", NULL, NULL, NULL);
|
|
goto fail;
|
|
}
|
|
|
|
/*
|
|
* Timeline data by executing a partial SQL query on pre-existing data.
|
|
* -- H
|
|
* -- ctxfc
|
|
* -- hTimeline
|
|
* -- pfnAddEntry
|
|
* -- pfnEntryAddBySql
|
|
*/
|
|
VOID MFcProc_FcTimeline(
|
|
_In_ VMM_HANDLE H,
|
|
_In_opt_ PVOID ctxfc,
|
|
_In_ HANDLE hTimeline,
|
|
_In_ VOID(*pfnAddEntry)(_In_ VMM_HANDLE H, _In_ HANDLE hTimeline, _In_ QWORD ft, _In_ DWORD dwAction, _In_ DWORD dwPID, _In_ DWORD dwData32, _In_ QWORD qwData64, _In_ LPCSTR uszText),
|
|
_In_ VOID(*pfnEntryAddBySql)(_In_ VMM_HANDLE H, _In_ HANDLE hTimeline, _In_ DWORD cEntrySql, _In_ LPCSTR *pszEntrySql)
|
|
) {
|
|
LPCSTR pszSql[] = {
|
|
"id_str_all, time_create, "STRINGIZE(FC_TIMELINE_ACTION_CREATE)", pid, ppid, eprocess FROM process WHERE time_create > 0;",
|
|
"id_str_all, time_exit, "STRINGIZE(FC_TIMELINE_ACTION_DELETE)", pid, ppid, eprocess FROM process WHERE time_exit > 0;"
|
|
};
|
|
pfnEntryAddBySql(H, hTimeline, sizeof(pszSql) / sizeof(LPSTR), pszSql);
|
|
}
|
|
|
|
VOID MFcProc_LogHeap(_In_ VMM_HANDLE H, _In_ PVMMDLL_FORENSIC_JSONDATA pd, _In_ VOID(*pfnLogJSON)(_In_ VMM_HANDLE H, _In_ PVMMDLL_FORENSIC_JSONDATA pData), _In_ PVMMOB_MAP_HEAP pMap)
|
|
{
|
|
DWORD i;
|
|
PVMM_MAP_HEAP_SEGMENTENTRY peR;
|
|
for(i = 0; i < pMap->cSegments; i++) {
|
|
peR = pMap->pSegments + i;
|
|
pd->i = i;
|
|
pd->va[0] = peR->va;
|
|
pd->va[1] = pMap->pMap[peR->iHeap].va;
|
|
pd->qwNum[0] = peR->cb;
|
|
pfnLogJSON(H, pd);
|
|
}
|
|
}
|
|
|
|
VOID MFcProc_LogProcess_GetUserName(_In_ VMM_HANDLE H, _In_ PVMM_PROCESS pProcess, _Out_writes_(17) LPSTR uszUserName, _Out_ PBOOL fAccountUser)
|
|
{
|
|
BOOL f, fWellKnownAccount = FALSE;
|
|
uszUserName[0] = 0;
|
|
f = pProcess->win.Token &&
|
|
pProcess->win.Token->fSidUserValid &&
|
|
VmmWinUser_GetName(H, &pProcess->win.Token->SidUser.SID, uszUserName, 17, &fWellKnownAccount);
|
|
*fAccountUser = f && !fWellKnownAccount;
|
|
}
|
|
|
|
VOID MFcProc_LogProcess(_In_ VMM_HANDLE H, _In_ PVMMDLL_FORENSIC_JSONDATA pd, _In_ VOID(*pfnLogJSON)(_In_ VMM_HANDLE H, _In_ PVMMDLL_FORENSIC_JSONDATA pData), PVMM_PROCESS pProcess)
|
|
{
|
|
SIZE_T o;
|
|
BOOL fStateTerminated, fAccountUser = FALSE;
|
|
CHAR uszBuffer[1024], szUserName[17], szTimeCRE[24], szTimeEXIT[24];
|
|
PVMMWIN_USER_PROCESS_PARAMETERS pu = VmmWin_UserProcessParameters_Get(H, pProcess);
|
|
SIZE_T cbu = sizeof(uszBuffer);
|
|
LPSTR usz = uszBuffer;
|
|
if((SIZE_T)pu->cbuImagePathName + pu->cbuCommandLine > sizeof(uszBuffer) - 512) {
|
|
cbu = (SIZE_T)pu->cbuImagePathName + pu->cbuCommandLine + 512;
|
|
usz = LocalAlloc(0, cbu);
|
|
if(!usz) { return; }
|
|
}
|
|
pd->i = pProcess->dwPID;
|
|
pd->vaObj = pProcess->win.EPROCESS.va;
|
|
pd->qwNum[1] = pProcess->dwPPID;
|
|
pd->qwHex[0] = pProcess->paDTB;
|
|
pd->qwHex[1] = pProcess->paDTB_UserOpt;
|
|
pd->usz[0] = pProcess->pObPersistent->uszPathKernel;
|
|
fStateTerminated = (pProcess->dwState != 0);
|
|
MFcProc_LogProcess_GetUserName(H, pProcess, szUserName, &fAccountUser);
|
|
Util_FileTime2String(VmmProcess_GetCreateTimeOpt(H, pProcess), szTimeCRE);
|
|
o = _snprintf_s(usz, cbu, _TRUNCATE, "flags:[%s%c%c%c] user:[%s] upath:[%s] cmd:[%s] createtime:[%s]",
|
|
pProcess->win.fWow64 ? "32" : " ",
|
|
pProcess->win.EPROCESS.fNoLink ? 'E' : ' ',
|
|
fStateTerminated ? 'T' : ' ',
|
|
fAccountUser ? 'U' : ' ',
|
|
szUserName,
|
|
pu->uszImagePathName ? pu->uszImagePathName : "",
|
|
pu->uszCommandLine ? pu->uszCommandLine : "",
|
|
szTimeCRE
|
|
);
|
|
if(VmmProcess_GetExitTimeOpt(H, pProcess)) {
|
|
Util_FileTime2String(VmmProcess_GetExitTimeOpt(H, pProcess), szTimeEXIT);
|
|
o += _snprintf_s(usz + o, cbu - o, _TRUNCATE, " exittime:[%s]", szTimeEXIT);
|
|
}
|
|
if(pProcess->win.Token && pProcess->win.Token->IntegrityLevel) {
|
|
o += _snprintf_s(usz + o, cbu - o, _TRUNCATE, " integrity:[%s]", VMM_TOKEN_INTEGRITY_LEVEL_STR[pProcess->win.Token->IntegrityLevel]);
|
|
}
|
|
pd->usz[1] = usz;
|
|
pfnLogJSON(H, pd);
|
|
if(usz != uszBuffer) { LocalFree(usz); }
|
|
}
|
|
|
|
VOID MFcProc_FcLogJSON(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, _In_ VOID(*pfnLogJSON)(_In_ VMM_HANDLE H, _In_ PVMMDLL_FORENSIC_JSONDATA pData))
|
|
{
|
|
PVMM_PROCESS pProcess = ctxP->pProcess;
|
|
PVMMDLL_FORENSIC_JSONDATA pd;
|
|
PVMMOB_MAP_HEAP pObHeapMap = NULL;
|
|
if(!pProcess || !(pd = LocalAlloc(LMEM_ZEROINIT, sizeof(VMMDLL_FORENSIC_JSONDATA)))) { return; }
|
|
// process:
|
|
FC_JSONDATA_INIT_PIDTYPE(pd, pProcess->dwPID, "process");
|
|
MFcProc_LogProcess(H, pd, pfnLogJSON, pProcess);
|
|
// heap:
|
|
FC_JSONDATA_INIT_PIDTYPE(pd, pProcess->dwPID, "heap");
|
|
if(VmmMap_GetHeap(H, pProcess, &pObHeapMap)) {
|
|
MFcProc_LogHeap(H, pd, pfnLogJSON, pObHeapMap);
|
|
}
|
|
Ob_DECREF(pObHeapMap);
|
|
LocalFree(pd);
|
|
}
|
|
|
|
static VOID MFcProc_BuildFlagString(_In_ PVMM_PROCESS pProcess, _In_ BOOL fAccountUser, _Out_writes_(8) LPSTR szFlag)
|
|
{
|
|
SIZE_T o = 0;
|
|
if(pProcess->win.fWow64) { szFlag[o++] = '3'; szFlag[o++] = '2'; }
|
|
if(pProcess->win.EPROCESS.fNoLink) { szFlag[o++] = 'E'; }
|
|
if(pProcess->dwState != 0) { szFlag[o++] = 'T'; }
|
|
if(fAccountUser) { szFlag[o++] = 'U'; }
|
|
if(o == 0) {
|
|
szFlag[o++] = '-';
|
|
}
|
|
szFlag[o] = '\0';
|
|
}
|
|
|
|
VOID MFcProc_FcLogCSV(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, _In_ VMMDLL_CSV_HANDLE hCSV)
|
|
{
|
|
PVMM_PROCESS pProcess = ctxP->pProcess;
|
|
PVMMWIN_USER_PROCESS_PARAMETERS pu;
|
|
BOOL fAccountUser = FALSE;
|
|
CHAR szUserName[17];
|
|
CHAR szFlag[8];
|
|
if(!pProcess) { return; }
|
|
pu = VmmWin_UserProcessParameters_Get(H, pProcess);
|
|
MFcProc_BuildFlagString(pProcess, fAccountUser, szFlag);
|
|
MFcProc_LogProcess_GetUserName(H, pProcess, szUserName, &fAccountUser);
|
|
FcCsv_Reset(hCSV);
|
|
FcFileAppend(H, "process.csv", "%i,%i,%i,%s,%s,%s,%s,%s,%s,%i,0x%llx,0x%llx,0x%x,0x%llx,0x%llx,%s,%s,%s,%s\n",
|
|
pProcess->dwPID,
|
|
pProcess->dwPPID,
|
|
pProcess->dwState,
|
|
FcCsv_String(hCSV, pProcess->szName),
|
|
FcCsv_String(hCSV, pProcess->pObPersistent->uszNameLong),
|
|
FcCsv_String(hCSV, (LPSTR)VMM_TOKEN_INTEGRITY_LEVEL_STR[pProcess->win.Token ? pProcess->win.Token->IntegrityLevel : 0]),
|
|
FcCsv_String(hCSV, szUserName),
|
|
FcCsv_FileTime(hCSV, VmmProcess_GetCreateTimeOpt(H, pProcess)),
|
|
FcCsv_FileTime(hCSV, VmmProcess_GetExitTimeOpt(H, pProcess)),
|
|
pProcess->win.fWow64 ? 1 : 0,
|
|
pProcess->win.EPROCESS.va,
|
|
pProcess->win.vaPEB,
|
|
pProcess->win.vaPEB32,
|
|
pProcess->paDTB,
|
|
pProcess->paDTB_UserOpt,
|
|
FcCsv_String(hCSV, pu->uszImagePathName),
|
|
FcCsv_String(hCSV, pProcess->pObPersistent->uszPathKernel),
|
|
FcCsv_String(hCSV, pu->uszCommandLine),
|
|
FcCsv_String(hCSV, szFlag)
|
|
);
|
|
}
|
|
|
|
/*
|
|
* Plugin initialization / registration function called by the plugin manager.
|
|
* -- H
|
|
* -- pRI
|
|
*/
|
|
VOID M_FcProc_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; }
|
|
strcpy_s(pRI->reg_info.uszPathName, 128, "\\forensic\\hidden\\proc"); // module name
|
|
pRI->reg_info.fRootModule = TRUE; // module shows in root directory
|
|
pRI->reg_info.fRootModuleHidden = TRUE; // module hidden by default
|
|
pRI->reg_fnfc.pfnInitialize = MFcProc_FcInitialize; // Forensic initialize function supported
|
|
pRI->reg_fnfc.pfnTimeline = MFcProc_FcTimeline; // Forensic timelining supported
|
|
pRI->reg_fnfc.pfnLogCSV = MFcProc_FcLogCSV; // CSV log function supported
|
|
pRI->reg_fnfc.pfnLogJSON = MFcProc_FcLogJSON; // JSON log function supported
|
|
memcpy(pRI->reg_info.sTimelineNameShort, "PROC", 5);
|
|
strncpy_s(pRI->reg_info.uszTimelineFile, 32, "timeline_process", _TRUNCATE);
|
|
pRI->pfnPluginManager_Register(H, pRI);
|
|
}
|