Version 5.18.10

This commit is contained in:
Ulf Frisk
2026-09-08 23:05:20 +02:00
parent 113c4d341a
commit 1172b5b2eb
29 changed files with 422 additions and 28 deletions
+1
View File
@@ -269,3 +269,4 @@ Latest:
* New module: /sys/eventlog (Windows event log recovery).
* New module: /sys/proc/cpu - process CPU usage, current and total.
* New API function: VMMDLL_MemVirtualQuery.
* New API function: VMMDLL_WinReg_QueryNameOriginalU.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+35 -1
View File
@@ -11,7 +11,7 @@
// (c) Ulf Frisk, 2018-2026
// Author: Ulf Frisk, [email protected]
//
// Header Version: 5.18.9
// Header Version: 5.18.10
//
#include "leechcore.h"
@@ -2842,6 +2842,23 @@ BOOL VMMDLL_WinReg_EnumValueU(
_Inout_opt_ LPDWORD lpcbData
);
/*
* Retrieve the original UTF-8 name of a registry key or value.
* -- uszFullPath = existing file-system path, including the value name if fValue.
* -- fValue = TRUE for a value, FALSE for a key.
* -- uszName = output buffer, or NULL to query the required size.
* -- pcbName = buffer capacity on input.
* -- return
*/
EXPORTED_FUNCTION _Success_(return)
BOOL VMMDLL_WinReg_QueryNameOriginalU(
_In_ VMM_HANDLE hVMM,
_In_ LPCSTR uszFullPath,
_In_ BOOL fValue,
_Out_writes_opt_(*pcbName) LPSTR uszName,
_When_(uszName == NULL, _Out_) _When_(uszName != NULL, _Inout_) PDWORD pcbName
);
/*
* Query a registry value given a registry key/value path - similar to WINAPI
* function 'RegQueryValueEx'.
@@ -2924,6 +2941,23 @@ BOOL VMMDLL_WinReg_EnumValueW(
_Inout_opt_ LPDWORD lpcbData
);
/*
* Retrieve the original UTF-16 name of a registry key or value.
* -- wszFullPath = existing file-system path, including the value name if fValue.
* -- fValue = TRUE for a value, FALSE for a key.
* -- wszName = output buffer, or NULL to query the required size.
* -- pcchName = buffer capacity on input.
* -- return
*/
_Success_(return)
BOOL VMMDLL_WinReg_QueryNameOriginalW(
_In_ VMM_HANDLE hVMM,
_In_ LPCWSTR wszFullPath,
_In_ BOOL fValue,
_Out_writes_opt_(*pcchName) LPWSTR wszName,
_When_(wszName == NULL, _Out_) _When_(wszName != NULL, _Inout_) PDWORD pcchName
);
/*
* Query a registry value given a registry key/value path - similar to WINAPI
* function 'RegQueryValueEx'.
+2 -2
View File
@@ -3,8 +3,8 @@
#define VERSION_MAJOR 5
#define VERSION_MINOR 18
#define VERSION_REVISION 9
#define VERSION_BUILD 248
#define VERSION_REVISION 10
#define VERSION_BUILD 249
#define VER_FILE_DESCRIPTION_STR "MemProcFS : Plugin vmemd"
#define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD
+2 -2
View File
@@ -3,8 +3,8 @@
#define VERSION_MAJOR 5
#define VERSION_MINOR 18
#define VERSION_REVISION 9
#define VERSION_BUILD 248
#define VERSION_REVISION 10
#define VERSION_BUILD 249
#define VER_FILE_DESCRIPTION_STR "MemProcFS"
#define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD
+4
View File
@@ -89,6 +89,8 @@ typedef enum tdSTATISTICS_ID {
STATISTICS_ID_VMMDLL_WinRegHive_Write,
STATISTICS_ID_VMMDLL_WinReg_EnumKeyExW,
STATISTICS_ID_VMMDLL_WinReg_EnumValueW,
STATISTICS_ID_VMMDLL_WinReg_QueryNameOriginalU,
STATISTICS_ID_VMMDLL_WinReg_QueryNameOriginalW,
STATISTICS_ID_VMMDLL_WinReg_QueryValueEx,
STATISTICS_ID_VMMDLL_YaraSearch,
STATISTICS_ID_VMMDLL_PdbLoad,
@@ -176,6 +178,8 @@ static LPCSTR STATISTICS_ID_STR[STATISTICS_ID_MAX] = {
[STATISTICS_ID_VMMDLL_WinRegHive_Write] = "VMMDLL_WinRegHive_Write",
[STATISTICS_ID_VMMDLL_WinReg_EnumKeyExW] = "VMMDLL_WinReg_EnumKeyExW",
[STATISTICS_ID_VMMDLL_WinReg_EnumValueW] = "VMMDLL_WinReg_EnumValueW",
[STATISTICS_ID_VMMDLL_WinReg_QueryNameOriginalU] = "VMMDLL_WinReg_QueryNameOriginalU",
[STATISTICS_ID_VMMDLL_WinReg_QueryNameOriginalW] = "VMMDLL_WinReg_QueryNameOriginalW",
[STATISTICS_ID_VMMDLL_WinReg_QueryValueEx] = "VMMDLL_WinReg_QueryValueEx",
[STATISTICS_ID_VMMDLL_VmGetVmmHandle] = "VMMDLL_VmGetVmmHandle",
[STATISTICS_ID_VMMDLL_VmMemTranslateGPA] = "VMMDLL_VmMemTranslateGPA",
+2 -2
View File
@@ -3,8 +3,8 @@
#define VERSION_MAJOR 5
#define VERSION_MINOR 18
#define VERSION_REVISION 9
#define VERSION_BUILD 248
#define VERSION_REVISION 10
#define VERSION_BUILD 249
#define VER_FILE_DESCRIPTION_STR "MemProcFS : Core"
#define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD
+41
View File
@@ -3075,6 +3075,47 @@ _Success_(return) BOOL VMMDLL_WinReg_EnumValueW(_In_ VMM_HANDLE H, _In_ LPCWSTR
CALL_IMPLEMENTATION_VMM(H, STATISTICS_ID_VMMDLL_WinReg_EnumValueW, VMMDLL_WinReg_EnumValue_Impl(H, NULL, wszFullPathKey, dwIndex, (PBYTE)lpValueName, *lpcchValueName << 1, lpcchValueName, lpType, lpData, lpcbData))
}
_Success_(return) BOOL VMMDLL_WinReg_QueryNameOriginalU(_In_ VMM_HANDLE H, _In_ LPCSTR uszFullPath, _In_ BOOL fValue, _Out_writes_opt_(*pcbName) LPSTR uszName, _When_(uszName == NULL, _Out_) _When_(uszName != NULL, _Inout_) PDWORD pcbName)
{
if(!pcbName) { return FALSE; }
CALL_IMPLEMENTATION_VMM(H, STATISTICS_ID_VMMDLL_WinReg_QueryNameOriginalU, VmmWinReg_QueryNameOriginal(H, uszFullPath, fValue, (PBYTE)uszName, uszName ? *pcbName : 0, pcbName))
}
_Success_(return) BOOL VMMDLL_WinReg_QueryNameOriginalW(_In_ VMM_HANDLE H, _In_ LPCWSTR wszFullPath, _In_ BOOL fValue, _Out_writes_opt_(*pcchName) LPWSTR wszName, _When_(wszName == NULL, _Out_) _When_(wszName != NULL, _Inout_) PDWORD pcchName)
{
BOOL f = FALSE;
QWORD tm;
DWORD cchName, cbuName = 0, cbwName = 0, cbwPart, o;
LPSTR uszFullPath, uszName = NULL;
PBYTE pbName = NULL;
BYTE pbBuffer[2 * MAX_PATH];
if(!pcchName) { return FALSE; }
cchName = wszName ? *pcchName : 0;
*pcchName = 0;
if(!wszFullPath || !CharUtil_WtoU(wszFullPath, -1, pbBuffer, sizeof(pbBuffer), &uszFullPath, NULL, 0)) { return FALSE; }
if(!VmmDllCore_HandleReserveExternal(H)) { return FALSE; }
tm = Statistics_CallStart(H);
if(!VmmWinReg_QueryNameOriginal(H, uszFullPath, fValue, NULL, 0, &cbuName)) { goto finish; }
if(!(uszName = LocalAlloc(0, cbuName))) { goto finish; }
if(!VmmWinReg_QueryNameOriginal(H, uszFullPath, fValue, (PBYTE)uszName, cbuName, &cbuName)) { goto finish; }
if(!(pbName = LocalAlloc(0, 2 * cbuName))) { goto finish; }
// Convert each null-delimited part to preserve embedded nulls in the name.
for(o = 0; o < cbuName; o += (DWORD)strlen(uszName + o) + 1) {
if(!CharUtil_UtoW(uszName + o, cbuName - o, pbName + cbwName, 2 * cbuName - cbwName, NULL, &cbwPart, CHARUTIL_FLAG_STR_BUFONLY)) { goto finish; }
cbwName += cbwPart;
}
*pcchName = cbwName / 2;
if(wszName && (cchName < *pcchName)) { goto finish; }
if(wszName) { memcpy(wszName, pbName, cbwName); }
f = TRUE;
finish:
LocalFree(pbName);
LocalFree(uszName);
Statistics_CallEnd(H, STATISTICS_ID_VMMDLL_WinReg_QueryNameOriginalW, tm);
VmmDllCore_HandleReturnExternal(H);
return f;
}
_Success_(return) BOOL VMMDLL_WinReg_QueryValueExU(_In_ VMM_HANDLE H, _In_ LPCSTR uszFullPathKeyValue, _Out_opt_ LPDWORD lpType, _Out_writes_opt_(*lpcbData) LPBYTE lpData, _When_(lpData == NULL, _Out_opt_) _When_(lpData != NULL, _Inout_opt_) LPDWORD lpcbData)
{
CALL_IMPLEMENTATION_VMM(H, STATISTICS_ID_VMMDLL_WinReg_QueryValueEx, VmmWinReg_ValueQuery2(H, uszFullPathKeyValue, lpType, lpData, lpcbData ? *lpcbData : 0, lpcbData))
+2
View File
@@ -119,6 +119,8 @@ EXPORTS
VMMDLL_WinReg_EnumKeyExW
VMMDLL_WinReg_EnumValueU
VMMDLL_WinReg_EnumValueW
VMMDLL_WinReg_QueryNameOriginalU
VMMDLL_WinReg_QueryNameOriginalW
VMMDLL_WinReg_QueryValueExU
VMMDLL_WinReg_QueryValueExW
+35 -1
View File
@@ -11,7 +11,7 @@
// (c) Ulf Frisk, 2018-2026
// Author: Ulf Frisk, [email protected]
//
// Header Version: 5.18.9
// Header Version: 5.18.10
//
#include "leechcore.h"
@@ -2842,6 +2842,23 @@ BOOL VMMDLL_WinReg_EnumValueU(
_Inout_opt_ LPDWORD lpcbData
);
/*
* Retrieve the original UTF-8 name of a registry key or value.
* -- uszFullPath = existing file-system path, including the value name if fValue.
* -- fValue = TRUE for a value, FALSE for a key.
* -- uszName = output buffer, or NULL to query the required size.
* -- pcbName = buffer capacity on input.
* -- return
*/
EXPORTED_FUNCTION _Success_(return)
BOOL VMMDLL_WinReg_QueryNameOriginalU(
_In_ VMM_HANDLE hVMM,
_In_ LPCSTR uszFullPath,
_In_ BOOL fValue,
_Out_writes_opt_(*pcbName) LPSTR uszName,
_When_(uszName == NULL, _Out_) _When_(uszName != NULL, _Inout_) PDWORD pcbName
);
/*
* Query a registry value given a registry key/value path - similar to WINAPI
* function 'RegQueryValueEx'.
@@ -2924,6 +2941,23 @@ BOOL VMMDLL_WinReg_EnumValueW(
_Inout_opt_ LPDWORD lpcbData
);
/*
* Retrieve the original UTF-16 name of a registry key or value.
* -- wszFullPath = existing file-system path, including the value name if fValue.
* -- fValue = TRUE for a value, FALSE for a key.
* -- wszName = output buffer, or NULL to query the required size.
* -- pcchName = buffer capacity on input.
* -- return
*/
_Success_(return)
BOOL VMMDLL_WinReg_QueryNameOriginalW(
_In_ VMM_HANDLE hVMM,
_In_ LPCWSTR wszFullPath,
_In_ BOOL fValue,
_Out_writes_opt_(*pcchName) LPWSTR wszName,
_When_(wszName == NULL, _Out_) _When_(wszName != NULL, _Inout_) PDWORD pcchName
);
/*
* Query a registry value given a registry key/value path - similar to WINAPI
* function 'RegQueryValueEx'.
+88 -9
View File
@@ -1243,7 +1243,7 @@ typedef struct tdREG_CM_KEY_NODE {
DWORD MaxValueNameLen; // +0x03c MaxValueNameLen : Uint4B
DWORD MaxValueDataLen; // +0x040 MaxValueDataLen : Uint4B
DWORD WorkVar; // +0x044 WorkVar : Uint4B
WORD NameLength; // +0x048 NameLength : Uint2B
WORD NameLength; // +0x048 NameLength : Uint2B (bytes)
WORD ClassLength; // +0x04a ClassLength : Uint2B
union {
CHAR szName[1]; // +0x04c Name : [1] Wchar
@@ -1254,7 +1254,7 @@ typedef struct tdREG_CM_KEY_NODE {
// "vk-key-value"
typedef struct tdREG_CM_KEY_VALUE {
WORD Signature; // 0x000 Signature : Uint2B
WORD NameLength; // 0x002 NameLength : Uint2B
WORD NameLength; // 0x002 NameLength : Uint2B (bytes)
DWORD DataLength; // 0x004 DataLength : Uint4B
DWORD Data; // 0x008 Data : Uint4B
DWORD Type; // 0x00c Type : Uint4B
@@ -1326,7 +1326,7 @@ DWORD VmmWinReg_KeyHashName(_In_ PREG_CM_KEY_NODE pnk, _In_ DWORD iSuffix)
CHAR uszBuffer[2 * MAX_PATH];
c = (pnk->Flags & REG_CM_KEY_NODE_FLAGS_COMP_NAME) ?
CharUtil_FixFsName(uszBuffer, sizeof(uszBuffer), NULL, pnk->szName, NULL, pnk->NameLength, iSuffix, TRUE) :
CharUtil_FixFsName(uszBuffer, sizeof(uszBuffer), NULL, NULL, pnk->wszName, pnk->NameLength, iSuffix, TRUE);
CharUtil_FixFsName(uszBuffer, sizeof(uszBuffer), NULL, NULL, pnk->wszName, pnk->NameLength / 2, iSuffix, TRUE);
if(c) { c--; }
for(i = 0; i < c; i++) {
dwHash = ((dwHash >> 13) | (dwHash << 19)) + (UCHAR)uszBuffer[i];
@@ -1418,7 +1418,7 @@ POB_REGISTRY_KEY VmmWinReg_KeyInitializeCreateKey(_In_ VMM_HANDLE H, _In_ POB_RE
cbKey = cbCell - 4;
pnk = (PREG_CM_KEY_NODE)(pHive->Snapshot._DUAL[iSV].pb + oCellRaw + 4);
if(pnk->Signature != REG_CM_KEY_SIGNATURE_KEYNODE) { goto fail; }
if(((QWORD)pnk->NameLength << ((pnk->Flags & REG_CM_KEY_NODE_FLAGS_COMP_NAME) ? 0 : 1)) > (cbKey - REG_CM_KEY_NODE_SIZEOF)) { goto fail; }
if((pnk->NameLength > cbKey - REG_CM_KEY_NODE_SIZEOF) || (!(pnk->Flags & REG_CM_KEY_NODE_FLAGS_COMP_NAME) && (pnk->NameLength & 1))) { goto fail; }
if(pnk->Parent == oCell) { goto fail; }
// 3: get parent key
pObKeyParent = ObMap_GetByKey(pHive->Snapshot.pmKeyOffset, pnk->Parent);
@@ -1495,7 +1495,7 @@ POB_REGISTRY_KEY VmmWinReg_KeyInitializeRootKeyDummy(_In_ VMM_HANDLE H, _In_ POB
pObKey->dwCellHead = pObKey->oCell + (fActive ? 0x80000000 : 0);
pObKey->pKey = (PREG_CM_KEY_NODE)((PBYTE)pObKey + sizeof(OB_REGISTRY_KEY));
CharUtil_UtoW(uszName, -1, (PBYTE)&pObKey->pKey->wszName, 2 * cbuName, NULL, &cbw, CHARUTIL_FLAG_TRUNCATE_ONFAIL_NULLSTR | CHARUTIL_FLAG_STR_BUFONLY);
pObKey->pKey->NameLength = cbw ? (WORD)(cbw >> 1) - 1 : 0;
pObKey->pKey->NameLength = cbw ? (WORD)(cbw - 2) : 0;
// 2: calculate lookup hashes
pObKey->qwHashKeyParent = qwKeyParentHash;
pObKey->qwHashKeyThis = CharUtil_HashNameFsU(uszName, 0) + ((pObKey->qwHashKeyParent >> 13) | (pObKey->qwHashKeyParent << 51));
@@ -1618,7 +1618,7 @@ POB_REGISTRY_VALUE VmmWinReg_KeyValueGetByOffset(_In_ VMM_HANDLE H, _In_ POB_REG
cbKeyValue = cbCell - 4;
pvk = (PREG_CM_KEY_VALUE)(pHive->Snapshot._DUAL[REG_CELL_SV(oCell)].pb + REG_CELL_ORAW(oCell) + 4);
if(pvk->Signature != REG_CM_KEY_SIGNATURE_KEYVALUE) { return NULL; }
if(((QWORD)pvk->NameLength << ((pvk->Flags & REG_CM_KEY_VALUE_FLAGS_COMP_NAME) ? 0 : 1)) > (cbKeyValue - REG_CM_KEY_VALUE_SIZEOF)) { return NULL; }
if((pvk->NameLength > cbKeyValue - REG_CM_KEY_VALUE_SIZEOF) || (!(pvk->Flags & REG_CM_KEY_VALUE_FLAGS_COMP_NAME) && (pvk->NameLength & 1))) { return NULL; }
// 2: allocate and prepare
pObKeyValue = Ob_AllocEx(H, OB_TAG_REG_KEYVALUE, LMEM_ZEROINIT, sizeof(OB_REGISTRY_VALUE), NULL, NULL);
if(!pObKeyValue) { return NULL; }
@@ -2025,6 +2025,85 @@ POB_MAP VmmWinReg_KeyList(_In_ VMM_HANDLE H, _In_ POB_REGISTRY_HIVE pHive, _In_o
return pmObSubkeys;
}
/*
* Copy a counted hive name without file-system substitutions or truncation.
* NameLength in both nk and vk cells is in bytes, including compressed names.
* Preserve embedded nulls; pcbName includes the additional terminating null.
*/
_Success_(return)
static BOOL VmmWinReg_NameOriginal(_In_reads_(cbName) PBYTE pbName, _In_ DWORD cbName, _In_ BOOL fCompressed, _Out_writes_opt_(cb) PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbName)
{
static const BYTE pbPrefix[5] = { 0, 0, 0xc0, 0xe0, 0xf0 };
DWORD i, j, iPass, cch, cp, cpLow, cbOut, cbChar;
*pcbName = 0;
if(!fCompressed && (cbName & 1)) { return FALSE; }
cch = fCompressed ? cbName : cbName / 2;
// Validate and size first, then encode without stopping at embedded nulls.
for(iPass = 0; iPass < 2; iPass++) {
cbOut = 0;
for(i = 0; i < cch; i++) {
cp = fCompressed ? pbName[i] : pbName[2 * i] | ((DWORD)pbName[2 * i + 1] << 8);
if((cp >= 0xd800) && (cp <= 0xdfff)) {
if((cp >= 0xdc00) || (++i == cch)) { return FALSE; }
cpLow = pbName[2 * i] | ((DWORD)pbName[2 * i + 1] << 8);
if((cpLow < 0xdc00) || (cpLow > 0xdfff)) { return FALSE; }
cp = 0x10000 + ((cp - 0xd800) << 10) + (cpLow - 0xdc00);
}
cbChar = 1 + (cp > 0x7f) + (cp > 0x7ff) + (cp > 0xffff);
if(iPass) {
for(j = cbChar - 1; j; j--) { pb[cbOut + j] = 0x80 | (cp & 0x3f); cp >>= 6; }
pb[cbOut] = (BYTE)cp | pbPrefix[cbChar];
}
cbOut += cbChar;
}
if(!iPass) {
*pcbName = cbOut + 1;
if(!pb) { return TRUE; }
if(cb <= cbOut) { return FALSE; }
}
}
pb[cbOut] = 0;
return TRUE;
}
_Success_(return)
BOOL VmmWinReg_QueryNameOriginal(_In_ VMM_HANDLE H, _In_ LPCSTR uszFullPath, _In_ BOOL fValue, _Out_writes_opt_(cb) PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbName)
{
BOOL f = FALSE;
CHAR uszPath[MAX_PATH], uszParent[MAX_PATH];
LPSTR uszName = NULL;
PBYTE pbCell;
PREG_CM_KEY_NODE pnk, pnkRoot;
POB_REGISTRY_HIVE pObHive = NULL;
POB_REGISTRY_KEY pObKey = NULL;
POB_REGISTRY_VALUE pObValue = NULL;
*pcbName = 0;
if(!uszFullPath || !VmmWinReg_PathHiveGetByFullPath(H, uszFullPath, &pObHive, uszPath)) { goto finish; }
if(fValue && !(uszName = CharUtil_PathSplitLastEx(uszPath, uszParent, sizeof(uszParent)))) { goto finish; }
if(!(pObKey = VmmWinReg_KeyGetByPath(H, pObHive, fValue ? uszParent : uszPath))) { goto finish; }
if(fValue) {
if(!(pObValue = VmmWinReg_ValueByKeyAndName(H, pObHive, pObKey, uszName))) { goto finish; }
f = VmmWinReg_NameOriginal((PBYTE)pObValue->pValue->szName, pObValue->pValue->NameLength,
pObValue->pValue->Flags & REG_CM_KEY_VALUE_FLAGS_COMP_NAME, pb, cb, pcbName);
} else {
pnk = pObKey->pKey;
// Use the backing nk cell for synthetic ROOT, when available.
if(!pnk->Signature && VmmWinReg_KeyValidateCellSize(pObHive, pObKey->oCell, REG_CM_KEY_NODE_SIZEOF + 4, 0x1000)) {
pbCell = pObHive->Snapshot._DUAL[REG_CELL_SV(pObKey->oCell)].pb + REG_CELL_ORAW(pObKey->oCell);
pnkRoot = (PREG_CM_KEY_NODE)(pbCell + 4);
f = pnkRoot->Signature == REG_CM_KEY_SIGNATURE_KEYNODE;
f = f && (pnkRoot->NameLength <= REG_CELL_SIZE(*(PDWORD)pbCell) - 4 - REG_CM_KEY_NODE_SIZEOF);
if(f) { pnk = pnkRoot; }
}
f = VmmWinReg_NameOriginal((PBYTE)pnk->szName, pnk->NameLength, pnk->Flags & REG_CM_KEY_NODE_FLAGS_COMP_NAME, pb, cb, pcbName);
}
finish:
Ob_DECREF(pObValue);
Ob_DECREF(pObKey);
Ob_DECREF(pObHive);
return f;
}
/*
* Retrieve information about a registry key.
* -- pHive
@@ -2040,7 +2119,7 @@ VOID VmmWinReg_KeyInfo(_In_ POB_REGISTRY_HIVE pHive, _In_ POB_REGISTRY_KEY pKey,
if(pKey->pKey->Flags & REG_CM_KEY_NODE_FLAGS_COMP_NAME) {
pKeyInfo->cbuName = CharUtil_FixFsName(pKeyInfo->uszName, sizeof(pKeyInfo->uszName), NULL, pKey->pKey->szName, NULL, pKey->pKey->NameLength, pKey->iSuffix, FALSE);
} else {
pKeyInfo->cbuName = CharUtil_FixFsName(pKeyInfo->uszName, sizeof(pKeyInfo->uszName), NULL, NULL, pKey->pKey->wszName, pKey->pKey->NameLength, pKey->iSuffix, FALSE);
pKeyInfo->cbuName = CharUtil_FixFsName(pKeyInfo->uszName, sizeof(pKeyInfo->uszName), NULL, NULL, pKey->pKey->wszName, pKey->pKey->NameLength / 2, pKey->iSuffix, FALSE);
}
}
@@ -2182,7 +2261,7 @@ VOID VmmWinReg_ValueInfo(_In_ POB_REGISTRY_HIVE pHive, _In_ POB_REGISTRY_VALUE p
} else if(pValue->pValue->Flags & REG_CM_KEY_VALUE_FLAGS_COMP_NAME) {
pValueInfo->cbuName = CharUtil_FixFsName(pValueInfo->uszName, sizeof(pValueInfo->uszName), NULL, pValue->pValue->szName, NULL, pValue->pValue->NameLength, 0, FALSE);
} else {
pValueInfo->cbuName = CharUtil_FixFsName(pValueInfo->uszName, sizeof(pValueInfo->uszName), NULL, NULL, pValue->pValue->wszName, pValue->pValue->NameLength, 0, FALSE);
pValueInfo->cbuName = CharUtil_FixFsName(pValueInfo->uszName, sizeof(pValueInfo->uszName), NULL, NULL, pValue->pValue->wszName, pValue->pValue->NameLength / 2, 0, FALSE);
}
}
@@ -2425,7 +2504,7 @@ BOOL VmmWinReg_KeyFullPath(_In_ POB_REGISTRY_HIVE pHive, _In_ POB_REGISTRY_KEY p
if(pk->pKey->Flags & REG_CM_KEY_NODE_FLAGS_COMP_NAME) {
CharUtil_AtoU(pk->pKey->szName, pk->pKey->NameLength, uszFullPath + o, 1024 - o, NULL, &cbName, CHARUTIL_FLAG_TRUNCATE_ONFAIL_NULLSTR | CHARUTIL_FLAG_STR_BUFONLY);
} else {
CharUtil_WtoU(pk->pKey->wszName, pk->pKey->NameLength, uszFullPath + o, 1024 - o, NULL, &cbName, CHARUTIL_FLAG_TRUNCATE_ONFAIL_NULLSTR | CHARUTIL_FLAG_STR_BUFONLY);
CharUtil_WtoU(pk->pKey->wszName, pk->pKey->NameLength / 2, uszFullPath + o, 1024 - o, NULL, &cbName, CHARUTIL_FLAG_TRUNCATE_ONFAIL_NULLSTR | CHARUTIL_FLAG_STR_BUFONLY);
}
if(cbName) {
o += cbName - 1;
+13
View File
@@ -216,6 +216,19 @@ POB_MAP VmmWinReg_KeyList(_In_ VMM_HANDLE H, _In_ POB_REGISTRY_HIVE pHive, _In_o
*/
VOID VmmWinReg_KeyInfo(_In_ POB_REGISTRY_HIVE pHive, _In_ POB_REGISTRY_KEY pKey, _Out_ PVMM_REGISTRY_KEY_INFO pKeyInfo);
/*
* Retrieve the original UTF-8 name of a registry key or value.
* -- H
* -- uszFullPath = existing file-system path to the registry key or value.
* -- fValue = TRUE for a value, FALSE for a key.
* -- pb = output buffer, or NULL to query the required size.
* -- cb = size of the output buffer in bytes.
* -- pcbName = required byte size, including the terminating null.
* -- return = success. An insufficient buffer fails without truncation.
*/
_Success_(return)
BOOL VmmWinReg_QueryNameOriginal(_In_ VMM_HANDLE H, _In_ LPCSTR uszFullPath, _In_ BOOL fValue, _Out_writes_opt_(cb) PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcbName);
/*
* Retrieve information about a registry key - pKeyInfo->wszName = set to full path.
* -- H
+24
View File
@@ -1416,6 +1416,30 @@ int main(_In_ int argc, _In_ char* argv[])
}
// Query original names of common Windows registry entries using their mangled paths.
// The '*' class key is exposed as '_', and '\DosDevices\C:' as '_DosDevices_C_'.
printf("------------------------------------------------------------\n");
printf("# Registry: Query original key and value names \n");
ShowKeyPress();
// The buffer capacity and returned size are UTF-8 bytes, including the final null.
cch = sizeof(usz);
printf("CALL: VMMDLL_WinReg_QueryNameOriginalU (key)\n");
result = VMMDLL_WinReg_QueryNameOriginalU(hVMM, "HKLM\\SOFTWARE\\Classes\\_", FALSE, usz, &cch);
if(result) {
printf("SUCCESS: Key name: '_' -> '%s'\n", usz);
} else {
printf("FAIL: VMMDLL_WinReg_QueryNameOriginalU (key)\n");
}
cch = sizeof(usz);
printf("CALL: VMMDLL_WinReg_QueryNameOriginalU (value)\n");
result = VMMDLL_WinReg_QueryNameOriginalU(hVMM, "HKLM\\SYSTEM\\MountedDevices\\_DosDevices_C_", TRUE, usz, &cch);
if(result) {
printf("SUCCESS: Value name: '_DosDevices_C_' -> '%s'\n", usz);
} else {
printf("FAIL: VMMDLL_WinReg_QueryNameOriginalU (value)\n");
}
// Retrieve Physical Memory Map
printf("------------------------------------------------------------\n");
printf("# Retrieve Physical Memory Map \n");
+2 -2
View File
@@ -3,8 +3,8 @@
#define VERSION_MAJOR 5
#define VERSION_MINOR 18
#define VERSION_REVISION 9
#define VERSION_BUILD 248
#define VERSION_REVISION 10
#define VERSION_BUILD 249
#define VER_FILE_DESCRIPTION_STR "MemProcFS : Python API"
#define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD
+1
View File
@@ -364,6 +364,7 @@ PyObj_RegHive* VmmPycRegHive_InitializeInternal(_In_ PyObj_Vmm *pyVMM, _In_ PVMM
PyObj_RegMemory* VmmPycRegMemory_InitializeInternal(_In_ PyObj_Vmm *pyVMM, _In_ QWORD vaCMHive);
PyObj_RegKey* VmmPycRegKey_InitializeInternal(_In_ PyObj_Vmm *pyVMM, _In_ LPSTR uszFullPathKey, _In_ BOOL fVerify);
PyObj_RegValue* VmmPycRegValue_InitializeInternal(_In_ PyObj_Vmm *pyVMM, _In_ LPSTR uszFullPathKeyValue, _In_ BOOL fVerify);
_Success_(return != NULL) PyObject* VmmPycReg_NameOriginal(_In_ PyObj_Vmm *pyVMM, _In_ LPSTR uszPath, _In_ BOOL fValue);
PyObj_Vmm *VmmPycVmm_InitializeInternal2(_In_ PyObj_Vmm *pyVMM, _In_ VMM_HANDLE hVMM);
PyObj_Search *VmmPycSearch_InitializeInternal(_In_ PyObj_Vmm *pyVMM, _In_opt_ DWORD dwPID, _In_ PyObject *args);
PyObj_Yara *VmmPycYara_InitializeInternal(_In_ PyObj_Vmm *pyVMM, _In_opt_ DWORD dwPID, _In_ PyObject *args);
+31 -1
View File
@@ -7,6 +7,27 @@
PyObject *g_pPyType_RegKey = NULL;
_Success_(return != NULL)
PyObject* VmmPycReg_NameOriginal(_In_ PyObj_Vmm *pyVMM, _In_ LPSTR uszPath, _In_ BOOL fValue)
{
BOOL fResult;
DWORD cbu = 0;
LPSTR usz;
PyObject *pyName;
if(!pyVMM->fValid) { return PyErr_Format(PyExc_RuntimeError, "Registry.name_original: Not initialized."); }
Py_BEGIN_ALLOW_THREADS;
fResult = VMMDLL_WinReg_QueryNameOriginalU(pyVMM->hVMM, uszPath, fValue, NULL, &cbu);
Py_END_ALLOW_THREADS;
if(!fResult || !cbu || !pyVMM->fValid) { return PyErr_Format(PyExc_RuntimeError, "Registry.name_original: Failed."); }
if(!(usz = LocalAlloc(0, cbu))) { return PyErr_NoMemory(); }
Py_BEGIN_ALLOW_THREADS;
fResult = VMMDLL_WinReg_QueryNameOriginalU(pyVMM->hVMM, uszPath, fValue, usz, &cbu);
Py_END_ALLOW_THREADS;
pyName = fResult && cbu && pyVMM->fValid ? PyUnicode_DecodeUTF8(usz, cbu - 1, NULL) : PyErr_Format(PyExc_RuntimeError, "Registry.name_original: Failed.");
LocalFree(usz);
return pyName;
}
static BOOL VmmPycRegKey_EnsureLastWrite(PyObj_RegKey *self)
{
DWORD cch = 0;
@@ -170,6 +191,14 @@ VmmPycRegKey_name(PyObj_RegKey *self, void *closure)
return self->pyName;
}
// -> STR
_Success_(return != NULL)
static PyObject* VmmPycRegKey_name_original(_In_ PyObj_RegKey *self, _In_opt_ void *closure)
{
if(!self->fValid) { return PyErr_Format(PyExc_RuntimeError, "RegKey.name_original: Not initialized."); }
return VmmPycReg_NameOriginal(self->pyVMM, self->uszPath, FALSE);
}
// -> STR
static PyObject*
VmmPycRegKey_path(PyObj_RegKey *self, void *closure)
@@ -236,7 +265,8 @@ BOOL VmmPycRegKey_InitializeType(PyObject *pModule)
{NULL}
};
static PyGetSetDef PyGetSet[] = {
{"name", (getter)VmmPycRegKey_name, (setter)NULL, "Key name.", NULL},
{"name", (getter)VmmPycRegKey_name, (setter)NULL, "File-system-safe key name.", NULL},
{"name_original", (getter)VmmPycRegKey_name_original, (setter)NULL, "Original key name stored in the hive.", NULL},
{"path", (getter)VmmPycRegKey_path, (setter)NULL, "Key path.", NULL},
{"parent", (getter)VmmPycRegKey_parent, (setter)NULL, "Parent key.", NULL},
{"time_int", (getter)VmmPycRegKey_time_int, (setter)NULL, "LastWrite timestamp in numeric format.", NULL},
+10 -1
View File
@@ -216,6 +216,14 @@ VmmPycRegValue_name(PyObj_RegValue *self, void *closure)
return self->pyName;
}
// -> STR
_Success_(return != NULL)
static PyObject* VmmPycRegValue_name_original(_In_ PyObj_RegValue *self, _In_opt_ void *closure)
{
if(!self->fValid) { return PyErr_Format(PyExc_RuntimeError, "RegValue.name_original: Not initialized."); }
return VmmPycReg_NameOriginal(self->pyVMM, self->uszPath, TRUE);
}
// -> STR
static PyObject*
VmmPycRegValue_path(PyObj_RegValue *self, void *closure)
@@ -285,7 +293,8 @@ BOOL VmmPycRegValue_InitializeType(PyObject *pModule)
{NULL}
};
static PyGetSetDef PyGetSet[] = {
{"name", (getter)VmmPycRegValue_name, (setter)NULL, "Value name.", NULL},
{"name", (getter)VmmPycRegValue_name, (setter)NULL, "File-system-safe value name.", NULL},
{"name_original", (getter)VmmPycRegValue_name_original, (setter)NULL, "Original value name stored in the hive; empty for the unnamed value.", NULL},
{"path", (getter)VmmPycRegValue_path, (setter)NULL, "Value path.", NULL},
{"parent", (getter)VmmPycRegValue_parent, (setter)NULL, "Parent key.", NULL},
{"size", (getter)VmmPycRegValue_size, (setter)NULL, "Value byte size.", NULL},
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "leechcore_example"
version = "5.18.9"
version = "5.18.10"
edition = "2021"
publish = false
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "m_example_plugin"
version = "5.18.9"
version = "5.18.10"
edition = "2021"
publish = false
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "memprocfs"
version = "5.18.9"
version = "5.18.10"
edition = "2021"
description = "MemProcFS - Physical Memory Analysis Framework"
documentation = "https://docs.rs/memprocfs"
+54
View File
@@ -3071,6 +3071,23 @@ pub struct VmmRegKey<'a> {
}
impl VmmRegKey<'_> {
/// Retrieve the original key name stored in the hive.
///
/// Use `path` for lookups; the original name may contain backslashes or
/// embedded nulls.
///
/// # Examples
/// ```no_run
/// # fn example(vmm : &memprocfs::Vmm<'_>) -> memprocfs::ResultEx<()> {
/// let regkey = vmm.reg_key(r"HKLM\SOFTWARE\Classes\_")?;
/// println!("{} -> {:?}", regkey.name, regkey.name_original()?);
/// # Ok(())
/// # }
/// ```
pub fn name_original(&self) -> ResultEx<String> {
return self.vmm.impl_reg_name_original(self.path.as_str(), false);
}
/// Retrieve the parent registry key of this registry key.
///
/// # Examples
@@ -3212,6 +3229,23 @@ pub struct VmmRegValue<'a> {
}
impl VmmRegValue<'_> {
/// Retrieve the original value name stored in the hive.
///
/// The unnamed value returns an empty string. Use `path` for lookups; the
/// original name may contain backslashes or embedded nulls.
///
/// # Examples
/// ```no_run
/// # fn example(vmm : &memprocfs::Vmm<'_>) -> memprocfs::ResultEx<()> {
/// let regvalue = vmm.reg_value(r"HKLM\SYSTEM\MountedDevices\_DosDevices_C_")?;
/// println!("{} -> {:?}", regvalue.name, regvalue.name_original()?);
/// # Ok(())
/// # }
/// ```
pub fn name_original(&self) -> ResultEx<String> {
return self.vmm.impl_reg_name_original(self.path.as_str(), true);
}
/// Retrieve the parent registry key.
///
/// # Examples
@@ -4623,6 +4657,7 @@ struct VmmNative {
VMMDLL_WinReg_HiveWrite : extern "C" fn(hVMM : usize, vaCMHive : u64, ra : u32, pb : *const u8, cb : u32) -> bool,
VMMDLL_WinReg_EnumKeyExU : extern "C" fn(hVMM : usize, uszFullPathKey : *const c_char, dwIndex : u32, lpcchName : *mut c_char, lpcchName : *mut u32, lpftLastWriteTime : *mut u64) -> bool,
VMMDLL_WinReg_EnumValueU : extern "C" fn(hVMM : usize, uszFullPathKey : *const c_char, dwIndex : u32, lpValueName : *mut c_char, lpcchValueName : *mut u32, lpType : *mut u32, lpcbData : *mut u32) -> bool,
VMMDLL_WinReg_QueryNameOriginalU : extern "C" fn(hVMM : usize, uszFullPath : *const c_char, fValue : c_int, uszName : *mut c_char, pcbName : *mut u32) -> c_int,
VMMDLL_WinReg_QueryValueExU : extern "C" fn(hVMM : usize, uszFullPathKeyValue : *const c_char, lpType : *mut u32, lpData : *mut u8, lpcbData : *mut u32) -> bool,
VMMDLL_ProcessGetModuleBaseU : extern "C" fn(hVMM : usize, pid : u32, uszModuleName : *const c_char) -> u64,
@@ -4721,6 +4756,7 @@ fn impl_new<'a>(vmm_lib_path : &str, lc_existing_opt : Option<&LeechCore>, h_vmm
let VMMDLL_WinReg_HiveWrite = *lib.get(b"VMMDLL_WinReg_HiveWrite")?;
let VMMDLL_WinReg_EnumKeyExU = *lib.get(b"VMMDLL_WinReg_EnumKeyExU")?;
let VMMDLL_WinReg_EnumValueU = *lib.get(b"VMMDLL_WinReg_EnumValueU")?;
let VMMDLL_WinReg_QueryNameOriginalU = *lib.get(b"VMMDLL_WinReg_QueryNameOriginalU")?;
let VMMDLL_WinReg_QueryValueExU = *lib.get(b"VMMDLL_WinReg_QueryValueExU")?;
let VMMDLL_ProcessGetModuleBaseU = *lib.get(b"VMMDLL_ProcessGetModuleBaseU")?;
let VMMDLL_ProcessGetProcAddressU = *lib.get(b"VMMDLL_ProcessGetProcAddressU")?;
@@ -4818,6 +4854,7 @@ fn impl_new<'a>(vmm_lib_path : &str, lc_existing_opt : Option<&LeechCore>, h_vmm
VMMDLL_WinReg_HiveWrite,
VMMDLL_WinReg_EnumKeyExU,
VMMDLL_WinReg_EnumValueU,
VMMDLL_WinReg_QueryNameOriginalU,
VMMDLL_WinReg_QueryValueExU,
VMMDLL_ProcessGetModuleBaseU,
VMMDLL_ProcessGetProcAddressU,
@@ -6075,6 +6112,23 @@ impl Vmm<'_> {
return Err(anyhow!("[err]"));
}
fn impl_reg_name_original(&self, path : &str, is_value : bool) -> ResultEx<String> {
let c_path = CString::new(path)?;
let mut cb = 0;
let r = (self.native.VMMDLL_WinReg_QueryNameOriginalU)(self.native.h, c_path.as_ptr(), is_value as c_int, std::ptr::null_mut(), &mut cb);
if r == 0 || cb == 0 {
return Err(anyhow!("VMMDLL_WinReg_QueryNameOriginalU: fail."));
}
let mut bytes = vec![0u8; cb as usize];
let r = (self.native.VMMDLL_WinReg_QueryNameOriginalU)(self.native.h, c_path.as_ptr(), is_value as c_int, bytes.as_mut_ptr() as *mut c_char, &mut cb);
if r == 0 || cb == 0 || cb as usize > bytes.len() || bytes[cb as usize - 1] != 0 {
return Err(anyhow!("VMMDLL_WinReg_QueryNameOriginalU: fail."));
}
// Remove only the final terminator, retaining embedded and trailing nulls.
bytes.truncate(cb as usize - 1);
return Ok(String::from_utf8(bytes)?);
}
fn impl_reg_key(&self, path : &str) -> ResultEx<VmmRegKey> {
let mut ftLastWrite = 0;
let mut cch = 0;
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "memprocfs_example"
version = "5.18.9"
version = "5.18.10"
edition = "2021"
publish = false
+18
View File
@@ -564,6 +564,15 @@ pub fn main_example() -> ResultEx<()> {
}
// Example: vmmregkey.name_original()
// The common '*' class key is exposed as '_' in file-system paths.
println!("========================================");
println!("vmmregkey.name_original():");
if let Ok(regkey) = vmm.reg_key(r"HKLM\SOFTWARE\Classes\_") {
println!("{} -> {:?}", regkey.name, regkey.name_original()?);
}
// Example: vmmregkey.parent()
println!("========================================");
println!("vmmregkey.parent():");
@@ -605,6 +614,15 @@ pub fn main_example() -> ResultEx<()> {
println!("{vmmregvalue} raw_type={} raw_size={}", vmmregvalue.raw_type, vmmregvalue.raw_size);
// Example: vmmregvalue.name_original()
// The common '\DosDevices\C:' value is exposed as '_DosDevices_C_'.
println!("========================================");
println!("vmmregvalue.name_original():");
if let Ok(regvalue) = vmm.reg_value(r"HKLM\SYSTEM\MountedDevices\_DosDevices_C_") {
println!("{} -> {:?}", regvalue.name, regvalue.name_original()?);
}
// Example: vmmregvalue.raw_value()
// Retrieve the raw underlying data backing the actual value.
println!("========================================");
+2 -2
View File
@@ -32,5 +32,5 @@ using System.Runtime.Versioning;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("5.18.9.248")]
[assembly: AssemblyFileVersion("5.18.9.248")]
[assembly: AssemblyVersion("5.18.10.249")]
[assembly: AssemblyFileVersion("5.18.10.249")]
+1 -1
View File
@@ -109,7 +109,7 @@
<None Include="logo.png" Pack="true" Visible="true" PackagePath="" />
</ItemGroup>
<PropertyGroup>
<Version>5.18.9</Version>
<Version>5.18.10</Version>
<RepositoryUrl>https://github.com/ufrisk/MemProcFS</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
@@ -70,6 +70,7 @@ namespace Vmmsharp.Internal
internal const string VMMDLL_Map_GetServicesX = "VMMDLL_Map_GetServicesU";
internal const string VMMDLL_WinReg_EnumKeyExX = "VMMDLL_WinReg_EnumKeyExU";
internal const string VMMDLL_WinReg_EnumValueX = "VMMDLL_WinReg_EnumValueU";
internal const string VMMDLL_WinReg_QueryNameOriginalX = "VMMDLL_WinReg_QueryNameOriginalU";
internal const string VMMDLL_WinReg_QueryValueExX = "VMMDLL_WinReg_QueryValueExU";
#else
internal const UnmanagedType VmmString = UnmanagedType.LPWStr;
@@ -98,6 +99,7 @@ namespace Vmmsharp.Internal
internal const string VMMDLL_Map_GetServicesX = "VMMDLL_Map_GetServicesW";
internal const string VMMDLL_WinReg_EnumKeyExX = "VMMDLL_WinReg_EnumKeyExW";
internal const string VMMDLL_WinReg_EnumValueX = "VMMDLL_WinReg_EnumValueW";
internal const string VMMDLL_WinReg_QueryNameOriginalX = "VMMDLL_WinReg_QueryNameOriginalW";
internal const string VMMDLL_WinReg_QueryValueExX = "VMMDLL_WinReg_QueryValueExW";
#endif
@@ -1443,6 +1445,15 @@ namespace Vmmsharp.Internal
byte* lpData,
ref uint lpcbData);
[LibraryImport("vmm", EntryPoint = "VMMDLL_WinReg_QueryNameOriginalU")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static unsafe partial bool VMMDLL_WinReg_QueryNameOriginal(
IntPtr hVMM,
[MarshalAs(UnmanagedType.LPUTF8Str)] string uszFullPath,
[MarshalAs(UnmanagedType.Bool)] bool fValue,
byte* lpName,
ref uint lpcchName);
[LibraryImport("vmm", EntryPoint = "VMMDLL_WinReg_QueryValueExU")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static unsafe partial bool VMMDLL_WinReg_QueryValueEx(
@@ -1961,6 +1972,15 @@ namespace Vmmsharp.Internal
byte* lpData,
ref uint lpcbData);
[DllImport("vmm", EntryPoint = VMMDLL_WinReg_QueryNameOriginalX)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern unsafe bool VMMDLL_WinReg_QueryNameOriginal(
IntPtr hVMM,
[MarshalAs(VmmString)] string uszFullPath,
[MarshalAs(UnmanagedType.Bool)] bool fValue,
byte* lpName,
ref uint lpcchName);
[DllImport("vmm", EntryPoint = VMMDLL_WinReg_QueryValueExX)]
internal static extern unsafe bool VMMDLL_WinReg_QueryValueEx(
IntPtr hVMM,
+30
View File
@@ -1136,6 +1136,36 @@ namespace Vmmsharp
return re;
}
/// <summary>
/// Retrieve the original name of a registry key or value stored in the hive.
/// Use the file-system-safe path for lookups; original names may contain backslashes or embedded nulls.
/// </summary>
/// <param name="sFullPath">Existing file-system path, including the value name if isValue is true.</param>
/// <param name="isValue">True for a value, false for a key.</param>
/// <returns>Original name on success, empty string for the unnamed value, or null on failure.</returns>
public unsafe string RegNameOriginal(string sFullPath, bool isValue = false)
{
#if NET5_0_OR_GREATER
const int cbChar = 1;
Encoding encoding = Encoding.UTF8;
#else
const int cbChar = 2;
Encoding encoding = Encoding.Unicode;
#endif
uint cchName = 0;
if (string.IsNullOrEmpty(sFullPath) || sFullPath.IndexOf('\0') >= 0) { return null; }
if (!Vmmi.VMMDLL_WinReg_QueryNameOriginal(hVMM, sFullPath, isValue, null, ref cchName)) { return null; }
if (cchName == 0 || cchName > int.MaxValue / cbChar) { return null; }
byte[] data = new byte[(int)cchName * cbChar];
fixed (byte* pb = data)
{
if (!Vmmi.VMMDLL_WinReg_QueryNameOriginal(hVMM, sFullPath, isValue, pb, ref cchName)) { return null; }
}
if (cchName == 0 || cchName > data.Length / cbChar) { return null; }
// Exclude only the final terminator, retaining embedded and trailing nulls.
return encoding.GetString(data, 0, ((int)cchName - 1) * cbChar);
}
/// <summary>
/// Read a registry value.
/// </summary>