首页
论坛
专栏
课程

[调试逆向] [系统底层] [原创]易受攻击驱动的分析和利用

2019-3-17 18:58 1567

[调试逆向] [系统底层] [原创]易受攻击驱动的分析和利用

2019-3-17 18:58
1567

在应用层和驱动的通讯中采用iocontrol的方式,应用层只要提供一个iocontrol码,就可以调用驱动中的内容。

Switch(iocontrol)

{

Case1:...

Case2:...

Case3:...

......

}

一般我们写程序,写好驱动和应用层,测试一下能用了就可以屁颠屁颠打游戏去了。但是这里面其实是存在一个问题的。

假如,别人写了一个应用层程序,他也需要一个驱动,但是他发现他想要实现的功能可以通过上面的case3里的内容实现,那他就不需要写驱动了,用别人的写的驱动就可以了。

总而言之,如果一个驱动不加校验,可以被任何应用层程序调用,这种驱动就有可能被利用。

今天我们要分析的驱动就是GPUZ驱动,从网上下载最新版GPUZ或者直接从我打包的文件里下载gpuz.sys文件。

Gpuz这种软件是肯定会加载驱动的,但是用pchunter只看到一个路径但是文件已经不存在


那是因为他加载完了就删除了,我们可以简单的写一个循环,对这个位置进行获取,可以使用以下代码

@echo off

set syspath=C:\Users\sword\AppData\Local\Temp\GPU-Z.sys

:loop

if exist "%syspath%" (copy "%syspath%" c:\sys.bak) else (

echo ...)

goto :loop

然后就获取到sys文件了。

接下来把sys文件拖到IDA里,找到driverentry一顿F5,就可以看到如下代码了


这里的MajorFunction14就是iocontrol函数,点进去之后,就找到了一开始说的switch结构了


这里的每个case我都瞟了一眼,挑有用的说


这个很简单就是readmsr的调用,只要调用的时候outlength和inputlength写成8和4就行。

还有一个有用的是这个



整个的代码可以以46行为分界线, 分为上半部分和下半部分。

根据这些API就可以把参数猜出来,我都已经把参数名改了,按照流程顺一遍

首先,outputlength=4  inputlenght=12这里简单的验证了一下

Output就是要一个四字节指针,input是struct{ULONG64 address;ULONG32 length;}这样的东西

MmMAPIOSPACE 是把一段物理内存映射到虚拟内存,这里的物理地址是应用层参数传过来的

MDL全称是memory descriptor list 内存描述符列表,网上有很多详细的介绍,而且一家有一种说法,反正知道他是描述内存的就行了

接下来的IoAllocateMdl  MmBuildMdlForNonPagedPool  MmMapLockedPagesSpecifyCache 就是mdl的一个配套用法,这里的mdl描述的就是那一段物理内存

看下MmMapLockedPagesSpecifyCache这个函数的第二个参数,


他这里指定的是1,也就是说是usermode,意思是把mdl描述的内存映射到用户空间

总的来说的意思就是,用户层传过来一个物理地址,驱动返回给他一个指针,而这个指针指向的是虚拟地址,但是虚拟地址实际上就是传过去的物理地址(这里不是说数值相等,而是等效的意思),也就是说可以读写物理内存了。

46行一下就是往devicecontext里面存了一些东西,感觉不重要,具体没看,这是个伏笔。。。

这下就厉害了,可以读写物理内存了,别管是用户层还是内核层,物理地址面前人人平等啊。

但是问题来了,

第一,我们一般使用的都是虚拟地址,怎么才能知道物理地址和虚拟地址是怎么对应的?

第二,即使知道物理地址和虚拟地址是怎么对应的,又有什么用?

按说应该先解决第二个问题,但是为连贯性,我们先解决第一个问题。

说起物理地址和虚拟地址的互换问题,那就不得不提CR3。CR3含有存放页目录表页面的物理地址,因此CR3也被称为PDBR。也就是说,只要有了CR3就可以做到物理地址和虚拟地址的互换,获取他的方法是  __readcr3()。很明显gpuz.sys并没有这个调用,所以这个办法就GG了?并没有,我动动脑子想想办法,这个问题还是可以解决。

既然有CR3就可以读写虚拟内存,我们现在又得不到CR3,那为什么不设CR3为X呢?我可以随便定义一个数组,然后设CR3=1 去读,如果结果不对,就设CR3=2,我一个一个试总能把他试出来的啊。理论上来说这种方法确实可以,就差实际操练一下了。

说干就干,先把代码写好


Data就是定义一个对比数组,循环里每次加0x1000是因为我观察到CR3的值结尾都是000,而且最大不超过0xffffff000,所以每次加0x1000就行了

ReadSystemAddress是先把data的地址转化成物理地址,然后去读的,至于如何用CR3来转化这个地址就不细说了,看代码就行了。

写到这里第一个问题理论上就解决了 ,接下来就是见证奇迹的时刻。

(真的懒得改代码去复现一个错误了,所以就不截图了,我印象中getlasterror=31,印象中。。。)

奇迹并没有发生,ERROR到时冒出一堆,而整个电脑的现象,轻则CR3 NOT FOUND ,重则直接搞蹦。虚拟机快照都不能回复,只能把宿主机重启了,才能继续用,就这么严重。

因为我之前是试验过读取一个物理地址是没有问题,为什么我循环读就会出问题?百思不得其解,我就一步一步控制变量的实验,干着干着就上头了,硬刚了一晚上没合眼,终于知道为什么了。因为我一开始是以为某一个地址不能读,所以我一直在找那个地址,我是想着跳过他或者怎么着,也没有看dbgview,结果这个地址我还真找到了,但是又经过漫长的实验发现这个地址之后的都不能读。反正就是经过一系列漫长的实验最后确定的结果跟dbgview的输出一样。。。

这里就要提到我前面说的伏笔了,IDA的46行往下我是没有分析的,感觉没什么卵用,但其实是有用~~~如果当初不懒,把他看完就不会有这么多情况了。。。

继续分析46行往下


这一段实际上就是40字节一个结构体,大概是这么个结构

Struct

{

ULONG64 virtualaddress;

ULONG64 usermodeaddress;

ULONG64 MDL;

ULONG64 length;

ULONG64 pid;

}

在DeviceExtension里面一共有256个这样的结构,每调用一次,就把这个信息放在一个空的struct里顺延。所以只能读256次。

但是想想也不可能啊,记录这么多东西不像是为了限制次数啊,所以肯定是为了释放啊!

又重新翻了翻其他的IOCONTROL,果然找到了这样的一个


这个明显是释放刚才读取物理内存用到的mdl什么的。也就是说,每次读取物理内存都要在调用这个把mdl什么的释放掉。有经验的肯定知道要释放掉,但是我不知道。菜就是原罪。

这样的话把代码改一改,果然能顺利运行了。



至此,第一个问题就算解决了。

第二个问题可以用来干什么,我只举一个例子,可以用这种方法来加载未签名的驱动。

原理就是禁用DSE,加载之后再启动DSE,具体就不说了,看代码吧,都很简单的。


最后说一下这种方法的局限性,目前这种方法只能在win7下运行,因为MmMAPIOSPACE 这个函数win10改了,似乎是加了什么验证吧,我不清楚,反正不能那么愉快的使用了,所以就不能读取物理内存了。然后是禁用dse这种方法win10也是不能用的,但是win10可以用manualmap的方法来代替,所以主要还是不能读取物理内存。




[招聘]欢迎市场人员加入看雪学院团队!

上传的附件:
最新回复 (3)
hzqst 3 2019-3-17 19:17
2
0
ZwQuerySystemInformation SystemProcessInformation了解一下
西瓜霜 2019-3-17 19:31
3
0
hzqst ZwQuerySystemInformation SystemProcessInformation了解一下
R3用不了这些函数啊
黑洛 1 2019-3-17 20:12
4
0
西瓜霜 R3用不了这些函数啊
谁告诉你R3用不了的。
ps:现在至少IO buffer和cmp是加密的,想盗用几乎是不可能的。
游客
登录 | 注册 方可回帖
返回