Files
MemProcFS/vmm/modules/m_fc_web.c
2026-01-20 20:33:17 +01:00

572 lines
21 KiB
C

// m_fc_web.c : web browsing forensic module.
//
// Module supports:
// - Brave
// - Google Chrome
// - Microsoft Edge (Chromium based)
// - Mozilla Firefox
//
// (c) Ulf Frisk, 2022-2026
// Author: Ulf Frisk, pcileech@frizk.net
//
#include "modules.h"
#include "../ext/sqlite3.h"
#include "../vmmwinobj.h"
LPSTR szMWEB_README =
"Web plugin for MemProcFS: \n" \
"========================= \n" \
" \n" \
"The web plugin tries to recover select web activity from supported browsers.\n" \
"Supported browsers: Chrome, Edge, Firefox. \n" \
" \n" \
"The plugin does not recover all activities. Activities such as Cookies, \n" \
"Favicons and Form fill data is currently not recovered. \n" \
" \n" \
"The plugin tries to recover data on a best-effort and recovery may fail. \n" \
"--- \n" \
"Documentation: https://github.com/ufrisk/MemProcFS/wiki/FS_Web \n";
#define MWEB_LINELENGTH_BASE 59ULL
#define MWEB_LINEHEADER " # PID Time Browser Type Url :: Info"
typedef enum tdVMM_MAP_WEB_TPACTION {
VMM_MAP_WEB_TPACTION_NA = 0,
VMM_MAP_WEB_TPACTION_BROWSE = 1,
VMM_MAP_WEB_TPACTION_DOWNLOAD = 2,
VMM_MAP_WEB_TPACTION_LOGIN_PWD = 3
} VMM_MAP_WEB_TPACTION;
// action string name - max 8 chars
static LPCSTR VMM_MAP_WEB_TPACTION_STR[] = {
"N/A",
"VISIT",
"DOWNLOAD",
"LOGINPWD"
};
typedef enum tdVMM_MAP_WEB_TPBROWSER {
VMM_MAP_WEB_TPBROWSER_NA = 0,
VMM_MAP_WEB_TPBROWSER_FIREFOX = 1,
VMM_MAP_WEB_TPBROWSER_CHROME = 2,
VMM_MAP_WEB_TPBROWSER_MSEDGE = 3,
VMM_MAP_WEB_TPBROWSER_BRAVE = 4,
} VMM_MAP_WEB_TPBROWSER;
// browser string name - max 7 chars
static LPCSTR VMM_MAP_WEB_TPBROWSER_STR[] = {
"N/A",
"FIREFOX",
"CHROME",
"EDGE",
"BRAVE"
};
typedef struct tdVMM_MAP_WEBENTRY {
VMM_MAP_WEB_TPACTION tpAction;
VMM_MAP_WEB_TPBROWSER tpBrowser;
DWORD dwPID;
DWORD _Reserved;
QWORD ftCreate;
QWORD ftAccess;
DWORD cbUrl;
DWORD cbInfo;
LPSTR uszUrl;
LPSTR uszInfo;
} VMM_MAP_WEBENTRY, *PVMM_MAP_WEBENTRY;
typedef struct tdVMMOB_MAP_WEB {
OB ObHdr;
PDWORD pdwLineOffset; // array of line ends in bytes
PBYTE pbMultiText;
DWORD cbMultiText;
DWORD cMap; // # map entries.
VMM_MAP_WEBENTRY pMap[0]; // map entries.
} VMMOB_MAP_WEB, *PVMMOB_MAP_WEB;
// ----------------------------------------------------------------------------
// WEB INITIALIZATION below:
// ----------------------------------------------------------------------------
typedef struct tdMWEB_CONTEXT {
VMMDLL_MODULE_ID MID;
POB_MAP pm; // result map
POB_STRMAP psm; // result string map
POB_MAP pmFileObjects; // k = _FILE_OBJECT va, v = POB_VMMWINOBJ_FILE
POB_SET psDuplicate;
// per-file analyzed below:
PVMM_PROCESS pProcessOpt;
POB_VMMWINOBJ_FILE pFile;
QWORD qwHashFile;
sqlite3 *hDB;
VMM_MAP_WEB_TPBROWSER tpBrowser;
} MWEB_CONTEXT, *PMWEB_CONTEXT;
#define FCWEB_TIME2FT_FIREFOX(fft) (fft ? ((fft * 10) + 116444736000000000) : 0)
#define FCWEB_TIME2FT_CHROMIUM(fft) (fft * 10)
/*
* Add browser entries from sqlite database to the initialization context.
* -- ctx
* -- szSql = sql clause to run against the data, should return as following:
* 1: create time (int64)
* 2: access time (int64)
* 3: url (text)
* 4: info (text)
*/
VOID FcWeb_AddEntryFromDB(_In_ VMM_HANDLE H, _In_ PMWEB_CONTEXT ctx, _In_ VMM_MAP_WEB_TPACTION tpAction, _In_ LPSTR szSql)
{
QWORD qwHash;
PVMM_MAP_WEBENTRY pe;
LPSTR uszUrl, uszInfo;
QWORD ftCreate, ftAccess;
sqlite3_stmt *hStmt = NULL;
if(SQLITE_OK == sqlite3_prepare_v2(ctx->hDB, szSql, -1, &hStmt, 0)) {
while(SQLITE_ROW == sqlite3_step(hStmt)) {
// 1: fetch from sqlite
ftCreate = sqlite3_column_int64(hStmt, 0);
ftAccess = sqlite3_column_int64(hStmt, 1);
uszUrl = (LPSTR)sqlite3_column_text(hStmt, 2);
uszInfo = (LPSTR)sqlite3_column_text(hStmt, 3);
if(!uszUrl && !uszInfo) { continue; }
if(ctx->tpBrowser == VMM_MAP_WEB_TPBROWSER_FIREFOX) {
ftCreate = FCWEB_TIME2FT_FIREFOX(ftCreate);
ftAccess = FCWEB_TIME2FT_FIREFOX(ftAccess);
} else {
ftCreate = FCWEB_TIME2FT_CHROMIUM(ftCreate);
ftAccess = FCWEB_TIME2FT_CHROMIUM(ftAccess);
}
// 2: add entry object
if((pe = LocalAlloc(LMEM_ZEROINIT, sizeof(VMM_MAP_WEBENTRY)))) {
qwHash = ctx->qwHashFile + ftCreate + ftAccess + CharUtil_Hash64U(uszUrl, FALSE) + CharUtil_Hash64U(uszInfo, FALSE);
pe->tpAction = tpAction;
pe->tpBrowser = ctx->tpBrowser;
pe->dwPID = ctx->pProcessOpt ? ctx->pProcessOpt->dwPID : 0;
pe->ftCreate = ftCreate;
pe->ftAccess = ftAccess;
if(ObMap_Push(ctx->pm, qwHash, pe)) {
ObStrMap_PushPtrUU(ctx->psm, uszUrl, &pe->uszUrl, &pe->cbUrl);
ObStrMap_PushPtrUU(ctx->psm, uszInfo, &pe->uszInfo, &pe->cbInfo);
} else {
// fail insert into map == object alread exists -> free it!
LocalFree(pe);
}
}
}
sqlite3_finalize(hStmt); hStmt = NULL;
}
}
/*
* Return a chromium compatible browser from the process name.
* If no process is provided default to chrome.
* -- H
* -- ctx
* -- return = chromium compatible browser.
*/
VMM_MAP_WEB_TPBROWSER FcWeb_GetChromiumBrowser(_In_ VMM_HANDLE H, _In_ PMWEB_CONTEXT ctx)
{
if(!ctx->pProcessOpt) {
return VMM_MAP_WEB_TPBROWSER_CHROME;
}
if(strstr(ctx->pProcessOpt->szName, "edge")) {
return VMM_MAP_WEB_TPBROWSER_MSEDGE;
}
if(strstr(ctx->pProcessOpt->szName, "brave")) {
return VMM_MAP_WEB_TPBROWSER_BRAVE;
}
return VMM_MAP_WEB_TPBROWSER_CHROME;
}
// ----------------------------------------------------------------------------
// WEB BROWSER SPECIFIC PARSING below:
// ----------------------------------------------------------------------------
typedef VOID(*FCWEB_SQLITE_PFN_CB)(_In_ VMM_HANDLE H, _In_ PMWEB_CONTEXT ctx);
VOID FcWeb_FF_Places_CB(_In_ VMM_HANDLE H, _In_ PMWEB_CONTEXT ctx)
{
ctx->tpBrowser = VMM_MAP_WEB_TPBROWSER_FIREFOX;
FcWeb_AddEntryFromDB(
H,
ctx,
VMM_MAP_WEB_TPACTION_BROWSE,
"SELECT 0, last_visit_date, url, title from moz_places WHERE last_visit_date > 0"
);
FcWeb_AddEntryFromDB(
H,
ctx,
VMM_MAP_WEB_TPACTION_DOWNLOAD,
"SELECT dateAdded, lastModified, content, '' from moz_annos WHERE anno_attribute_id = 4 AND dateAdded > 0"
);
}
VOID FcWeb_Chromium_History_CB(_In_ VMM_HANDLE H, _In_ PMWEB_CONTEXT ctx)
{
ctx->tpBrowser = FcWeb_GetChromiumBrowser(H, ctx);
FcWeb_AddEntryFromDB(
H,
ctx,
VMM_MAP_WEB_TPACTION_BROWSE,
"SELECT v.visit_time, 0, u.url, u.title FROM visits v, urls u WHERE v.url = u.id AND v.visit_time > 0"
);
FcWeb_AddEntryFromDB(
H,
ctx,
VMM_MAP_WEB_TPACTION_DOWNLOAD,
"SELECT last_access_time, 0, tab_url, current_path FROM downloads WHERE last_access_time > 0"
);
}
VOID FcWeb_Chromium_LoginData_CB(_In_ VMM_HANDLE H, _In_ PMWEB_CONTEXT ctx)
{
ctx->tpBrowser = FcWeb_GetChromiumBrowser(H, ctx);
FcWeb_AddEntryFromDB(
H,
ctx,
VMM_MAP_WEB_TPACTION_LOGIN_PWD,
"SELECT date_password_modified, date_last_used, origin_url, username_value FROM logins"
);
}
// ----------------------------------------------------------------------------
// WEB INITIALIZATION (contd) below:
// ----------------------------------------------------------------------------
/*
* Dispatch a file for analysis to the appropriate browser tanalysis functions.
* -- H
* -- ctx
* -- pfnCB = browser-dependent analysis callback function.
*/
VOID FcWeb_LoadSqliteDispatch(_In_ VMM_HANDLE H, _In_ PMWEB_CONTEXT ctx, _In_ FCWEB_SQLITE_PFN_CB pfnCB)
{
int rc;
DWORD cbDB;
PBYTE pbDB = NULL;
BYTE pbTest[0x10] = { 0 };
CHAR szFile[MAX_PATH], szURI[MAX_PATH];
FILE *phFile = NULL;
// 1: sanity checks
if((ctx->pFile->cb < 0x1000) || (ctx->pFile->cb > 0x04000000)) { goto fail; }
VmmWinObjFile_Read(H, ctx->pFile, 0, pbTest, sizeof(pbTest) - 1, 0, VMMWINOBJ_FILE_TP_DEFAULT);
if(pbTest != (PBYTE)strstr((LPCSTR)pbTest, "SQLite ")) { goto fail; }
// 2: read file handle into memory
cbDB = (DWORD)ctx->pFile->cb;
if(!(pbDB = LocalAlloc(0, cbDB))) { goto fail; }
cbDB = VmmWinObjFile_Read(H, ctx->pFile, 0, pbDB, cbDB, 0, VMMWINOBJ_FILE_TP_DEFAULT);
// 3: create and write to temp file
if(tmpnam_s(szFile, MAX_PATH)) { goto fail; }
strncat_s(szFile, _countof(szFile), ".vmmsqlite3.tmp", _TRUNCATE);
if(fopen_su(&phFile, szFile, "wb")) {
VmmLog(H, ctx->MID, LOGLEVEL_DEBUG, "fail open temp file: %s", szFile);
goto fail;
}
fwrite(pbDB, 1, cbDB, phFile);
fclose(phFile);
// 4: open sqlite and dispatch
sprintf_s(szURI, _countof(szURI), "file:///%s", szFile);
CharUtil_ReplaceAllA(szURI, '\\', '/');
rc = sqlite3_open_v2(szURI, &ctx->hDB, SQLITE_OPEN_URI | SQLITE_OPEN_READONLY | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_EXCLUSIVE, NULL);
if(rc == SQLITE_OK) {
sqlite3_db_config(ctx->hDB, SQLITE_DBCONFIG_ENABLE_TRIGGER, 0, 0);
sqlite3_db_config(ctx->hDB, SQLITE_DBCONFIG_ENABLE_VIEW, 0, 0);
pfnCB(H, ctx);
} else {
VmmLog(H, ctx->MID, LOGLEVEL_DEBUG, "fail sqlite3 open: rc=%i db='%s'", rc, ctx->pFile->uszName);
}
fail:
sqlite3_close(ctx->hDB); ctx->hDB = NULL;
if(phFile) { remove(szFile); }
LocalFree(pbDB);
}
int MWeb_Initialize_CmpSort(_In_ POB_MAP_ENTRY p1, _In_ POB_MAP_ENTRY p2)
{
PVMM_MAP_WEBENTRY e1 = p1->v;
PVMM_MAP_WEBENTRY e2 = p2->v;
QWORD ft1 = max(e1->ftAccess, e1->ftCreate);
QWORD ft2 = max(e2->ftAccess, e2->ftCreate);
if(ft1 != ft2) {
return (ft1 < ft2) ? 1 : -1;
}
return e1->dwPID - e2->dwPID;
}
/*
* Initialize a new web map in a single-threaded context.
* -- H
* -- ctxP
*/
PVMMOB_MAP_WEB MWeb_Initialize_DoWork(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP)
{
DWORD i, cbDataStr = 0, cMap, cbData, cbOffset = 0;
FCWEB_SQLITE_PFN_CB pfnCB;
PVMM_MAP_WEBENTRY peWeb;
PVMMOB_MAP_WEB pObMap = NULL;
MWEB_CONTEXT ctxInit = { 0 };
PVMM_PROCESS pObProcessOpt = NULL;
POB_VMMWINOBJ_FILE pObFile = NULL;
// 1: Initialize and get all file handles:
// getting all file handles have the advantage of potentially hitting a
// file handle belonging to a terminated process which is not yet closed
ctxInit.MID = ctxP->MID;
if(!(ctxInit.psDuplicate = ObSet_New(H))) { goto fail; }
if(!(ctxInit.pm = ObMap_New(H, OB_MAP_FLAGS_OBJECT_LOCALFREE))) { goto fail; }
if(!(ctxInit.psm = ObStrMap_New(H, OB_STRMAP_FLAGS_CASE_SENSITIVE))) { goto fail; }
if(!VmmWinObjFile_GetAll(H, &ctxInit.pmFileObjects)) { goto fail; }
if(H->fAbort) { goto fail; }
// 2: Analyze interesting file handles:
while((pObFile = ObMap_GetNext(ctxInit.pmFileObjects, pObFile))) {
// Duplicate check and init analyze context:
if(!pObFile->vaSectionObjectPointers || !pObFile->pData || ObSet_Exists(ctxInit.psDuplicate, pObFile->vaSectionObjectPointers)) {
continue;
}
pfnCB = NULL;
// Firefox:
if(CharUtil_StrEquals(pObFile->uszName, "places.sqlite", FALSE)) {
pfnCB = FcWeb_FF_Places_CB;
}
// Chrome/Edge:
if(CharUtil_StrEquals(pObFile->uszName, "History", FALSE)) {
pfnCB = FcWeb_Chromium_History_CB;
}
if(CharUtil_StrEquals(pObFile->uszName, "Login Data", FALSE)) {
pfnCB = FcWeb_Chromium_LoginData_CB;
}
// Dispatch to analysis:
if(pfnCB) {
ObSet_Push(ctxInit.psDuplicate, pObFile->vaSectionObjectPointers);
pObProcessOpt = VmmWinObj_GetProcessAssociated(H, pObFile->va);
ctxInit.pProcessOpt = pObProcessOpt;
ctxInit.pFile = pObFile;
ctxInit.qwHashFile = CharUtil_Hash64U(pObFile->uszPath, TRUE);
FcWeb_LoadSqliteDispatch(H, &ctxInit, pfnCB);
VmmLog(H, ctxP->MID, LOGLEVEL_DEBUG, "analyze: pid=%i db=%s", (pObProcessOpt ? pObProcessOpt->dwPID : 0), pObFile->uszName);
Ob_DECREF_NULL(&pObProcessOpt);
}
}
// 3: CREATE MAP / FINALIZE:
if(!ObStrMap_FinalizeBufferU(ctxInit.psm, 0, NULL, &cbDataStr)) { goto fail; }
cMap = ObMap_Size(ctxInit.pm);
cbData = sizeof(VMMOB_MAP_WEB) + cMap * (sizeof(VMM_MAP_WEBENTRY) + sizeof(DWORD)) + cbDataStr;
if(!(pObMap = Ob_AllocEx(H, OB_TAG_MAP_WEB, LMEM_ZEROINIT, cbData, NULL, NULL))) { goto fail; }
pObMap->cMap = cMap;
pObMap->pdwLineOffset = (PDWORD)(pObMap->pMap + pObMap->cMap);
pObMap->pbMultiText = (LPSTR)((QWORD)pObMap + cbData - cbDataStr);
ObStrMap_FinalizeBufferU(ctxInit.psm, cbDataStr, pObMap->pbMultiText, &pObMap->cbMultiText);
ObMap_SortEntryIndex(ctxInit.pm, MWeb_Initialize_CmpSort);
for(i = 0; i < pObMap->cMap; i++) { // COPY WEBENTRY TO MAP
if(!(peWeb = ObMap_GetByIndex(ctxInit.pm, i))) { goto fail; }
memcpy(pObMap->pMap + i, peWeb, sizeof(VMM_MAP_WEBENTRY));
cbOffset += MWEB_LINELENGTH_BASE + peWeb->cbUrl + peWeb->cbInfo;
pObMap->pdwLineOffset[i] = cbOffset;
}
Ob_INCREF(pObMap);
fail:
Ob_DECREF(ctxInit.pm);
Ob_DECREF(ctxInit.psm);
Ob_DECREF(ctxInit.psDuplicate);
Ob_DECREF(ctxInit.pmFileObjects);
return Ob_DECREF(pObMap);
}
/*
* Fetch the 'WebMap' object.
* CALLER DECREF: return
* -- H
* -- ctxP
* -- return
*/
PVMMOB_MAP_WEB MWeb_GetWebMap(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP)
{
PVMMOB_MAP_WEB pObMap = NULL;
pObMap = ObContainer_GetOb((POB_CONTAINER)ctxP->ctxM);
if(!pObMap) {
AcquireSRWLockExclusive(&H->vmm.LockSRW.ModuleMiscWeb);
pObMap = ObContainer_GetOb((POB_CONTAINER)ctxP->ctxM);
if(!pObMap) {
pObMap = MWeb_Initialize_DoWork(H, ctxP);
ObContainer_SetOb((POB_CONTAINER)ctxP->ctxM, pObMap);
}
ReleaseSRWLockExclusive(&H->vmm.LockSRW.ModuleMiscWeb);
}
return pObMap;
}
// ----------------------------------------------------------------------------
// WEB MODULE ACCESS FUNCTIONALITY BELOW:
// ----------------------------------------------------------------------------
/*
* Generate a single line in the web.txt file.
*/
VOID MWeb_ReadLine_CB(_In_ VMM_HANDLE H, _In_ PVMMOB_MAP_WEB pObWebMap, _In_ DWORD cbLineLength, _In_ DWORD ie, _In_ PVOID pv, _Out_writes_(cbLineLength + 1) LPSTR szu8)
{
PVMM_MAP_WEBENTRY pe = pObWebMap->pMap + ie;
CHAR szTime[24];
QWORD ftMax = max(pe->ftCreate, pe->ftAccess);
Util_FileTime2String(ftMax, szTime);
Util_usnprintf_ln(szu8, cbLineLength,
"%6x%7i %s %-7s %-8s %s :: %s",
ie,
pe->dwPID,
szTime,
VMM_MAP_WEB_TPBROWSER_STR[pe->tpBrowser],
VMM_MAP_WEB_TPACTION_STR[pe->tpAction],
pe->uszUrl,
pe->uszInfo
);
}
NTSTATUS MWeb_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 = VMMDLL_STATUS_FILE_INVALID;
PVMMOB_MAP_WEB pObWebMap = NULL;
if(!(pObWebMap = MWeb_GetWebMap(H, ctxP))) { goto finish; }
if(!_stricmp("readme.txt", ctxP->uszPath)) {
return VMMDLL_UtilVfsReadFile_FromPBYTE(szMWEB_README, strlen(szMWEB_README), pb, cb, pcbRead, cbOffset);
}
if(!_stricmp(ctxP->uszPath, "web.txt")) {
nt = Util_VfsLineVariable_Read(
H, (UTIL_VFSLINEFIXED_PFN_CB)MWeb_ReadLine_CB, pObWebMap, MWEB_LINEHEADER,
pObWebMap->pMap, pObWebMap->cMap, sizeof(VMM_MAP_WEBENTRY), pObWebMap->pdwLineOffset,
pb, cb, pcbRead, cbOffset
);
goto finish;
}
finish:
Ob_DECREF(pObWebMap);
return nt;
}
BOOL MWeb_List(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, _Inout_ PHANDLE pFileList)
{
PVMMOB_MAP_WEB pObWebMap = NULL;
if(!(pObWebMap = MWeb_GetWebMap(H, ctxP))) { goto finish; }
if(!ctxP->uszPath[0]) {
VMMDLL_VfsList_AddFile(pFileList, "readme.txt", strlen(szMWEB_README), NULL);
VMMDLL_VfsList_AddFile(pFileList, "web.txt", UTIL_VFSLINEVARIABLE_BYTECOUNT(H, pObWebMap->cMap, pObWebMap->pdwLineOffset, MWEB_LINEHEADER), NULL);
goto finish;
}
finish:
Ob_DECREF(pObWebMap);
return TRUE;
}
VOID MWeb_Notify(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, _In_ DWORD fEvent, _In_opt_ PVOID pvEvent, _In_opt_ DWORD cbEvent)
{
if(fEvent == VMMDLL_PLUGIN_NOTIFY_FORENSIC_INIT_COMPLETE) {
PluginManager_SetVisibility(H, TRUE, "\\forensic\\web", TRUE);
}
}
VOID MWeb_Close(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP)
{
Ob_DECREF(ctxP->ctxM);
}
VOID MWeb_FcLogJSON(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, _In_ VOID(*pfnLogJSON)(_In_ VMM_HANDLE H, _In_ PVMMDLL_FORENSIC_JSONDATA pData))
{
DWORD ie;
PVMM_MAP_WEBENTRY pe = NULL;
PVMMOB_MAP_WEB pObWebMap = NULL;
PVMMDLL_FORENSIC_JSONDATA pd = NULL;
CHAR usz[2048], szTime[24];
if(ctxP->pProcess) { return; }
if(!(pObWebMap = MWeb_GetWebMap(H, ctxP))) { goto fail; }
if(!(pd = LocalAlloc(LMEM_ZEROINIT, sizeof(VMMDLL_FORENSIC_JSONDATA)))) { goto fail; }
pd->dwVersion = VMMDLL_FORENSIC_JSONDATA_VERSION;
pd->szjType = "web";
for(ie = 0; ie < pObWebMap->cMap; ie++) {
pe = pObWebMap->pMap + ie;
Util_FileTime2String(max(pe->ftAccess, pe->ftCreate), szTime);
_snprintf_s(usz, _countof(usz), _TRUNCATE, "type:[%s] time:[%s] info:[%s]",
VMM_MAP_WEB_TPACTION_STR[pe->tpAction],
szTime,
pe->uszInfo
);
pd->i = ie;
pd->dwPID = pe->dwPID;
pd->usz[0] = pe->uszUrl;
pd->usz[1] = usz;
pfnLogJSON(H, pd);
}
fail:
Ob_DECREF(pObWebMap);
LocalFree(pd);
}
VOID MWeb_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)
) {
PVMMOB_MAP_WEB pWebMap = (PVMMOB_MAP_WEB)ctxfc;
PVMM_MAP_WEBENTRY pe;
DWORD i;
int cch;
CHAR usz[2048];
if(!pWebMap) { return; }
for(i = 0; i < pWebMap->cMap; i++) {
pe = pWebMap->pMap + i;
cch = _snprintf_s(usz, _countof(usz), _TRUNCATE, "browser:[%s] type:[%s] url:[%s] info:[%s]",
VMM_MAP_WEB_TPBROWSER_STR[pe->tpBrowser],
VMM_MAP_WEB_TPACTION_STR[pe->tpAction],
pe->uszUrl,
pe->uszInfo);
if(cch > 10) {
if(pe->ftCreate) {
pfnAddEntry(H, hTimeline, pe->ftCreate, FC_TIMELINE_ACTION_CREATE, pe->dwPID, 0, 0, usz);
}
if(pe->ftAccess && (pe->ftAccess != pe->ftCreate)) {
pfnAddEntry(H, hTimeline, pe->ftAccess, FC_TIMELINE_ACTION_READ, pe->dwPID, 0, 0, usz);
}
}
}
}
VOID MWeb_FcFinalize(_In_ VMM_HANDLE H, _In_opt_ PVOID ctxfc)
{
Ob_DECREF(ctxfc);
}
PVOID MWeb_FcInitialize(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP)
{
return MWeb_GetWebMap(H, ctxP);
}
VOID M_FcWeb_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 = (PVMMDLL_PLUGIN_INTERNAL_CONTEXT)ObContainer_New())) { return; }
strcpy_s(pRI->reg_info.uszPathName, 128, "\\forensic\\web"); // module name
pRI->reg_info.fRootModule = TRUE; // module shows in root directory
pRI->reg_info.fRootModuleHidden = TRUE; // module hidden by default
// functions supported:
pRI->reg_fn.pfnList = MWeb_List;
pRI->reg_fn.pfnRead = MWeb_Read;
pRI->reg_fn.pfnNotify = MWeb_Notify;
pRI->reg_fn.pfnClose = MWeb_Close;
pRI->reg_fnfc.pfnLogJSON = MWeb_FcLogJSON; // JSON log function supported
// timelining support:
pRI->reg_fnfc.pfnInitialize = MWeb_FcInitialize; // Forensic initialize function supported
pRI->reg_fnfc.pfnTimeline = MWeb_FcTimeline; // Forensic timelining supported
pRI->reg_fnfc.pfnFinalize = MWeb_FcFinalize; // Forensic finalize function supported
memcpy(pRI->reg_info.sTimelineNameShort, "WEB", 4);
strncpy_s(pRI->reg_info.uszTimelineFile, 32, "timeline_web", _TRUNCATE);
pRI->pfnPluginManager_Register(H, pRI);
}