mirror of
https://github.com/ufrisk/MemProcFS.git
synced 2026-06-01 16:19:42 +08:00
572 lines
21 KiB
C
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);
|
|
}
|