mirror of
https://github.com/ufrisk/MemProcFS.git
synced 2026-05-23 01:41:35 +08:00
1211 lines
51 KiB
C
1211 lines
51 KiB
C
// pluginmanager.c : implementation of the plugin manager for MemProcFS plugins.
|
|
//
|
|
// (c) Ulf Frisk, 2018-2026
|
|
// Author: Ulf Frisk, pcileech@frizk.net
|
|
//
|
|
#include "pluginmanager.h"
|
|
#include "statistics.h"
|
|
#include "charutil.h"
|
|
#include "util.h"
|
|
#include "vmm.h"
|
|
#include "vmmex.h"
|
|
#include "vmmdll.h"
|
|
#include "vmmlog.h"
|
|
#include "fc.h"
|
|
#include "modules/modules_init.h"
|
|
|
|
#ifdef VMM_PROFILE_FULL
|
|
#include "ex/vmmex_modules_init.h"
|
|
#endif /* VMM_PROFILE_FULL */
|
|
|
|
//
|
|
// This file contains functionality related to keeping track of plugins, both
|
|
// internal built-in ones and loadable plugins in the form of compliant DLLs.
|
|
//
|
|
// The functionality and data structures are at this moment single-threaded in
|
|
// this implementation and should be protected by a lock (H->vmm.LockMaster).
|
|
//
|
|
// Core module calls are: List, Read, Write.
|
|
// Other module calls are: Notify and Close.
|
|
//
|
|
// In general, a pointer to a stored-away module specific handle is given in
|
|
// every call together with a plugin/process specific pointer to a handle.
|
|
// The plugin/process specific handle is stored per module, per PID.
|
|
//
|
|
// A pProcess struct (if applicable) and a PID is also given in each call
|
|
// together with the module name and path.
|
|
//
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MODULES CORE FUNCTIONALITY - DEFINES BELOW:
|
|
// ----------------------------------------------------------------------------
|
|
|
|
typedef struct tdPLUGIN_ENTRY {
|
|
struct tdPLUGIN_ENTRY *FLinkAll;
|
|
struct tdPLUGIN_ENTRY *FLinkNotify;
|
|
struct tdPLUGIN_ENTRY *FLinkForensic;
|
|
DWORD MID; // module id (used by logging)
|
|
HMODULE hDLL;
|
|
CHAR uszName[32];
|
|
DWORD dwNameHash;
|
|
BOOL fRootModule;
|
|
BOOL fProcessModule;
|
|
PVMMDLL_PLUGIN_INTERNAL_CONTEXT ctxM;
|
|
BOOL(*pfnVisibleModule)(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP);
|
|
BOOL(*pfnList)(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, _Inout_ PHANDLE pFileList);
|
|
NTSTATUS(*pfnRead)(_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(*pfnWrite)(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, _In_reads_(cb) PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbWrite, _In_ QWORD cbOffset);
|
|
VOID(*pfnNotify)(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, _In_ DWORD fEvent, _In_opt_ PVOID pvEvent, _In_opt_ DWORD cbEvent);
|
|
VOID(*pfnClose)(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP);
|
|
struct {
|
|
PVOID ctxfc;
|
|
PVOID(*pfnInitialize)(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP);
|
|
VOID(*pfnFinalize)(_In_ VMM_HANDLE H, _In_opt_ PVOID ctxfc);
|
|
VOID(*pfnTimeline)(
|
|
_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));
|
|
VOID(*pfnLogCSV)(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, VMMDLL_CSV_HANDLE hCSV);
|
|
VOID(*pfnLogJSON)(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, _In_ VOID(*pfnLogJSON)(_In_ VMM_HANDLE H, _In_ PVMMDLL_FORENSIC_JSONDATA pData));
|
|
VOID(*pfnFindEvil)(_In_ VMM_HANDLE H, _In_ VMMDLL_MODULE_ID MID, _In_opt_ PVOID ctxfc);
|
|
VOID(*pfnIngestObject)(_In_ VMM_HANDLE H, _In_opt_ PVOID ctxfc, _In_ PVMMDLL_FORENSIC_INGEST_OBJECT pIngestObject);
|
|
VOID(*pfnIngestPhysmem)(_In_ VMM_HANDLE H, _In_opt_ PVOID ctxfc, _In_ PVMMDLL_FORENSIC_INGEST_PHYSMEM pIngestPhysmem);
|
|
VOID(*pfnIngestVirtmem)(_In_ VMM_HANDLE H, _In_opt_ PVOID ctxfc, _In_ PVMMDLL_FORENSIC_INGEST_VIRTMEM pIngestVirtmem);
|
|
VOID(*pfnIngestFinalize)(_In_ VMM_HANDLE H, _In_opt_ PVOID ctxfc);
|
|
struct {
|
|
PVMMDLL_FORENSIC_INGEST_PHYSMEM p;
|
|
} IngestPhysmem;
|
|
struct {
|
|
CHAR sNameShort[6];
|
|
CHAR _Reserved[2];
|
|
CHAR szFileUTF8[32];
|
|
} Timeline;
|
|
} fc;
|
|
} PLUGIN_ENTRY, *PPLUGIN_ENTRY;
|
|
|
|
#define PLUGIN_TREE_MAX_CHILDITEMS 32
|
|
|
|
typedef struct tdPLUGIN_TREE {
|
|
CHAR uszName[32];
|
|
DWORD dwHashName;
|
|
DWORD cChild;
|
|
BOOL fVisible;
|
|
struct tdPLUGIN_TREE *pParent;
|
|
struct tdPLUGIN_TREE *Child[PLUGIN_TREE_MAX_CHILDITEMS];
|
|
PPLUGIN_ENTRY pPlugin;
|
|
} PLUGIN_TREE, *PPLUGIN_TREE;
|
|
|
|
VOID PluginManager_Initialize_Python(_In_ VMM_HANDLE H);
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MODULES GENERAL FUNCTIONALITY - IMPLEMENTATION BELOW:
|
|
// ----------------------------------------------------------------------------
|
|
|
|
VOID PluginManager_SetTreeVisibility(_In_opt_ PPLUGIN_TREE pTree, _In_ BOOL fVisible)
|
|
{
|
|
DWORD i;
|
|
if(!pTree || (pTree->fVisible == fVisible)) { return; }
|
|
for(i = 0; !fVisible && (i < pTree->cChild); i++) {
|
|
fVisible = pTree->Child[i]->fVisible;
|
|
}
|
|
if(pTree->fVisible != fVisible) {
|
|
pTree->fVisible = fVisible;
|
|
if(fVisible || (pTree->pParent && !pTree->pParent->pPlugin)) {
|
|
PluginManager_SetTreeVisibility(pTree->pParent, fVisible);
|
|
}
|
|
}
|
|
}
|
|
|
|
PPLUGIN_TREE PluginManager_Register_GetCreateTree(_In_ PPLUGIN_TREE pTree, _In_ LPCSTR uszPathName, _In_ BOOL fVisible)
|
|
{
|
|
DWORD i, dwHash;
|
|
CHAR uszEntry[32];
|
|
PPLUGIN_TREE pChild;
|
|
// 1: no more levels to create - return
|
|
if(!uszPathName[0]) { return pTree; }
|
|
// 2: check existing tree child entries
|
|
uszPathName = CharUtil_PathSplitFirst(uszPathName, uszEntry, _countof(uszEntry));
|
|
dwHash = CharUtil_HashNameFsU(uszEntry, 0);
|
|
for(i = 0; i < pTree->cChild; i++) {
|
|
if(pTree->Child[i]->dwHashName == dwHash) {
|
|
return PluginManager_Register_GetCreateTree(pTree->Child[i], uszPathName, fVisible);
|
|
}
|
|
}
|
|
// 3: create new entry
|
|
if(pTree->cChild == PLUGIN_TREE_MAX_CHILDITEMS) { return NULL; }
|
|
if(!(pTree->Child[pTree->cChild] = pChild = LocalAlloc(LMEM_ZEROINIT, sizeof(PLUGIN_TREE)))) { return NULL; }
|
|
pTree->cChild++;
|
|
strncpy_s(pChild->uszName, _countof(pChild->uszName), uszEntry, _TRUNCATE);
|
|
pChild->dwHashName = dwHash;
|
|
pChild->pParent = pTree;
|
|
PluginManager_SetTreeVisibility(pChild, fVisible);
|
|
return PluginManager_Register_GetCreateTree(pChild, uszPathName, fVisible);
|
|
}
|
|
|
|
/*
|
|
* Retrieve the PLUGIN_TREE entry and the remaining path given a root tree and a root path.
|
|
* -- pTree
|
|
* -- uszPath
|
|
* -- pTree
|
|
* -- pwszSubPath
|
|
*/
|
|
VOID PluginManager_GetTree(_In_ PPLUGIN_TREE pTree, _In_ LPCSTR uszPath, _Out_ PPLUGIN_TREE *ppTree, _Out_ LPSTR *puszSubPath)
|
|
{
|
|
DWORD i, dwHash;
|
|
CHAR uszEntry[32];
|
|
LPCSTR uszSubPath;
|
|
if(!uszPath[0]) { goto finish; }
|
|
uszSubPath = CharUtil_PathSplitFirst(uszPath, uszEntry, _countof(uszEntry));
|
|
dwHash = CharUtil_HashNameFsU(uszEntry, 0);
|
|
for(i = 0; i < pTree->cChild; i++) {
|
|
if(pTree->Child[i]->dwHashName == dwHash) {
|
|
PluginManager_GetTree(pTree->Child[i], uszSubPath, ppTree, puszSubPath);
|
|
return;
|
|
}
|
|
}
|
|
finish:
|
|
*ppTree = pTree;
|
|
*puszSubPath = (LPSTR)uszPath;
|
|
}
|
|
|
|
VOID PluginManager_SetVisibility(_In_ VMM_HANDLE H, _In_ BOOL fRoot, _In_ LPCSTR uszPluginPath, _In_ BOOL fVisible)
|
|
{
|
|
LPSTR uszSubPath;
|
|
PPLUGIN_TREE pTree;
|
|
if(uszPluginPath[0] == '\\') { uszPluginPath++; }
|
|
PluginManager_GetTree((fRoot ? H->vmm.PluginManager.Root : H->vmm.PluginManager.Proc), uszPluginPath, &pTree, &uszSubPath);
|
|
PluginManager_SetTreeVisibility(pTree, fVisible);
|
|
}
|
|
|
|
BOOL PluginManager_ModuleExistsDll(_In_ VMM_HANDLE H, _In_opt_ HMODULE hDLL) {
|
|
PPLUGIN_ENTRY pModule = (PPLUGIN_ENTRY)H->vmm.PluginManager.FLinkAll;
|
|
while(pModule) {
|
|
if(hDLL && (hDLL == pModule->hDLL)) { return TRUE; }
|
|
pModule = pModule->FLinkAll;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL PluginManager_ModuleExists(_In_ PPLUGIN_TREE pTree, _In_ LPSTR uszPath) {
|
|
LPSTR uszSubPath;
|
|
PPLUGIN_TREE pTreePlugin;
|
|
PluginManager_GetTree(pTree, uszPath, &pTreePlugin, &uszSubPath);
|
|
return pTreePlugin->pPlugin && !uszSubPath[0];
|
|
}
|
|
|
|
VOID PluginManager_ContextInitialize(_Out_ PVMMDLL_PLUGIN_CONTEXT ctx, _In_ PPLUGIN_ENTRY pModule, _In_opt_ PVMM_PROCESS pProcess, _In_opt_ LPSTR uszPath)
|
|
{
|
|
ctx->magic = VMMDLL_PLUGIN_CONTEXT_MAGIC;
|
|
ctx->wVersion = VMMDLL_PLUGIN_CONTEXT_VERSION;
|
|
ctx->wSize = sizeof(VMMDLL_PLUGIN_CONTEXT);
|
|
ctx->dwPID = (pProcess ? pProcess->dwPID : (DWORD)-1);
|
|
ctx->pProcess = pModule->hDLL ? NULL : pProcess;
|
|
ctx->uszModule = pModule->uszName;
|
|
ctx->uszPath = uszPath;
|
|
ctx->ctxM = pModule->ctxM;
|
|
ctx->MID = pModule->MID;
|
|
}
|
|
|
|
VOID PluginManager_List(_In_ VMM_HANDLE H, _In_opt_ PVMM_PROCESS pProcess, _In_ LPCSTR uszPath, _Inout_ PHANDLE pFileList)
|
|
{
|
|
DWORD i;
|
|
BOOL fVisibleProgrammatic, result = TRUE;
|
|
QWORD tmStart = Statistics_CallStart(H);
|
|
VMMDLL_PLUGIN_CONTEXT ctxPlugin;
|
|
LPSTR uszSubPath;
|
|
PPLUGIN_TREE pTree;
|
|
PPLUGIN_ENTRY pPlugin;
|
|
pTree = pProcess ? H->vmm.PluginManager.Proc : H->vmm.PluginManager.Root;
|
|
if(!pTree) { return; }
|
|
PluginManager_GetTree((pProcess ? H->vmm.PluginManager.Proc : H->vmm.PluginManager.Root), uszPath, &pTree, &uszSubPath);
|
|
if(pTree->fVisible) {
|
|
if(pTree->cChild && !uszSubPath[0]) {
|
|
for(i = 0; i < pTree->cChild; i++) {
|
|
if(pTree->Child[i]->fVisible) {
|
|
fVisibleProgrammatic = pTree->Child[i]->cChild || !pTree->Child[i]->pPlugin || !pTree->Child[i]->pPlugin->pfnVisibleModule;
|
|
if(!fVisibleProgrammatic) {
|
|
PluginManager_ContextInitialize(&ctxPlugin, pTree->Child[i]->pPlugin, pProcess, uszSubPath);
|
|
fVisibleProgrammatic = pTree->Child[i]->pPlugin->pfnVisibleModule(H, &ctxPlugin);
|
|
}
|
|
if(fVisibleProgrammatic) {
|
|
VMMDLL_VfsList_AddDirectory(pFileList, pTree->Child[i]->uszName, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if((pPlugin = pTree->pPlugin) && pPlugin->pfnList) {
|
|
PluginManager_ContextInitialize(&ctxPlugin, pPlugin, pProcess, uszSubPath);
|
|
if(!pPlugin->pfnVisibleModule || pPlugin->pfnVisibleModule(H, &ctxPlugin)) {
|
|
pTree->pPlugin->pfnList(H, &ctxPlugin, pFileList);
|
|
}
|
|
}
|
|
}
|
|
Statistics_CallEnd(H, STATISTICS_ID_PluginManager_List, tmStart);
|
|
}
|
|
|
|
NTSTATUS PluginManager_Read(_In_ VMM_HANDLE H, _In_opt_ PVMM_PROCESS pProcess, _In_ LPCSTR uszPath, _Out_writes_to_(cb, *pcbRead) PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ QWORD cbOffset)
|
|
{
|
|
QWORD tmStart = Statistics_CallStart(H);
|
|
NTSTATUS nt;
|
|
VMMDLL_PLUGIN_CONTEXT ctxPlugin;
|
|
LPSTR uszSubPath;
|
|
PPLUGIN_TREE pTree;
|
|
PPLUGIN_ENTRY pPlugin;
|
|
pTree = pProcess ? H->vmm.PluginManager.Proc : H->vmm.PluginManager.Root;
|
|
if(!pTree) { return VMMDLL_STATUS_FILE_INVALID; }
|
|
PluginManager_GetTree((pProcess ? H->vmm.PluginManager.Proc : H->vmm.PluginManager.Root), uszPath, &pTree, &uszSubPath);
|
|
if(pTree->fVisible) {
|
|
if((pPlugin = pTree->pPlugin) && pPlugin->pfnRead) {
|
|
PluginManager_ContextInitialize(&ctxPlugin, pPlugin, pProcess, uszSubPath);
|
|
nt = pPlugin->pfnRead(H, &ctxPlugin, pb, cb, pcbRead, cbOffset);
|
|
Statistics_CallEnd(H, STATISTICS_ID_PluginManager_Read, tmStart);
|
|
return nt;
|
|
}
|
|
}
|
|
Statistics_CallEnd(H, STATISTICS_ID_PluginManager_Read, tmStart);
|
|
return VMMDLL_STATUS_FILE_INVALID;
|
|
}
|
|
|
|
NTSTATUS PluginManager_Write(_In_ VMM_HANDLE H, _In_opt_ PVMM_PROCESS pProcess, _In_ LPCSTR uszPath, _In_reads_(cb) PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbWrite, _In_ QWORD cbOffset)
|
|
{
|
|
QWORD tmStart = Statistics_CallStart(H);
|
|
NTSTATUS nt;
|
|
VMMDLL_PLUGIN_CONTEXT ctxPlugin;
|
|
LPSTR uszSubPath;
|
|
PPLUGIN_TREE pTree;
|
|
PPLUGIN_ENTRY pPlugin;
|
|
pTree = pProcess ? H->vmm.PluginManager.Proc : H->vmm.PluginManager.Root;
|
|
if(!pTree) { return VMMDLL_STATUS_FILE_INVALID; }
|
|
PluginManager_GetTree((pProcess ? H->vmm.PluginManager.Proc : H->vmm.PluginManager.Root), uszPath, &pTree, &uszSubPath);
|
|
if(pTree->fVisible) {
|
|
if((pPlugin = pTree->pPlugin) && pPlugin->pfnWrite) {
|
|
PluginManager_ContextInitialize(&ctxPlugin, pPlugin, pProcess, uszSubPath);
|
|
nt = pPlugin->pfnWrite(H, &ctxPlugin, pb, cb, pcbWrite, cbOffset);
|
|
Statistics_CallEnd(H, STATISTICS_ID_PluginManager_Write, tmStart);
|
|
return nt;
|
|
}
|
|
}
|
|
Statistics_CallEnd(H, STATISTICS_ID_PluginManager_Write, tmStart);
|
|
return VMMDLL_STATUS_FILE_INVALID;
|
|
}
|
|
|
|
BOOL PluginManager_Notify(_In_ VMM_HANDLE H, _In_ DWORD fEvent, _In_opt_ PVOID pvEvent, _In_opt_ DWORD cbEvent)
|
|
{
|
|
VMMDLL_PLUGIN_CONTEXT ctxPlugin;
|
|
QWORD tmStart = Statistics_CallStart(H);
|
|
PPLUGIN_ENTRY pPlugin = (PPLUGIN_ENTRY)H->vmm.PluginManager.FLinkNotify;
|
|
while(pPlugin) {
|
|
if(pPlugin->pfnNotify) {
|
|
PluginManager_ContextInitialize(&ctxPlugin, pPlugin, NULL, NULL);
|
|
pPlugin->pfnNotify(H, &ctxPlugin, fEvent, pvEvent, cbEvent);
|
|
}
|
|
pPlugin = pPlugin->FLinkNotify;
|
|
}
|
|
Statistics_CallEnd(H, STATISTICS_ID_PluginManager_Notify, tmStart);
|
|
return TRUE;
|
|
}
|
|
|
|
VOID PluginManager_FcInitialize_ThreadProc(_In_ VMM_HANDLE H, _In_ PPLUGIN_ENTRY pPlugin)
|
|
{
|
|
VMMDLL_PLUGIN_CONTEXT ctxPlugin;
|
|
if(!H->fAbort && pPlugin->fc.pfnInitialize) {
|
|
PluginManager_ContextInitialize(&ctxPlugin, pPlugin, NULL, NULL);
|
|
pPlugin->fc.ctxfc = pPlugin->fc.pfnInitialize(H, &ctxPlugin);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialize plugins with forensic mode capabilities.
|
|
* -- H
|
|
*/
|
|
VOID PluginManager_FcInitialize(_In_ VMM_HANDLE H)
|
|
{
|
|
DWORD cWork = 0;
|
|
QWORD tmStart = Statistics_CallStart(H);
|
|
PPLUGIN_ENTRY pPlugin = (PPLUGIN_ENTRY)H->vmm.PluginManager.FLinkForensic;
|
|
PVMM_WORK_START_ROUTINE_PVOID_PFN pfns[MAXIMUM_WAIT_OBJECTS];
|
|
PVOID ctxs[MAXIMUM_WAIT_OBJECTS];
|
|
if(H->fAbort) { return; }
|
|
while(pPlugin) {
|
|
if(pPlugin->fc.pfnInitialize) {
|
|
pfns[cWork] = (PVMM_WORK_START_ROUTINE_PVOID_PFN)PluginManager_FcInitialize_ThreadProc;
|
|
ctxs[cWork] = pPlugin;
|
|
cWork++;
|
|
if(cWork == MAXIMUM_WAIT_OBJECTS) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_2_WARNING, "FcInitialize max plugins reached. Some plugins may not be run.");
|
|
break;
|
|
}
|
|
}
|
|
pPlugin = pPlugin->FLinkForensic;
|
|
}
|
|
if(H->fAbort) { return; }
|
|
VmmWorkWaitMultiple2_Void(H, cWork, pfns, ctxs);
|
|
Statistics_CallEnd(H, STATISTICS_ID_PluginManager_FcInitialize, tmStart);
|
|
}
|
|
|
|
/*
|
|
* Finalize plugins with forensic mode capabilities.
|
|
* -- H
|
|
*/
|
|
VOID PluginManager_FcFinalize(_In_ VMM_HANDLE H)
|
|
{
|
|
QWORD tmStart = Statistics_CallStart(H);
|
|
PPLUGIN_ENTRY pModule = (PPLUGIN_ENTRY)H->vmm.PluginManager.FLinkForensic;
|
|
while(pModule) {
|
|
if(pModule->fc.pfnFinalize) {
|
|
pModule->fc.pfnFinalize(H, pModule->fc.ctxfc);
|
|
pModule->fc.pfnFinalize = NULL;
|
|
pModule->fc.ctxfc = NULL;
|
|
}
|
|
pModule = pModule->FLinkForensic;
|
|
}
|
|
Statistics_CallEnd(H, STATISTICS_ID_PluginManager_FcFinalize, tmStart);
|
|
}
|
|
|
|
/*
|
|
* Worker thread entry point:
|
|
* Ingest physical memory into plugins with forensic mode capabilities.
|
|
* -- H
|
|
* -- ctx
|
|
*/
|
|
VOID PluginManager_FcIngestPhysmem_ThreadProc(_In_ VMM_HANDLE H, _In_ PVOID ctx)
|
|
{
|
|
PPLUGIN_ENTRY pModule = ctx;
|
|
if(H->fAbort) { return; }
|
|
pModule->fc.pfnIngestPhysmem(H, pModule->fc.ctxfc, pModule->fc.IngestPhysmem.p);
|
|
}
|
|
|
|
/*
|
|
* Ingest physical memory into plugins with forensic mode capabilities.
|
|
* -- H
|
|
* -- pIngestPhysmem
|
|
*/
|
|
VOID PluginManager_FcIngestPhysmem(_In_ VMM_HANDLE H, _In_ PVMMDLL_FORENSIC_INGEST_PHYSMEM pIngestPhysmem)
|
|
{
|
|
DWORD cWork = 0;
|
|
PVOID ctxs[MAXIMUM_WAIT_OBJECTS];
|
|
PVMM_WORK_START_ROUTINE_PVOID_PFN pfns[MAXIMUM_WAIT_OBJECTS];
|
|
QWORD tmStart = Statistics_CallStart(H);
|
|
PPLUGIN_ENTRY pModule = (PPLUGIN_ENTRY)H->vmm.PluginManager.FLinkForensic;
|
|
while(pModule) {
|
|
if(pModule->fc.pfnIngestPhysmem) {
|
|
pModule->fc.IngestPhysmem.p = pIngestPhysmem;
|
|
ctxs[cWork] = pModule;
|
|
pfns[cWork] = PluginManager_FcIngestPhysmem_ThreadProc;
|
|
cWork++;
|
|
if(cWork >= MAXIMUM_WAIT_OBJECTS) { return; }
|
|
}
|
|
pModule = pModule->FLinkForensic;
|
|
}
|
|
VmmWorkWaitMultiple2_Void(H, cWork, pfns, ctxs);
|
|
Statistics_CallEnd(H, STATISTICS_ID_PluginManager_FcIngestPhysmem, tmStart);
|
|
}
|
|
|
|
/*
|
|
* Ingest virtual memory into plugins with forensic mode capabilities.
|
|
* -- H
|
|
* -- pIngestVirtmem
|
|
*/
|
|
VOID PluginManager_FcIngestVirtmem(_In_ VMM_HANDLE H, _In_ PVMMDLL_FORENSIC_INGEST_VIRTMEM pIngestVirtmem)
|
|
{
|
|
QWORD tmStart = Statistics_CallStart(H);
|
|
PPLUGIN_ENTRY pModule = (PPLUGIN_ENTRY)H->vmm.PluginManager.FLinkForensic;
|
|
while(pModule) {
|
|
if(pModule->fc.pfnIngestVirtmem) {
|
|
pModule->fc.pfnIngestVirtmem(H, pModule->fc.ctxfc, pIngestVirtmem);
|
|
}
|
|
pModule = pModule->FLinkForensic;
|
|
}
|
|
Statistics_CallEnd(H, STATISTICS_ID_PluginManager_FcIngestVirtmem, tmStart);
|
|
}
|
|
|
|
/*
|
|
* Ingest an object into plugins with forensic mode capabilities.
|
|
* -- H
|
|
* -- pIngestVirtmem
|
|
*/
|
|
VOID PluginManager_FcIngestObject(_In_ VMM_HANDLE H, _In_ PVMMDLL_FORENSIC_INGEST_OBJECT pIngestObject)
|
|
{
|
|
QWORD tmStart = Statistics_CallStart(H);
|
|
PPLUGIN_ENTRY pModule = (PPLUGIN_ENTRY)H->vmm.PluginManager.FLinkForensic;
|
|
while(pModule) {
|
|
if(pModule->fc.pfnIngestObject) {
|
|
pModule->fc.pfnIngestObject(H, pModule->fc.ctxfc, pIngestObject);
|
|
}
|
|
pModule = pModule->FLinkForensic;
|
|
}
|
|
Statistics_CallEnd(H, STATISTICS_ID_PluginManager_FcIngestObject, tmStart);
|
|
}
|
|
|
|
/*
|
|
* All ingestion actions are completed.
|
|
* -- H
|
|
*/
|
|
VOID PluginManager_FcIngestFinalize(_In_ VMM_HANDLE H)
|
|
{
|
|
QWORD tmStart = Statistics_CallStart(H);
|
|
PPLUGIN_ENTRY pModule = (PPLUGIN_ENTRY)H->vmm.PluginManager.FLinkForensic;
|
|
while(pModule) {
|
|
if(pModule->fc.pfnIngestFinalize) {
|
|
pModule->fc.pfnIngestFinalize(H, pModule->fc.ctxfc);
|
|
}
|
|
pModule = pModule->FLinkForensic;
|
|
}
|
|
Statistics_CallEnd(H, STATISTICS_ID_PluginManager_FcIngestFinalize, tmStart);
|
|
}
|
|
|
|
/*
|
|
* Register plugins with timelining capabilities with the timeline manager
|
|
* and call into each plugin to allow them to add their timelining entries.
|
|
* NB! This function is meant to be called by the core forensic subsystem only.
|
|
* -- H
|
|
* -- pfnRegister = callback function to register timeline module.
|
|
* -- pfnClose = function to close the timeline handle.
|
|
* -- pfnAddEntry = callback function to call to add a timelining entry.
|
|
* -- pfnEntryAddBySql = callback function to add timelining entries by sqlite query.
|
|
*/
|
|
VOID PluginManager_FcTimeline(
|
|
_In_ VMM_HANDLE H,
|
|
_In_ HANDLE(*pfnRegister)(_In_ VMM_HANDLE H, _In_reads_(6) LPCSTR sNameShort, _In_reads_(32) LPCSTR szFileUTF8),
|
|
_In_ VOID(*pfnClose)(_In_ VMM_HANDLE H, _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)
|
|
) {
|
|
HANDLE hTimeline;
|
|
QWORD tmStart = Statistics_CallStart(H);
|
|
PPLUGIN_ENTRY pModule = (PPLUGIN_ENTRY)H->vmm.PluginManager.FLinkForensic;
|
|
while(pModule) {
|
|
if(pModule->fc.pfnTimeline) {
|
|
hTimeline = pfnRegister(H, pModule->fc.Timeline.sNameShort, pModule->fc.Timeline.szFileUTF8);
|
|
if(hTimeline) {
|
|
pModule->fc.pfnTimeline(H, pModule->fc.ctxfc, hTimeline, pfnAddEntry, pfnEntryAddBySql);
|
|
pfnClose(H, hTimeline);
|
|
hTimeline = NULL;
|
|
}
|
|
}
|
|
pModule = pModule->FLinkForensic;
|
|
}
|
|
Statistics_CallEnd(H, STATISTICS_ID_PluginManager_FcTimeline, tmStart);
|
|
}
|
|
|
|
/*
|
|
* Call each plugin capable of forensic csv log. Plugins may be process or global.
|
|
* NB! This function is meant to be called by the core forensic subsystem only.
|
|
* -- H
|
|
* -- hCSV
|
|
* -- return = 0 (to make function compatible with LPTHREAD_START_ROUTINE).
|
|
*/
|
|
DWORD PluginManager_FcLogCSV(_In_ VMM_HANDLE H, _In_ VMMDLL_CSV_HANDLE hCSV)
|
|
{
|
|
VMMDLL_PLUGIN_CONTEXT ctxPlugin;
|
|
QWORD tmStart = Statistics_CallStart(H);
|
|
PPLUGIN_ENTRY pPlugin = (PPLUGIN_ENTRY)H->vmm.PluginManager.FLinkForensic;
|
|
PVMM_PROCESS pObProcess = NULL;
|
|
while(pPlugin && !H->fAbort) {
|
|
if(pPlugin->fc.pfnLogCSV) {
|
|
// global plugins:
|
|
PluginManager_ContextInitialize(&ctxPlugin, pPlugin, NULL, NULL);
|
|
pPlugin->fc.pfnLogCSV(H, &ctxPlugin, hCSV);
|
|
// per-process plugins:
|
|
while((pObProcess = VmmProcessGetNext(H, pObProcess, VMM_FLAG_PROCESS_SHOW_TERMINATED | VMM_FLAG_PROCESS_TOKEN))) {
|
|
PluginManager_ContextInitialize(&ctxPlugin, pPlugin, pObProcess, NULL);
|
|
FcCsv_Reset(hCSV);
|
|
pPlugin->fc.pfnLogCSV(H, &ctxPlugin, hCSV);
|
|
}
|
|
}
|
|
pPlugin = pPlugin->FLinkForensic;
|
|
}
|
|
Statistics_CallEnd(H, STATISTICS_ID_PluginManager_FcLogCSV, tmStart);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Call each plugin capable of forensic json log. Plugins may be process or global.
|
|
* NB! This function is meant to be called by the core forensic subsystem only.
|
|
* -- H
|
|
* -- pfnAddEntry = callback function to call to add a json entry.
|
|
* -- return = 0 (to make function compatible with LPTHREAD_START_ROUTINE).
|
|
*/
|
|
DWORD PluginManager_FcLogJSON(_In_ VMM_HANDLE H, _In_ VOID(*pfnLogJSON)(_In_ VMM_HANDLE H, _In_ PVMMDLL_FORENSIC_JSONDATA pData))
|
|
{
|
|
VMMDLL_PLUGIN_CONTEXT ctxPlugin;
|
|
QWORD tmStart = Statistics_CallStart(H);
|
|
PPLUGIN_ENTRY pPlugin = (PPLUGIN_ENTRY)H->vmm.PluginManager.FLinkForensic;
|
|
PVMM_PROCESS pObProcess = NULL;
|
|
while(pPlugin && !H->fAbort) {
|
|
if(pPlugin->fc.pfnLogJSON) {
|
|
// global plugins:
|
|
PluginManager_ContextInitialize(&ctxPlugin, pPlugin, NULL, NULL);
|
|
pPlugin->fc.pfnLogJSON(H, &ctxPlugin, pfnLogJSON);
|
|
// per-process plugins:
|
|
while((pObProcess = VmmProcessGetNext(H, pObProcess, VMM_FLAG_PROCESS_SHOW_TERMINATED | VMM_FLAG_PROCESS_TOKEN))) {
|
|
PluginManager_ContextInitialize(&ctxPlugin, pPlugin, pObProcess, NULL);
|
|
pPlugin->fc.pfnLogJSON(H, &ctxPlugin, pfnLogJSON);
|
|
}
|
|
}
|
|
pPlugin = pPlugin->FLinkForensic;
|
|
}
|
|
Statistics_CallEnd(H, STATISTICS_ID_PluginManager_FcLogJSON, tmStart);
|
|
return 0;
|
|
}
|
|
|
|
VOID PluginManager_FcFindEvil_ThreadProc(_In_ VMM_HANDLE H, _In_ PPLUGIN_ENTRY pPlugin)
|
|
{
|
|
if(!H->fAbort && pPlugin->fc.pfnFindEvil) {
|
|
pPlugin->fc.pfnFindEvil(H, pPlugin->MID, pPlugin->fc.ctxfc);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Call each plugin capable of FindEvil functionality.
|
|
* NB! This function is meant to be called by the core forensic subsystem only.
|
|
* -- H
|
|
*/
|
|
VOID PluginManager_FcFindEvil(_In_ VMM_HANDLE H)
|
|
{
|
|
DWORD cWork = 0;
|
|
QWORD tmStart = Statistics_CallStart(H);
|
|
PPLUGIN_ENTRY pPlugin = (PPLUGIN_ENTRY)H->vmm.PluginManager.FLinkForensic;
|
|
PVMM_WORK_START_ROUTINE_PVOID_PFN pfns[MAXIMUM_WAIT_OBJECTS];
|
|
PVOID ctxs[MAXIMUM_WAIT_OBJECTS];
|
|
if(H->fAbort) { return; }
|
|
while(pPlugin) {
|
|
if(pPlugin->fc.pfnFindEvil) {
|
|
pfns[cWork] = (PVMM_WORK_START_ROUTINE_PVOID_PFN)PluginManager_FcFindEvil_ThreadProc;
|
|
ctxs[cWork] = pPlugin;
|
|
cWork++;
|
|
if(cWork == MAXIMUM_WAIT_OBJECTS) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_2_WARNING, "FindEvil max plugins reached. Some plugins may not be run.");
|
|
break;
|
|
}
|
|
}
|
|
pPlugin = pPlugin->FLinkForensic;
|
|
}
|
|
if(H->fAbort) { return; }
|
|
VmmWorkWaitMultiple2_Void(H, cWork, pfns, ctxs);
|
|
Statistics_CallEnd(H, STATISTICS_ID_PluginManager_FcFindEvil, tmStart);
|
|
}
|
|
|
|
/*
|
|
* Execute python code in the python plugin sub-system and retrieve its result.
|
|
* -- CALLER LocalFree: *puszResultOfExec
|
|
* -- H
|
|
* -- uszPythonCodeToExec
|
|
* -- puszResultOfExec
|
|
* -- return
|
|
*/
|
|
_Success_(return)
|
|
BOOL PluginManager_PythonExecCode(_In_ VMM_HANDLE H, _In_ LPSTR uszPythonCodeToExec, _Out_ LPSTR *puszResultOfExec)
|
|
{
|
|
BOOL fResult = FALSE;
|
|
LPSTR uszSubPath;
|
|
PPLUGIN_TREE pTree = NULL;
|
|
BOOL(*pfnPY2C_Exec)(VMM_HANDLE, LPSTR, LPSTR*);
|
|
*puszResultOfExec = NULL;
|
|
if(PluginManager_Initialize(H)) {
|
|
PluginManager_GetTree(H->vmm.PluginManager.Root, "py", &pTree, &uszSubPath);
|
|
}
|
|
if(pTree && pTree->pPlugin && pTree->pPlugin->hDLL) {
|
|
if((pfnPY2C_Exec = (BOOL(*)(VMM_HANDLE, LPSTR, LPSTR*))GetProcAddress(pTree->pPlugin->hDLL, "PY2C_Exec"))) {
|
|
// wait for forensic mode to complete (if enabled and not completed already)
|
|
if(H->fc && H->fc->fInitStart) {
|
|
VmmLog(H, MID_PYTHON, LOGLEVEL_4_VERBOSE, "Python Code Execute: Wait for forensic mode to finish.")
|
|
while(!H->fAbort && !H->fc->fInitFinish) {
|
|
Sleep(100);
|
|
}
|
|
VmmLog(H, MID_PYTHON, LOGLEVEL_4_VERBOSE, "Python Code Execute: Wait for forensic mode completed.")
|
|
}
|
|
// dispatch to python plugin to execute python code in vmm context.
|
|
if(!H->fAbort) {
|
|
fResult = pfnPY2C_Exec(H, uszPythonCodeToExec, puszResultOfExec);
|
|
if(!fResult) {
|
|
VmmLog(H, MID_PYTHON, LOGLEVEL_1_CRITICAL, "Python Code Execute: Fail executing code.");
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
VmmLog(H, MID_PYTHON, LOGLEVEL_1_CRITICAL, "Python Code Execute: Fail - Unable to load Python plugin.");
|
|
}
|
|
return fResult;
|
|
}
|
|
|
|
/*
|
|
* Execute python code in the python plugin sub-system and print it's result on-screen.
|
|
* -- H
|
|
* -- szPythonFileToExec
|
|
*/
|
|
VOID PluginManager_PythonExecFile(_In_ VMM_HANDLE H, _In_ LPCSTR szPythonFileToExec)
|
|
{
|
|
BOOL f;
|
|
FILE *hFile = NULL;
|
|
DWORD cbPythonProgram;
|
|
LPSTR uszResultOfExec = NULL;
|
|
PBYTE pbPythonProgram = NULL;
|
|
f = (pbPythonProgram = LocalAlloc(LMEM_ZEROINIT, 0x01000000)) &&
|
|
!fopen_su(&hFile, H->cfg.szPythonExecuteFile, "rb") && hFile &&
|
|
(cbPythonProgram = (DWORD)fread(pbPythonProgram, 1, 0x01000000, hFile)) && (cbPythonProgram < 0x01000000);
|
|
if(f) {
|
|
if(PluginManager_PythonExecCode(H, (LPSTR)pbPythonProgram, &uszResultOfExec) && uszResultOfExec) {
|
|
vmmprintf(H, "%s", uszResultOfExec);
|
|
}
|
|
} else {
|
|
VmmLog(H, MID_PYTHON, LOGLEVEL_1_CRITICAL, "Python Code Execute: Unable to read file: '%s'", H->cfg.szPythonExecuteFile);
|
|
}
|
|
LocalFree(uszResultOfExec);
|
|
LocalFree(pbPythonProgram);
|
|
if(hFile) { fclose(hFile); }
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MODULES REGISTRATION/CLEANUP FUNCTIONALITY - IMPLEMENTATION BELOW:
|
|
// ----------------------------------------------------------------------------
|
|
|
|
BOOL PluginManager_Register(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_REGINFO pRegInfo)
|
|
{
|
|
DWORD iPluginNameStart;
|
|
LPCSTR uszPluginName, uszLogName;
|
|
PPLUGIN_ENTRY pModule;
|
|
PPLUGIN_TREE pPluginTreeEntry;
|
|
// 1: tests if plugin is valid
|
|
pRegInfo->reg_info.uszPathName[127] = 0;
|
|
if(!pRegInfo || (pRegInfo->magic != VMMDLL_PLUGIN_REGINFO_MAGIC) || (pRegInfo->wVersion > VMMDLL_PLUGIN_REGINFO_VERSION)) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_WARNING, "LOAD_FAIL: invalid plugin magic/version va=%p", pRegInfo);
|
|
return FALSE;
|
|
}
|
|
if(!pRegInfo->reg_info.uszPathName[0]) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_WARNING, "LOAD_FAIL: missing plugin path/name");
|
|
return FALSE;
|
|
}
|
|
uszPluginName = CharUtil_PathSplitLast(pRegInfo->reg_info.uszPathName);
|
|
if(strlen(uszPluginName) > 31) { return FALSE; }
|
|
if(pRegInfo->reg_info.fRootModule && PluginManager_ModuleExists(H->vmm.PluginManager.Root, pRegInfo->reg_info.uszPathName)) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_WARNING, "LOAD_FAIL: root plugin '%s' already exists", uszPluginName);
|
|
return FALSE;
|
|
}
|
|
if(pRegInfo->reg_info.fProcessModule && PluginManager_ModuleExists(H->vmm.PluginManager.Proc, pRegInfo->reg_info.uszPathName)) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_WARNING, "LOAD_FAIL: process plugin '%s' already exists", uszPluginName);
|
|
return FALSE;
|
|
}
|
|
if(!pRegInfo->reg_info.fRootModule && !pRegInfo->reg_info.fProcessModule) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_WARNING, "LOAD_FAIL: plugin '%s' is neither root/process type", uszPluginName);
|
|
return FALSE;
|
|
}
|
|
if(!pRegInfo->reg_fnfc.pfnIngestPhysmem && (H->vmm.PluginManager.cIngestPhysmem >= MAXIMUM_WAIT_OBJECTS)) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_WARNING, "LOAD_FAIL: plugin '%s' would exceed max # IngestPhysmem modules (%i)", uszPluginName, MAXIMUM_WAIT_OBJECTS);
|
|
return FALSE;
|
|
}
|
|
if(!pRegInfo->reg_fnfc.pfnIngestVirtmem && (H->vmm.PluginManager.cIngestVirtmem >= MAXIMUM_WAIT_OBJECTS)) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_WARNING, "LOAD_FAIL: plugin '%s' would exceed max # Ingestvirtmem modules (%i)", uszPluginName, MAXIMUM_WAIT_OBJECTS);
|
|
return FALSE;
|
|
}
|
|
// 2: register plugin
|
|
pModule = (PPLUGIN_ENTRY)LocalAlloc(LMEM_ZEROINIT, sizeof(PLUGIN_ENTRY));
|
|
if(!pModule) { return FALSE; }
|
|
pModule->hDLL = pRegInfo->hDLL;
|
|
pModule->MID = InterlockedIncrement(&H->vmm.PluginManager.NextMID);
|
|
strncpy_s(pModule->uszName, 32, uszPluginName, _TRUNCATE);
|
|
pModule->dwNameHash = CharUtil_HashNameFsU(pModule->uszName, TRUE);
|
|
pModule->fRootModule = pRegInfo->reg_info.fRootModule;
|
|
pModule->fProcessModule = pRegInfo->reg_info.fProcessModule;
|
|
pModule->ctxM = pRegInfo->reg_info.ctxM;
|
|
pModule->pfnList = pRegInfo->reg_fn.pfnList;
|
|
pModule->pfnRead = pRegInfo->reg_fn.pfnRead;
|
|
pModule->pfnWrite = pRegInfo->reg_fn.pfnWrite;
|
|
pModule->pfnNotify = pRegInfo->reg_fn.pfnNotify;
|
|
pModule->pfnClose = pRegInfo->reg_fn.pfnClose;
|
|
pModule->pfnVisibleModule = pRegInfo->reg_fn.pfnVisibleModule;
|
|
// 3: register plugin (forensic functionality)
|
|
pModule->fc.pfnInitialize = pRegInfo->reg_fnfc.pfnInitialize;
|
|
pModule->fc.pfnFinalize = pRegInfo->reg_fnfc.pfnFinalize;
|
|
pModule->fc.pfnTimeline = pRegInfo->reg_fnfc.pfnTimeline;
|
|
pModule->fc.pfnLogCSV = pRegInfo->reg_fnfc.pfnLogCSV;
|
|
pModule->fc.pfnLogJSON = pRegInfo->reg_fnfc.pfnLogJSON;
|
|
pModule->fc.pfnFindEvil = pRegInfo->reg_fnfc.pfnFindEvil;
|
|
pModule->fc.pfnIngestObject = pRegInfo->reg_fnfc.pfnIngestObject;
|
|
pModule->fc.pfnIngestPhysmem = pRegInfo->reg_fnfc.pfnIngestPhysmem;
|
|
pModule->fc.pfnIngestVirtmem = pRegInfo->reg_fnfc.pfnIngestVirtmem;
|
|
pModule->fc.pfnIngestFinalize = pRegInfo->reg_fnfc.pfnIngestFinalize;
|
|
memcpy(pModule->fc.Timeline.sNameShort, pRegInfo->reg_info.sTimelineNameShort, _countof(pModule->fc.Timeline.sNameShort));
|
|
memcpy(pModule->fc.Timeline.szFileUTF8, pRegInfo->reg_info.uszTimelineFile, _countof(pModule->fc.Timeline.szFileUTF8));
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_VERBOSE, "LOAD: %s module: '%s'", (pModule->hDLL ? " native " : "built-in"), pRegInfo->reg_info.uszPathName);
|
|
if(pModule->pfnNotify) {
|
|
pModule->FLinkNotify = (PPLUGIN_ENTRY)H->vmm.PluginManager.FLinkNotify;
|
|
H->vmm.PluginManager.FLinkNotify = pModule;
|
|
}
|
|
if(pModule->fc.pfnInitialize || pModule->fc.pfnFinalize || pModule->fc.pfnTimeline || pModule->fc.pfnLogCSV || pModule->fc.pfnLogJSON || pModule->fc.pfnFindEvil || pModule->fc.pfnIngestObject || pModule->fc.pfnIngestPhysmem || pModule->fc.pfnIngestVirtmem || pModule->fc.pfnIngestFinalize) {
|
|
if(pModule->fc.pfnIngestPhysmem) { H->vmm.PluginManager.cIngestPhysmem++; }
|
|
if(pModule->fc.pfnIngestVirtmem) { H->vmm.PluginManager.cIngestVirtmem++; }
|
|
pModule->FLinkForensic = (PPLUGIN_ENTRY)H->vmm.PluginManager.FLinkForensic;
|
|
H->vmm.PluginManager.FLinkForensic = pModule;
|
|
}
|
|
pModule->FLinkAll = (PPLUGIN_ENTRY)H->vmm.PluginManager.FLinkAll;
|
|
H->vmm.PluginManager.FLinkAll = pModule;
|
|
// 3: register plugin in plugin tree
|
|
iPluginNameStart = (pRegInfo->reg_info.uszPathName[0] == '\\') ? 1 : 0;
|
|
if(pModule->fRootModule) {
|
|
pPluginTreeEntry = PluginManager_Register_GetCreateTree(H->vmm.PluginManager.Root, pRegInfo->reg_info.uszPathName + iPluginNameStart, !pRegInfo->reg_info.fRootModuleHidden);
|
|
if(pPluginTreeEntry && !pPluginTreeEntry->pPlugin) {
|
|
pPluginTreeEntry->pPlugin = pModule;
|
|
}
|
|
}
|
|
if(pModule->fProcessModule) {
|
|
pPluginTreeEntry = PluginManager_Register_GetCreateTree(H->vmm.PluginManager.Proc, pRegInfo->reg_info.uszPathName + iPluginNameStart, !pRegInfo->reg_info.fProcessModuleHidden);
|
|
if(pPluginTreeEntry && !pPluginTreeEntry->pPlugin) {
|
|
pPluginTreeEntry->pPlugin = pModule;
|
|
}
|
|
}
|
|
// 4: register module id with logging sub-system
|
|
if(pModule->MID > 2) {
|
|
uszLogName = pModule->uszName;
|
|
} else if(pModule->MID == 2) {
|
|
uszLogName = "process";
|
|
} else {
|
|
uszLogName = "root";
|
|
}
|
|
VmmLog_RegisterModule(H, pModule->MID, uszLogName, (pModule->hDLL ? TRUE : FALSE));
|
|
return TRUE;
|
|
}
|
|
|
|
VOID PluginManager_Close_Tree(_In_ PPLUGIN_TREE pTree)
|
|
{
|
|
DWORD i;
|
|
if(!pTree) { return; }
|
|
for(i = 0; i < pTree->cChild; i++) {
|
|
PluginManager_Close_Tree(pTree->Child[i]);
|
|
}
|
|
LocalFree(pTree);
|
|
}
|
|
|
|
VOID PluginManager_Close(_In_ VMM_HANDLE H)
|
|
{
|
|
PPLUGIN_ENTRY pPlugin;
|
|
VMMDLL_PLUGIN_CONTEXT ctxPlugin;
|
|
PPLUGIN_TREE pTreeRoot = H->vmm.PluginManager.Root, pTreeProc = H->vmm.PluginManager.Proc;
|
|
H->vmm.PluginManager.Root = NULL;
|
|
H->vmm.PluginManager.Proc = NULL;
|
|
PluginManager_Close_Tree(pTreeRoot);
|
|
PluginManager_Close_Tree(pTreeProc);
|
|
H->vmm.PluginManager.FLinkNotify = NULL;
|
|
while((pPlugin = (PPLUGIN_ENTRY)H->vmm.PluginManager.FLinkAll)) {
|
|
// 1: Detach current module list entry from list
|
|
H->vmm.PluginManager.FLinkAll = pPlugin->FLinkAll;
|
|
// 2: Close module callback
|
|
if(pPlugin->pfnClose) {
|
|
PluginManager_ContextInitialize(&ctxPlugin, pPlugin, NULL, NULL);
|
|
pPlugin->pfnClose(H, &ctxPlugin);
|
|
}
|
|
// 3: FreeLibrary (if last module belonging to specific Library)
|
|
if(pPlugin->hDLL && !PluginManager_ModuleExistsDll(H, pPlugin->hDLL)) { FreeLibrary(pPlugin->hDLL); }
|
|
// 4: LocalFree this ListEntry
|
|
LocalFree(pPlugin);
|
|
}
|
|
}
|
|
|
|
VOID PluginManager_Initialize_RegInfoInit(_In_ VMM_HANDLE H, _Out_ PVMMDLL_PLUGIN_REGINFO pRI, _In_opt_ HMODULE hDLL)
|
|
{
|
|
ZeroMemory(pRI, sizeof(VMMDLL_PLUGIN_REGINFO));
|
|
pRI->magic = VMMDLL_PLUGIN_REGINFO_MAGIC;
|
|
pRI->wVersion = VMMDLL_PLUGIN_REGINFO_VERSION;
|
|
pRI->wSize = sizeof(VMMDLL_PLUGIN_REGINFO);
|
|
pRI->hDLL = hDLL;
|
|
pRI->uszPathVmmDLL = H->cfg.szPathLibraryVmm;
|
|
pRI->tpMemoryModel = (VMMDLL_MEMORYMODEL_TP)H->vmm.tpMemoryModel;
|
|
pRI->tpSystem = (VMMDLL_SYSTEM_TP)H->vmm.tpSystem;
|
|
pRI->pfnPluginManager_Register = PluginManager_Register;
|
|
pRI->sysinfo.f32 = H->vmm.f32;
|
|
pRI->sysinfo.dwVersionMajor = H->vmm.kernel.dwVersionMajor;
|
|
pRI->sysinfo.dwVersionMinor = H->vmm.kernel.dwVersionMinor;
|
|
pRI->sysinfo.dwVersionBuild = H->vmm.kernel.dwVersionBuild;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
VOID PluginManager_Initialize_Python(_In_ VMM_HANDLE H)
|
|
{
|
|
LPSTR szPYTHON_VERSIONS_SUPPORTED[] = {
|
|
"python315.dll",
|
|
"python314.dll",
|
|
"python313.dll",
|
|
"python312.dll",
|
|
"python311.dll",
|
|
"python310.dll",
|
|
"python39.dll",
|
|
"python38.dll",
|
|
"python37.dll",
|
|
"python36.dll"
|
|
};
|
|
DWORD cszPYTHON_VERSIONS_SUPPORTED = (sizeof(szPYTHON_VERSIONS_SUPPORTED) / sizeof(LPSTR));
|
|
DWORD i;
|
|
BOOL fBitnessFail = FALSE, fPythonStandalone = FALSE;
|
|
VMMDLL_PLUGIN_REGINFO ri;
|
|
CHAR uszPythonPath[MAX_PATH];
|
|
CHAR uszVmmPycPath[MAX_PATH] = { 0 };
|
|
HMODULE hDllPython3X = NULL, hDllPython3 = NULL, hDllPyPlugin = NULL;
|
|
VOID(*pfnInitializeVmmPlugin)(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_REGINFO pRegInfo);
|
|
// 0: Verify that Python should be enabled
|
|
if(H->cfg.fDisablePython) { return; }
|
|
// 1: Locate Python by trying user-defined path
|
|
if(H->cfg.uszPythonPath[0]) {
|
|
for(i = 0; i < cszPYTHON_VERSIONS_SUPPORTED; i++) {
|
|
ZeroMemory(uszPythonPath, MAX_PATH);
|
|
strcpy_s(uszPythonPath, MAX_PATH, H->cfg.uszPythonPath);
|
|
strcat_s(uszPythonPath, MAX_PATH, "\\");
|
|
strcat_s(uszPythonPath, MAX_PATH, szPYTHON_VERSIONS_SUPPORTED[i]);
|
|
hDllPython3X = LoadLibraryU(uszPythonPath);
|
|
if(hDllPython3X) { break; }
|
|
fBitnessFail = fBitnessFail || (ERROR_BAD_EXE_FORMAT == GetLastError());
|
|
}
|
|
if(!hDllPython3X) {
|
|
ZeroMemory(H->cfg.uszPythonPath, MAX_PATH);
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_INFO,
|
|
fBitnessFail ?
|
|
"Python initialization failed. Unable to load 32-bit Python. 64-bit required." :
|
|
"Python initialization failed. Python 3.6 or later not found on user specified path."
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
// 2: If Python is already loaded - use it!
|
|
if(0 == H->cfg.uszPythonPath[0]) {
|
|
for(i = 0; i < cszPYTHON_VERSIONS_SUPPORTED; i++) {
|
|
if((hDllPython3X = GetModuleHandleA(szPYTHON_VERSIONS_SUPPORTED[i]))) {
|
|
GetModuleFileNameA(hDllPython3X, uszPythonPath, MAX_PATH);
|
|
hDllPython3X = LoadLibraryU(uszPythonPath);
|
|
if(hDllPython3X) {
|
|
Util_GetPathDll(H->cfg.uszPythonPath, hDllPython3X);
|
|
fPythonStandalone = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// 3: Try locate Python by checking the python36 sub-directory relative to the current library (vmm.dll).
|
|
if(0 == H->cfg.uszPythonPath[0]) {
|
|
for(i = 0; i < cszPYTHON_VERSIONS_SUPPORTED; i++) {
|
|
ZeroMemory(uszPythonPath, MAX_PATH);
|
|
Util_GetPathLib(uszPythonPath);
|
|
strcat_s(uszPythonPath, MAX_PATH, "python\\");
|
|
strcat_s(uszPythonPath, MAX_PATH, szPYTHON_VERSIONS_SUPPORTED[i]);
|
|
hDllPython3X = LoadLibraryU(uszPythonPath);
|
|
if(hDllPython3X) { break; }
|
|
fBitnessFail = fBitnessFail || (ERROR_BAD_EXE_FORMAT == GetLastError());
|
|
}
|
|
if(hDllPython3X) {
|
|
Util_GetPathLib(H->cfg.uszPythonPath);
|
|
strcat_s(H->cfg.uszPythonPath, MAX_PATH, "python\\");
|
|
}
|
|
}
|
|
// 4: Try locate Python by loading from the current path.
|
|
if(0 == H->cfg.uszPythonPath[0]) {
|
|
for(i = 0; i < cszPYTHON_VERSIONS_SUPPORTED; i++) {
|
|
hDllPython3X = LoadLibraryExA(szPYTHON_VERSIONS_SUPPORTED[i], NULL, LOAD_LIBRARY_SAFE_CURRENT_DIRS);
|
|
if(hDllPython3X) {
|
|
DWORD DEBUG = 1;
|
|
break;
|
|
}
|
|
fBitnessFail = fBitnessFail || (ERROR_BAD_EXE_FORMAT == GetLastError());
|
|
}
|
|
if(hDllPython3X) {
|
|
Util_GetPathDll(H->cfg.uszPythonPath, hDllPython3X);
|
|
}
|
|
}
|
|
// 5: Python is not found?
|
|
if(0 == H->cfg.uszPythonPath[0]) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_INFO,
|
|
fBitnessFail ?
|
|
"Python initialization failed. Unable to load 32-bit Python. 64-bit required." :
|
|
"Python initialization failed. Python 3.6 or later not found."
|
|
);
|
|
goto fail;
|
|
}
|
|
// 6: Load Python3.dll as well (i.e. prevent vmmpyc.pyd to fetch the wrong one by mistake...)
|
|
Util_GetPathDll(uszPythonPath, hDllPython3X);
|
|
strcat_s(uszPythonPath, MAX_PATH, "python3.dll");
|
|
hDllPython3 = LoadLibraryU(uszPythonPath);
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_DEBUG, "PYTHON_PATH: %s", H->cfg.uszPythonPath);
|
|
// 7: process 'special status' python plugin manager.
|
|
Util_GetPathLib(uszVmmPycPath);
|
|
strcat_s(uszVmmPycPath, MAX_PATH - 4, "vmmpyc.pyd");
|
|
hDllPyPlugin = LoadLibraryU(uszVmmPycPath);
|
|
if(!hDllPyPlugin) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_4_VERBOSE, "Python plugin manager failed to load.");
|
|
goto fail;
|
|
}
|
|
pfnInitializeVmmPlugin = (VOID(*)(VMM_HANDLE, PVMMDLL_PLUGIN_REGINFO))GetProcAddress(hDllPyPlugin, "InitializeVmmPlugin");
|
|
if(!pfnInitializeVmmPlugin) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_4_VERBOSE, "Python plugin manager failed to load due to corrupt DLL.");
|
|
goto fail;
|
|
}
|
|
PluginManager_Initialize_RegInfoInit(H, &ri, hDllPyPlugin);
|
|
ri.python.fPythonStandalone = fPythonStandalone;
|
|
ri.python.hReservedDllPython3X = hDllPython3X;
|
|
ri.python.hReservedDllPython3 = hDllPython3;
|
|
pfnInitializeVmmPlugin(H, &ri);
|
|
if(!PluginManager_ModuleExistsDll(H, hDllPyPlugin)) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_4_VERBOSE, "Python plugin manager failed to load due to internal error.");
|
|
return;
|
|
}
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_4_VERBOSE, "PluginManager: Python plugin loaded.");
|
|
if(hDllPython3X) { FreeLibrary(hDllPython3X); }
|
|
return;
|
|
fail:
|
|
if(hDllPyPlugin) { FreeLibrary(hDllPyPlugin); }
|
|
if(hDllPython3X) { FreeLibrary(hDllPython3X); }
|
|
if(hDllPython3) { FreeLibrary(hDllPython3); }
|
|
}
|
|
|
|
VOID PluginManager_Initialize_ExternalDlls(_In_ VMM_HANDLE H)
|
|
{
|
|
VMMDLL_PLUGIN_REGINFO ri;
|
|
CHAR uszPath[MAX_PATH];
|
|
WCHAR wszPath[MAX_PATH];
|
|
DWORD cchPathBase;
|
|
HANDLE hFindFile;
|
|
WIN32_FIND_DATAW FindData;
|
|
HMODULE hDLL;
|
|
VOID(*pfnInitializeVmmPlugin)(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_REGINFO pRegInfo);
|
|
Util_GetPathLib(uszPath);
|
|
if(!CharUtil_UtoW(uszPath, (DWORD)-1, (PBYTE)wszPath, sizeof(wszPath), NULL, NULL, CHARUTIL_FLAG_STR_BUFONLY)) { return; }
|
|
cchPathBase = (DWORD)wcsnlen(wszPath, MAX_PATH - 1);
|
|
wcscat_s(wszPath, MAX_PATH, L"plugins\\m_*.dll");
|
|
hFindFile = FindFirstFileW(wszPath, &FindData);
|
|
if(hFindFile != INVALID_HANDLE_VALUE) {
|
|
do {
|
|
wszPath[min(cchPathBase, MAX_PATH - 1)] = L'\0';
|
|
wcscat_s(wszPath, MAX_PATH, L"plugins\\");
|
|
wcscat_s(wszPath, MAX_PATH, FindData.cFileName);
|
|
hDLL = LoadLibraryExW(wszPath, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
|
|
if(!hDLL) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_DEBUG, "FAIL: Load DLL: '%S' - missing dependencies?", FindData.cFileName);
|
|
continue;
|
|
}
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_DEBUG, "Load DLL: '%S'", FindData.cFileName);
|
|
pfnInitializeVmmPlugin = (VOID(*)(VMM_HANDLE, PVMMDLL_PLUGIN_REGINFO))GetProcAddress(hDLL, "InitializeVmmPlugin");
|
|
if(!pfnInitializeVmmPlugin) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_DEBUG, "Unload DLL: '%S' - Plugin Entry Point not found", FindData.cFileName);
|
|
FreeLibrary(hDLL);
|
|
continue;
|
|
}
|
|
PluginManager_Initialize_RegInfoInit(H, &ri, hDLL);
|
|
pfnInitializeVmmPlugin(H, &ri);
|
|
if(!PluginManager_ModuleExistsDll(H, hDLL)) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_DEBUG, "Unload DLL: '%S' - not registered with plugin manager", FindData.cFileName);
|
|
FreeLibrary(hDLL);
|
|
continue;
|
|
}
|
|
} while(FindNextFileW(hFindFile, &FindData) && !H->fAbort);
|
|
FindClose(hFindFile);
|
|
}
|
|
}
|
|
#endif /* _WIN32 */
|
|
|
|
#if defined(LINUX) || defined(MACOS)
|
|
VOID PluginManager_Initialize_Python(_In_ VMM_HANDLE H)
|
|
{
|
|
struct link_map *lm;
|
|
LPSTR szPYTHON_VERSIONS_SUPPORTED[] = {
|
|
"libpython3.15.so.1", "libpython3.15.so",
|
|
"libpython3.14.so.1", "libpython3.14.so",
|
|
"libpython3.13.so.1", "libpython3.13.so",
|
|
"libpython3.12.so.1", "libpython3.12.so",
|
|
"libpython3.11.so.1", "libpython3.11.so",
|
|
"libpython3.10.so.1", "libpython3.10.so",
|
|
"libpython3.9.so.1", "libpython3.9.so",
|
|
"libpython3.8.so.1", "libpython3.8.so",
|
|
"libpython3.7.so.1", "libpython3.7.so",
|
|
"libpython3.6.so.1", "libpython3.6.so"
|
|
};
|
|
DWORD cszPYTHON_VERSIONS_SUPPORTED = (sizeof(szPYTHON_VERSIONS_SUPPORTED) / sizeof(LPSTR));
|
|
DWORD i;
|
|
BOOL fPythonStandalone = FALSE;
|
|
VMMDLL_PLUGIN_REGINFO ri;
|
|
CHAR uszPythonPath[MAX_PATH];
|
|
CHAR uszVmmPycPath[MAX_PATH] = { 0 };
|
|
HMODULE hDllPython3X = NULL, hDllPyPlugin = NULL;
|
|
VOID(*pfnInitializeVmmPlugin)(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_REGINFO pRegInfo);
|
|
// 0: Verify that Python should be enabled
|
|
if(H->cfg.fDisablePython) { return; }
|
|
// 1: Locate Python by trying user-defined path
|
|
if(H->cfg.uszPythonPath[0]) {
|
|
for(i = 0; i < cszPYTHON_VERSIONS_SUPPORTED; i++) {
|
|
ZeroMemory(uszPythonPath, MAX_PATH);
|
|
strcpy_s(uszPythonPath, MAX_PATH, H->cfg.uszPythonPath);
|
|
strcat_s(uszPythonPath, MAX_PATH, "/");
|
|
strcat_s(uszPythonPath, MAX_PATH, szPYTHON_VERSIONS_SUPPORTED[i]);
|
|
hDllPython3X = dlopen(uszPythonPath, RTLD_NOW | RTLD_GLOBAL);
|
|
if(hDllPython3X) { break; }
|
|
}
|
|
if(!hDllPython3X) {
|
|
ZeroMemory(H->cfg.uszPythonPath, MAX_PATH);
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_INFO, "Python initialization failed. Python 3.6 or later not found on user specified path.");
|
|
return;
|
|
}
|
|
}
|
|
// 2: If Python is already loaded - use it!
|
|
if(0 == H->cfg.uszPythonPath[0]) {
|
|
for(i = 0; i < cszPYTHON_VERSIONS_SUPPORTED; i++) {
|
|
if((hDllPython3X = GetModuleHandleA(szPYTHON_VERSIONS_SUPPORTED[i]))) {
|
|
GetModuleFileNameA(hDllPython3X, uszPythonPath, MAX_PATH);
|
|
hDllPython3X = dlopen(uszPythonPath, RTLD_NOW | RTLD_GLOBAL);
|
|
if(hDllPython3X) {
|
|
Util_GetPathDll(H->cfg.uszPythonPath, hDllPython3X);
|
|
fPythonStandalone = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// 3: Try locate Python by checking the python36 sub-directory relative to the current executable (.exe).
|
|
if(0 == H->cfg.uszPythonPath[0]) {
|
|
for(i = 0; i < cszPYTHON_VERSIONS_SUPPORTED; i++) {
|
|
ZeroMemory(uszPythonPath, MAX_PATH);
|
|
Util_GetPathDll(uszPythonPath, NULL);
|
|
strcat_s(uszPythonPath, MAX_PATH, "python/");
|
|
strcat_s(uszPythonPath, MAX_PATH, szPYTHON_VERSIONS_SUPPORTED[i]);
|
|
hDllPython3X = dlopen(uszPythonPath, RTLD_NOW | RTLD_GLOBAL);
|
|
if(hDllPython3X) { break; }
|
|
}
|
|
if(hDllPython3X) {
|
|
Util_GetPathDll(H->cfg.uszPythonPath, NULL);
|
|
strcat_s(H->cfg.uszPythonPath, MAX_PATH, "python/");
|
|
}
|
|
}
|
|
// 4: Try locate Python by loading from the current path.
|
|
if(0 == H->cfg.uszPythonPath[0]) {
|
|
for(i = 0; i < cszPYTHON_VERSIONS_SUPPORTED; i++) {
|
|
hDllPython3X = dlopen(szPYTHON_VERSIONS_SUPPORTED[i], RTLD_NOW | RTLD_GLOBAL);
|
|
if(hDllPython3X) { break; }
|
|
}
|
|
if(hDllPython3X) {
|
|
Util_GetPathDll(H->cfg.uszPythonPath, hDllPython3X);
|
|
}
|
|
}
|
|
// 5: Python is not found?
|
|
if(0 == H->cfg.uszPythonPath[0]) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_INFO, "Python initialization failed. Python 3.6 or later not found.");
|
|
goto fail;
|
|
}
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_DEBUG, "PYTHON_PATH: %s", H->cfg.uszPythonPath);
|
|
// 7: process 'special status' python plugin manager.
|
|
Util_GetPathLib(uszVmmPycPath);
|
|
strcat_s(uszVmmPycPath, MAX_PATH - 4, "vmmpyc"VMM_LIBRARY_FILETYPE);
|
|
hDllPyPlugin = dlopen(uszVmmPycPath, RTLD_NOW | RTLD_GLOBAL);
|
|
if(!hDllPyPlugin) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_4_VERBOSE, "Python plugin manager failed to load.");
|
|
goto fail;
|
|
}
|
|
pfnInitializeVmmPlugin = (VOID(*)(VMM_HANDLE, PVMMDLL_PLUGIN_REGINFO))GetProcAddress(hDllPyPlugin, "InitializeVmmPlugin");
|
|
if(!pfnInitializeVmmPlugin) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_4_VERBOSE, "Python plugin manager failed to load due to corrupt DLL.");
|
|
goto fail;
|
|
}
|
|
PluginManager_Initialize_RegInfoInit(H, &ri, hDllPyPlugin);
|
|
ri.python.fPythonStandalone = fPythonStandalone;
|
|
ri.python.hReservedDllPython3X = NULL;
|
|
ri.python.hReservedDllPython3 = hDllPython3X;
|
|
pfnInitializeVmmPlugin(H, &ri);
|
|
if(!PluginManager_ModuleExistsDll(H, hDllPyPlugin)) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_4_VERBOSE, "Python plugin manager failed to load due to internal error.");
|
|
return;
|
|
}
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_4_VERBOSE, "PluginManager: Python plugin loaded.");
|
|
return;
|
|
fail:
|
|
if(hDllPyPlugin) { dlclose(hDllPyPlugin); }
|
|
if(hDllPython3X) { dlclose(hDllPython3X); }
|
|
}
|
|
|
|
VOID PluginManager_Initialize_ExternalDlls(_In_ VMM_HANDLE H)
|
|
{
|
|
HMODULE hDLL;
|
|
VMMDLL_PLUGIN_REGINFO ri;
|
|
CHAR szPath[MAX_PATH];
|
|
DWORD cchPathBase;
|
|
DIR *dp;
|
|
struct dirent *ep;
|
|
VOID(*pfnInitializeVmmPlugin)(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_REGINFO pRegInfo);
|
|
Util_GetPathLib(szPath);
|
|
strcat_s(szPath, MAX_PATH, "/plugins/");
|
|
cchPathBase = (DWORD)strnlen(szPath, MAX_PATH - 1);
|
|
if(cchPathBase > MAX_PATH - 0x20) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_DEBUG, "FAIL load external modules - path too long");
|
|
return;
|
|
}
|
|
if(!(dp = opendir(szPath))) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_DEBUG, "FAIL load external modules - plugins directory missing");
|
|
return;
|
|
}
|
|
while((ep = readdir(dp)) && !H->fAbort) {
|
|
if((ep->d_name[0] != 'm') || (ep->d_name[1] != '_')) { continue; }
|
|
if(!CharUtil_StrEndsWith(ep->d_name, VMM_LIBRARY_FILETYPE, TRUE)) { continue; }
|
|
szPath[cchPathBase] = '\0';
|
|
strcat_s(szPath + cchPathBase, MAX_PATH - cchPathBase, ep->d_name);
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_6_TRACE, "Try load external module '%s'", szPath);
|
|
hDLL = dlopen(szPath, RTLD_NOW);
|
|
if(!hDLL) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_DEBUG, "FAIL load external module '%s' - ('%s') missing dependencies?", ep->d_name, dlerror());
|
|
continue;
|
|
}
|
|
pfnInitializeVmmPlugin = (VOID(*)(VMM_HANDLE, PVMMDLL_PLUGIN_REGINFO))dlsym(hDLL, "InitializeVmmPlugin");
|
|
if(!pfnInitializeVmmPlugin) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_DEBUG, "FAIL load external module '%s' - plugin entry point not found", ep->d_name);
|
|
dlclose(hDLL);
|
|
continue;
|
|
}
|
|
PluginManager_Initialize_RegInfoInit(H, &ri, hDLL);
|
|
pfnInitializeVmmPlugin(H, &ri);
|
|
if(!PluginManager_ModuleExistsDll(H, hDLL)) {
|
|
VmmLog(H, MID_PLUGIN, LOGLEVEL_DEBUG, "FAIL load external module '%s' - not registered with plugin manager", ep->d_name);
|
|
dlclose(hDLL);
|
|
continue;
|
|
}
|
|
}
|
|
closedir(dp);
|
|
}
|
|
#endif /* LINUX || MACOS */
|
|
|
|
BOOL PluginManager_Initialize(_In_ VMM_HANDLE H)
|
|
{
|
|
DWORD i;
|
|
VMMDLL_PLUGIN_REGINFO ri;
|
|
// 1: check if already initialized
|
|
if(H->vmm.PluginManager.FLinkAll) { return TRUE; }
|
|
AcquireSRWLockExclusive(&H->vmm.LockSRW.PluginMgr);
|
|
if(H->vmm.PluginManager.FLinkAll) {
|
|
ReleaseSRWLockExclusive(&H->vmm.LockSRW.PluginMgr);
|
|
return TRUE;
|
|
}
|
|
// 2: set up root nodes of process plugin tree
|
|
H->vmm.PluginManager.Root = LocalAlloc(LMEM_ZEROINIT, sizeof(PLUGIN_TREE));
|
|
H->vmm.PluginManager.Proc = LocalAlloc(LMEM_ZEROINIT, sizeof(PLUGIN_TREE));
|
|
if(!H->vmm.PluginManager.Root || !H->vmm.PluginManager.Proc) { goto fail; }
|
|
// 3: process built-in modules
|
|
for(i = 0; i < sizeof(g_pfnModulesAllInternal) / sizeof(PVOID); i++) {
|
|
if(H->fAbort) { goto fail; }
|
|
PluginManager_Initialize_RegInfoInit(H, &ri, NULL);
|
|
g_pfnModulesAllInternal[i](H, &ri);
|
|
}
|
|
for(i = 0; i < sizeof(g_pfnModulesExAllInternal) / sizeof(PVOID); i++) {
|
|
if(H->fAbort) { goto fail; }
|
|
PluginManager_Initialize_RegInfoInit(H, &ri, NULL);
|
|
g_pfnModulesExAllInternal[i](H, &ri);
|
|
}
|
|
// 4: process dll modules
|
|
PluginManager_Initialize_ExternalDlls(H);
|
|
// 5: process 'special status' python plugin manager.
|
|
if(H->fAbort) { goto fail; }
|
|
PluginManager_Initialize_Python(H);
|
|
// 6: refresh logging (module specific overrides not yet applied may exist)
|
|
VmmLog_LevelRefresh(H);
|
|
ReleaseSRWLockExclusive(&H->vmm.LockSRW.PluginMgr);
|
|
return TRUE;
|
|
fail:
|
|
ReleaseSRWLockExclusive(&H->vmm.LockSRW.PluginMgr);
|
|
return FALSE;
|
|
}
|