驱动开发:内核实现SSDT挂钩与摘钩

news2024/12/24 2:53:42

在前面的文章《驱动开发:内核解析PE结构导出表》中我们封装了两个函数KernelMapFile()函数可用来读取内核文件,GetAddressFromFunction()函数可用来在导出表中寻找指定函数的导出地址,本章将以此为基础实现对特定SSDT函数的Hook挂钩操作,与《驱动开发:内核层InlineHook挂钩函数》所使用的挂钩技术基本一致,不同点是前者使用了CR3的方式改写内存,而今天所讲的是通过MDL映射实现,此外前者挂钩中所取到的地址是通过GetProcessAddress()取到的动态地址,而今天所使用的方式是通过读取导出表寻找。

挂钩的目的就是要为特定函数增加功能,挂钩的实现方式无非就是替换原函数地址,我们以内核函数ZwQueryDirectoryFile()为例,ZwQueryDirectoryFile例程返回给定文件句柄指定的目录中文件的各种信息,其微软定义如下;

NTSYSAPI NTSTATUS ZwQueryDirectoryFile(
  [in]           HANDLE                 FileHandle,
  [in, optional] HANDLE                 Event,
  [in, optional] PIO_APC_ROUTINE        ApcRoutine,
  [in, optional] PVOID                  ApcContext,
  [out]          PIO_STATUS_BLOCK       IoStatusBlock,
  [out]          PVOID                  FileInformation,
  [in]           ULONG                  Length,
  [in]           FILE_INFORMATION_CLASS FileInformationClass,
  [in]           BOOLEAN                ReturnSingleEntry,
  [in, optional] PUNICODE_STRING        FileName,
  [in]           BOOLEAN                RestartScan
);

如果需要Hook一个函数则你需要去微软官方得到该函数的具体声明部分包括其返回值,而Hook的目的只是为函数增加或处理新功能,则在执行完自定义函数后一定要跳回到原始函数上,此时定义一个typedef_ZwQueryDirectoryFile函数指针在调用结束后即可很容易的跳转回原函数上,保证流程被正确执行,如果需要Hook其他函数其编写模板也是如下所示;

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com

// 保存原函数地址
PVOID gOldFunctionAddress = NULL;

// Hook后被替换的新函数
NTSTATUS MyZwQueryDirectoryFile(
	IN HANDLE               FileHandle,
	IN HANDLE               Event OPTIONAL,
	IN PIO_APC_ROUTINE      ApcRoutine OPTIONAL,
	IN PVOID                ApcContext OPTIONAL,
	OUT PIO_STATUS_BLOCK    IoStatusBlock,
	OUT PVOID               FileInformation,
	IN ULONG                Length,
	IN FILE_INFORMATION_CLASS FileInformationClass,
	IN BOOLEAN              ReturnSingleEntry,
	IN PUNICODE_STRING      FileMask OPTIONAL,
	IN BOOLEAN              RestartScan
	)
{
	NTSTATUS status = STATUS_SUCCESS;

	// 定义函数指针
	typedef NTSTATUS(*typedef_ZwQueryDirectoryFile)(
		IN HANDLE               FileHandle,
		IN HANDLE               Event OPTIONAL,
		IN PIO_APC_ROUTINE      ApcRoutine OPTIONAL,
		IN PVOID                ApcContext OPTIONAL,
		OUT PIO_STATUS_BLOCK    IoStatusBlock,
		OUT PVOID               FileInformation,
		IN ULONG                Length,
		IN FILE_INFORMATION_CLASS FileInformationClass,
		IN BOOLEAN              ReturnSingleEntry,
		IN PUNICODE_STRING      FileMask OPTIONAL,
		IN BOOLEAN              RestartScan
		);

	DbgPrint("MyZwQueryDirectoryFile 自定义功能 \n");

	// 执行原函数
	status = ((typedef_ZwQueryDirectoryFile)gOldFunctionAddress)(FileHandle,
		Event,
		ApcRoutine,
		ApcContext,
		IoStatusBlock,
		FileInformation,
		Length,
		FileInformationClass,
		ReturnSingleEntry,
		FileMask,
		RestartScan);

	return status;
}

接着就是如何挂钩并让其中转到我们自己的代码流程中的问题,由于挂钩与恢复代码是一样的此处就以挂钩为例,首先调用MmCreateMdl()创建MDL,接着调用MmBuildMdlForNonPagedPool()接收一个 MDL,该MDL指定非分页虚拟内存缓冲区,并对其进行更新以描述基础物理页。调用MmMapLockedPages()将此段内存提交为锁定状态,最后就是调用RtlCopyMemory()将新函数地址写出到内存中实现替换,最后释放MDL句柄即可,这段代码如下所示,看过驱动读写篇的你一定很容易就能理解。

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com

// 挂钩SSDT函数
BOOLEAN SSDTFunctionHook(ULONG64 FunctionAddress)
{
	PMDL pMdl = NULL;
	PVOID pNewAddress = NULL;
	ULONG ulNewFuncAddr = 0;

	gOldFunctionAddress = FunctionAddress;

	// 使用MDL修改SSDT
	pMdl = MmCreateMdl(NULL, &FunctionAddress, sizeof(ULONG));
	if (NULL == pMdl)
	{
		return FALSE;
	}

	MmBuildMdlForNonPagedPool(pMdl);

	// 锁定内存
	pNewAddress = MmMapLockedPages(pMdl, KernelMode);
	if (NULL == pNewAddress)
	{
		IoFreeMdl(pMdl);
		return FALSE;
	}

	// 写入新函数地址
	ulNewFuncAddr = (ULONG)MyZwQueryDirectoryFile;
	RtlCopyMemory(pNewAddress, &ulNewFuncAddr, sizeof(ULONG));

	// 释放
	MmUnmapLockedPages(pNewAddress, pMdl);
	IoFreeMdl(pMdl);

	return TRUE;
}

Hook核心代码如下所示,为了节约篇幅,如果您找不到程序中的核心功能,请看前面的几篇文章,这里就不在赘述了。

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com

// 保存原函数地址
PVOID gOldFunctionAddress = NULL;

// Hook后被替换的新函数
NTSTATUS MyZwQueryDirectoryFile(
	IN HANDLE               FileHandle,
	IN HANDLE               Event OPTIONAL,
	IN PIO_APC_ROUTINE      ApcRoutine OPTIONAL,
	IN PVOID                ApcContext OPTIONAL,
	OUT PIO_STATUS_BLOCK    IoStatusBlock,
	OUT PVOID               FileInformation,
	IN ULONG                Length,
	IN FILE_INFORMATION_CLASS FileInformationClass,
	IN BOOLEAN              ReturnSingleEntry,
	IN PUNICODE_STRING      FileMask OPTIONAL,
	IN BOOLEAN              RestartScan
	)
{
	NTSTATUS status = STATUS_SUCCESS;

	// 定义函数指针
	typedef NTSTATUS(*typedef_ZwQueryDirectoryFile)(
		IN HANDLE               FileHandle,
		IN HANDLE               Event OPTIONAL,
		IN PIO_APC_ROUTINE      ApcRoutine OPTIONAL,
		IN PVOID                ApcContext OPTIONAL,
		OUT PIO_STATUS_BLOCK    IoStatusBlock,
		OUT PVOID               FileInformation,
		IN ULONG                Length,
		IN FILE_INFORMATION_CLASS FileInformationClass,
		IN BOOLEAN              ReturnSingleEntry,
		IN PUNICODE_STRING      FileMask OPTIONAL,
		IN BOOLEAN              RestartScan
		);

	DbgPrint("MyZwQueryDirectoryFile 自定义功能 \n");

	// 执行原函数
	status = ((typedef_ZwQueryDirectoryFile)gOldFunctionAddress)(FileHandle,
		Event,
		ApcRoutine,
		ApcContext,
		IoStatusBlock,
		FileInformation,
		Length,
		FileInformationClass,
		ReturnSingleEntry,
		FileMask,
		RestartScan);

	return status;
}

// 挂钩SSDT函数
BOOLEAN SSDTFunctionHook(ULONG64 FunctionAddress)
{
	PMDL pMdl = NULL;
	PVOID pNewAddress = NULL;
	ULONG ulNewFuncAddr = 0;

	gOldFunctionAddress = FunctionAddress;

	// 使用MDL修改SSDT
	pMdl = MmCreateMdl(NULL, &FunctionAddress, sizeof(ULONG));
	if (NULL == pMdl)
	{
		return FALSE;
	}

	MmBuildMdlForNonPagedPool(pMdl);

	// 锁定内存
	pNewAddress = MmMapLockedPages(pMdl, KernelMode);
	if (NULL == pNewAddress)
	{
		IoFreeMdl(pMdl);
		return FALSE;
	}

	// 写入新函数地址
	ulNewFuncAddr = (ULONG)MyZwQueryDirectoryFile;
	RtlCopyMemory(pNewAddress, &ulNewFuncAddr, sizeof(ULONG));

	// 释放
	MmUnmapLockedPages(pNewAddress, pMdl);
	IoFreeMdl(pMdl);

	return TRUE;
}

// 恢复SSDT函数
BOOLEAN SSDTFunctionUnHook(ULONG64 FunctionAddress)
{
	PMDL pMdl = NULL;
	PVOID pNewAddress = NULL;
	ULONG ulOldFuncAddr = 0;

	gOldFunctionAddress = FunctionAddress;

	// 使用MDL修改SSDT
	pMdl = MmCreateMdl(NULL, &FunctionAddress, sizeof(ULONG));
	if (NULL == pMdl)
	{
		return FALSE;
	}

	MmBuildMdlForNonPagedPool(pMdl);

	// 锁定内存
	pNewAddress = MmMapLockedPages(pMdl, KernelMode);
	if (NULL == pNewAddress)
	{
		IoFreeMdl(pMdl);
		return FALSE;
	}

	// 写入新函数地址
	ulOldFuncAddr = (ULONG)gOldFunctionAddress;
	RtlCopyMemory(pNewAddress, &ulOldFuncAddr, sizeof(ULONG));

	// 释放
	MmUnmapLockedPages(pNewAddress, pMdl);
	IoFreeMdl(pMdl);

	return TRUE;
}

// 关闭驱动
VOID UnDriver(PDRIVER_OBJECT driver)
{
	SSDTFunctionUnHook(gOldFunctionAddress);
	DbgPrint("驱动卸载 \n");
}

// 驱动入口
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint("hello lyshark.com \n");

	NTSTATUS status = STATUS_SUCCESS;

	HANDLE hFile = NULL;
	HANDLE hSection = NULL;
	PVOID pBaseAddress = NULL;
	UNICODE_STRING FileName = { 0 };
	ULONG64 FunctionAddress = 0;

	// 初始化字符串
	RtlInitUnicodeString(&FileName, L"\\??\\C:\\Windows\\System32\\ntdll.dll");

	// 内存映射文件
	status = KernelMapFile(FileName, &hFile, &hSection, &pBaseAddress);
	if (NT_SUCCESS(status))
	{
		DbgPrint("读取内存地址 = %p \n", pBaseAddress);
	}

	// 获取指定模块导出函数地址
	FunctionAddress = GetAddressFromFunction(FileName, "ZwQueryDirectoryFile");
	DbgPrint("ZwQueryVirtualMemory内存地址 = %p \n", FunctionAddress);

	// 开始Hook挂钩
	if (FunctionAddress != 0)
	{
		BOOLEAN ref = SSDTFunctionHook(FunctionAddress);
		if (ref == TRUE)
		{
			DbgPrint("[+] Hook已挂钩 \n");
		}
	}

	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

编译并运行这段驱动程序,则你会看到挂钩成功的提示信息;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/610463.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Django 网页Web开发】07. 快捷的表单生成 Form与MoudleForm(保姆级图文)

目录 注意 正规写法是 ModelForm,下面文章我多实现效果url.py新建3个html文件数据库连接model.py 数据表1. 原始方法view.pytestOrgion.html 2. Form方法view.pytestForm.html 3. MoudleForm方法给字段设置样式面向对象的思路,批量添加样式错误信息的显示…

ASIC-WORLD Verilog(10)编写测试脚本Testbench的艺术

写在前面 在自己准备写一些简单的verilog教程之前,参考了许多资料----Asic-World网站的这套verilog教程即是其一。这套教程写得极好,奈何没有中文,在下只好斗胆翻译过来(加了自己的理解)分享给大家。 这是网站原文&…

干货!来自北大、KAUST、斯坦福、达摩院的大模型前沿动态:表格推理、代码生成、MiniGPT-4、生成式推理...

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入! ChatGPT的发布使得国内外众多的研究机构掀起了一股AI热潮,而这也进一步推动了人们对大语言模型的深入研究。2023年4月26日,AI TIME举办的大模型专场四活动邀请了阿里巴巴达摩院NLP研究员…

在 IDEA 中配置 JavaFX 11

因为从 Java8/openjdk 之后,javafx 从 jdk 中移除,如果进行 JavaFX 开发需要在 module 中添加 lib,并对 IDE 进行配置,确保 jdk 可以与 javafx 正常调用。 javafx 下载路径,主页网址:https://openjfx.io/ …

开发实践|程序员是如何刷抖音、玩快手、看头条进行赚米的?

欢迎关注「全栈工程师修炼指南」公众号 点击 👇 下方卡片 即可关注我哟! 设为「星标⭐」每天带你 基础入门 到 进阶实践 再到 放弃学习! “ 花开堪折直须折,莫待无花空折枝。 ” 作者主页:[ https://www.weiyigeek.top ] 博客&…

【计算机组成原理与体系结构】数据的表示与运算

目录 一、进位计数制 二、信息编码 三、定点数数据表示 四、校验码 五、定点数补码加减运算 六、标志位的生成 七、定点数的移位运算 八、定点数的乘除运算 九、浮点数的表示 十、浮点数的运算 一、进位计数制 整数部分: 二进制、八进制、十六进制 --…

穿越认知峡谷

十年前,2013 年的这个时候,“互联网思维”在国内大火。我没有认真研究过这件事的来龙去脉,不过印象里 2012 年底《罗辑思维》视频栏目的开播,以及差不多同时小米手机的爆发,对“互联网思维”的大流行应该是起了重要的推…

【ABAP】数据类型(一)「数据类型概要及分类」

💂作者简介: THUNDER王,一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学本科在读,同时任汉硕云(广东)科技有限公司ABAP开发顾问。在学习工作中,我通常使用偏后端的开发语言A…

Nginx正则表达式、location、rewrite

目录 一、常用的Nginx正则表达式 二:localtion 1、location 分类 2、 location 常用的匹配规则 3、location 优先级 4、 location 示例 5、优先级总结 6、实际网站使用中,至少有三个匹配规则定义 (1)第一个必选规则 &…

深入理解设计原则之接口隔离原则(ISP)【软件架构设计】

系列文章目录 C高性能优化编程系列 深入理解软件架构设计系列 深入理解设计模式系列 高级C并发线程编程 LSP:接口隔离原则 系列文章目录1、接口隔离原则的定义和解读2、案例解读3、如何判断一个接口是否符合接口隔离原则?小结 1、接口隔离原则的定义和…

CVPR 2023 医学图像分割论文大盘点

点击下方卡片,关注“CVer”公众号 AI/CV重磅干货,第一时间送达 点击进入—>【医学图像分割】微信交流群 被催了很久,CVer 正式开启 CVPR 2023 论文大盘点系列!Amusi 一共搜集了13篇医学图像分割论文,这应该是目前各…

HTML 5中的文件处理之FileAPI

在众多HTML5规范中,有一部分规范是跟文件处理有关的,在早期的浏览器技术中,处理小量字符串是js最擅 长的处理之一。但文件处理,尤其是二进制文件处理,一直是个空白。在一些情况下,我们不得不通过Flash/Acti…

GPT国内的一些产品真的比国外的差吗?(篇幅较长,请收藏)

关注并星标 从此不迷路 计算机视觉研究院 公众号ID|ComputerVisionGzq 学习群|扫码在主页获取加入方式 计算机视觉研究院专栏 作者:Edison_G 本次讨论的话题仅限于计算机视觉研究院个人观点,若有说的不对的地方勿喷,有…

k8s之docker-扩展知识(八)

一.Docker的应用场景 Web 应用的自动化打包和发布。 自动化测试和持续集成、发布。 在服务型环境中部署和调整数据库或其他的后台应用。 从头编译或者扩展现有的 OpenShift 或 Cloud Foundry 平台来搭建自己的 PaaS 环境。 二.Docker 的优点 Docker 是一个用于开发&#…

【ChatGPT】Mr. Ranedeer:可定制个性化学习体验的 GPT-4 AI 导师提示

Mr. Ranedeer AI Tutor是一个可定制的提示,为具有不同需求和兴趣的用户提供个性化的学习体验。它使用GPT-4来释放AI的潜力,并允许您调整知识深度以匹配您的学习需求,自定义学习风格,沟通类型,语气和推理框架 。 当您使…

ISO21434 组织网络安全管理(二)

目录 一、概述 二、目标 三、输入 3.1 先决条件 3.2 进一步支持信息 四、要求和建议 4.1 网络安全治理 4.2 网络安全文化 4.3 信息共享 4.4 管理系统 4.5 工具管理 4.6 信息安全管理 4.7 组织网络安全审计 五、输出 一、概述 为了实现网络安全工程,该…

自动驾驶TPM技术杂谈 ———— 车辆分类

文章目录 机动车规格机动车结构机动车使用性质机动车和挂车分类接近角定义离去角定义纵向通过角定义离地间隙定义前后轴之间的离地间隙轴下离地间隙 机动车规格 机动车规格分类 分类 说明 汽车 载客汽车 大型 车长大于或等于 6000mm 或者乘坐人数大于或等于20 人的载客汽车。 …

【微信小程序开发】第 4 节 - 创建小程序项目

欢迎来到博主 Apeiron 的博客,祝您旅程愉快 ! 时止则止,时行则行。动静不失其时,其道光明。 目录 1、缘起 2、点击 “加号” 按钮 3、项目创建完成 4、在模拟器上查看项目效果 5、在真机上预览项目效果 6、主页面的 5 个组…

安卓调试|一文归纳总结adb调试工具常规命令

欢迎关注「全栈工程师修炼指南」公众号 点击 👇 下方卡片 即可关注我哟! 设为「星标⭐」每天带你 基础入门 到 进阶实践 再到 放弃学习! “ 花开堪折直须折,莫待无花空折枝。 ” 作者主页:[ https://www.weiyigeek.top ] 博客&…

FPGA PAL视频BT656解码Video Processing Subsystem去隔行工程源码 TW2867采集4路视频拼接输出 提供技术支持

目录 1、前言2、我这里已有的PAL视频解码方案3、模拟视频概述4、模拟视频颜色空间5、逐行与隔行6、BT656数据与解码BT656数据格式BT656数据解码 7、TW2867芯片解读与配置TW2867芯片解读TW2867芯片配置TW2867时序分析 8、设计思路与框架9、vivado工程详解Block Design设计SDK设计…