首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >VDM - 易受攻击的驱动程序操纵

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

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

关键词


易受攻击的驱动程序、物理内存、虚拟地址、相对虚拟地址 (RVA)、内联挂钩、任意物理内存访问。

介绍


利用易受攻击的 Windows 驱动程序来利用内核执行并不是一个新概念。尽管利用易受攻击的驱动程序的软件已经存在了很长时间,但还没有一个高度模块化的代码库可以用来利用暴露相同漏洞的多个驱动程序。暴露任意物理内存读写原语的 Windows 驱动程序是最丰富的易受攻击的驱动程序形式。这些驱动程序用于许多事情,从读取 CPU 风扇速度到刷新 BIOS。尽管有成千上万的驱动程序暴露了这个原语;对这些驱动程序做任何有用的事情并不一定是一项简单的任务。在这篇研究论文中,我将描述如何使用任意物理内存读写原语获得内核执行的步骤。此外,

查找易受攻击的驱动程序


查找公开任意物理内存读写的驱动程序就像在谷歌上搜索以下短语一样简单:Windows 的 BIOS 刷新实用程序、Windows 的 CPU 风扇速度实用程序或 Windows 的华硕超频实用程序。有成百上千个允许任意物理内存读写的驱动程序。在这篇研究论文中,我将专门讨论 phymem.sys;我在介绍本文的过程中发现的 Supermicro BIOS 刷新 Windows 实用程序。尽管有大量易受攻击的驱动程序会暴露物理内存的读写;通常,可以操作的物理内存范围是有限的。在 phymem.sys 的情况下,只有前 4GB 的物理内存可以任意读写。

一旦你认为你发现了一个易受攻击的驱动程序,确定它实际上是易受攻击的,可以通过确定用户控制的数据被传递到以下任何一个来完成:MmMapIoSpace、ZwMapViewOfSection 或 MmCopyMemory。这个用户控制的数据通过调用DeviceIoControl传递给驱动的设备控制主函数。在 phymem.sys 的情况下,用户控制的数据被传递到 MmMapIoSpace。

与易受攻击的驱动程序交互


在确定驱动程序易受攻击后,下一步是列出如何与所述易受攻击的驱动程序交互。在对 IRP_MJ_DEVICE_CONTROL 函数进行逆向工程时应该寻找的三个最重要的值是:I/O 控制代码、IOCTL 输入和输出缓冲区长度,最后是输入和输出缓冲区。通过观察用户控制的数据是如何使用的;可以构建一个结构。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
InputBufferLength = StackLocation->Parameters
    .DeviceIoControl
    .InputBufferLength;
    
OutPutBufferLength = StackLocation->Parameters
    .DeviceIoControl
    .OutputBufferLength;
    
// IRP_MJ_DEVICE_CONTROL...
if (StackLocation->MajorFunction == 0xE) 
{
	v22 = StackLocation->Parameters
	    .Read
	    .ByteOffset
	    .LowPart;
	    
	// 0x80002000 (MAP_PHYSICAL_MEMORY)
	switch (v22 + 0x7FFFE000)
	{
	case 0u:
	    // mind lengths for DeviceIoControl...
		if (InputBufferLength != 16i64 ||
		    OutPutBufferLength != 8i64)
			    return {}; // invalid lengths...
		else
		{
			// 4gb of physical memory limit
			UserControlledPhysicalAddress.QuadPart = 
			    *((_QWORD*)SystemBuffer + 1) 
			        & 0xFFFFFFFFi64;
			        
			VirtualAddress = MmMapIoSpace
			    (UserControlledPhysicalAddress,
			        *(_QWORD*)SystemBuffer, NULL);
			        
			// IoAllocateMdl
			// MmBuildMdlForNonPagedPool...
			// MmMapLockedPagesSpecifyCache...

对于 phymem.sys,输入缓冲区长度为 16 字节,输出缓冲区长度为 8 字节。查看清单 1 中如何使用 SystemBuffer,您可以看到它是一个包含两个 QWORD 大小的字段的结构。进一步检查得出结论,第一个 QWORD 字段包含要映射多少物理内存的字节大小值,第二个 QWORD 字段是要映射的内存的物理地址。正如您在清单 1 第 33 行中看到的,物理地址的前 32 位被忽略了。这将物理地址的大小限制为 32 位,因此驱动程序只允许我们映射位于物理内存的前 4GB 中的物理内存。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define MAP_PHYSICAL_MEMORY 0x80002000
#define UNMAP_PHYSICAL_MEMORY 0x80002004

// 16 bytes
typedef struct _map_phys_t
{
	union
	{
		std::uintptr_t map_size;  // + 0x0
		std::uintptr_t virt_addr; // + 0x0
	}
	std::uintptr_t phys_addr; // + 0x8
} map_phys_t, *pmap_phys_t;

一旦定义了结构;与易受攻击的驱动程序交互只需使用 NtLoadDriver 将驱动程序加载到内核中,然后使用 DeviceIoControl 控制驱动程序。

扫描物理内存


尽管物理内存可能看起来不明确,但它被组织成固定大小的块,称为页面。使用四层分页表配置的 64 位系统上的每个页面为 4kB。在这个块大小内存是连续的。每个 64 位虚拟地址的最后 12 位称为页偏移。知道这一点可以扫描特定字节的特定偏移量的每一页。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
PAGE:00000001C01265F0 ; Exported entry 294. NtGdiDdDDICreateContext
PAGE:00000001C01265F0
PAGE:00000001C01265F0 ; =============== S U B R O U T I N E =======================================
PAGE:00000001C01265F0
PAGE:00000001C01265F0 ; Attributes:
PAGE:00000001C01265F0
PAGE:00000001C01265F0                 public NtGdiDdDDICreateContext
PAGE:00000001C01265F0 NtGdiDdDDICreateContext proc near             
PAGE:00000001C01265F0                 mov     [rsp+arg_8], rbx ; DxgkCreateContext
PAGE:00000001C01265F5                 mov     [rsp+arg_10], rdi
PAGE:00000001C01265FA                 mov     [rsp+arg_18], r12
PAGE:00000001C01265FF                 push    r13
PAGE:00000001C0126601                 push    r14
PAGE:00000001C0126603                 push    r15
PAGE:00000001C0126605                 sub     rsp, 200h
PAGE:00000001C012660C                 mov     rax, cs:__security_cookie
PAGE:00000001C0126613                 xor     rax, rsp
PAGE:00000001C0126616                 mov     [rsp+218h+var_28], rax
PAGE:00000001C012661E                 mov     r14, rcx
PAGE:00000001C0126621                 mov     [rsp+218h+var_170], rcx
PAGE:00000001C0126629                 mov     [rsp+218h+var_1A0], rcx
PAGE:00000001C012662E                 or      rdi, 0FFFFFFFFFFFFFFFFh
PAGE:00000001C0126632                 mov     [rsp+218h+var_1C0], edi
PAGE:00000001C0126636                 xor     ebx, ebx
PAGE:00000001C0126638                 mov     [rsp+218h+var_1B8], rbx
PAGE:00000001C012663D                 mov     rax, cs:qword_1C00AE960
PAGE:00000001C0126644                 test    al, 2
PAGE:00000001C0126646                 jnz     loc_1C01C3E8C
PAGE:00000001C012664C                 mov     [rsp+218h+var_1B0], bl

在清单 3 中,系统例程 NtGdiDdDDICreateContext 的页面偏移量是 0x5F0。简单地在 NtGdiDdDDICreateContext 中扫描偏移 0x5F0 处的每个物理页面以获取操作码就足以获得少量结果。为了确定我们在物理内存中找到了真正的 NtGdiDdDDICreateContext ,需要测试每个事件。一次扫描每一页非常慢,因此为了加快进程,VDM 会为每个物理内存范围创建一个新线程。

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

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

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

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

评论
登录后参与评论
1 条评论
热度
最新
666
666
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
[ Windows 10 x64中的RFG(Return Flow Guard)技术研究 ]1
让我们梳理下一RFG防护的基本思路:1) 在每一个函数开始处,”读”取当前栈rsp里面的值到rax中,该值就是函数的返回地址,我们记作return_value2) 然后将rax里面值”写”入fs:[rsp] 偏移处 , 也就是保留函数返回地址return__value的值到”影子栈“Thread Control stack当中。3) 在函数即将结束的时候检测当前返回地址值return_value和fs:[rsp]中保存的是否一致,如果一致,函数正常返回,如果不一致将跳向 xxx!_guard_ss_verf
franket
2022/06/29
5370
一个来自fairgame.co 的逆向工具(2)
现在我们了解了这种挂钩/通信方法的基础知识,所有其他对 MmGetPhysicalAddress 的调用的意图变得更加清晰。下次调用 MmGetPhysicalAddress 时,将传递驻留在 ntoskrnl 内部的指针。这个地址就是 ExAllocatePool 的地址。通常,ExAllocatePool 的这种用法用于为未签名的驱动程序分配空间。
franket
2021/08/09
2K0
一个来自fairgame.co 的逆向工具(1)
Unfairgame是一家在线游戏作弊提供商,专门为竞技游戏销售游戏作弊。这些游戏包括《彩虹六号:围攻》、《守望先锋》、《 铁锈》(https://en.wikipedia.org/wiki/Rust_(video_game%29)、《逃离塔科夫》和《Valorant》。这个作弊提供者已经存在近两年了,并且轻松赚取了超过 10 万美元的利润。一直以来,他们都通过构建不良的作弊加载系统向用户出售公开可用的代码。
franket
2021/08/09
1.8K0
VDM - 易受攻击的驱动程序操纵1
每次在物理内存中发现 NtGdiDdDDICreateContext 的字节时,都会进行测试以确定是否已找到正确的内存。这个测试在 NtGdiDdDDICreateContext 的前几条指令上放置了一些汇编代码。然后调用 NtGdiDdDDICreateContext 来查看是否执行了所需的指令。最后不管情况如何,原始字节都被恢复了。
franket
2022/05/29
4K0
Amlegit - amlegit.com 的逆向工程(2)
由于我们知道 GetDriver 的参数和返回类型,我们实际上可以用我们自己的代码调用这个函数。为了清楚起见,函数原型看起来像这样std::uintptr_t GetDriver(unsigned* size);
franket
2021/11/19
9800
基于WDF的PCI/PCIe接口卡Windows驱动程序(4)- 驱动程序代码(源文件)
原文出处:http://www.cnblogs.com/jacklu/p/4687325.html
用户7043923
2020/03/12
2.6K0
应用层与内核的几种通信方式
应用程序与驱动程序据我所知,细分可以分6种,ReadFile,WirteFile方式的缓冲区设备读写,直接方式读写,和其他方式读写。Io设备控制操作(即DeviceControl)的缓冲内存模式IOCTL,直接内存方式的IOCTL,其他内存方式的IOCTL!当然还有一种就是创建文件,然后文件读写也应该算是一种通信吧,这里不讨论这个!
IBinary
2021/12/01
1.3K0
驱动开发:内核枚举Registry注册表回调
在笔者上一篇文章《驱动开发:内核枚举LoadImage映像回调》中LyShark教大家实现了枚举系统回调中的LoadImage通知消息,本章将实现对Registry注册表通知消息的枚举,与LoadImage消息不同Registry消息不需要解密只要找到CallbackListHead消息回调链表头并解析为_CM_NOTIFY_ENTRY结构即可实现枚举。
王 瑞
2022/12/28
5750
驱动开发:内核枚举Registry注册表回调
64位内核开发第一讲,IRP 派遣函数 与 通信。 驱动框架补充
在Windows内核中,有一种数据结构叫做 IRP(I/O Request Package) 也就是输入输出请求包。它是与输入输出相关的重要数据结构 只要了解了IRP 那么驱动开发以及内核你就会了解一大半了。
IBinary
2022/05/10
1.6K0
64位内核开发第一讲,IRP 派遣函数 与 通信。 驱动框架补充
CreateFile DeviceIoControl dwIoControlCode——应用程序与驱动程序通信
在“进程内存管理器中”的一个Ring0,Ring3层通信问题,之前也见过这样的代码,这次拆分出来详细总结一下。
战神伽罗
2019/07/24
2.1K0
Amlegit - amlegit.com 的逆向工程(1)
Amlegit 是与 HWID 欺骗器捆绑在一起的 Apex 传奇作弊软件,其用户群略高于三千用户。作弊本身提供了一个 2d 框 esp、无声瞄准和其他一些功能。正如您将在这篇广泛的文章中看到的那样,这个作弊只不过是公开发布的漏洞利用和源代码的粘贴。如下所示的通信方式是一个系统驱动程序的基本IOCTL钩子。
franket
2021/11/19
9940
6.8 Windows驱动开发:内核枚举Registry注册表回调
在笔者上一篇文章《内核枚举LoadImage映像回调》中LyShark教大家实现了枚举系统回调中的LoadImage通知消息,本章将实现对Registry注册表通知消息的枚举,与LoadImage消息不同Registry消息不需要解密只要找到CallbackListHead消息回调链表头并解析为_CM_NOTIFY_ENTRY结构即可实现枚举。
王 瑞
2023/12/02
3490
6.8 Windows驱动开发:内核枚举Registry注册表回调
内核知识第七讲,内核中设备常用的三种通信方式,以及控制回调的编写
我们的ring3和ring0通讯的时候.ring3会给一个虚拟地址. 然后内核中的参数会通过IRP来获取.
IBinary
2022/05/10
5190
内核知识第七讲,内核中设备常用的三种通信方式,以及控制回调的编写
应用程序与驱动程序通信 DeviceIoControl
这种通信方式,就是驱动程序和应用程序自定义一种IO控制码,然后调用DeviceIoControl函数,IO管理器会产生一个MajorFunction 为IRP_MJ_DEVICE_CONTROL(DeviceIoControl函数会产生此IRP),MinorFunction 为自己定义的控制码的IRP,系统就调用相应的处理IRP_MJ_DEVICE_CONTROL的派遣函数,你在派遣函数中判断MinorFunction ,是自定义的控制码你就进行相应的处理。
战神伽罗
2019/10/12
1.8K0
驱动开发:内核枚举DpcTimer定时器
在笔者上一篇文章《驱动开发:内核枚举IoTimer定时器》中我们通过IoInitializeTimer这个API函数为跳板,向下扫描特征码获取到了IopTimerQueueHead也就是IO定时器的队列头,本章学习的枚举DPC定时器依然使用特征码扫描,唯一不同的是在新版系统中DPC是被异或加密的,想要找到正确的地址,只是需要在找到DPC表头时进行解密操作即可。
王 瑞
2022/12/28
1.1K0
驱动开发:内核枚举DpcTimer定时器
Swift 汇编(一)Protocol Witness Table 初探
由于工作中接触到 Swift 汇编与逆向知识,所以整理了这篇博客。内容与顺序无关,第一篇文章并非入门,单纯只是第一篇文章。建议有一定汇编基础的读者学习。
酷酷的哀殿
2021/06/22
1.9K0
Swift 汇编(一)Protocol Witness Table 初探
分享如何阅读Go源码
以我个人理解,Go源码主要分为两部分,一部分是官方提供的标准库,一部分是Go语言的底层实现,Go语言的所有源码/标准库/编译器都在src目录下:https://github.com/golang/go/tree/master/src,想看什么库的源码任君选择;
Golang梦工厂
2022/07/11
8100
分享如何阅读Go源码
Windows 内核驱动程序完整性校验的原理分析
在上一篇文章中提到了 Windows Vista 及之后版本的 Windows 操作系统在驱动程序加载完成后,驱动中调用的一些系统回调函数(如 ObRegisterCallbacks,可用来监控系统中对进线程句柄的操作,如打开进程、复制线程句柄等)等 API 中会通过 MmVerifyCallbackFunction 函数对该驱动程序进行完整性检查,检测未通过则会返回 0xC0000022 拒绝访问的返回值。在这篇文章中将会对这个函数进行简单的分析,以明确其原理。
稻草小刀
2022/12/12
1.3K0
Windows 内核驱动程序完整性校验的原理分析
windows kernel之HEVD栈溢出
首先使用osrload安装HEVD驱动,win10系统需要禁用驱动签名检测,然后成功安装后使用windbg下面的命令可以看到驱动已成功安装
鸿鹄实验室
2021/12/01
8230
windows kernel之HEVD栈溢出
说一说c++ static变量----log4cxx也会导致程序Crash?
在项目中碰到程序启动后偶尔很快就crash,查看函数调用栈后,居然在log4cxx的模块。对于常用的开源库,笔者一般还是比较放心的,于是目光一直聚焦在产品的代码,搜寻无果后,只能去看看一看log4cxx的源码了,果不其然,最终寻得是log4cxx的一个多线程bug所致,而这个bug和C++函数内的static变量是否线程安全有关。环境相关信息如下:
河边一枝柳
2021/08/06
8610
推荐阅读
相关推荐
[ Windows 10 x64中的RFG(Return Flow Guard)技术研究 ]1
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档