Files

554 lines
18 KiB
C++

#include "pch.h"
#include "ObjectManager.h"
#include <assert.h>
#include "DriverHelper.h"
#include <VersionHelpers.h>
using namespace WinSys;
#pragma pack(push, 1)
typedef struct _GDI_HANDLE_ENTRY {
union {
PVOID Object;
PVOID NextFree;
};
union {
struct {
USHORT ProcessId;
USHORT Lock : 1;
USHORT Count : 15;
};
ULONG Value;
} Owner;
USHORT Unique;
UCHAR Type;
UCHAR Flags;
PVOID UserPointer;
} GDI_HANDLE_ENTRY, * PGDI_HANDLE_ENTRY;
#pragma pack(pop)
// static_assert(sizeof(GDI_HANDLE_ENTRY) == 24);
#define GDI_HANDLE_INDEX_BITS 16
#define GDI_MAKE_HANDLE(Index, Unique) ((ULONG)(((ULONG)(Unique) << GDI_HANDLE_INDEX_BITS) | (ULONG)(Index)))
std::vector<std::shared_ptr<ObjectTypeInfo>> ObjectManager::_types;
std::unordered_map<int16_t, std::shared_ptr<ObjectTypeInfo>> ObjectManager::_typesMap;
std::unordered_map<std::wstring, std::shared_ptr<ObjectTypeInfo>> ObjectManager::_typesNameMap;
std::vector<ObjectManager::Change> ObjectManager::_changes;
int64_t ObjectManager::_totalHandles;
int64_t ObjectManager::_totalObjects;
int ObjectManager::EnumTypes() {
const ULONG len = 1 << 14;
BYTE buffer[len];
if (!NT_SUCCESS(::NtQueryObject(nullptr, ObjectTypesInformation, buffer, len,nullptr)))
return 0;
auto p = reinterpret_cast<OBJECT_TYPES_INFORMATION*>(buffer);
bool empty = _types.empty();
auto count = p->NumberOfTypes;
if (empty) {
_types.reserve(count);
_typesMap.reserve(count);
_changes.reserve(32);
}
else {
_changes.clear();
assert(count == _types.size());
}
auto raw = &p->TypeInformation[0];
_totalHandles = _totalObjects = 0;
for (ULONG i = 0; i < count; i++) {
if (!IsWindows8OrGreater()) {
// TypeIndex is only suported since Win8,Uses the fake index for previous OS.
raw->TypeIndex = static_cast<decltype(raw->TypeIndex)>(i);
}
auto type = empty ? std::make_shared<ObjectTypeInfo>() : _typesMap[raw->TypeIndex];
if (empty) {
type->GenericMapping = raw->GenericMapping;
type->TypeIndex = raw->TypeIndex;
type->DefaultNonPagedPoolCharge = raw->DefaultNonPagedPoolCharge;
type->DefaultPagedPoolCharge = raw->DefaultPagedPoolCharge;
type->TypeName = std::wstring(raw->TypeName.Buffer, raw->TypeName.Length / sizeof(WCHAR));
type->PoolType = (PoolType)raw->PoolType;
type->DefaultNonPagedPoolCharge = raw->DefaultNonPagedPoolCharge;
type->DefaultPagedPoolCharge = raw->DefaultPagedPoolCharge;
type->ValidAccessMask = raw->ValidAccessMask;
type->InvalidAttributes = raw->InvalidAttributes;
}
else {
if (type->TotalNumberOfHandles != raw->TotalNumberOfHandles)
_changes.push_back({ type, ChangeType::TotalHandles, (int32_t)raw->TotalNumberOfHandles - (int32_t)type->TotalNumberOfHandles });
if (type->TotalNumberOfObjects != raw->TotalNumberOfObjects)
_changes.push_back({ type, ChangeType::TotalObjects, (int32_t)raw->TotalNumberOfObjects - (int32_t)type->TotalNumberOfObjects });
if (type->HighWaterNumberOfHandles != raw->HighWaterNumberOfHandles)
_changes.push_back({ type, ChangeType::PeakHandles, (int32_t)raw->HighWaterNumberOfHandles - (int32_t)type->HighWaterNumberOfHandles });
if (type->HighWaterNumberOfObjects != raw->HighWaterNumberOfObjects)
_changes.push_back({ type, ChangeType::PeakObjects, (int32_t)raw->HighWaterNumberOfObjects - (int32_t)type->HighWaterNumberOfObjects });
}
type->TotalNumberOfHandles = raw->TotalNumberOfHandles;
type->TotalNumberOfObjects = raw->TotalNumberOfObjects;
type->TotalNonPagedPoolUsage = raw->TotalNonPagedPoolUsage;
type->TotalPagedPoolUsage = raw->TotalPagedPoolUsage;
type->HighWaterNumberOfObjects = raw->HighWaterNumberOfObjects;
type->HighWaterNumberOfHandles = raw->HighWaterNumberOfHandles;
type->TotalNamePoolUsage = raw->TotalNamePoolUsage;
_totalObjects += raw->TotalNumberOfObjects;
_totalHandles += raw->TotalNumberOfHandles;
if (empty) {
_types.emplace_back(type);
_typesMap.insert({ type->TypeIndex, type });
_typesNameMap.insert({ std::wstring(type->TypeName), type });
}
auto temp = (BYTE*)raw + sizeof(OBJECT_TYPE_INFORMATION) + raw->TypeName.MaximumLength;
temp += sizeof(PVOID) - 1;
raw = reinterpret_cast<OBJECT_TYPE_INFORMATION*>((ULONG_PTR)temp / sizeof(PVOID) * sizeof(PVOID));
}
return static_cast<int>(_types.size());
}
bool ObjectManager::EnumHandlesAndObjects(PCWSTR type, DWORD pid, PCWSTR prefix, bool namedOnly) {
EnumTypes();
ULONG len = 1 << 25;
std::unique_ptr<BYTE[]> buffer;
do {
buffer = std::make_unique<BYTE[]>(len);
auto status = ::NtQuerySystemInformation(SystemExtendedHandleInformation, buffer.get(), len, &len);
if (status == STATUS_INFO_LENGTH_MISMATCH) {
len <<= 1;
continue;
}
if (status == 0)
break;
return false;
} while (true);
auto filteredTypeIndex = type == nullptr || ::wcslen(type) == 0 ? -1 : _typesNameMap.at(type)->TypeIndex;
auto p = (SYSTEM_HANDLE_INFORMATION_EX*)buffer.get();
auto count = p->NumberOfHandles;
_objects.clear();
_objectsByAddress.clear();
_objects.reserve(count / 2);
_objectsByAddress.reserve(count / 2);
std::wstring sprefix(prefix ? prefix : L"");
auto& handles = p->Handles;
for (decltype(count) i = 0; i < count; i++) {
auto& handle = handles[i];
if (filteredTypeIndex >= 0 && handle.ObjectTypeIndex != filteredTypeIndex)
continue;
if (pid && handle.UniqueProcessId != pid)
continue;
auto name = GetObjectName((HANDLE)handle.HandleValue, (ULONG)handle.UniqueProcessId, handle.ObjectTypeIndex);
if (prefix) {
if (name.empty() ||
_wcsicmp(name.c_str(),sprefix.c_str())!=0)
//name.Left(sprefix.GetLength()).CompareNoCase(sprefix) != 0)
continue;
}
if (namedOnly && name.empty())
continue;
auto hi = std::make_shared<HandleInfo>();
hi->HandleValue = (ULONG)handle.HandleValue;
hi->GrantedAccess = handle.GrantedAccess;
hi->Object = handle.Object;
hi->HandleAttributes = handle.HandleAttributes;
hi->ProcessId = (ULONG)handle.UniqueProcessId;
hi->ObjectTypeIndex = handle.ObjectTypeIndex;
if (auto it = _objectsByAddress.find(handle.Object); it == _objectsByAddress.end()) {
auto obj = std::make_shared<ObjectInfo>();
obj->HandleCount = 1;
obj->Object = handle.Object;
obj->Handles.push_back(hi);
obj->TypeIndex = handle.ObjectTypeIndex;
obj->TypeName = GetType(obj->TypeIndex)->TypeName.c_str();
hi->ObjectInfo = obj.get();
if (!name.empty())
obj->Name = name;
_objects.push_back(obj);
_objectsByAddress.insert({ handle.Object, obj });
}
else {
hi->ObjectInfo = nullptr;
it->second->HandleCount++;
it->second->Handles.push_back(hi);
}
_handles.push_back(hi);
}
return true;
}
bool ObjectManager::EnumHandles(PCWSTR type, DWORD pid, bool namedObjectsOnly) {
EnumTypes();
ULONG len = 1 << 25;
std::unique_ptr<BYTE[]> buffer;
do {
buffer = std::make_unique<BYTE[]>(len);
auto status = ::NtQuerySystemInformation(SystemExtendedHandleInformation, buffer.get(), len, &len);
if (status == STATUS_INFO_LENGTH_MISMATCH) {
len <<= 1;
continue;
}
if (status == 0)
break;
return false;
} while (true);
auto filteredTypeIndex = type == nullptr || ::wcslen(type) == 0 ? -1 : _typesNameMap.at(type)->TypeIndex;
auto p = (SYSTEM_HANDLE_INFORMATION_EX*)buffer.get();
auto count = p->NumberOfHandles;
_handles.clear();
_handles.reserve(count);
for (decltype(count) i = 0; i < count; i++) {
auto& handle = p->Handles[i];
if (pid && handle.UniqueProcessId != pid)
continue;
if (filteredTypeIndex >= 0 && handle.ObjectTypeIndex != filteredTypeIndex)
continue;
// skip Object Explorer process?
if (_skipThisProcess && handle.UniqueProcessId == ::GetCurrentProcessId())
continue;
std::wstring name = GetObjectName((HANDLE)handle.HandleValue, (DWORD)handle.UniqueProcessId, handle.ObjectTypeIndex);
if (namedObjectsOnly && name.empty())
continue;
if (name.length() != 0) {
auto pos = name.find(L"\\Device\\PhysicalMemory", 0);
if (pos != std::string::npos) {
}
}
auto hi = std::make_shared<HandleInfo>();
hi->HandleValue = (ULONG)handle.HandleValue;
hi->GrantedAccess = handle.GrantedAccess;
hi->Object = handle.Object;
hi->HandleAttributes = handle.HandleAttributes;
hi->ProcessId = (ULONG)handle.UniqueProcessId;
hi->ObjectTypeIndex = handle.ObjectTypeIndex;
hi->Name = name;
_handles.emplace_back(hi);
}
return true;
}
const std::vector<std::shared_ptr<ObjectInfo>>& ObjectManager::GetObjects() const {
return _objects;
}
const std::vector<ObjectManager::Change>& ObjectManager::GetChanges() const {
return _changes;
}
std::vector<ObjectNameAndType> ObjectManager::EnumDirectoryObjects(PCWSTR path) {
std::vector<ObjectNameAndType> objects;
wil::unique_handle hDirectory;
OBJECT_ATTRIBUTES attr;
UNICODE_STRING name;
RtlInitUnicodeString(&name, path);
InitializeObjectAttributes(&attr, &name, 0, nullptr, nullptr);
if (!NT_SUCCESS(::NtOpenDirectoryObject(hDirectory.addressof(), DIRECTORY_QUERY, &attr)))
return objects;
objects.reserve(128);
BYTE buffer[1 << 12];
auto info = reinterpret_cast<OBJECT_DIRECTORY_INFORMATION*>(buffer);
bool first = true;
ULONG size, index = 0;
for (;;) {
auto start = index;
if (!NT_SUCCESS(::NtQueryDirectoryObject(hDirectory.get(), info, sizeof(buffer), FALSE, first, &index, &size)))
break;
first = false;
for (ULONG i = 0; i < index - start; i++) {
ObjectNameAndType data;
auto& p = info[i];
data.Name = std::wstring(p.Name.Buffer, p.Name.Length / sizeof(WCHAR));
data.TypeName = std::wstring(p.TypeName.Buffer, p.TypeName.Length / sizeof(WCHAR));
objects.push_back(std::move(data));
}
}
return objects;
}
std::wstring ObjectManager::GetSymbolicLinkTarget(PCWSTR path) {
wil::unique_handle hLink;
OBJECT_ATTRIBUTES attr;
std::wstring target;
UNICODE_STRING name;
RtlInitUnicodeString(&name, path);
InitializeObjectAttributes(&attr, &name, 0, nullptr, nullptr);
if (NT_SUCCESS(::NtOpenSymbolicLinkObject(hLink.addressof(), GENERIC_READ, &attr))) {
WCHAR buffer[1 << 10];
UNICODE_STRING result;
result.Buffer = buffer;
result.MaximumLength = sizeof(buffer);
if (NT_SUCCESS(::NtQuerySymbolicLinkObject(hLink.get(), &result, nullptr)))
target = std::wstring(result.Buffer, result.Length / sizeof(WCHAR));
}
return target;
}
NTSTATUS ObjectManager::OpenObject(PCWSTR path, PCWSTR typeName, HANDLE* pHandle, DWORD access) {
assert(pHandle);
if (pHandle == nullptr)
return STATUS_INVALID_PARAMETER;
HANDLE hObject = nullptr;
std::wstring type(typeName);
OBJECT_ATTRIBUTES attr;
UNICODE_STRING uname;
RtlInitUnicodeString(&uname, path);
InitializeObjectAttributes(&attr, &uname, 0, nullptr, nullptr);
NTSTATUS status = STATUS_UNSUCCESSFUL;
if (type == L"Event")
status = ::NtOpenEvent(&hObject, access, &attr);
else if (type == L"Mutant")
status = ::NtOpenMutant(&hObject, access, &attr);
else if (type == L"Section")
status = ::NtOpenSection(&hObject, access, &attr);
else if (type == L"Semaphore")
status = ::NtOpenSemaphore(&hObject, access, &attr);
else if (type == L"EventPair")
status = ::NtOpenEventPair(&hObject, access, &attr);
else if (type == L"IoCompletion")
status = ::NtOpenIoCompletion(&hObject, access, &attr);
else if (type == L"SymbolicLink")
status = ::NtOpenSymbolicLinkObject(&hObject, access, &attr);
else if (type == L"Key")
status = ::NtOpenKey(&hObject, access, &attr);
else if (type == L"Job")
status = ::NtOpenJobObject(&hObject, access, &attr);
else if (type == L"File" || type == L"Device") {
IO_STATUS_BLOCK ioStatus;
status = ::NtOpenFile(&hObject, access, &attr, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0);
}
*pHandle = hObject;
return status;
}
int64_t ObjectManager::GetTotalHandles() {
return _totalHandles;
}
int64_t ObjectManager::GetTotalObjects() {
return _totalObjects;
}
std::vector<HWND> ObjectManager::EnumDsktopWindows(HANDLE hDesktop) {
std::vector<HWND> windows;
windows.reserve(128);
::EnumDesktopWindows((HDESK)hDesktop, [](auto h, auto p) {
reinterpret_cast<std::vector<HWND>*>(p)->push_back(h);
return TRUE;
}, reinterpret_cast<LPARAM>(&windows));
return windows;
}
std::vector<HWND> ObjectManager::EnumChildWindows(HWND hWnd) {
std::vector<HWND> windows;
::EnumChildWindows(hWnd, [](auto h, auto p) {
reinterpret_cast<std::vector<HWND>*>(p)->push_back(h);
return TRUE;
}, reinterpret_cast<LPARAM>(&windows));
return windows;
}
std::vector<GdiObject> ObjectManager::EnumGdiObjects(DWORD pid) {
std::vector<GdiObject> objects;
auto hProcess = ::GetCurrentProcess();
PROCESS_BASIC_INFORMATION pbi;
::NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), nullptr);
auto const GdiSharedHandleTable = 0xf8;
GDI_HANDLE_ENTRY* GdiAddress;
SIZE_T read;
if (!::ReadProcessMemory(hProcess, (BYTE*)pbi.PebBaseAddress + GdiSharedHandleTable, &GdiAddress, sizeof(GdiAddress), &read))
return objects;
unsigned tableSize = 64 << 10;
auto table = std::make_unique<GDI_HANDLE_ENTRY[]>(tableSize);
if (!::ReadProcessMemory(hProcess, GdiAddress, table.get(), tableSize * sizeof(GDI_HANDLE_ENTRY), &read))
return objects;
objects.reserve(128);
for (unsigned i = 0; i < tableSize; i++) {
auto obj = table.get() + i;
if (obj->Owner.ProcessId == 0)
continue;
if (obj->Owner.ProcessId != pid)
continue;
GdiObject object;
object.Object = obj->Object;
object.ProcessId = obj->Owner.ProcessId;
object.Index = (USHORT)i;
object.Handle = GDI_MAKE_HANDLE(i, obj->Unique);
object.Type = (GdiObjectType)(obj->Type & 0x7f);
object.Count = obj->Owner.Count;
objects.push_back(object);
}
return objects;
}
bool ObjectManager::GetObjectInfo(ObjectInfo* info, HANDLE hObject, ULONG pid, USHORT type) const {
auto hDup = DupHandle(info);
if (hDup) {
info->Name = GetObjectName(hDup, type);
::CloseHandle(hDup);
return true;
}
return false;
}
std::wstring ObjectManager::GetObjectName(HANDLE hDup, USHORT type) {
static int processTypeIndex = _typesNameMap.find(L"Process")->second->TypeIndex;
static int threadTypeIndex = _typesNameMap.find(L"Thread")->second->TypeIndex;
static int fileTypeIndex = _typesNameMap.find(L"File")->second->TypeIndex;
assert(processTypeIndex > 0 && threadTypeIndex > 0);
std::wstring sname;
do {
if (type == processTypeIndex || type == threadTypeIndex)
break;
BYTE buffer[2048];
if (type == fileTypeIndex) {
// special case for files in case they're locked
struct Data {
HANDLE hDup;
BYTE* buffer;
} data = { hDup, buffer };
wil::unique_handle hThread(::CreateThread(nullptr, 1 << 13, [](auto p) {
auto d = (Data*)p;
return (DWORD)::NtQueryObject(d->hDup, ObjectNameInformation, d->buffer, sizeof(buffer), nullptr);
}, &data, STACK_SIZE_PARAM_IS_A_RESERVATION, nullptr));
if (::WaitForSingleObject(hThread.get(), 6) == WAIT_TIMEOUT) {
::TerminateThread(hThread.get(), 1);
}
else {
DWORD code;
::GetExitCodeThread(hThread.get(), &code);
if (code == STATUS_SUCCESS) {
auto name = (POBJECT_NAME_INFORMATION)buffer;
sname = std::wstring(name->Name.Buffer, name->Name.Length / sizeof(WCHAR));
}
}
}
else if (NT_SUCCESS(::NtQueryObject(hDup, ObjectNameInformation, buffer, sizeof(buffer), nullptr))) {
auto name = (::POBJECT_NAME_INFORMATION)buffer;
sname = std::wstring(name->Name.Buffer, name->Name.Length / sizeof(WCHAR));
}
} while (false);
return sname;
}
std::shared_ptr<ObjectTypeInfo> ObjectManager::GetType(USHORT index) {
return _typesMap.at(index);
}
std::shared_ptr<ObjectTypeInfo> ObjectManager::GetType(PCWSTR name) {
return _typesNameMap.at(name);
}
const std::vector<std::shared_ptr<ObjectTypeInfo>>& ObjectManager::GetObjectTypes() {
return _types;
}
const std::vector<std::shared_ptr<HandleInfo>>& ObjectManager::GetHandles() const {
return _handles;
}
bool ObjectManager::GetStats(ObjectAndHandleStats& stats) {
const ULONG len = 1 << 14;
BYTE buffer[len];
if (!NT_SUCCESS(::NtQueryObject(nullptr, ObjectTypesInformation, buffer, len, nullptr)))
return false;
auto p = reinterpret_cast<OBJECT_TYPES_INFORMATION*>(buffer);
auto count = p->NumberOfTypes;
::ZeroMemory(&stats, sizeof(stats));
auto raw = &p->TypeInformation[0];
for (ULONG i = 0; i < count; i++) {
stats.TotalHandles += raw->TotalNumberOfHandles;
stats.TotalObjects += raw->TotalNumberOfObjects;
stats.PeakHandles += raw->HighWaterNumberOfHandles;
stats.PeakObjects += raw->HighWaterNumberOfObjects;
auto temp = (BYTE*)raw + sizeof(OBJECT_TYPE_INFORMATION) + raw->TypeName.MaximumLength;
temp += sizeof(PVOID) - 1;
raw = reinterpret_cast<OBJECT_TYPE_INFORMATION*>((ULONG_PTR)temp / sizeof(PVOID) * sizeof(PVOID));
}
return true;
}
std::wstring ObjectManager::GetObjectName(HANDLE hObject, ULONG pid, USHORT type) const {
auto hDup = DriverHelper::DupHandle(hObject, pid, 0);
if (hDup == nullptr) {
OBJECT_ATTRIBUTES procAttr = RTL_CONSTANT_OBJECT_ATTRIBUTES(nullptr, 0);
CLIENT_ID cid{};
cid.UniqueProcess = UlongToHandle(pid);
HANDLE hProcess;
NTSTATUS status = NtOpenProcess(&hProcess, PROCESS_DUP_HANDLE, &procAttr, &cid);
if (!NT_SUCCESS(status))
return L"";
status = NtDuplicateObject(hProcess, hObject, NtCurrentProcess(), &hDup, READ_CONTROL, 0, 0);
if (!NT_SUCCESS(status))
return L"";
}
std::wstring name = GetObjectName(hDup, type);
if (hDup != nullptr)
::CloseHandle(hDup);
return name;
}
HANDLE ObjectManager::DupHandle(ObjectInfo* pObject, ACCESS_MASK access) {
for (auto& h : pObject->Handles) {
auto hDup = DriverHelper::DupHandle(ULongToHandle(h->HandleValue), h->ProcessId, //access);
_typesMap.at(h->ObjectTypeIndex)->ValidAccessMask);
if (hDup)
return hDup;
}
return nullptr;
}
HANDLE ObjectManager::DupHandle(HANDLE h, DWORD pid, USHORT type, ACCESS_MASK access, DWORD flags) {
auto hDup = DriverHelper::DupHandle(h, pid, _typesMap.at(type)->ValidAccessMask, flags);
return hDup;
}