4.1 Windows驱动开发:内核中进程与句柄互转
在内核开发中,经常需要进行进程和句柄之间的互相转换。进程通常由一个唯一的进程标识符(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);
// 返回进程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,输出效果图如下所示;

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转换为PEPROCESS
PEPROCESS 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结构转换为Handle
HANDLE handle = EprocessToHandle(eprocess);
DbgPrint("[*] EProcess --> HANDLE = %p \n", handle);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
编译并运行如上这段代码片段,将把进程HANDLE与EProcess结构互转,输出效果图如下所示;

4.1 Windows驱动开发:内核中进程与句柄互转的更多相关文章
- Windows驱动开发-内核常用内存函数
搞内存常用函数 C语言 内核 malloc ExAllocatePool memset RtlFillMemory memcpy RtlMoveMemory free ExFreePool
- windows 驱动开发入门——驱动中的数据结构
最近在学习驱动编程方面的内容,在这将自己的一些心得分享出来,供大家参考,与大家共同进步,本人学习驱动主要是通过两本书--<独钓寒江 windows安全编程> 和 <windows驱动 ...
- C++第三十三篇 -- 研究一下Windows驱动开发(一)内部构造介绍
因为工作原因,需要做一些与网卡有关的测试,其中涉及到了驱动这一块的知识,虽然程序可以运行,但是不搞清楚,心里总是不安,觉得没理解清楚.因此想看一下驱动开发.查了很多资料,看到有人推荐Windows驱动 ...
- Windows驱动开发(中间层)
Windows驱动开发 一.前言 依据<Windows内核安全与驱动开发>及MSDN等网络质料进行学习开发. 二.初步环境 1.下载安装WDK7.1.0(WinDDK\7600.16385 ...
- [Windows驱动开发](一)序言
笔者学习驱动编程是从两本书入门的.它们分别是<寒江独钓——内核安全编程>和<Windows驱动开发技术详解>.两本书分别从不同的角度介绍了驱动程序的制作方法. 在我理解,驱动程 ...
- Windows驱动——读书笔记《Windows驱动开发技术详解》
=================================版权声明================================= 版权声明:原创文章 谢绝转载 请通过右侧公告中的“联系邮 ...
- Windows驱动开发-IRP的完成例程
<Windows驱动开发技术详解 >331页, 在将IRP发送给底层驱动或其他驱动之前,可以对IRP设置一个完成例程,一旦底层驱动将IRP完成后,IRP完成例程立刻被处罚,通过设置完成例程 ...
- C++第三十八篇 -- 研究一下Windows驱动开发(二)--WDM式驱动的加载
基于Windows驱动开发技术详解这本书 一.简单的INF文件剖析 INF文件是一个文本文件,由若干个节(Section)组成.每个节的名称用一个方括号指示,紧接着方括号后面的就是节内容.每一行就是一 ...
- windows驱动开发推荐书籍
[作者] 猪头三 个人网站 :http://www.x86asm.com/ [序言] 很多人都对驱动开发有兴趣,但往往找不到正确的学习方式.当然这跟驱动开发的本土化资料少有关系.大多学的驱动开发资料都 ...
- Windows 驱动开发 - 5
上篇<Windows 驱动开发 - 4>我们已经完毕了硬件准备. 可是我们还没有详细的数据操作,比如接收读写操作. 在WDF中进行此类操作前须要进行设备的IO控制,已保持数据的完整性. 我 ...
随机推荐
- DNS--简介&&解析过程
1 功能 将域名解析为IP地址 基本解决IP难于记忆的问题 也可以将IP地址解析为域名 2 域名 由一连串用点(.)分隔的字符串组成的 标识计算机在网络中的电子方位的 3 存储位置 linux /et ...
- ME21N 采购订单屏幕增强
1.业务需求 采购订单行项目新增"图号"和"价格类型"字段.其中图号只查询底表展示,不做修改:价格类型做下拉框: 2.增强实现 增强标准表EKPO结构CI_EK ...
- 【TCP】学习笔记:application/octet-stream
当浏览器在请求资源时,会通过http返回头中的content-type决定如何显示/处理将要加载的数据,如果这个类型浏览器能够支持阅览,浏览器就会直接展示该资源,比如png.jpeg.video等格式 ...
- 4.0 《数据库系统概论》数据库安全性详解---(存取控制、DAC&MAC、视图机制、审计Audit....)
前言 本篇文章学习书籍:<数据库系统概论>第5版 王珊 萨师煊编著 视频资源来自:数据库系统概论完整版(基础篇+高级篇+新技术篇) 由于 BitHachi 学长已经系统的整理过本书了,我在 ...
- 3.1 《数据库系统概论》SQL概述及数据定义(模式SCHEMA、表TABLE、索引INDEX)
前言 本篇文章学习书籍:<数据库系统概论>第5版 王珊 萨师煊编著 视频资源来自:数据库系统概论完整版(基础篇+高级篇+新技术篇) 由于 BitHachi 学长已经系统的整理过本书了,我在 ...
- 第四届蓝桥杯(2013)C/C++大学A组省赛题解
第一题:高斯日记 大数学家高斯有个好习惯:无论如何都要记日记. 他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210 后来人们知道,那个整数就是日期,它表示那一天是高斯出生 ...
- 《vuejs快跑构建触手可及的高性能web应用》读书笔记
1.cdn:内容分发网络(CDN)是将资源托管到全世界各处的服务器上以实现快速分发.CDN版本对于开发和快速验证比较有用,但是将unpkg应用于生产环境前,需要检查它是否适合你. 2.假值包括fals ...
- .NET CORE实战项目之CMS 部署篇 思维导图
.NET Core实战项目之CMS 第十七章 CMS网站系统的部署 如何优雅的利用Windows服务来部署ASP.NET Core程序
- 无向图求桥 UVA 796
***桥的概念:无向连通图中,如果删除某边后,图变 成不连通,则称该边为桥.*** ***一条边(u,v)是桥,当且仅当(u,v)为树枝边,且 满足dfn(u)<low(v)(前提是其没有重边) ...
- java基础-idea的使用-day07
目录 1. idea的获取 2. 已经安装的idea 如何卸载 3. idea的安装与破解 3. 设置 4. 写代码常用快捷建的使用 1. idea的获取 链接:https://pan.baidu.c ...