在内核开发中,经常需要进行进程和句柄之间的互相转换。进程通常由一个唯一的进程标识符(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。
掌握这些内核函数的使用,可以方便地实现进程与句柄之间的互相转换。在进行进程和线程的内核开发之前,了解这些转换功能是非常重要的。
进程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);
// 返回进程pid
return processbasicinfor.uniqueprocessid;
}
void undriver(pdriver_object driver)
{
dbgprint("[-] 驱动卸载 \n");
}
ntstatus driverentry(in pdriver_object driver, punicode_string registrypath)
{
dbgprint("hello lyshark \n");
// 将pid转换为handle
handle ptr = pidtohandle(6932);
dbgprint("[*] pid --> handle = %p \n", ptr);
// 句柄转为pid
ulong pid = handletopid(ptr);
dbgprint("[*] handle --> pid = %d \n", pid);
driver->driverunload = undriver;
return status_success;
}
编译并运行如上这段代码片段,将把进程pid转为handle句柄,再通过句柄将其转为pid,输出效果图如下所示;
进程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转换为peprocess
peprocess ptr = pidtoobject(6932);
dbgprint("[*] pid --> peprocess = %p \n", ptr);
driver->driverunload = undriver;
return status_success;
}
编译并运行如上这段代码片段,将把进程pid转为eprocess结构,输出效果图如下所示;
进程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结构转换为handle
handle handle = eprocesstohandle(eprocess);
dbgprint("[*] eprocess --> handle = %p \n", handle);
driver->driverunload = undriver;
return status_success;
}
编译并运行如上这段代码片段,将把进程handle
与eprocess
结构互转,输出效果图如下所示;
发表评论