Files
hfiref0x 65ba203877 2.1.0
Reduced duplicate code in plugins
2025-10-05 11:08:16 +07:00

830 lines
20 KiB
C

/*******************************************************************************
*
* (C) COPYRIGHT AUTHORS, 2019 - 2025
*
* TITLE: QUERY.C
*
* VERSION: 1.20
*
* DATE: 03 Oct 2025
*
* Query and output ApiSet specific data.
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
* PARTICULAR PURPOSE.
*
*******************************************************************************/
#include "global.h"
typedef VOID(CALLBACK* pfnApiSetQueryMap)(
_In_ PVOID ApiSetMap,
_In_ HTREEITEM RootItem,
_In_opt_ LPCWSTR FilterByName,
_In_ SIZE_T ApiSetSize);
#define APISET_QUERY_ROUTINE(n) VOID n( \
_In_ PVOID ApiSetMap, \
_In_ HTREEITEM RootItem, \
_In_opt_ LPCWSTR FilterByName, \
_In_ SIZE_T ApiSetSize)
VALUE_DESC g_apiSetEntryFlags[] = {
{ API_SET_SCHEMA_ENTRY_FLAGS_SEALED, L"Sealed" },
{ API_SET_SCHEMA_ENTRY_FLAGS_EXTENSION, L"Extension" }
};
/*
* DisplayErrorText
*
* Purpose:
*
* In debug build send string to debugger else show message box.
*
*/
VOID DisplayErrorText(
_In_ LPWSTR ErrorMsg)
{
#ifdef _DEBUG
OutputDebugString(ErrorMsg);
#else
MessageBox(g_ctx.MainWindow, ErrorMsg, NULL, MB_ICONERROR);
#endif
}
/*
* IsRangeValid
*
* Purpose:
*
* Validate that Offset..Offset+Length lies within 0..BaseSize.
*
*/
BOOLEAN IsRangeValid(
_In_ SIZE_T BaseSize,
_In_ ULONG Offset,
_In_ ULONG Length
)
{
SIZE_T start = (SIZE_T)Offset;
SIZE_T len = (SIZE_T)Length;
SIZE_T end;
if (start > BaseSize) return FALSE;
end = start + len;
if (end > BaseSize) return FALSE;
return TRUE;
}
/*
* GetApiSetEntryName
*
* Purpose:
*
* Return apiset entry name, use HeapFree to release allocated memory.
*
*/
LPWSTR GetApiSetEntryName(
_In_ PBYTE Namespace,
_In_ SIZE_T NamespaceSize,
_In_ ULONG NameOffset,
_In_ ULONG NameLength
)
{
PWSTR lpEntryName;
if (NameLength == 0)
return NULL;
if ((NameLength % sizeof(WCHAR)) != 0)
return NULL;
if (!IsRangeValid(NamespaceSize, NameOffset, NameLength))
return NULL;
{
SIZE_T cch = NameLength / sizeof(WCHAR);
lpEntryName = HeapAlloc(
g_ctx.PluginHeap,
HEAP_ZERO_MEMORY,
(cch + 1) * sizeof(WCHAR));
if (lpEntryName) {
RtlCopyMemory(
lpEntryName,
RtlOffsetToPointer(Namespace, NameOffset),
NameLength);
lpEntryName[cch] = 0;
}
}
return lpEntryName;
}
/*
* OutNamespaceEntry
*
* Purpose:
*
* Namespace entry formatted output routine.
*
*/
HTREEITEM OutNamespaceEntry(
_In_ HTREEITEM RootItem,
_In_ LPWSTR EntryName,
_In_ ULONG Flags
)
{
ULONG i, flagsValue;
LPTSTR lpText = NULL;
HTREEITEM h_tviRootItem;
TL_SUBITEMS_FIXED tlSubItems;
WCHAR szBuffer[20];
RtlZeroMemory(&tlSubItems, sizeof(tlSubItems));
flagsValue = Flags;
//
// Output first flag from combination.
//
if (flagsValue) {
for (i = 0; i < RTL_NUMBER_OF(g_apiSetEntryFlags); i++) {
if (flagsValue & g_apiSetEntryFlags[i].Value) {
lpText = (LPTSTR)g_apiSetEntryFlags[i].Desc;
flagsValue &= ~g_apiSetEntryFlags[i].Value;
break;
}
}
//
// Unrecognized flags combination.
//
if (lpText == NULL) {
szBuffer[0] = 0;
ultostr(flagsValue, szBuffer);
lpText = szBuffer;
flagsValue = 0;
}
}
if (lpText == NULL) lpText = T_EmptyString;
tlSubItems.Text[0] = lpText;
tlSubItems.Text[1] = T_EmptyString;
tlSubItems.Count = 2;
h_tviRootItem = supTreeListAddItem(
g_ctx.TreeList,
RootItem,
TVIF_TEXT | TVIF_STATE,
(UINT)0,
(UINT)0,
EntryName,
&tlSubItems);
//
// List rest of the flags.
//
if (h_tviRootItem && flagsValue) {
for (i = 0; i < RTL_NUMBER_OF(g_apiSetEntryFlags); i++) {
if (flagsValue & g_apiSetEntryFlags[i].Value) {
flagsValue &= ~g_apiSetEntryFlags[i].Value;
tlSubItems.Text[0] = (LPTSTR)g_apiSetEntryFlags[i].Desc;
tlSubItems.Text[1] = T_EmptyString;
tlSubItems.Count = 2;
supTreeListAddItem(
g_ctx.TreeList,
h_tviRootItem,
TVIF_TEXT | TVIF_STATE,
(UINT)0,
(UINT)0,
T_EmptyString,
&tlSubItems);
}
}
//
// Unrecognized flags.
//
if (flagsValue) {
szBuffer[0] = 0;
ultostr(flagsValue, szBuffer);
tlSubItems.Text[0] = szBuffer;
tlSubItems.Text[1] = T_EmptyString;
tlSubItems.Count = 2;
supTreeListAddItem(
g_ctx.TreeList,
h_tviRootItem,
TVIF_TEXT | TVIF_STATE,
(UINT)0,
(UINT)0,
T_EmptyString,
&tlSubItems);
}
}
return h_tviRootItem;
}
/*
* OutNamespaceValue
*
* Purpose:
*
* Add entry to treelist with namespace value information.
*
*/
void OutNamespaceValue(
_In_ HTREEITEM RootItem,
_In_ PBYTE Namespace,
_In_ SIZE_T NamespaceSize,
_In_ ULONG ValueOffset,
_In_ ULONG ValueLength,
_In_ ULONG NameOffset,
_In_ ULONG NameLength,
_In_ ULONG Flags
)
{
TL_SUBITEMS_FIXED tlSubItems;
LPWSTR lpValueName = NULL, lpAliasName = NULL;
WCHAR szBuffer[20];
//
// Get value name.
//
lpValueName = GetApiSetEntryName(
Namespace,
NamespaceSize,
ValueOffset,
ValueLength);
//
// Get value alias if present.
//
lpAliasName = GetApiSetEntryName(Namespace,
NamespaceSize,
NameOffset,
NameLength);
RtlSecureZeroMemory(&tlSubItems, sizeof(tlSubItems));
tlSubItems.Count = 2;
if (Flags) {
szBuffer[0] = 0;
ultostr(Flags, szBuffer);
tlSubItems.Text[0] = szBuffer;
}
else {
tlSubItems.Text[0] = T_EmptyString;
}
if (lpAliasName) {
TreeList_Expand(g_ctx.TreeList, RootItem, TVE_EXPAND);
tlSubItems.Text[1] = lpAliasName;
}
else {
tlSubItems.Text[1] = T_EmptyString;
}
supTreeListAddItem(
g_ctx.TreeList,
RootItem,
TVIF_TEXT | TVIF_STATE,
(UINT)0,
(UINT)0,
lpValueName,
&tlSubItems);
if (lpValueName) HeapFree(g_ctx.PluginHeap, 0, lpValueName);
if (lpAliasName) HeapFree(g_ctx.PluginHeap, 0, lpAliasName);
}
/*
* ListApiSetV2
*
* Purpose:
*
* Parse and output ApiSet Version 2 (Windows 7).
*
*/
APISET_QUERY_ROUTINE(ListApiSetV2)
{
API_SET_NAMESPACE_ARRAY_V2* namespace = (API_SET_NAMESPACE_ARRAY_V2*)ApiSetMap;
ULONG i, j;
API_SET_NAMESPACE_ENTRY_V2* nsEntry;
API_SET_VALUE_ARRAY_V2* valuesArray;
API_SET_VALUE_ENTRY_V2* valueEntry;
HTREEITEM hSubItem;
LPWSTR lpEntryName;
for (i = 0; i < namespace->Count; i++) {
nsEntry = &namespace->Array[i];
lpEntryName = GetApiSetEntryName(
(PBYTE)namespace,
ApiSetSize,
nsEntry->NameOffset,
nsEntry->NameLength);
if (lpEntryName) {
if (FilterByName) {
if (_strstri(lpEntryName, FilterByName) == NULL) {
HeapFree(g_ctx.PluginHeap, 0, lpEntryName);
continue;
}
}
hSubItem = OutNamespaceEntry(
RootItem,
lpEntryName,
0);
//
// Namespace entry name no longer needed.
//
HeapFree(g_ctx.PluginHeap, 0, lpEntryName);
//
// List values array.
//
if (!IsRangeValid(ApiSetSize, nsEntry->DataOffset, sizeof(API_SET_VALUE_ARRAY_V2)))
continue;
valuesArray = (API_SET_VALUE_ARRAY_V2*)RtlOffsetToPointer(
namespace,
nsEntry->DataOffset);
for (j = 0; j < valuesArray->Count; j++) {
valueEntry = &valuesArray->Array[j];
if (!API_SET_EMPTY_NAMESPACE_VALUE(valueEntry)) {
OutNamespaceValue(
hSubItem,
(PBYTE)namespace,
ApiSetSize,
valueEntry->ValueOffset,
valueEntry->ValueLength,
valueEntry->NameOffset,
valueEntry->NameLength,
0);
}
}
} //if (lpEntryName)
}
}
/*
* ListApiSetV4
*
* Purpose:
*
* Parse and output ApiSet Version 4 (Windows 8.x).
*
*/
APISET_QUERY_ROUTINE(ListApiSetV4)
{
API_SET_NAMESPACE_ARRAY_V4* namespace = (API_SET_NAMESPACE_ARRAY_V4*)ApiSetMap;
ULONG i, j;
API_SET_NAMESPACE_ENTRY_V4* nsEntry;
API_SET_VALUE_ARRAY_V4* valuesArray;
API_SET_VALUE_ENTRY_V4* valueEntry;
HTREEITEM hSubItem;
LPWSTR lpEntryName;
for (i = 0; i < namespace->Count; i++) {
nsEntry = &namespace->Array[i];
lpEntryName = GetApiSetEntryName(
(PBYTE)namespace,
ApiSetSize,
nsEntry->NameOffset,
nsEntry->NameLength);
if (lpEntryName) {
if (FilterByName) {
if (_strstri(lpEntryName, FilterByName) == NULL) {
HeapFree(g_ctx.PluginHeap, 0, lpEntryName);
continue;
}
}
hSubItem = OutNamespaceEntry(
RootItem,
lpEntryName,
nsEntry->Flags);
//
// Namespace entry name no longer needed.
//
HeapFree(g_ctx.PluginHeap, 0, lpEntryName);
//
// List values array.
//
if (!IsRangeValid(ApiSetSize, nsEntry->DataOffset, sizeof(API_SET_VALUE_ARRAY_V4)))
continue;
valuesArray = (API_SET_VALUE_ARRAY_V4*)RtlOffsetToPointer(
namespace,
nsEntry->DataOffset);
for (j = 0; j < valuesArray->Count; j++) {
valueEntry = &valuesArray->Array[j];
if (!API_SET_EMPTY_NAMESPACE_VALUE(valueEntry)) {
OutNamespaceValue(
hSubItem,
(PBYTE)namespace,
ApiSetSize,
valueEntry->ValueOffset,
valueEntry->ValueLength,
valueEntry->NameOffset,
valueEntry->NameLength,
valueEntry->Flags);
}
}
} //if (lpEntryName)
}
}
/*
* ListApiSetV6
*
* Purpose:
*
* Parse and output ApiSet Version 6 (Windows 10).
*
*/
APISET_QUERY_ROUTINE(ListApiSetV6)
{
API_SET_NAMESPACE_ARRAY_V6* namespace = (API_SET_NAMESPACE_ARRAY_V6*)ApiSetMap;
ULONG i, j;
API_SET_NAMESPACE_ENTRY_V6* nsEntry;
API_SET_VALUE_ENTRY_V6* valueEntry;
HTREEITEM hSubItem;
LPWSTR lpEntryName;
nsEntry = (API_SET_NAMESPACE_ENTRY_V6*)RtlOffsetToPointer(
namespace,
namespace->NamespaceEntryOffset);
for (i = 0; i < namespace->Count; i++) {
if (!IsRangeValid(ApiSetSize, (ULONG)((PBYTE)nsEntry - (PBYTE)namespace), sizeof(API_SET_NAMESPACE_ENTRY_V6)))
break;
lpEntryName = GetApiSetEntryName(
(PBYTE)namespace,
ApiSetSize,
nsEntry->NameOffset,
nsEntry->NameLength);
if (lpEntryName) {
if (FilterByName && _strstri(lpEntryName, FilterByName) == NULL) {
HeapFree(g_ctx.PluginHeap, 0, lpEntryName);
}
else {
hSubItem = OutNamespaceEntry(
RootItem,
lpEntryName,
nsEntry->Flags);
//
// Namespace entry name no longer needed.
//
HeapFree(g_ctx.PluginHeap, 0, lpEntryName);
//
// List values array.
//
valueEntry = (API_SET_VALUE_ENTRY_V6*)RtlOffsetToPointer(
namespace,
nsEntry->DataOffset);
for (j = 0; j < nsEntry->Count; j++) {
if (!API_SET_EMPTY_NAMESPACE_VALUE(valueEntry)) {
OutNamespaceValue(
hSubItem,
(PBYTE)namespace,
ApiSetSize,
valueEntry->ValueOffset,
valueEntry->ValueLength,
valueEntry->NameOffset,
valueEntry->NameLength,
valueEntry->Flags);
}
valueEntry = (API_SET_VALUE_ENTRY_V6*)RtlOffsetToPointer(
valueEntry,
sizeof(API_SET_VALUE_ENTRY_V6));
}
}
} //if (lpEntryName)
//
// Go to next entry.
//
nsEntry = (API_SET_NAMESPACE_ENTRY_V6*)RtlOffsetToPointer(
nsEntry,
sizeof(API_SET_NAMESPACE_ENTRY_V6));
}
}
/*
* ResolveDllData
*
* Purpose:
*
* Process apiset file, locate apiset section and schema version.
*
*/
BOOL ResolveDllData(
_In_ HMODULE DllHandle,
_Inout_ PVOID* ApiSetData,
_Out_ PULONG SchemaVersion,
_Out_opt_ PULONG ApiSetSize
)
{
ULONG dataSize = 0;
UINT i;
ULONG currentSchemaVersion = 0;
PIMAGE_NT_HEADERS ntHeaders;
IMAGE_SECTION_HEADER* sectionTableEntry;
PBYTE baseAddress;
PBYTE dataPtr = NULL;
*SchemaVersion = 0;
if (ApiSetSize) *ApiSetSize = 0;
baseAddress = (PBYTE)(((ULONG_PTR)DllHandle) & ~3);
ntHeaders = RtlImageNtHeader(baseAddress);
if (ntHeaders == NULL)
return FALSE;
sectionTableEntry = IMAGE_FIRST_SECTION(ntHeaders);
i = ntHeaders->FileHeader.NumberOfSections;
while (i > 0) {
if (_strncmpi_a((CHAR*)&sectionTableEntry->Name,
API_SET_SECTION_NAME,
sizeof(API_SET_SECTION_NAME)) == 0)
{
dataSize = sectionTableEntry->SizeOfRawData;
dataPtr = (PBYTE)RtlOffsetToPointer(
baseAddress,
sectionTableEntry->PointerToRawData);
break;
}
i -= 1;
sectionTableEntry += 1;
}
if (dataPtr == NULL || dataSize == 0) {
return FALSE;
}
if (dataSize < sizeof(ULONG))
return FALSE;
currentSchemaVersion = *(ULONG*)dataPtr;
*SchemaVersion = currentSchemaVersion;
*ApiSetData = dataPtr;
if (ApiSetSize) *ApiSetSize = dataSize;
return TRUE;
}
/*
* ListApiSetFromFileWorker
*
* Purpose:
*
* Processing apiset file.
*
*/
VOID WINAPI ListApiSetFromFileWorker(
_In_ LPCWSTR SchemaFileName,
_In_opt_ LPCWSTR FilterByName,
_In_ PVOID ApiSetData,
_In_ ULONG SchemaVersion,
_In_ SIZE_T ApiSetSize
)
{
pfnApiSetQueryMap queryMapRoutine = NULL;
WCHAR szBuffer[MAX_PATH * 2];
HTREEITEM h_tviRootItem, h_tviSubItem;
//
// Disable controls.
//
EnableWindow(GetDlgItem(g_ctx.MainWindow, IDC_BROWSE_BUTTON), FALSE);
//
// Reset output controls.
//
SetDlgItemText(g_ctx.MainWindow, IDC_ENTRY_EDIT, T_EmptyString);
TreeList_ClearTree(g_ctx.TreeList);
TreeList_RedrawDisable(g_ctx.TreeList);
StringCchPrintf(szBuffer, RTL_NUMBER_OF(szBuffer), TEXT("Schema Version %lu"), SchemaVersion);
//
// Parse and output apiset.
//
h_tviRootItem = supTreeListAddItem(
g_ctx.TreeList,
(HTREEITEM)NULL,
TVIF_TEXT | TVIF_STATE,
TVIS_EXPANDED,
TVIS_EXPANDED,
(LPWSTR)SchemaFileName,
(PVOID)NULL);
if (h_tviRootItem) {
h_tviSubItem = supTreeListAddItem(
g_ctx.TreeList,
(HTREEITEM)h_tviRootItem,
TVIF_TEXT | TVIF_STATE,
TVIS_EXPANDED,
TVIS_EXPANDED,
(LPWSTR)szBuffer,
(PVOID)NULL);
if (h_tviSubItem) {
switch (SchemaVersion) {
case API_SET_SCHEMA_VERSION_V2:
queryMapRoutine = (pfnApiSetQueryMap)ListApiSetV2;
break;
case API_SET_SCHEMA_VERSION_V4:
queryMapRoutine = (pfnApiSetQueryMap)ListApiSetV4;
break;
case API_SET_SCHEMA_VERSION_V6:
queryMapRoutine = (pfnApiSetQueryMap)ListApiSetV6;
break;
default:
queryMapRoutine = NULL;
break;
}
__try {
if (queryMapRoutine)
queryMapRoutine(ApiSetData, h_tviSubItem, FilterByName, ApiSetSize);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
szBuffer[0] = 0;
StringCchPrintf(
szBuffer,
RTL_NUMBER_OF(szBuffer),
TEXT("ApiSetView: Exception %lu thrown while processing apiset, schema version %lu"),
GetExceptionCode(),
SchemaVersion);
DisplayErrorText(szBuffer);
}
}
}
//
// Reenable controls.
//
EnableWindow(GetDlgItem(g_ctx.MainWindow, IDC_BROWSE_BUTTON), TRUE);
TreeList_RedrawEnableAndUpdateNow(g_ctx.TreeList);
}
/*
* ListApiSetFromFile
*
* Purpose:
*
* Load file or use default system apiset and output it contents.
*
*/
VOID ListApiSetFromFile(
_In_opt_ LPCWSTR FileName,
_In_opt_ LPCWSTR FilterByName
)
{
ULONG cch;
ULONG schemaVersion = 0;
HMODULE hApiSetDll;
LPWSTR lpFileName = NULL;
PVOID dataPtr = NULL;
ULONG dataSize = 0;
WCHAR szErrorMsg[MAX_PATH + 1];
WCHAR szSystemDirectory[MAX_PATH + 1];
//
// Select apiset dll name.
//
if (FileName) {
lpFileName = (LPWSTR)FileName;
}
else {
RtlSecureZeroMemory(&g_ctx.SchemaFileName, sizeof(g_ctx.SchemaFileName));
RtlSecureZeroMemory(szSystemDirectory, sizeof(szSystemDirectory));
cch = GetSystemDirectory(szSystemDirectory, MAX_PATH);
if (cch && cch < MAX_PATH) {
StringCchPrintf(g_ctx.SchemaFileName,
RTL_NUMBER_OF(g_ctx.SchemaFileName) - 1,
TEXT("%s\\apisetschema.dll"),
szSystemDirectory);
lpFileName = g_ctx.SchemaFileName;
}
}
if (lpFileName == NULL) {
DisplayErrorText(TEXT("ApiSet dll filename not specified"));
return;
}
//
// Load library and locate apiset section.
//
hApiSetDll = LoadLibraryEx(lpFileName, NULL, LOAD_LIBRARY_AS_DATAFILE);
if (hApiSetDll) {
if (ResolveDllData(hApiSetDll, &dataPtr, &schemaVersion, &dataSize)) {
if (schemaVersion != API_SET_SCHEMA_VERSION_V2 &&
schemaVersion != API_SET_SCHEMA_VERSION_V4 &&
schemaVersion != API_SET_SCHEMA_VERSION_V6)
{
StringCchPrintf(szErrorMsg,
RTL_NUMBER_OF(szErrorMsg),
TEXT("ApiSetView: Unknown schema version %lu"), schemaVersion);
DisplayErrorText(szErrorMsg);
}
else {
ListApiSetFromFileWorker(
lpFileName,
FilterByName,
dataPtr,
schemaVersion,
dataSize);
}
}
else {
DisplayErrorText(TEXT("ApiSetView: could not resolve data, probably not apiset file or data corrupted"));
}
FreeLibrary(hApiSetDll);
}
else {
DisplayErrorText(TEXT("ApiSetView: could not load apiset library"));
}
}