Files
hfiref0x 8bf8ed1add 2.1.1
Change "Type" object access flags decoding
Internal rearrange
2026-05-24 23:37:12 +07:00

780 lines
20 KiB
C

/*******************************************************************************
*
* (C) COPYRIGHT AUTHORS, 2015 - 2026
*
* TITLE: LIST.C
*
* VERSION: 2.11
*
* DATE: 21 May 2026
*
* Program main object listing and search logic.
*
* 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"
#include "props/propTypeConsts.h"
HANDLE ListObjectsHeap = NULL;
HANDLE TreeObjectsHeap = NULL;
BOOLEAN ListHeapCreate(
_Inout_ _At_(*HeapHandle, _Pre_valid_) _At_(*HeapHandle, _Post_valid_) PHANDLE HeapHandle
)
{
HANDLE handle;
if (HeapHandle == NULL)
return FALSE;
if (*HeapHandle)
supDestroyHeap(*HeapHandle);
handle = supCreateHeap(HEAP_GROWABLE, TRUE);
*HeapHandle = handle;
return (handle != NULL);
}
VOID ListHeapDestroy(
VOID
)
{
if (ListObjectsHeap) {
supDestroyHeap(ListObjectsHeap);
ListObjectsHeap = NULL;
}
if (TreeObjectsHeap) {
supDestroyHeap(TreeObjectsHeap);
TreeObjectsHeap = NULL;
}
}
/*
* AllocateObjectItem
*
* Purpose:
*
* Create an OBEX_ITEM.
*
*/
POBEX_ITEM AllocateObjectItem(
_In_ HANDLE HeapHandle,
_In_ WOBJ_OBJECT_TYPE TypeIndex,
_In_ PUNICODE_STRING Name,
_In_ PUNICODE_STRING TypeName,
_In_opt_ OBEX_ITEM* Parent
)
{
POBEX_ITEM item;
item = (OBEX_ITEM*)supHeapAllocEx(HeapHandle, sizeof(OBEX_ITEM));
if (item == NULL) {
return NULL;
}
item->Prev = Parent;
item->TypeIndex = TypeIndex;
if (!supDuplicateUnicodeString(HeapHandle, &item->Name, Name)) {
supHeapFreeEx(HeapHandle, item);
return NULL;
}
if (!supDuplicateUnicodeString(HeapHandle, &item->TypeName, TypeName)) {
supFreeDuplicatedUnicodeString(HeapHandle, &item->Name, FALSE);
supHeapFreeEx(HeapHandle, item);
return NULL;
}
return item;
}
/*
* GetNextSub
*
* Purpose:
*
* Returns next subitem in object full pathname.
*
*/
LPWSTR GetNextSub(
_In_ _Notnull_ LPWSTR ObjectFullPathName,
_Out_writes_(MAX_PATH + 1) LPWSTR Sub
)
{
SIZE_T i;
for (i = 0; (*ObjectFullPathName != 0) && (*ObjectFullPathName != L'\\')
&& (i < MAX_PATH); i++, ObjectFullPathName++)
{
Sub[i] = *ObjectFullPathName;
}
Sub[i] = 0;
if (*ObjectFullPathName == L'\\')
ObjectFullPathName++;
return ObjectFullPathName;
}
/*
* ListToObject
*
* Purpose:
*
* Select and focus list view item by given object name.
*
*/
VOID ListToObject(
_In_z_ LPWSTR ObjectName
)
{
INT i, iSelectedItem, listItemCount;
HTREEITEM lastfound, item;
LPWSTR pathPtr, nextPathPtr;
LVITEM lvitem;
TVITEMEX ritem;
WCHAR object[MAX_PATH + 1], sobject[MAX_PATH + 1];
if (ObjectName == NULL)
return;
if (*ObjectName != L'\\')
return;
object[0] = 0;
pathPtr = ObjectName + 1;
item = TreeView_GetRoot(g_hwndObjectTree);
lastfound = item;
while ((item != NULL) && (*pathPtr != 0)) {
item = TreeView_GetChild(g_hwndObjectTree, item);
RtlSecureZeroMemory(object, sizeof(object));
nextPathPtr = GetNextSub(pathPtr, object);
while (item != NULL) {
RtlSecureZeroMemory(&ritem, sizeof(ritem));
RtlSecureZeroMemory(&sobject, sizeof(sobject));
ritem.mask = TVIF_TEXT;
ritem.hItem = item;
ritem.cchTextMax = MAX_PATH;
ritem.pszText = sobject;
if (!TreeView_GetItem(g_hwndObjectTree, &ritem))
break;
if (_strcmpi(sobject, object) == 0) {
lastfound = item;
break;
}
item = TreeView_GetNextSibling(g_hwndObjectTree, item);
}
// Matching node found, continue with next path component
if ((item != NULL) && (*nextPathPtr != 0)) {
pathPtr = nextPathPtr;
continue;
}
// Last component found, select it and return
if ((item != NULL) && (*nextPathPtr == 0)) {
TreeView_SelectItem(g_hwndObjectTree, lastfound);
SetFocus(g_hwndObjectTree);
return;
}
// Not found
break;
}
TreeView_SelectItem(g_hwndObjectTree, lastfound);
listItemCount = ListView_GetItemCount(g_hwndObjectList);
for (i = 0; i < listItemCount; i++) {
RtlSecureZeroMemory(&lvitem, sizeof(lvitem));
RtlSecureZeroMemory(&sobject, sizeof(sobject));
lvitem.mask = LVIF_TEXT;
lvitem.iItem = i;
lvitem.cchTextMax = MAX_PATH;
lvitem.pszText = sobject;
if (!ListView_GetItem(g_hwndObjectList, &lvitem))
break;
if (_strcmpi(sobject, object) == 0) {
iSelectedItem = ListView_GetSelectionMark(g_hwndObjectList);
lvitem.mask = LVIF_STATE;
lvitem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
if (iSelectedItem >= 0) {
lvitem.iItem = iSelectedItem;
lvitem.state = 0;
ListView_SetItem(g_hwndObjectList, &lvitem);
}
lvitem.iItem = i;
lvitem.state = LVIS_SELECTED | LVIS_FOCUSED;
ListView_SetItem(g_hwndObjectList, &lvitem);
ListView_EnsureVisible(g_hwndObjectList, i, FALSE);
ListView_SetSelectionMark(g_hwndObjectList, i);
SetFocus(g_hwndObjectList);
return;
}
}
}
/*
* AddTreeViewItem
*
* Purpose:
*
* Add item to the tree view.
*
*/
HTREEITEM AddTreeViewItem(
_In_ HANDLE HeapHandle,
_In_ PUNICODE_STRING ItemName,
_In_opt_ HTREEITEM Root,
_Inout_opt_ OBEX_ITEM** Parent
)
{
BOOL bNeedFree = FALSE;
HTREEITEM result;
TVINSERTSTRUCT treeItem;
OBEX_ITEM* objectRef;
UNICODE_STRING objectName;
bNeedFree = supNormalizeUnicodeStringForDisplay(g_obexHeap,
ItemName,
&objectName);
if (!bNeedFree)
objectName = *ItemName;
RtlSecureZeroMemory(&treeItem, sizeof(treeItem));
treeItem.hParent = Root;
treeItem.item.mask = TVIF_TEXT | TVIF_SELECTEDIMAGE | TVIF_PARAM;
if (Root == NULL) {
treeItem.item.mask |= TVIF_STATE;
treeItem.item.state = TVIS_EXPANDED;
treeItem.item.stateMask = TVIS_EXPANDED;
}
treeItem.item.iSelectedImage = 1;
treeItem.item.pszText = objectName.Buffer;
objectRef = AllocateObjectItem(HeapHandle,
ObjectTypeDirectory,
ItemName,
ObGetPredefinedUnicodeString(OBP_DIRECTORY),
(Parent == NULL) ? NULL : *Parent);
if (Parent) *Parent = objectRef;
treeItem.item.lParam = (LPARAM)objectRef;
result = TreeView_InsertItem(g_hwndObjectTree, &treeItem);
if (result == NULL) {
// Failed to insert item, clean up the allocated object
if (objectRef != NULL) {
supFreeDuplicatedUnicodeString(HeapHandle, &objectRef->Name, FALSE);
supFreeDuplicatedUnicodeString(HeapHandle, &objectRef->TypeName, FALSE);
supHeapFreeEx(HeapHandle, objectRef);
}
if (Parent) *Parent = NULL;
}
if (bNeedFree)
supFreeUnicodeString(g_obexHeap, &objectName);
return result;
}
/*
* AppendDirectoryPath
*
* Purpose:
*
* Helper function to construct full object path.
*
*/
BOOLEAN AppendDirectoryPath(
_In_ PUNICODE_STRING DirectoryName,
_In_ PUNICODE_STRING ObjectName,
_Out_ PUNICODE_STRING FullPath
)
{
SIZE_T pathLength, allocSize;
PWCH target, source;
// Calculate required buffer size
pathLength = DirectoryName->Length;
if (!supIsRootDirectory(DirectoryName))
pathLength += OBJ_NAME_PATH_SEPARATOR_SIZE;
pathLength += ObjectName->Length + sizeof(UNICODE_NULL);
allocSize = pathLength;
// Allocate buffer
FullPath->Buffer = (PWSTR)supHeapAlloc(allocSize);
if (!FullPath->Buffer)
return FALSE;
// Copy directory path
target = FullPath->Buffer;
source = DirectoryName->Buffer;
RtlCopyMemory(target, source, DirectoryName->Length);
target = (PWCH)RtlOffsetToPointer(target, DirectoryName->Length);
// Add separator if not root
if (!supIsRootDirectory(DirectoryName))
*target++ = OBJ_NAME_PATH_SEPARATOR;
// Copy object name
RtlCopyMemory(target, ObjectName->Buffer, ObjectName->Length);
target = (PWCH)RtlOffsetToPointer(target, ObjectName->Length);
*target = UNICODE_NULL;
// Set string properties
FullPath->Length = (USHORT)(pathLength - sizeof(UNICODE_NULL));
FullPath->MaximumLength = (USHORT)allocSize;
return TRUE;
}
/*
* xxxListObjectDirectoryTree
*
* Purpose:
*
* List given directory to the treeview.
*
*/
VOID xxxListObjectDirectoryTree(
_In_ HANDLE HeapHandle,
_In_ PUNICODE_STRING SubDirName,
_In_opt_ HANDLE RootHandle,
_In_opt_ HTREEITEM ViewRootHandle,
_In_opt_ OBEX_ITEM* Parent
)
{
ULONG queryContext = 0, rLength;
HANDLE directoryHandle = NULL;
OBEX_ITEM* prevItem = Parent;
POBJECT_DIRECTORY_INFORMATION directoryEntry;
ViewRootHandle = AddTreeViewItem(HeapHandle, SubDirName, ViewRootHandle, &prevItem);
if (ViewRootHandle == NULL)
return;
supOpenDirectoryEx(&directoryHandle, RootHandle, SubDirName, DIRECTORY_QUERY);
if (directoryHandle == NULL) {
return;
}
__try {
do {
directoryEntry = ObQueryObjectDirectory(directoryHandle, &queryContext, g_WinObj.IsWine, &rLength);
if (directoryEntry == NULL)
break;
if (RtlEqualUnicodeString(
&directoryEntry->TypeName,
ObGetPredefinedUnicodeString(OBP_DIRECTORY),
TRUE))
{
xxxListObjectDirectoryTree(HeapHandle,
&directoryEntry->Name,
directoryHandle,
ViewRootHandle,
prevItem);
}
supHeapFree(directoryEntry);
} while (TRUE);
}
__finally {
NtClose(directoryHandle);
}
}
/*
* ListObjectDirectoryTree
*
* Purpose:
*
* List given directory to the treeview.
*
*/
VOID ListObjectDirectoryTree(
_In_ PUNICODE_STRING SubDirName,
_In_opt_ HANDLE RootHandle,
_In_opt_ HTREEITEM ViewRootHandle
)
{
ListHeapCreate(&TreeObjectsHeap);
if (TreeObjectsHeap) {
// Suspend tree redraw for batch operations
supDisableRedraw(g_hwndObjectTree);
xxxListObjectDirectoryTree(TreeObjectsHeap, SubDirName, RootHandle, ViewRootHandle, NULL);
supEnableRedraw(g_hwndObjectTree);
}
}
/*
* AddListViewItem
*
* Purpose:
*
* Add item to the object listview.
*
*/
VOID AddListViewItem(
_In_ HANDLE HeapHandle,
_In_ HANDLE RootDirectoryHandle,
_In_ POBJECT_DIRECTORY_INFORMATION Entry,
_In_ OBEX_ITEM* Parent
)
{
BOOL bFound = FALSE, bNameAllocated;
INT lvItemIndex;
PWSTR objectTypeName;
LVITEM lvItem;
WCHAR szBuffer[MAX_PATH + 1];
WOBJ_TYPE_DESC* typeDesc;
OBEX_ITEM* objRef;
UNICODE_STRING objectName, normalizedLinkTarget;
objectTypeName = Entry->TypeName.Buffer;
typeDesc = ObManagerGetEntryByTypeName(objectTypeName);
bNameAllocated = supNormalizeUnicodeStringForDisplay(g_obexHeap,
&Entry->Name,
&objectName);
if (!bNameAllocated)
objectName = Entry->Name;
//
// Object name column.
//
RtlSecureZeroMemory(&lvItem, sizeof(lvItem));
lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
lvItem.pszText = objectName.Buffer;
lvItem.iItem = MAXINT;
lvItem.iImage = typeDesc->ImageIndex;
objRef = AllocateObjectItem(HeapHandle,
typeDesc->Index,
&Entry->Name,
&Entry->TypeName,
Parent);
lvItem.lParam = (LPARAM)objRef;
lvItemIndex = ListView_InsertItem(g_hwndObjectList, &lvItem);
//
// Object type column.
//
lvItem.mask = LVIF_TEXT;
lvItem.iSubItem = 1;
lvItem.pszText = objectTypeName;
lvItem.iItem = lvItemIndex;
ListView_SetItem(g_hwndObjectList, &lvItem);
RtlSecureZeroMemory(&szBuffer, sizeof(szBuffer));
// Special case for symbolic links as their link targets must be normalized before output.
// Do not set bFound to TRUE so we will fall through the end of routine.
if (typeDesc->NameHash == OBTYPE_HASH_SYMBOLIC_LINK) {
if (supResolveSymbolicLinkTargetNormalized(
NULL,
RootDirectoryHandle,
&Entry->Name,
&normalizedLinkTarget))
{
lvItem.mask = LVIF_TEXT;
lvItem.iSubItem = 2;
lvItem.pszText = normalizedLinkTarget.Buffer;
lvItem.iItem = lvItemIndex;
ListView_SetItem(g_hwndObjectList, &lvItem);
supFreeDuplicatedUnicodeString(g_obexHeap, &normalizedLinkTarget, FALSE);
}
}
else {
// Look for object type in well known type names hashes.
// If found - query information for additional description field.
switch (typeDesc->NameHash) {
case OBTYPE_HASH_SECTION:
bFound = supQuerySectionFileInfo(RootDirectoryHandle,
&Entry->Name,
szBuffer,
MAX_PATH);
break;
case OBTYPE_HASH_DRIVER:
bFound = supQueryDriverDescription(objectName.Buffer,
szBuffer,
MAX_PATH);
break;
case OBTYPE_HASH_DEVICE:
bFound = supQueryDeviceDescription(NULL,
&Entry->Name,
szBuffer,
MAX_PATH);
break;
case OBTYPE_HASH_WINSTATION:
bFound = supQueryWinstationDescription(objectName.Buffer,
szBuffer,
MAX_PATH);
break;
case OBTYPE_HASH_TYPE:
bFound = supQueryTypeInfo(&Entry->Name,
szBuffer,
MAX_PATH,
RTL_NUMBER_OF(a_PoolTypes),
a_PoolTypes);
break;
}
}
// Finally add information column if something found.
if (bFound != FALSE) {
lvItem.mask = LVIF_TEXT;
lvItem.iSubItem = 2;
lvItem.pszText = szBuffer;
lvItem.iItem = lvItemIndex;
ListView_SetItem(g_hwndObjectList, &lvItem);
}
if (bNameAllocated)
supFreeUnicodeString(g_obexHeap, &objectName);
}
/*
* xxxListCurrentDirectoryObjects
*
* Purpose:
*
* List directory objects to the listview.
*
*/
VOID xxxListCurrentDirectoryObjects(
_In_ HANDLE HeapHandle,
_In_ OBEX_ITEM* Parent
)
{
ULONG queryContext = 0, rLength;
HANDLE directoryHandle = NULL;
UNICODE_STRING usDirectoryName;
POBJECT_DIRECTORY_INFORMATION infoBuffer;
ListView_DeleteAllItems(g_hwndObjectList);
if (supGetCurrentObjectPath(TRUE, &usDirectoryName)) {
supOpenDirectoryEx(&directoryHandle, NULL, &usDirectoryName, DIRECTORY_QUERY);
supFreeDuplicatedUnicodeString(g_obexHeap, &usDirectoryName, FALSE);
}
if (directoryHandle == NULL)
return;
supDisableRedraw(g_hwndObjectList);
do {
infoBuffer = ObQueryObjectDirectory(directoryHandle, &queryContext, g_WinObj.IsWine, &rLength);
if (infoBuffer) {
AddListViewItem(HeapHandle, directoryHandle, infoBuffer, Parent);
supHeapFree(infoBuffer);
}
else {
break;
}
} while (TRUE);
supEnableRedraw(g_hwndObjectList);
NtClose(directoryHandle);
}
/*
* ListCurrentDirectoryObjects
*
* Purpose:
*
* List directory objects to the listview.
*
*/
VOID ListCurrentDirectoryObjects(
_In_ HTREEITEM ViewRootHandle
)
{
OBEX_ITEM* objRef = NULL;
ListHeapCreate(&ListObjectsHeap);
if (ListObjectsHeap) {
if (supGetTreeViewItemParam(g_hwndObjectTree,
ViewRootHandle,
(PVOID*)&objRef))
{
xxxListCurrentDirectoryObjects(ListObjectsHeap, objRef);
}
}
}
/*
* AllocateFoundItem
*
* Purpose:
*
* Allocate item for search dialog results.
*
*/
PFO_LIST_ITEM AllocateFoundItem(
_In_ PFO_LIST_ITEM Previous,
_In_ PUNICODE_STRING DirectoryName,
_In_ POBJECT_DIRECTORY_INFORMATION InfoBuffer
)
{
PFO_LIST_ITEM Item;
SIZE_T BufferLength;
UNICODE_STRING fullPath;
// Calculate the full objectname path and store it in fullPath
RtlInitEmptyUnicodeString(&fullPath, NULL, 0);
if (!AppendDirectoryPath(DirectoryName, &InfoBuffer->Name, &fullPath)) {
return NULL;
}
// Allocate memory for the item structure and string data
BufferLength = sizeof(FO_LIST_ITEM) +
fullPath.MaximumLength +
InfoBuffer->TypeName.Length + sizeof(UNICODE_NULL);
Item = (PFO_LIST_ITEM)supHeapAlloc(BufferLength);
if (Item == NULL) {
supHeapFree(fullPath.Buffer);
return NULL;
}
// Setup the item
Item->Prev = Previous;
// Set up the ObjectName
Item->ObjectName.Buffer = (PWSTR)Item->NameBuffer;
Item->ObjectName.Length = fullPath.Length;
Item->ObjectName.MaximumLength = fullPath.MaximumLength;
RtlCopyMemory(Item->ObjectName.Buffer, fullPath.Buffer, fullPath.Length);
Item->ObjectName.Buffer[fullPath.Length / sizeof(WCHAR)] = UNICODE_NULL;
// Set up the ObjectType
Item->ObjectType.Buffer = (PWSTR)(Item->NameBuffer + (fullPath.MaximumLength / sizeof(WCHAR)));
Item->ObjectType.Length = InfoBuffer->TypeName.Length;
Item->ObjectType.MaximumLength = InfoBuffer->TypeName.Length + sizeof(UNICODE_NULL);
RtlCopyMemory(Item->ObjectType.Buffer, InfoBuffer->TypeName.Buffer, InfoBuffer->TypeName.Length);
Item->ObjectType.Buffer[InfoBuffer->TypeName.Length / sizeof(WCHAR)] = UNICODE_NULL;
// Free temporary buffer
supHeapFree(fullPath.Buffer);
return Item;
}
/*
* FindObject
*
* Purpose:
*
* Find object by given name in object directory.
*
*/
VOID FindObject(
_In_ PUNICODE_STRING DirectoryName,
_In_opt_ PUNICODE_STRING NameSubstring,
_In_opt_ PUNICODE_STRING TypeName,
_In_ PFO_LIST_ITEM* List
)
{
ULONG ctx, rlen;
HANDLE directoryHandle = NULL;
PFO_LIST_ITEM item;
UNICODE_STRING subDirectory;
POBJECT_DIRECTORY_INFORMATION infoBuffer;
supOpenDirectoryEx(&directoryHandle, NULL, DirectoryName, DIRECTORY_QUERY);
if (directoryHandle == NULL)
return;
ctx = 0;
do {
infoBuffer = ObQueryObjectDirectory(directoryHandle, &ctx, g_WinObj.IsWine, &rlen);
if (!infoBuffer)
break;
if (TypeName) {
if (RtlEqualUnicodeString(&infoBuffer->TypeName, TypeName, TRUE)) {
if (NameSubstring) {
if (ULLONG_MAX != supFindUnicodeStringSubString(&infoBuffer->Name, NameSubstring)) {
item = AllocateFoundItem(*List, DirectoryName, infoBuffer);
if (item) *List = item;
}
}
else {
item = AllocateFoundItem(*List, DirectoryName, infoBuffer);
if (item) *List = item;
}
}
}
else if (NameSubstring) {
// Only name substring specified - check for substring
if (ULLONG_MAX != supFindUnicodeStringSubString(&infoBuffer->Name, NameSubstring)) {
item = AllocateFoundItem(*List, DirectoryName, infoBuffer);
if (item) *List = item;
}
}
else {
// No filter specified - add all objects
item = AllocateFoundItem(*List, DirectoryName, infoBuffer);
if (item) *List = item;
}
// If this is directory, go inside.
RtlInitEmptyUnicodeString(&subDirectory, NULL, 0);
if (RtlEqualUnicodeString(&infoBuffer->TypeName,
ObGetPredefinedUnicodeString(OBP_DIRECTORY),
TRUE))
{
if (AppendDirectoryPath(DirectoryName, &infoBuffer->Name, &subDirectory)) {
FindObject(&subDirectory, NameSubstring, TypeName, List);
supHeapFree(subDirectory.Buffer);
subDirectory.Buffer = NULL;
}
}
supHeapFree(infoBuffer);
} while (TRUE);
NtClose(directoryHandle);
}