驱动开发:内核中进程与句柄互转

打印 上一主题 下一主题

主题 890|帖子 890|积分 2670

在内核开发中,经常需要进行进程和句柄之间的互相转换。进程通常由一个唯一的进程标识符(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动态获取到,该函数的原型定义如下:
  1. NTSTATUS ZwQueryInformationProcess(
  2.   HANDLE           ProcessHandle,
  3.   PROCESSINFOCLASS ProcessInformationClass,
  4.   PVOID            ProcessInformation,
  5.   ULONG            ProcessInformationLength,
  6.   PULONG           ReturnLength
  7. );
复制代码
函数可以接受一个进程句柄ProcessHandle、一个PROCESSINFOCLASS枚举类型的参数ProcessInformationClass、一个用于存储返回信息的缓冲区ProcessInformation、缓冲区大小ProcessInformationLength和一个指向ULONG类型变量的指针ReturnLength作为参数。
在调用该函数时,ProcessInformationClass参数指定要获取的进程信息的类型。例如,如果要获取进程的基本信息,则需要将该参数设置为ProcessBasicInformation;如果要获取进程的映像文件名,则需要将该参数设置为ProcessImageFileName。调用成功后,返回的信息存储在ProcessInformation缓冲区中。
在调用该函数时,如果ProcessInformation缓冲区的大小小于需要返回的信息大小,则该函数将返回STATUS_INFO_LENGTH_MISMATCH错误代码,并将所需信息的大小存储在ReturnLength指针指向的ULONG类型变量中。
ZwQueryInformationProcess函数的返回值为NTSTATUS类型,表示函数执行的结果状态。如果函数执行成功,则返回STATUS_SUCCESS,否则返回其他错误代码。
掌握这些转换方法可以方便地在内核开发中进行进程PID和句柄HANDLE之间的互相转换。
  1. #include <ntifs.h>
  2. // 定义函数指针
  3. typedef NTSTATUS(*PfnZwQueryInformationProcess)(
  4.         __in HANDLE ProcessHandle,
  5.         __in PROCESSINFOCLASS ProcessInformationClass,
  6.         __out_bcount(ProcessInformationLength) PVOID ProcessInformation,
  7.         __in ULONG ProcessInformationLength,
  8.         __out_opt PULONG ReturnLength
  9. );
  10. PfnZwQueryInformationProcess ZwQueryInformationProcess;
  11. // 传入PID传出HANDLE句柄
  12. HANDLE PidToHandle(ULONG PID)
  13. {
  14.         HANDLE hProcessHandle;
  15.         OBJECT_ATTRIBUTES obj;
  16.         CLIENT_ID clientid;
  17.         clientid.UniqueProcess = PID;
  18.         clientid.UniqueThread = 0;
  19.         // 属性初始化
  20.         InitializeObjectAttributes(&obj, 0, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);
  21.         NTSTATUS status = ZwOpenProcess(&hProcessHandle, PROCESS_ALL_ACCESS, &obj, &clientid);
  22.         if (status == STATUS_SUCCESS)
  23.         {
  24.                 // DbgPrint("[*] 已打开 \n");
  25.                 ZwClose(&hProcessHandle);
  26.                 return hProcessHandle;
  27.         }
  28.         return 0;
  29. }
  30. // HANDLE句柄转换为PID
  31. ULONG HandleToPid(HANDLE handle)
  32. {
  33.         PROCESS_BASIC_INFORMATION ProcessBasicInfor;
  34.         // 初始化字符串,并获取动态地址
  35.         UNICODE_STRING UtrZwQueryInformationProcessName = RTL_CONSTANT_STRING(L"ZwQueryInformationProcess");
  36.         ZwQueryInformationProcess = (PfnZwQueryInformationProcess)MmGetSystemRoutineAddress(&UtrZwQueryInformationProcessName);
  37.         // 调用查询
  38.         ZwQueryInformationProcess(
  39.                 handle,
  40.                 ProcessBasicInformation,
  41.                 (PVOID)&ProcessBasicInfor,
  42.                 sizeof(ProcessBasicInfor),
  43.                 NULL);
  44.         // 返回进程PID
  45.         return ProcessBasicInfor.UniqueProcessId;
  46. }
  47. VOID UnDriver(PDRIVER_OBJECT driver)
  48. {
  49.         DbgPrint("[-] 驱动卸载 \n");
  50. }
  51. NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
  52. {
  53.         DbgPrint("Hello LyShark \n");
  54.         // 将PID转换为HANDLE
  55.         HANDLE ptr = PidToHandle(6932);
  56.         DbgPrint("[*] PID  --> HANDLE = %p \n", ptr);
  57.         // 句柄转为PID
  58.         ULONG pid = HandleToPid(ptr);
  59.         DbgPrint("[*] HANDLE  --> PID = %d \n", pid);
  60.         Driver->DriverUnload = UnDriver;
  61.         return STATUS_SUCCESS;
  62. }
复制代码
编译并运行如上这段代码片段,将把进程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函数会被调用。
  1. #include <ntifs.h>
  2. #include <windef.h>
  3. // 将Pid转换为Object or EProcess
  4. PEPROCESS PidToObject(ULONG Pid)
  5. {
  6.         PEPROCESS pEprocess;
  7.         NTSTATUS status = PsLookupProcessByProcessId((HANDLE)Pid, &pEprocess);
  8.         if (status == STATUS_SUCCESS)
  9.         {
  10.                 ObDereferenceObject(pEprocess);
  11.                 return pEprocess;
  12.         }
  13.         return 0;
  14. }
  15. VOID UnDriver(PDRIVER_OBJECT driver)
  16. {
  17.         DbgPrint("[-] 驱动卸载 \n");
  18. }
  19. NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
  20. {
  21.         DbgPrint("Hello LyShark \n");
  22.         // 将PID转换为PEPROCESS
  23.         PEPROCESS ptr = PidToObject(6932);
  24.         DbgPrint("[*] PID  --> PEPROCESS = %p \n", ptr);
  25.         Driver->DriverUnload = UnDriver;
  26.         return STATUS_SUCCESS;
  27. }
复制代码
编译并运行如上这段代码片段,将把进程PID转为EProcess结构,输出效果图如下所示;

进程HANDLE与EPROCESS互相转换: 将Handle转换为EProcess结构可使用内核函数ObReferenceObjectByHandle实现,反过来EProcess转换为Handle句柄可使用ObOpenObjectByPointer内核函数实现,具体转换实现方法如下所示;
首先,将Handle转换为EProcess结构体,可以使用ObReferenceObjectByHandle内核函数。该函数接受一个Handle参数,以及对应的对象类型(这里为EProcess),并返回对应对象的指针。此函数会对返回的对象增加引用计数,因此在使用完毕后,需要使用ObDereferenceObject将引用计数减少。
其次,将EProcess结构体转换为Handle句柄,可以使用ObOpenObjectByPointer内核函数。该函数接受一个指向对象的指针(这里为EProcess结构体的指针),以及所需的访问权限和对象类型,并返回对应的Handle句柄。此函数会将返回的句柄添加到当前进程的句柄表中,因此在使用完毕后,需要使用CloseHandle函数将句柄关闭,以避免资源泄漏。
综上所述,我们可以通过这两个内核函数实现Handle和EProcess之间的相互转换,转换代码如下所示;
  1. #include <ntifs.h>
  2. #include <windef.h>
  3. // 传入PID传出HANDLE句柄
  4. HANDLE PidToHandle(ULONG PID)
  5. {
  6.         HANDLE hProcessHandle;
  7.         OBJECT_ATTRIBUTES obj;
  8.         CLIENT_ID clientid;
  9.         clientid.UniqueProcess = PID;
  10.         clientid.UniqueThread = 0;
  11.         // 属性初始化
  12.         InitializeObjectAttributes(&obj, 0, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);
  13.         NTSTATUS status = ZwOpenProcess(&hProcessHandle, PROCESS_ALL_ACCESS, &obj, &clientid);
  14.         if (status == STATUS_SUCCESS)
  15.         {
  16.                 // DbgPrint("[*] 已打开 \n");
  17.                 ZwClose(&hProcessHandle);
  18.                 return hProcessHandle;
  19.         }
  20.         return 0;
  21. }
  22. // 将Handle转换为EProcess结构
  23. PEPROCESS HandleToEprocess(HANDLE handle)
  24. {
  25.         PEPROCESS pEprocess;
  26.         NTSTATUS status = ObReferenceObjectByHandle(handle, GENERIC_ALL, *PsProcessType, KernelMode, &pEprocess, NULL);
  27.         if (status == STATUS_SUCCESS)
  28.         {
  29.                 return pEprocess;
  30.         }
  31.         return 0;
  32. }
  33. // EProcess转换为Handle句柄
  34. HANDLE EprocessToHandle(PEPROCESS eprocess)
  35. {
  36.         HANDLE hProcessHandle = (HANDLE)-1;
  37.         NTSTATUS status = ObOpenObjectByPointer(
  38.                 eprocess,
  39.                 OBJ_KERNEL_HANDLE,
  40.                 0,
  41.                 0,
  42.                 *PsProcessType,
  43.                 KernelMode,
  44.                 &hProcessHandle
  45.                 );
  46.         if (status == STATUS_SUCCESS)
  47.         {
  48.                 return hProcessHandle;
  49.         }
  50.         return 0;
  51. }
  52. VOID UnDriver(PDRIVER_OBJECT driver)
  53. {
  54.         DbgPrint("[-] 驱动卸载 \n");
  55. }
  56. NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
  57. {
  58.         DbgPrint("Hello LyShark \n");
  59.         // 将Handle转换为EProcess结构
  60.         PEPROCESS eprocess = HandleToEprocess(PidToHandle(6932));
  61.         DbgPrint("[*] HANDLE --> EProcess = %p \n", eprocess);
  62.         // 将EProcess结构转换为Handle
  63.         HANDLE handle = EprocessToHandle(eprocess);
  64.         DbgPrint("[*] EProcess --> HANDLE = %p \n", handle);
  65.         Driver->DriverUnload = UnDriver;
  66.         return STATUS_SUCCESS;
  67. }
复制代码
编译并运行如上这段代码片段,将把进程HANDLE与EProcess结构互转,输出效果图如下所示;


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

小秦哥

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表