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

这篇具有很好参考价值的文章主要介绍了驱动开发:内核中进程与句柄互转。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在内核开发中,经常需要进行进程和句柄之间的互相转换。进程通常由一个唯一的进程标识符(PID)来标识,而句柄是指对内核对象的引用。在Windows内核中,EProcess结构表示一个进程,而HANDLE是一个句柄。

为了实现进程与句柄之间的转换,我们需要使用一些内核函数。对于进程PID和句柄的互相转换,可以使用函数如OpenProcessGetProcessId。OpenProcess函数接受一个PID作为参数,并返回一个句柄。GetProcessId函数接受一个句柄作为参数,并返回该进程的PID。

对于进程PID和EProcess结构的互相转换,可以使用函数如PsGetProcessIdPsGetCurrentProcess。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函数将句柄关闭,以避免资源泄漏。

综上所述,我们可以通过这两个内核函数实现HandleEProcess之间的相互转换,转换代码如下所示;

#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;
}

编译并运行如上这段代码片段,将把进程HANDLEEProcess结构互转,输出效果图如下所示;

驱动开发:内核中进程与句柄互转文章来源地址https://www.toymoban.com/news/detail-496432.html

到了这里,关于驱动开发:内核中进程与句柄互转的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux驱动开发——内核模块

    目录 内核模块的由来 第一个内核模块程序  内核模块工具  将多个源文件编译生成一个内核模块  内核模块参数 内核模块依赖 关于内核模块的进一步讨论  习题 最近一直在玩那些其它的技术,眼看快暑假了,我决定夯实一下我的驱动方面的技能,迎接我的实习,找了一本

    2024年02月04日
    浏览(83)
  • linux内核网络驱动框架(linux驱动开发篇)

    网络驱动的核心: 1、就是初始化 net_device 结构体中的各个成员变量, 2、然后将初始化完成以后的 net_device 注册到 Linux 内核中 1、网络设备(用net_device结构体) 2、网络设备的操作集( net_device_ops结构体 ) 3、sk_buff结构体 网络是分层的,对于应用层而言不用关系具体的底层是

    2023年04月08日
    浏览(80)
  • 驱动开发:内核遍历文件或目录

    在笔者前一篇文章 《驱动开发:内核文件读写系列函数》 简单的介绍了内核中如何对文件进行基本的读写操作,本章我们将实现内核下遍历文件或目录这一功能,该功能的实现需要依赖于 ZwQueryDirectoryFile 这个内核API函数来实现,该函数可返回给定文件句柄指定的目录中文件

    2024年02月08日
    浏览(83)
  • 驱动开发:内核文件读写系列函数

    在应用层下的文件操作只需要调用微软应用层下的 API 函数及 C库 标准函数即可,而如果在内核中读写文件则应用层的API显然是无法被使用的,内核层需要使用内核专有API,某些应用层下的API只需要增加Zw开头即可在内核中使用,例如本章要讲解的文件与目录操作相关函数,多

    2024年02月08日
    浏览(46)
  • 驱动开发:内核ShellCode线程注入

    还记得 《驱动开发:内核LoadLibrary实现DLL注入》 中所使用的注入技术吗,我们通过 RtlCreateUserThread 函数调用实现了注入DLL到应用层并执行,本章将继续探索一个简单的问题,如何注入 ShellCode 代码实现反弹Shell,这里需要注意一般情况下 RtlCreateUserThread 需要传入两个最重要的

    2024年02月08日
    浏览(52)
  • 驱动开发:摘除InlineHook内核钩子

    在笔者上一篇文章 《驱动开发:内核层InlineHook挂钩函数》 中介绍了通过替换 函数 头部代码的方式实现 Hook 挂钩,对于ARK工具来说实现扫描与摘除 InlineHook 钩子也是最基本的功能,此类功能的实现一般可在应用层进行,而驱动层只需要保留一个 读写字节 的函数即可,将复杂

    2024年02月10日
    浏览(38)
  • 驱动开发:内核物理内存寻址读写

    在某些时候我们需要读写的进程可能存在虚拟内存保护机制,在该机制下用户的 CR3 以及 MDL 读写将直接失效,从而导致无法读取到正确的数据,本章我们将继续研究如何实现物理级别的寻址读写。 首先,驱动中的物理页读写是指在驱动中直接读写物理内存页(而不是虚拟内

    2024年02月11日
    浏览(44)
  • 驱动开发:内核读写内存多级偏移

    让我们继续在 《内核读写内存浮点数》 的基础之上做一个简单的延申,如何实现多级偏移读写,其实很简单,读写函数无需改变,只是在读写之前提前做好计算工作,以此来得到一个内存偏移值,并通过调用内存写入原函数实现写出数据的目的。 以读取偏移内存为例,如下

    2024年02月11日
    浏览(39)
  • 驱动开发:内核读写内存浮点数

    如前所述,在前几章内容中笔者简单介绍了 内存读写 的基本实现方式,这其中包括了 CR3切换 读写, MDL映射 读写, 内存拷贝 读写,本章将在如前所述的读写函数进一步封装,并以此来实现驱动读写内存浮点数的目的。内存 浮点数 的读写依赖于 读写内存字节 的实现,因为

    2024年02月06日
    浏览(55)
  • 驱动开发:内核扫描SSDT挂钩状态

    在笔者上一篇文章 《驱动开发:内核实现SSDT挂钩与摘钩》 中介绍了如何对 SSDT 函数进行 Hook 挂钩与摘钩的,本章将继续实现一个新功能,如何 检测SSDT 函数是否挂钩,要实现检测 挂钩状态 有两种方式,第一种方式则是类似于 《驱动开发:摘除InlineHook内核钩子》 文章中所

    2024年02月08日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包