在内核开发中,经常需要进行进程和句柄之间的互相转换。进程通常由一个唯一的进程标识符(PID)来标识,而句柄是指对内核对象的引用。在Windows内核中,EProcess
结构表示一个进程,而HANDLE是一个句柄。
为了实现进程与句柄之间的转换,我们需要使用一些内核函数。对于进程PID和句柄的互相转换,可以使用函数如OpenProcess
和GetProcessId
。OpenProcess函数接受一个PID作为参数,并返回一个句柄。GetProcessId函数接受一个句柄作为参数,并返回该进程的PID。
对于进程PID和EProcess
结构的互相转换,可以使用函数如PsGetProcessId
和PsGetCurrentProcess
。PsGetProcessId函数接受一个EProcess
结构作为参数,并返回该进程的PID。PsGetCurrentProcess
函数返回当前进程的EProcess
结构。
最后,对于句柄和EProcess
结构的互相转换,可以使用函数如ObReferenceObjectByHandle和PsGetProcessId
。ObReferenceObjectByHandle函数接受一个句柄和一个对象类型作为参数,并返回对该对象的引用。PsGetProcessId
函数接受一个EProcess结构作为参数,并返回该进程的PID。
掌握这些内核函数的使用,可以方便地实现进程与句柄之间的互相转换。在进行进程和线程的内核开发之前,了解这些转换功能是非常重要的。
4.1.1 进程PID与进程HANDLE转换
进程PID
转化为HANDLE
句柄,可通过ZwOpenProcess
这个内核函数,传入PID
传出进程HANDLE
句柄,如果需要将HANDLE
句柄转化为PID
则可通过ZwQueryInformationProcess
这个内核函数来实现,具体转换实现方法如下所示;
在内核开发中,经常需要进行进程PID
和句柄HANDLE
之间的互相转换。将进程PID
转化为句柄HANDLE
的方法是通过调用ZwOpenProcess
内核函数,传入PID作为参数,函数返回对应进程的句柄HANDLE。具体实现方法是,定义一个OBJECT_ATTRIBUTES
结构体和CLIENT_ID
结构体,将进程PID赋值给CLIENT_ID
结构体的UniqueProcess
字段,调用ZwOpenProcess
函数打开进程,如果函数执行成功,将返回进程句柄HANDLE,否则返回NULL。
将句柄HANDLE
转化为进程PID
的方法是通过调用ZwQueryInformationProcess
内核函数,传入进程句柄和信息类别作为参数,函数返回有关指定进程的信息,包括进程PID。具体实现方法是,定义一个PROCESS_BASIC_INFORMATION
结构体和一个NTSTATUS
变量,调用ZwQueryInformationProcess
函数查询进程基本信息,如果函数执行成功,将返回进程PID,否则返回0。
其中ZwQueryInformationProcess
是一个未被导出的函数如需使用要通过MmGetSystemRoutineAddress
动态获取到,该函数的原型定义如下:
NTSTATUS ZwQueryInformationProcess(HANDLE ProcessHandle,PROCESSINFOCLASS ProcessInformationClass,PVOID ProcessInformation,ULONG ProcessInformationLength,PULONG ReturnLength
);
函数可以接受一个进程句柄ProcessHandle
、一个PROCESSINFOCLASS
枚举类型的参数ProcessInformationClass
、一个用于存储返回信息的缓冲区ProcessInformation
、缓冲区大小ProcessInformationLength
和一个指向ULONG类型变量的指针ReturnLength
作为参数。
在调用该函数时,ProcessInformationClass
参数指定要获取的进程信息的类型。例如,如果要获取进程的基本信息,则需要将该参数设置为ProcessBasicInformation
;如果要获取进程的映像文件名,则需要将该参数设置为ProcessImageFileName
。调用成功后,返回的信息存储在ProcessInformation
缓冲区中。
在调用该函数时,如果ProcessInformation
缓冲区的大小小于需要返回的信息大小,则该函数将返回STATUS_INFO_LENGTH_MISMATCH
错误代码,并将所需信息的大小存储在ReturnLength
指针指向的ULONG类型变量中。
ZwQueryInformationProcess函数的返回值为NTSTATUS
类型,表示函数执行的结果状态。如果函数执行成功,则返回STATUS_SUCCESS
,否则返回其他错误代码。
掌握这些转换方法可以方便地在内核开发中进行进程PID和句柄HANDLE之间的互相转换。
#include <ntifs.h>// 定义函数指针
typedef NTSTATUS(*PfnZwQueryInformationProcess)(__in HANDLE ProcessHandle,__in PROCESSINFOCLASS ProcessInformationClass,__out_bcount(ProcessInformationLength) PVOID ProcessInformation,__in ULONG ProcessInformationLength,__out_opt PULONG ReturnLength
);PfnZwQueryInformationProcess ZwQueryInformationProcess;// 传入PID传出HANDLE句柄
HANDLE PidToHandle(ULONG PID)
{HANDLE hProcessHandle;OBJECT_ATTRIBUTES obj;CLIENT_ID clientid;clientid.UniqueProcess = PID;clientid.UniqueThread = 0;// 属性初始化InitializeObjectAttributes(&obj, 0, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);NTSTATUS status = ZwOpenProcess(&hProcessHandle, PROCESS_ALL_ACCESS, &obj, &clientid);if (status == STATUS_SUCCESS){// DbgPrint("[*] 已打开 \n");ZwClose(&hProcessHandle);return hProcessHandle;}return 0;
}// HANDLE句柄转换为PID
ULONG HandleToPid(HANDLE handle)
{PROCESS_BASIC_INFORMATION ProcessBasicInfor;// 初始化字符串,并获取动态地址UNICODE_STRING UtrZwQueryInformationProcessName = RTL_CONSTANT_STRING(L"ZwQueryInformationProcess");ZwQueryInformationProcess = (PfnZwQueryInformationProcess)MmGetSystemRoutineAddress(&UtrZwQueryInformationProcessName);// 调用查询ZwQueryInformationProcess(handle,ProcessBasicInformation,(PVOID)&ProcessBasicInfor,sizeof(ProcessBasicInfor),NULL);// 返回进程PIDreturn ProcessBasicInfor.UniqueProcessId;
}VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint("[-] 驱动卸载 \n");
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint("Hello LyShark \n");// 将PID转换为HANDLEHANDLE ptr = PidToHandle(6932);DbgPrint("[*] PID --> HANDLE = %p \n", ptr);// 句柄转为PIDULONG pid = HandleToPid(ptr);DbgPrint("[*] HANDLE --> PID = %d \n", pid);Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}
编译并运行如上这段代码片段,将把进程PID转为HANDLE句柄,再通过句柄将其转为PID,输出效果图如下所示;
4.1.2 进程PID转换为EProcess结构
通过PsLookUpProcessByProcessId
函数,该函数传入一个PID
则可获取到该PID的EProcess
结构体,具体转换实现方法如下所示;
本段代码展示了如何使用Windows内核API函数PsLookupProcessByProcessId
将一个PID(Process ID)转换为对应的EProcess
结构体,EProcess是Windows内核中描述进程的数据结构之一。
代码段中定义了一个名为PidToObject
的函数,该函数的输入参数是一个PID
,输出参数是对应的EProcess
结构体。
在函数中,通过调用PsLookupProcessByProcessId
函数来获取对应PID的EProcess
结构体,如果获取成功,则调用ObDereferenceObject
函数来减少EProcess
对象的引用计数,并返回获取到的EProcess
指针;否则返回0。
在DriverEntry
函数中,调用了PidToObject
函数将PID 6932转换为对应的EProcess
结构体,并使用DbgPrint
函数输出了转换结果。最后设置了驱动程序卸载函数为UnDriver
,当驱动程序被卸载时,UnDriver
函数会被调用。
#include <ntifs.h>
#include <windef.h>// 将Pid转换为Object or EProcess
PEPROCESS PidToObject(ULONG Pid)
{PEPROCESS pEprocess;NTSTATUS status = PsLookupProcessByProcessId((HANDLE)Pid, &pEprocess);if (status == STATUS_SUCCESS){ObDereferenceObject(pEprocess);return pEprocess;}return 0;
}VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint("[-] 驱动卸载 \n");
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint("Hello LyShark \n");// 将PID转换为PEPROCESSPEPROCESS ptr = PidToObject(6932);DbgPrint("[*] PID --> PEPROCESS = %p \n", ptr);Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}
编译并运行如上这段代码片段,将把进程PID转为EProcess结构,输出效果图如下所示;
4.1.3 进程HANDLE与EPROCESS转换
将Handle
转换为EProcess
结构可使用内核函数ObReferenceObjectByHandle
实现,反过来EProcess
转换为Handle
句柄可使用ObOpenObjectByPointer
内核函数实现,具体转换实现方法如下所示;
首先,将Handle
转换为EProcess
结构体,可以使用ObReferenceObjectByHandle
内核函数。该函数接受一个Handle
参数,以及对应的对象类型(这里为EProcess),并返回对应对象的指针。此函数会对返回的对象增加引用计数,因此在使用完毕后,需要使用ObDereferenceObject
将引用计数减少。
其次,将EProcess
结构体转换为Handle
句柄,可以使用ObOpenObjectByPointer
内核函数。该函数接受一个指向对象的指针(这里为EProcess结构体的指针),以及所需的访问权限和对象类型,并返回对应的Handle
句柄。此函数会将返回的句柄添加到当前进程的句柄表中,因此在使用完毕后,需要使用CloseHandle
函数将句柄关闭,以避免资源泄漏。
综上所述,我们可以通过这两个内核函数实现Handle
和EProcess
之间的相互转换,转换代码如下所示;
#include <ntifs.h>
#include <windef.h>// 传入PID传出HANDLE句柄
HANDLE PidToHandle(ULONG PID)
{HANDLE hProcessHandle;OBJECT_ATTRIBUTES obj;CLIENT_ID clientid;clientid.UniqueProcess = PID;clientid.UniqueThread = 0;// 属性初始化InitializeObjectAttributes(&obj, 0, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);NTSTATUS status = ZwOpenProcess(&hProcessHandle, PROCESS_ALL_ACCESS, &obj, &clientid);if (status == STATUS_SUCCESS){// DbgPrint("[*] 已打开 \n");ZwClose(&hProcessHandle);return hProcessHandle;}return 0;
}// 将Handle转换为EProcess结构
PEPROCESS HandleToEprocess(HANDLE handle)
{PEPROCESS pEprocess;NTSTATUS status = ObReferenceObjectByHandle(handle, GENERIC_ALL, *PsProcessType, KernelMode, &pEprocess, NULL);if (status == STATUS_SUCCESS){return pEprocess;}return 0;
}// EProcess转换为Handle句柄
HANDLE EprocessToHandle(PEPROCESS eprocess)
{HANDLE hProcessHandle = (HANDLE)-1;NTSTATUS status = ObOpenObjectByPointer(eprocess,OBJ_KERNEL_HANDLE,0,0,*PsProcessType,KernelMode,&hProcessHandle);if (status == STATUS_SUCCESS){return hProcessHandle;}return 0;
}VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint("[-] 驱动卸载 \n");
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint("Hello LyShark \n");// 将Handle转换为EProcess结构PEPROCESS eprocess = HandleToEprocess(PidToHandle(6932));DbgPrint("[*] HANDLE --> EProcess = %p \n", eprocess);// 将EProcess结构转换为HandleHANDLE handle = EprocessToHandle(eprocess);DbgPrint("[*] EProcess --> HANDLE = %p \n", handle);Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}
编译并运行如上这段代码片段,将把进程HANDLE
与EProcess
结构互转,输出效果图如下所示;