首页
论坛
专栏
课程

[调试逆向] [系统底层] [原创]在win7 x64下做个简单的内核绘制

2018-11-8 10:09 3441

[调试逆向] [系统底层] [原创]在win7 x64下做个简单的内核绘制

2018-11-8 10:09
3441

1.0

       本贴只针对win7 x64系统下的 32位D3D9程序,局限性太大,仅作为抛砖引玉 。
下面进入正题。

基本流程如下:
1:在内核中 hook相关shadowSSDT或SSDT函数
2:在目标进程申请内存,写入用于D3D9绘制的shellcode
3:利用APC的力量回到 用户模式,执行用于D3D9绘制的 shellcode。

1.1

       首先,如果我们要做一个hook D3D9的绘制。
自然是要编写dll,
注入到目标进程,
hook D3D9虚函数,
获取到D3D9设备,
执行绘制代码。

        时至今日,这种技术已经是基本操作了。
经历各大游戏厂商(代理商)以及外挂作者们之间的斗争,
这种注入以及hook的方式已然不可取。

        那么是否有一种相对隐蔽的方式来完成这项工作呢?
答案是有的。(废话,没有你写这贴干嘛【滑稽】)
        从我们能想到的根本问题入手的话......
在应用层 hook D3D9虚函数以及直接注入dll会拉闸,
既然如此,我们在内核层做hook,代码全都以shellcode的形式
在目标进程里跑起来。

1.2

       在win7 x64下, 通过调试一个网上下载的D3D9示例Demo
仔细跟踪其 D3D9的Present函数,我们发现 Present 最终会调用
USER32.HungWindowFromGhostWindow+20处的代码
反汇编如下:

call fs:[000000C0]其实是call Wow64子系统的调用
经过Wow64子系统的包装,最终进入内核。

       mov eax, 000012CF这句汇编代码
其中 000012CF代表的是 shadowSSDT 的索引值
了解过 SSDT&shadowsSSDT的朋友都知道
第一个4k页面指向的是SSDT函数
第二个4k页面指向的是shadowSSDT函数
另外两个页面未使用(这是题外话。)
00000012CF在第二个页面上,所以这个索引指向shadowSSDT函数。
我们减去 0x1000 就是它的真实索引值 0x02CF,十进制就是 719。

       我们打开PChunter这款非常有用的软件, 我们可以看到索引719指向的
shadowSSDT函数名 NtUserHwndQueryRedirectionInfo


       接下来我们就hook这个函数并写好过滤
一般来说,这种shadowSSDT函数,只传进来一个参数(rcx), 保险起见我们写四个。
示例过滤函数如下:(自备shadowSSDT hook引擎,本贴不会放完整代码,防止伸手)



       But, 光是这样是不够的, 我们还要在这里获取到D3D9的设备。
这就给我们出了一个难题,一般在应用层都是hook取设备的。
经过查资料,我们想到了一个好办法,利用栈回溯,找到最初调用
Present函数的地方, 并把第一个参数(也就是设备)取出来。
好在windows x86下的栈回溯是基于EBP来回溯的,原理相对简单,
我们可以手动编写一下。

1.3


       应用层在通过syscall进入内核之前,会把当前的TrapFrame保存
起来。在内核中当前_KTHREAD成员 TrapFrame 就是应用层保存的
TrapFrame 指针。


在win7 x64下,其结构偏移为 0x1D8。
       至此,我们可以顺利的获取应用层最后保存的RBP了。

接下来写栈回溯,具体原理我就不多做描述,大家可以移步这两个帖子看一下
https://blog.csdn.net/chenlycly/article/details/78144769
https://blog.csdn.net/wu330/article/details/29213201
示例代码如下:

笔者这里偷了个懒,直接判断call 地址是否小于 0x00500000
以此确定 当前帧就是call present的。
       当然,获取到设备的同时,得以确认,当前线程就是从
Present函数一直call 进内核的。下面就可以放心的执行绘制操作了。

2.0

      在内核中给当前进程申请内存,最方便的莫过于ZwAllocateVirtualMemory
申请内存成功之后,写入shell code。
比如直接call D3D9的 Clear函数,画个方块。
示例如下:

要注意的是,不要一直不停的申请内存,我想各位看官应该不会这么做[滑稽]。

3.0

      做完如上工作,接下来就可以想办法回到用户模式,执行shellcode了。
回到用户模式的方法有好几种,比如KeUserModeCallBack, APC。
其中 KeUserModeCallBack 需要在应用层中写好代理分发,并且如果目标进程是
wow64进程,还需要多写一层代理函数,着实麻烦。
      所以我们采用相对简单的 APC,利用APC机制回到用户模式。
代码如下:


注意这里的细节,如果目标进程是 wow64进程,起始地址需要 / -4。
然后就是常规的初始化APC,插入队列。
      由于我们是回到当前进程的应用层,所以初始化APC时,
KeInitializeApc第二个参数填 PsGetCurrentThread()。
最后利用KeTestAlertThread直接触发队列里的APC Routine。

3.1

最后放上效果图如下:


结尾:

经过笔者测试,有些系统并不走 NtUserHwndQueryRedirectionInfo
而是另外的ShadowSSDT函数,不过我们依然有办法解决这种小问题。
有时间开下一帖讲一下






[推荐]看雪企服平台,提供安全分析、定制项目开发、APP等级保护、渗透测试等安全服务!

上一主题 下一主题
最新回复 (16)
hzqst 3 2018-11-8 10:17
2
1
内核hook->插APC->回到R3干活->留下R3的尾巴->被安排->999大礼包
刘铠文 2018-11-8 10:23
3
0
还行,有时间玩玩看
niuzuoquan 2018-11-8 10:33
4
0
mark
御剑残风 2018-11-8 10:44
5
0
mark 内核绘制  牛逼
叶惠 2018-11-8 11:19
6
1
感觉更容易被查了
yewenwenye 2018-11-8 11:32
7
1
被安排得明明白白
malokch 3 2018-11-8 12:04
8
1
牛逼了,八仙过海各显神通。其实还有很多强势的不着痕迹的shellcode注入方案,大家都不舍得公开罢了。
二娃 2018-11-8 13:10
9
0
围观一下
最后于 2018-11-8 13:10 被二娃编辑 ,原因:
yy虫子yy 2018-11-8 14:21
10
0
显存为什么不会自动释放?调用Present才会占用显存是吗?
那如果不调用Present的话,就不占用显存了吧?
万剑归宗 1 2018-11-8 16:00
11
0
yy虫子yy 显存为什么不会自动释放?调用Present才会占用显存是吗? 那如果不调用Present的话,就不占用显存了吧?
win7 x64下,显存是在GUI进程之间全局共享的。
最后于 2018-11-8 16:00 被万剑归宗编辑 ,原因:
yy虫子yy 2018-11-8 16:07
12
0
万剑归宗 yy虫子yy 显存为什么不会自动释放?调用Present才会占用显存是吗? 那如果不调用Present的话,就不占用显存了吧? win7&nb ...
D3D该怎么优化显存占用呢?
万剑归宗 1 2018-11-8 16:39
13
0
yy虫子yy D3D该怎么优化显存占用呢?
hook present函数 不做处理,直接返回0
yy虫子yy 2018-11-8 17:33
14
0
万剑归宗 hook present函数 不做处理,直接返回0
就是这样做的,不调用Present啊
万剑归宗 1 2018-11-8 17:35
15
0
yy虫子yy 就是这样做的,不调用Present啊
别的没什么可优化的了
maomaolk 2018-12-5 10:39
16
0
Mark
黑洛 1 2018-12-18 14:42
17
0
老铁, 你这个还是要回r3啊,还是会被安排了。不如考虑一下直接注入怎么样,puxg不是只拦截注入但是不检测注入吗?直接hookD3D函数好像也不会出事,你这里从内核走一下反而容易直接被爆掉。不过思路是不错的,可以用到别的情景不同的游戏上。
游客
登录 | 注册 方可回帖
返回