在win7 x64下做个简单的内核绘制

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函数,不过我们依然有办法解决这种小问题。

有时间开下一帖讲一下。



- End -



原文作者:万剑归宗

原文链接:https://bbs.pediy.com/thread-247671.htm

转载请注明:转自看雪学院



更多阅读:

1、[原创]Flare-on5 12题 suspicious_floppy writeup

2、[原创] ratel,让xposed模块在免root的环境下跑起来

3、[原创]反编译原理(2)-中间表示

4、[原创]代码混淆之我见(一)

你可能感兴趣的:(在win7 x64下做个简单的内核绘制)