mirror of
https://github.com/ufrisk/MemProcFS.git
synced 2026-06-02 08:40:25 +08:00
499 lines
19 KiB
C
499 lines
19 KiB
C
// m_misc_bitlocker.c : implementation of the bitlocker key recovery built-in module.
|
|
//
|
|
// (c) Ulf Frisk, 2022-2025
|
|
// Author: Ulf Frisk, pcileech@frizk.net
|
|
//
|
|
|
|
#include "modules.h"
|
|
|
|
LPSTR szMBDE_README =
|
|
"Bitlocker plugin for MemProcFS: \n" \
|
|
"=============================== \n" \
|
|
" \n" \
|
|
"The BitLocker plugin tries to recover BitLocker key material from memory. \n" \
|
|
" \n" \
|
|
"The MemProcFS BitLocker plugin is to some degree inspired by the excellent \n" \
|
|
"Volatility plugin at: https://github.com/breppo/Volatility-BitLocker \n" \
|
|
" \n" \
|
|
"To mount a bitlocker drive please use Linux dislocker. You may mount with \n" \
|
|
"the recovered key material in the .fvek file by: \n" \
|
|
"dislocker -k <recovered_key>.fvek /path/to/disk /path/to/dislocker \n" \
|
|
"mount /path/to/dislocker/dislocker-file /path/to/mount \n" \
|
|
" \n" \
|
|
"The plugin works best analyzing Windows 7 and Windows 10/11 targets. \n" \
|
|
"Windows 8 support is more error prone - but will most often work. \n" \
|
|
"--- \n" \
|
|
"Documentation: https://github.com/ufrisk/MemProcFS/wiki/FS_BitLocker \n";
|
|
|
|
typedef struct tdMBDE_KEY {
|
|
QWORD va;
|
|
BOOL fWin8Merge;
|
|
DWORD cbKey;
|
|
union {
|
|
BYTE pbDislocker[66];
|
|
struct {
|
|
BYTE dwMode;
|
|
BYTE dw80;
|
|
BYTE pbKey1[32];
|
|
BYTE pbKey2[32];
|
|
};
|
|
struct {
|
|
BYTE _Reserved1;
|
|
BYTE _Reserved2;
|
|
BYTE pbKey12[64];
|
|
};
|
|
};
|
|
BYTE pbBuffer[32];
|
|
CHAR szNameFVEK[32];
|
|
CHAR szNameBIN[32];
|
|
CHAR szNameTXT[32];
|
|
CHAR szTXT[0x100];
|
|
DWORD cbBlob;
|
|
BYTE pbBlob[0];
|
|
} MBDE_KEY, *PMBDE_KEY;
|
|
|
|
typedef struct tdMBDE_CONTEXT {
|
|
PVMMDLL_PLUGIN_CONTEXT ctxP;
|
|
PVMMOB_MAP_POOL pPoolMap;
|
|
POB_MAP pmBDE;
|
|
BOOL fWin8;
|
|
} MBDE_CONTEXT, *PMBDE_CONTEXT;
|
|
|
|
typedef struct tdMBDE_OFFSET {
|
|
DWORD o0;
|
|
DWORD o1;
|
|
DWORD o2;
|
|
DWORD o3;
|
|
} MBDE_OFFSET, *PMBDE_OFFSET;
|
|
|
|
static BYTE pbMBDE_ZERO32[32] = { 0 };
|
|
|
|
#define MBDE_MODE_AES128_DIFFUSER 0
|
|
#define MBDE_MODE_AES256_DIFFUSER 1
|
|
#define MBDE_MODE_AES128 2
|
|
#define MBDE_MODE_AES256 3
|
|
#define MBDE_MODE_AES256_XTS 4
|
|
#define MBDE_MODE_AES128_XTS 5
|
|
|
|
static LPCSTR szMBDE_MODE_STR[] = {
|
|
"AES 128-bit with Diffuser",
|
|
"AES 256-bit with Diffuser",
|
|
"AES 128-bit",
|
|
"AES 256-bit",
|
|
"AES-XTS 256-bit",
|
|
"AES-XTS 128-bit"
|
|
};
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// BITLOCKER KEY RETRIEVAL / ANALYSIS BELOW:
|
|
// ----------------------------------------------------------------------------
|
|
|
|
/*
|
|
* Update a key with display information.
|
|
* -- ctxBDE
|
|
* -- pk
|
|
*/
|
|
VOID MBDE_ContextKeyUpdate(_In_ PMBDE_CONTEXT ctxBDE, _In_ PMBDE_KEY pk)
|
|
{
|
|
DWORD o, i = 0;
|
|
CHAR szKey1[129], szKey2[129];
|
|
pk->dw80 = 0x80;
|
|
snprintf(pk->szNameFVEK, _countof(pk->szNameFVEK), "%llx.fvek", pk->va);
|
|
snprintf(pk->szNameBIN, _countof(pk->szNameBIN), "%llx.bin", pk->va);
|
|
snprintf(pk->szNameTXT, _countof(pk->szNameTXT), "%llx.txt", pk->va);
|
|
for(i = 0; i < 64; i++) {
|
|
snprintf(szKey1 + i * 2ULL, 3, "%02x", pk->pbKey12[i]);
|
|
}
|
|
for(i = 0; i < 32; i++) {
|
|
snprintf(szKey2 + i * 2ULL, 3, "%02x", pk->pbKey2[i]);
|
|
}
|
|
o = snprintf(pk->szTXT, _countof(pk->szTXT),
|
|
"Address: 0x%llx\nCipher: %s\nKey: %.*s\n",
|
|
pk->va,
|
|
szMBDE_MODE_STR[pk->dwMode],
|
|
2 * pk->cbKey, szKey1
|
|
);
|
|
if((pk->dwMode == MBDE_MODE_AES128_DIFFUSER) || (pk->dwMode == MBDE_MODE_AES256_DIFFUSER) || ctxBDE->fWin8) {
|
|
snprintf(pk->szTXT + o, _countof(pk->szTXT) - o, "Tweak: %.*s\n", 2 * pk->cbKey, szKey2);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Add a key context. This is usually a recovered bitlocker AES key.
|
|
* In case of Win8 it may be a partial key if elephant diffuser is used.
|
|
* -- H
|
|
* -- ctxBDE
|
|
* -- peKey
|
|
* -- pbKeyBlob
|
|
*/
|
|
VOID MBDE_ContextKeyAdd(_In_ VMM_HANDLE H, _In_ PMBDE_CONTEXT ctxBDE, _In_ PMBDE_KEY peKey, _In_ PBYTE pbKeyBlob)
|
|
{
|
|
PMBDE_KEY pk;
|
|
if(!(pk = LocalAlloc(0, sizeof(MBDE_KEY) + peKey->cbBlob))) { return; }
|
|
if(!ObMap_Push(ctxBDE->pmBDE, peKey->va, pk)) {
|
|
LocalFree(pk);
|
|
return;
|
|
}
|
|
memcpy(pk, peKey, sizeof(MBDE_KEY));
|
|
memcpy(pk->pbBlob, pbKeyBlob, peKey->cbBlob);
|
|
MBDE_ContextKeyUpdate(ctxBDE, pk);
|
|
VmmLog(H, ctxBDE->ctxP->MID, LOGLEVEL_TRACE, "Key located at %llx", pk->va);
|
|
}
|
|
|
|
/*
|
|
* Merge any Win8 diffuser keys. In Win8 and early win10 keys are split between
|
|
* two different key blobs. There is a pointer which ties keys together. Merge
|
|
* these key blobs into two different versions - one correct and one fail.
|
|
* -- ctxBDE
|
|
*/
|
|
VOID MBDE_Win8_PostProcess(_In_ VMM_HANDLE H, _In_ PMBDE_CONTEXT ctxBDE)
|
|
{
|
|
PMBDE_KEY pKey1 = NULL, pKey2;
|
|
QWORD va1 = 0, va2 = 0;
|
|
while((pKey1 = ObMap_GetNext(ctxBDE->pmBDE, pKey1))) {
|
|
va1 = VMM_PTR_OFFSET_DUAL(H->vmm.f32, pKey1->pbBlob, 0x10, 8);
|
|
pKey2 = NULL;
|
|
while((pKey2 = ObMap_GetNext(ctxBDE->pmBDE, pKey2))) {
|
|
va2 = VMM_PTR_OFFSET_DUAL(H->vmm.f32, pKey2->pbBlob, 0x10, 8);
|
|
if((pKey1 == pKey2) || (va1 != va2) || pKey1->fWin8Merge || pKey2->fWin8Merge) { continue; }
|
|
// pointer match - merge keys in two different ways!
|
|
if(pKey1->dwMode == MBDE_MODE_AES128) { pKey1->dwMode = MBDE_MODE_AES128_DIFFUSER; }
|
|
if(pKey1->dwMode == MBDE_MODE_AES256) { pKey1->dwMode = MBDE_MODE_AES256_DIFFUSER; }
|
|
if(pKey2->dwMode == MBDE_MODE_AES128) { pKey2->dwMode = MBDE_MODE_AES128_DIFFUSER; }
|
|
if(pKey2->dwMode == MBDE_MODE_AES256) { pKey2->dwMode = MBDE_MODE_AES256_DIFFUSER; }
|
|
memcpy(pKey1->pbKey2, pKey2->pbKey1, 32);
|
|
memcpy(pKey2->pbKey2, pKey1->pbKey1, 32);
|
|
MBDE_ContextKeyUpdate(ctxBDE, pKey1);
|
|
MBDE_ContextKeyUpdate(ctxBDE, pKey2);
|
|
pKey1->fWin8Merge = TRUE;
|
|
pKey2->fWin8Merge = TRUE;
|
|
VmmLog(H, ctxBDE->ctxP->MID, LOGLEVEL_TRACE, "Keys updated at %llx %llx", pKey1->va, pKey2->va);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Parse a Win10 14393+ bitlocker key.
|
|
* -- H
|
|
* -- ctxBDE
|
|
* -- pe
|
|
* -- pb
|
|
*/
|
|
VOID MBDE_Win10(_In_ VMM_HANDLE H, _In_ PMBDE_CONTEXT ctxBDE, _In_ PVMM_MAP_POOLENTRY pe, _In_ PBYTE pb)
|
|
{
|
|
MBDE_KEY e = { 0 };
|
|
MBDE_OFFSET o = { 0 };
|
|
DWORD i, dw, cbKey, dwMode;
|
|
BOOL fWin8 = (H->vmm.kernel.dwVersionBuild < 14393);
|
|
if(pe->cb > 0x1000) { return; }
|
|
if(H->vmm.f32) {
|
|
// 32-bit
|
|
if(pe->cb < (fWin8 ? 0x268UL : 0x400UL)) { return; }
|
|
if(H->vmm.kernel.dwVersionBuild <= 17134) {
|
|
o.o0 = 0x50; o.o1 = 0x54; o.o2 = 0x78; o.o3 = 0x98;
|
|
} else {
|
|
o.o0 = 0x54; o.o1 = 0x58; o.o2 = 0x7c; o.o3 = 0x9c;
|
|
}
|
|
} else {
|
|
// 64-bit
|
|
if(pe->cb < (fWin8 ? 0x290UL : 0x4c0UL)) { return; }
|
|
o.o0 = 0x54; o.o1 = 0x58; o.o2 = 0x7c; o.o3 = 0x9c;
|
|
}
|
|
e.va = pe->va;
|
|
e.cbBlob = pe->cb;
|
|
for(i = 0; i < pe->cb - 0x80; i += 4) {
|
|
if((*(PDWORD)(pb + i + 0x00) == 'UUUR') && (*(PDWORD)(pb + i + 0x20) == 'MSSK')) {
|
|
if(!memcmp(e.pbKey1, pb + i + o.o1, 16)) { continue; } // key already processed - continue!
|
|
// length/signature:
|
|
dw = *(PDWORD)(pb + i + o.o0);
|
|
if((dw != 0x10) && (dw != 0x20) && (dw != 0x40)) {
|
|
VmmLog(H, ctxBDE->ctxP->MID, LOGLEVEL_TRACE, "Failed candidate at %llx (signature)", pe->va);
|
|
return;
|
|
}
|
|
// mode & length:
|
|
dwMode = 0;
|
|
if(!memcmp(pb + i + o.o1, pb + i + o.o2, 16)) {
|
|
if(!memcmp(pb + i + o.o1, pb + i + o.o2, 32)) {
|
|
cbKey = 0x20;
|
|
dwMode = MBDE_MODE_AES256;
|
|
} else {
|
|
cbKey = 0x10;
|
|
dwMode = MBDE_MODE_AES128;
|
|
}
|
|
}
|
|
if(!memcmp(pb + i + o.o1, pb + i + o.o3, 16)) {
|
|
if(!memcmp(pb + i + o.o1, pb + i + o.o3, 32)) {
|
|
cbKey = 0x40;
|
|
dwMode = MBDE_MODE_AES256_XTS;
|
|
} else {
|
|
cbKey = 0x20;
|
|
dwMode = MBDE_MODE_AES128_XTS;
|
|
}
|
|
}
|
|
if(!dwMode || (e.dwMode && (e.dwMode != dwMode))) {
|
|
VmmLog(H, ctxBDE->ctxP->MID, LOGLEVEL_TRACE, "Failed candidate at %llx (mode #1)", pe->va);
|
|
return;
|
|
}
|
|
e.cbKey = cbKey;
|
|
e.dwMode = (BYTE)dwMode;
|
|
// key:
|
|
if(memcmp(e.pbKey1, pbMBDE_ZERO32, 32)) {
|
|
// pre-existing key - this is a tweak key (diffuser)
|
|
if((e.dwMode == MBDE_MODE_AES128_XTS) || (e.dwMode == MBDE_MODE_AES256_XTS)) {
|
|
VmmLog(H, ctxBDE->ctxP->MID, LOGLEVEL_TRACE, "Failed candidate at %llx (mode #2)", pe->va);
|
|
return;
|
|
}
|
|
e.dwMode = (e.cbKey == 16) ? MBDE_MODE_AES128_DIFFUSER : MBDE_MODE_AES256_DIFFUSER;
|
|
memcpy(e.pbKey2, pb + i + o.o1, e.cbKey);
|
|
break;
|
|
} else {
|
|
memcpy(e.pbKey12, pb + i + o.o1, e.cbKey);
|
|
}
|
|
}
|
|
}
|
|
// finish:
|
|
if(!memcmp(e.pbKey1, pbMBDE_ZERO32, 32)) {
|
|
VmmLog(H, ctxBDE->ctxP->MID, LOGLEVEL_TRACE, "Failed candidate at %llx (no key found)", pe->va);
|
|
return;
|
|
}
|
|
MBDE_ContextKeyAdd(H, ctxBDE, &e, pb);
|
|
}
|
|
|
|
/*
|
|
* Parse a Win7 bitlocker key.
|
|
* -- H
|
|
* -- ctxBDE
|
|
* -- pe
|
|
* -- pb
|
|
*/
|
|
VOID MBDE_Win7(_In_ VMM_HANDLE H, _In_ PMBDE_CONTEXT ctxBDE, _In_ PVMM_MAP_POOLENTRY pe, _In_ PBYTE pb)
|
|
{
|
|
MBDE_KEY e = { 0 };
|
|
MBDE_OFFSET o = { 0 };
|
|
// 1: initialize static offsets
|
|
if(H->vmm.f32) {
|
|
if(pe->cb == 0x1f0) { o.o0 = 0x10; o.o1 = 0x18; o.o2 = 0; }
|
|
if(pe->cb == 0x3c8) { o.o0 = 0x10; o.o1 = 0x18; o.o2 = 0x1f0; }
|
|
} else {
|
|
if(pe->cb == 0x200) { o.o0 = 0x1c; o.o1 = 0x20; o.o2 = 0; }
|
|
if(pe->cb == 0x3e0) { o.o0 = 0x1c; o.o1 = 0x20; o.o2 = 0x200; }
|
|
}
|
|
if(!o.o0) {
|
|
VmmLog(H, ctxBDE->ctxP->MID, LOGLEVEL_TRACE, "Failed candidate at %llx [%i] (length)", pe->va, pe->cb);
|
|
return;
|
|
}
|
|
// 2: parse mode & key
|
|
if((pb[o.o0] > 0x03) || (pb[o.o0 + 1] != 0x80)) {
|
|
VmmLog(H, ctxBDE->ctxP->MID, LOGLEVEL_TRACE, "Failed candidate at %llx (mode)", pe->va);
|
|
return;
|
|
}
|
|
e.va = pe->va;
|
|
e.cbBlob = pe->cb;
|
|
e.dwMode = pb[o.o0];
|
|
e.cbKey = ((e.dwMode == 0x00) || (e.dwMode == 0x02)) ? 16 : 32;
|
|
memcpy(e.pbKey1, pb + o.o1, e.cbKey);
|
|
if((e.dwMode == MBDE_MODE_AES128_DIFFUSER) || (e.dwMode == MBDE_MODE_AES256_DIFFUSER)) {
|
|
memcpy(e.pbKey2, pb + o.o2, e.cbKey);
|
|
}
|
|
MBDE_ContextKeyAdd(H, ctxBDE, &e, pb);
|
|
}
|
|
|
|
/*
|
|
* Pool scanner function - locates bitlocker pool tags and send them onwards
|
|
* for analysis in os-dependent pfnCB callback function.
|
|
* -- H
|
|
* -- ctxBDE
|
|
* -- dwPoolTag
|
|
* -- pfnCB
|
|
*/
|
|
VOID MBDE_PoolScan(_In_ VMM_HANDLE H, _In_ PMBDE_CONTEXT ctxBDE, _In_ DWORD dwPoolTag, _In_ VOID(*pfnCB)(VMM_HANDLE H, PMBDE_CONTEXT, PVMM_MAP_POOLENTRY, PBYTE))
|
|
{
|
|
DWORD i, j;
|
|
BYTE pb[4096];
|
|
PVMM_MAP_POOLENTRY pe;
|
|
PVMM_MAP_POOLENTRYTAG pTag;
|
|
for(i = 0; i < ctxBDE->pPoolMap->cTag; i++) {
|
|
pTag = ctxBDE->pPoolMap->pTag + i;
|
|
if(pTag->dwTag == dwPoolTag) {
|
|
for(j = 0; j < pTag->cEntry; j++) {
|
|
pe = ctxBDE->pPoolMap->pMap + ctxBDE->pPoolMap->piTag2Map[pTag->iTag2Map + j];
|
|
if((pe->cb < sizeof(pb)) && VmmRead(H, PVMM_PROCESS_SYSTEM, pe->va, pb, pe->cb)) {
|
|
pfnCB(H, ctxBDE, pe, pb);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Build a new context with results.
|
|
* CALLER DECREF: return
|
|
* -- H
|
|
* -- ctxP
|
|
* -- return
|
|
*/
|
|
POB_MAP MBDE_ContextFetch_DoWork(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP)
|
|
{
|
|
MBDE_CONTEXT ctxBDE = { 0 };
|
|
DWORD dwBuild = H->vmm.kernel.dwVersionBuild;
|
|
VmmLog(H, ctxP->MID, LOGLEVEL_TRACE, "Initialization started");
|
|
// 1: context init
|
|
ctxBDE.ctxP = ctxP;
|
|
ctxBDE.pmBDE = ObMap_New(H, OB_MAP_FLAGS_OBJECT_LOCALFREE);
|
|
VmmMap_GetPool(H, &ctxBDE.pPoolMap, TRUE);
|
|
if(!ctxBDE.pmBDE || !ctxBDE.pPoolMap) {
|
|
VmmLog(H, ctxP->MID, LOGLEVEL_VERBOSE, "Initialization failed: POOL/OOM");
|
|
goto fail;
|
|
}
|
|
// 2: dispatch
|
|
if(dwBuild >= 14393) {
|
|
MBDE_PoolScan(H, &ctxBDE, 'enoN', MBDE_Win10);
|
|
} else if(dwBuild >= 9200) {
|
|
ctxBDE.fWin8 = TRUE;
|
|
MBDE_PoolScan(H, &ctxBDE, 'bgnC', MBDE_Win10);
|
|
MBDE_Win8_PostProcess(H, &ctxBDE);
|
|
} else if(dwBuild >= 7600) {
|
|
MBDE_PoolScan(H, &ctxBDE, 'cEVF', MBDE_Win7);
|
|
}
|
|
VmmLog(H, ctxP->MID, LOGLEVEL_TRACE, "Initialization completed");
|
|
fail:
|
|
Ob_DECREF(ctxBDE.pPoolMap);
|
|
return ctxBDE.pmBDE;
|
|
}
|
|
|
|
/*
|
|
* Fetch, and if required, build a new context with results.
|
|
* CALLER DECREF: return
|
|
* -- H
|
|
* -- ctxP
|
|
* -- return
|
|
*/
|
|
POB_MAP MBDE_ContextFetch(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP)
|
|
{
|
|
POB_MAP pmObBDE = ObContainer_GetOb((POB_CONTAINER)ctxP->ctxM);
|
|
if(!pmObBDE) {
|
|
EnterCriticalSection(&H->vmm.LockPlugin);
|
|
pmObBDE = ObContainer_GetOb((POB_CONTAINER)ctxP->ctxM);
|
|
if(!pmObBDE) {
|
|
pmObBDE = MBDE_ContextFetch_DoWork(H, ctxP);
|
|
ObContainer_SetOb((POB_CONTAINER)ctxP->ctxM, pmObBDE);
|
|
}
|
|
LeaveCriticalSection(&H->vmm.LockPlugin);
|
|
}
|
|
return pmObBDE;
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// PLUGIN ACCESS FUNCTIONALITY BELOW:
|
|
// ----------------------------------------------------------------------------
|
|
|
|
VOID MBDE_FcLogJSON(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, _In_ VOID(*pfnLogJSON)(_In_ VMM_HANDLE H, _In_ PVMMDLL_FORENSIC_JSONDATA pData))
|
|
{
|
|
DWORD i, cKey = 0;
|
|
PMBDE_KEY pKey = NULL;
|
|
POB_MAP pmObBDE = NULL;
|
|
CHAR szDislocker[MAX_PATH];
|
|
PVMMDLL_FORENSIC_JSONDATA pd = NULL;
|
|
if(ctxP->pProcess) { return; }
|
|
if(!(pmObBDE = MBDE_ContextFetch(H, ctxP))) { goto fail; }
|
|
if(!(pd = LocalAlloc(LMEM_ZEROINIT, sizeof(VMMDLL_FORENSIC_JSONDATA)))) { goto fail; }
|
|
pd->dwVersion = VMMDLL_FORENSIC_JSONDATA_VERSION;
|
|
pd->szjType = "bitlocker";
|
|
while((pKey = ObMap_GetNext(pmObBDE, pKey))) {
|
|
for(i = 0; i < sizeof(pKey->pbDislocker); i++) {
|
|
snprintf(szDislocker + i * 2ULL, 3, "%02x", pKey->pbDislocker[i]);
|
|
}
|
|
pd->i = cKey++;
|
|
pd->vaObj = pKey->va;
|
|
pd->usz[0] = szMBDE_MODE_STR[pKey->dwMode];
|
|
pd->usz[1] = szDislocker;
|
|
pfnLogJSON(H, pd);
|
|
}
|
|
fail:
|
|
Ob_DECREF(pmObBDE);
|
|
LocalFree(pd);
|
|
}
|
|
|
|
NTSTATUS MBDE_ReadInternal(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, _In_ POB_MAP pmObBDE, _Out_ LPVOID pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ ULONG64 cbOffset)
|
|
{
|
|
PMBDE_KEY pKey = NULL;
|
|
if(0 == _stricmp("readme.txt", ctxP->uszPath)) {
|
|
return VMMDLL_UtilVfsReadFile_FromPBYTE(szMBDE_README, strlen(szMBDE_README), pb, cb, pcbRead, cbOffset);
|
|
}
|
|
while((pKey = ObMap_GetNext(pmObBDE, pKey))) {
|
|
if(0 == _stricmp(pKey->szNameBIN, ctxP->uszPath)) {
|
|
return VMMDLL_UtilVfsReadFile_FromPBYTE(pKey->pbBlob, pKey->cbBlob, pb, cb, pcbRead, cbOffset);
|
|
}
|
|
if(0 == _stricmp(pKey->szNameTXT, ctxP->uszPath)) {
|
|
return VMMDLL_UtilVfsReadFile_FromPBYTE(pKey->szTXT, strlen(pKey->szTXT), pb, cb, pcbRead, cbOffset);
|
|
}
|
|
if(0 == _stricmp(pKey->szNameFVEK, ctxP->uszPath)) {
|
|
return VMMDLL_UtilVfsReadFile_FromPBYTE(pKey->pbDislocker, sizeof(pKey->pbDislocker), pb, cb, pcbRead, cbOffset);
|
|
}
|
|
}
|
|
return VMMDLL_STATUS_FILE_INVALID;
|
|
}
|
|
|
|
NTSTATUS MBDE_Read(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, _Out_writes_to_(cb, *pcbRead) PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbRead, _In_ ULONG64 cbOffset)
|
|
{
|
|
NTSTATUS nt = VMMDLL_STATUS_FILE_INVALID;
|
|
POB_MAP pmObBDE = MBDE_ContextFetch(H, ctxP);
|
|
*pcbRead = 0;
|
|
if(pmObBDE) {
|
|
nt = MBDE_ReadInternal(H, ctxP, pmObBDE, pb, cb, pcbRead, cbOffset);
|
|
}
|
|
Ob_DECREF(pmObBDE);
|
|
return nt;
|
|
}
|
|
|
|
BOOL MBDE_List(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, _Inout_ PHANDLE pFileList)
|
|
{
|
|
PMBDE_KEY pKey = NULL;
|
|
POB_MAP pmObBDE = MBDE_ContextFetch(H, ctxP);
|
|
if(!ctxP->uszPath[0]) {
|
|
VMMDLL_VfsList_AddFile(pFileList, "readme.txt", strlen(szMBDE_README), NULL);
|
|
while((pKey = ObMap_GetNext(pmObBDE, pKey))) {
|
|
VMMDLL_VfsList_AddFile(pFileList, pKey->szNameTXT, strlen(pKey->szTXT), NULL);
|
|
VMMDLL_VfsList_AddFile(pFileList, pKey->szNameFVEK, 66, NULL);
|
|
VMMDLL_VfsList_AddFile(pFileList, pKey->szNameBIN, pKey->cbBlob, NULL);
|
|
}
|
|
}
|
|
Ob_DECREF(pmObBDE);
|
|
return TRUE;
|
|
}
|
|
|
|
VOID MBDE_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_REFRESH_SLOW) {
|
|
ObContainer_SetOb((POB_CONTAINER)ctxP->ctxM, NULL);
|
|
}
|
|
}
|
|
|
|
VOID MBDE_Close(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP)
|
|
{
|
|
Ob_DECREF((POB_CONTAINER)ctxP->ctxM);
|
|
}
|
|
|
|
VOID M_BDE_Initialize(_In_ VMM_HANDLE H, _Inout_ PVMMDLL_PLUGIN_REGINFO pRI)
|
|
{
|
|
if((pRI->magic != VMMDLL_PLUGIN_REGINFO_MAGIC) || (pRI->wVersion != VMMDLL_PLUGIN_REGINFO_VERSION)) { return; }
|
|
// the bitlocker plugin is only supported on: 64-bit Windows or 32-bit Windows 10 14393 or later.
|
|
if((pRI->tpSystem != VMMDLL_SYSTEM_WINDOWS_32) && (pRI->tpSystem != VMMDLL_SYSTEM_WINDOWS_64)) { return; }
|
|
if(pRI->sysinfo.dwVersionBuild < 7600) { return; } // WIN7+ are supported
|
|
if(!(pRI->reg_info.ctxM = (PVMMDLL_PLUGIN_INTERNAL_CONTEXT)ObContainer_New())) { return; }
|
|
strcpy_s(pRI->reg_info.uszPathName, 128, "\\misc\\bitlocker"); // module name - 'bitlocker'.
|
|
pRI->reg_info.fRootModule = TRUE; // module shows in root.
|
|
pRI->reg_fn.pfnList = MBDE_List; // List function supported.
|
|
pRI->reg_fn.pfnRead = MBDE_Read; // Read function supported.
|
|
pRI->reg_fn.pfnNotify = MBDE_Notify; // Notify function supported.
|
|
pRI->reg_fn.pfnClose = MBDE_Close; // Close function supported.
|
|
pRI->reg_fnfc.pfnLogJSON = MBDE_FcLogJSON; // JSON log function supported
|
|
pRI->pfnPluginManager_Register(H, pRI); // Register with the plugin manager
|
|
}
|