导读
玩过电脑的都知道,Widnows任务管理器,我们经常用它查看机器的CPU内存等信息,也用它杀死一些卡主的进程。
而作为开发人员,我们为了更了解进行的运行状况,就不得不说另外两个工具:
- procexp.exe,某大佬写的Sysinternals系列工具中的其中一个工具,可查看操作系统中的各种信息,丰富无比,绝对的Windows瑞士军刀。
- ProcessHacker.exe,功能和procexp.exe很像,不过是开源项目,学习Windows的可以看看它的实现。
今天我们就获取自己进程主线程的内核对象信息。
API介绍
一个内核对象有两个计数器:一个是句柄计数,句柄是给用户态用的;另一个是指针计数,也叫引用计数,因为核心态也常常用到内核对象,为了方便,在核心态的代码用指针直接访问对象,所以Object Manager维护了这个指针引用计数。只有在句柄计数和引用计数都为0时,对象才被释放。一般而言,指针引用计数值比句柄计数值大。
再进一步,实际上,在用户态其实是可以查询一个内核对象的句柄计数和引用计数的。Ntdll.dll里导出的NtQueryObject函数可以查询内核对象的当前状态,只不过它没有被文档化。
参考微软官网 NtQueryObject function https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryobject
函数声明如下:
DWORD WINAPI NtQueryObject( HANDLE handle, DWORD nQueryIndex, VOID* pOutBuffer, DWORD cbInBufferSize, VOID* cbOutBufferSize);
handle -- 待查询的句柄
nQueryIndex -- 0为查询对象的当前状态,包括句柄计数,引用计数等等。
pOutBuffer -- 存放查询结果
cbInBufferSize -- pOutBuffer的大小,注意,如果nQueryIndex为0,这里一定得是0x38
cbOutBufferSize -- 实际大小。
返回值:如果成功则返回0
pOutBuffer里返回的数据结构如下:
typedef struct _SYSTEM_HANDLE_STATE {
DWORD r1;
DWORD GrantedAccess;
DWORD HandleCount; // 减1为句柄计数
DWORD ReferenceCount; // 减1为指针引用计数
DWORD r5;
DWORD r6;
DWORD r7;
DWORD r8;
DWORD r9;
DWORD r10;
DWORD r11;
DWORD r12;
DWORD r13;
DWORD r14;
}SYSTEM_HANDLE_STATE, *PSYSTEM_HANDLE_STATE;
功能实现
#include <string>
#include <ntdll/ntdll.h>
#pragma comment(lib,"ntdll.lib")
int test()
{
HANDLE Handle = GetCurrentThread();
OBJECT_BASIC_INFORMATION o_BASIC_INFORMATION;
OBJECT_TYPE_INFORMATION o_TYPE_INFORMATION[10];
OBJECT_NAME_INFORMATION o_NAME_INFORMATION;
ULONG ReturnLength = 0;
NtQueryObject(Handle, ObjectBasicInformation, &o_BASIC_INFORMATION, sizeof(o_BASIC_INFORMATION), &ReturnLength);
NtQueryObject(Handle, ObjectNameInformation, &o_NAME_INFORMATION, sizeof(o_NAME_INFORMATION), &ReturnLength);
NtQueryObject(Handle, ObjectTypeInformation, &o_TYPE_INFORMATION, o_BASIC_INFORMATION.TypeInfoSize, &ReturnLength);
std::wstring TypeName(o_TYPE_INFORMATION[0].TypeName.Buffer, o_TYPE_INFORMATION[0].TypeName.Length / 2);
std::wstring ObjName(L"[NULL]");
if (o_NAME_INFORMATION.Name.Length)
{
ObjName.assign(o_NAME_INFORMATION.Name.Buffer, o_NAME_INFORMATION.Name.Length / 2);
}
printf("Name: %ws \nType: %ws \n \nGranted access:%8X \nReferences: %d \nHandles: %d \n",
ObjName.data(), TypeName.data(), o_BASIC_INFORMATION.GrantedAccess,
o_BASIC_INFORMATION.PointerCount, o_BASIC_INFORMATION.HandleCount);
return 0;
}
这里我们使用了https://github.com/x64dbg/x64dbg项目中的ntdll.h和ntdll.lib,里面讲ntdll中的函数及数据结构整理出来了,我们直接使用就行了,通过NtQueryObject接口,我们可以查询到丰富的句柄信息。运行结果如下:
ProcessHacker.exe查看的结果如下:
内容基本相同,句柄数和引用数有差异,暂时不深入研究了。
参考资料
- Process Hacker官网 https://processhacker.sourceforge.io/
- NtQueryObject function https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryobject
- qq群:夜猫逐梦技术交流裙/953949723