首页
社区
课程
招聘
[分享]Windows 内存隐藏/内存欺骗 ShadowWalker 加强版
发表于: 2014-11-27 00:09 41148

[分享]Windows 内存隐藏/内存欺骗 ShadowWalker 加强版

2014-11-27 00:09
41148
第一次在看雪发技术帖拋代码 知道这里大神众多 **会被打 所以我今天尽可能的表现的低调些 改变以往风格 不**

知道对于程序员来说“版权”概念尤为重要 所以事先说几句:

1:程序的思路在可查阅到资料中 追溯到最早看到的是在ShadowWalker中使用的。

2:这个程序在写之前我参考了以下两位的代码 可以理解成踩在以下这两位巨人的肩膀上(以发布时间顺序):

http://bbs.pediy.com/showthread.php?t=56689

http://bbs.pediy.com/showthread.php?t=185087

3:关于详细的实现思路可查看: (我不知道PEDIY是否允许发外链 若不允许的话 会将此外链删除)

http://www.xfocus.net/articles/200508/812.html

目前网上能找的到的“用0E中断刷TLB实现 内存隐藏/内存欺骗”相关代码都历史悠久(最早发布日期可以翻到200X年)

我找到的(包括在PEDIY站里 也包括以上两位的代码 和一些其他来源代码)内存隐藏的程序里 按我的理解都是“例子”程序

也就是说只是证明了刷TLB这种方案是可行的 的确隐藏了内核态内存 但没有封装好直接可以用的那种提供完整接口的

(也许有 但我在写这程序前费了好大的力气四处搜寻 反正我是没找到) 所以决定自己试着写一下 并且请允许将他称为“ShadowWalker 加强版”

而在写的过程中遇到了些麻烦 但毕竟写了 所以最终决定把源码放出来 也算取之看雪 还之看雪吧 也许可以帮到一些有跟我类似需求的人

再此怕有人骂我唠叨 先说明一下这套源码的功能:

1:隐藏/欺骗内核态内存 (Ring0) 可以将指定内核态虚拟地址在执行和读取时得到不同的数据

2:被隐藏的内存在windbg中查看  得到的全是  ? 号 (这应该也算是间接的反调试吧)

3:XP SP3 32位 PAE模式下测试通过 非PAE模式的代码里面也写了 包括判断PTE等 只不过时间有限 也没有非PAE模式的环境

所以是用不了的 例如没有对非PAE的0E中断过滤函数写进一步的处理 但对分页机制稍有了解是可以很轻松完善出来的

4:提供了尽可能完善的接口和注释 (我已经很努力的让这套代码没有BUG 但能力和开发时间有限 测试条件也有限 所以存在BUG请别拍砖)

5:全套代码包括了20多个函数和若干结构体 关于隐藏页的记录是用三层链表实现的 用到了若干个锁 处理了IRQL的问题

6:小部分结构体和函数声明之类的可看下图:















如果打算用这代码隐藏自己的驱动 很明显这套代码也许会帮到你 如果只是希望隐藏内核态内存的话 你可以直接下载附件关闭本贴了

如果还想隐藏进程下的内存 虽然理论上讲 我也算是接近"成功"了 但很抱歉我还是失败了 这套代码无法隐藏进程内存 请不要尝试。

我把进程部分的代码去除了(因为写的很凌乱 在底线很低的标准中写的)

所以目前这套代码对于隐藏进程内存的话 我总结可能有如下问题 以及存在某些未预料到的问题 (我个人总觉得总结问题远比发源码有意义的多):

// 存在问题:
//
// 0:无法隐藏内核态某些"不可分页"地址 例如NTOS模块等 其内部0E中断 若产生缺页将导致无法正常执行 以及某些内核态大页内存
// 1: 0号问题可以通过IDT HOOK解决 但可能严重影响系统效率 暂时禁止欺骗NTOS等重要系统模块
// 2:禁止一次修改超过1页 4KB大小 添加记录结点时跨页的处理
// 3:隐藏进程可分页内存成功后 内存被改变属性或释放 写时复制 切换缺页 去交换中获取又发现不存在 或PTE检查不合法 等问题
// 4:如果对无效属性页访问 产生异常的分发是保护优先还是缺页优先
// 5:PTE结点中获取到的RET指令可能被新隐藏的记录结点数据所覆盖
// 6:PAE检测用全局头指针中的域 当前所有代码都假定PAE模式 未实现非PAE的0E HOOK (但已实现大部分PAE和非PAE模式下的检测和判断等)
// 7:用于欺骗的页分配问题 不分页内存PDE是大页 用小页大小对其基址 分页内存PDE是小页 用分配的大小对其基址 (因此本套代码只可隐藏内核态小页内存)

毕竟折腾了好多天 最终把失败的原因总结一下 也希望可以帮到后来的人:

查看这套代码可以看到里面加入了隐藏进程内存的一些检测和条件 其实这也是我写这套程序的本意

想实现隐藏进程内存的功能后在这个基础模拟硬件断点 实现万能断点 或用于一些其他的RK相关技术

恕我无知 最初想法很简单:既然成功隐藏了内核态内存 那进程中的内存也应该可以隐藏

代码上的东西 NT架构内的东西 都是人写出来的 在复杂的逻辑也只是敲代码 只要可以人工控制 写出来也只是时间问题 刚好 我有很多时间。

但我跟其他人讨论这个问题的时候 得到的回复通常是:进程中的内存是隐藏不了的 或者说大多数人都失败了。

近一步问原因 得到的答案通常是 没见到人成功过 和 一些奇葩的解释 例如XX函数会操作PFN数据库 进程切换会XXX什么的。

毕竟是技术讨论 或者理解成请教 但我得说句实话 这些答案没有帮到我的 或者说没有一个人说到重点上

当我写完这套代码准备开始测试的时候发现了个问题 一个特别小的问题 但也是最致命的一个问题 导致我发现这套代码是不可能实现进程中内存的隐藏的

整套代码逻辑中最重要的一个步骤就是ITLB和DTLB 对于操作系统来说 这两个缓冲区是同步的

而对于隐藏内存的实现来说 就是让这两个缓冲区不同步 让线程(或者说是处理器)读写某个页 和 执行某个页 对应不同的页

我最开始写之前测试以上两位“巨人”的代码时 是用A线程执行 B线程读写 这样的测试条件下 内存隐藏的确是成功的

但我把这套代码写完后 测试进程内存的隐藏时 发现了问题 下面我叙述一下 (希望我的表达能力能说的清楚。。)

若需要隐藏/欺骗某个虚拟地址的内存 即让读写和执行对应不同的数据 那么隐藏的并不是只这个虚拟地址的数据 而是这个虚拟地址所属的整个页

假设现在需要隐藏某个页的内存(无论是R3进程内的页的还是R0内核态的页) 假设这个页是小页 即4KB大小 我们将他称为"被隐藏页"

隐藏方式是 创建出两个新的页来(都是4KB大小) 分别是读写页 和 执行页  

现假设执行页中的数据与"被隐藏页"相同  而读写页中的数据则全部为0

接下来还是老生常谈 将"被隐藏页"所属PTE设置无效 当这个页被读写/执行时都会产生0E中断 而0E中断已被我们接管

在0E中断中 可以通过陷阱帧中的 EIP == CR2 (产生异常的地址是否是当前执行的地址) 来判断是执行异常还是读写异常

如果是执行异常 则 将执行页的PFN写入到被隐藏页PTE的PFN域->设置PTE有效->CALL这个页中的ret指令刷新ITLB(对应机器码0xC3)->重新设置PTE无效

如果是读写异常 则 将读写页的PFN写入到被隐藏页PTE的PFN域->设置PTE有效->读写这个页中的任何一个虚拟地址刷新DTLB ->重新设置PTE无效

虽然最后一步中又设置了PTE无效 但CPU在执行/读写的时候 会从ITLB/DTLB的缓冲区去查找对应的物理页帧 而不会在产生0E异常。

这样 TLB的同步规则被拆分开 实现了内存隐藏/欺骗 看起来并不复杂 但却是整套逻辑最重要的一部分

现假设出现两条线程 A线程在被隐藏页中执行代码 B线程在读取被隐藏页中的数据 很明显 这两条线程没什么实际关系

由于这个页被我们隐藏了 所以A线程实际执行的是执行页  B线程实际读写的是读写页 也就是说 虽然虚拟地址相同 但数据完全不同

即A线程执行的是与"被隐藏页"相同的代码 B线程读到的数据全是0(因为上面也提到了读写页中全部是0)

到目前为止 一切都和我们预想的一样 没有任何问题

但如果这个时候 A线程在执行的过程中 读取这个页中某个虚拟地址的数据 会发现他读到了正常的执行页中的数据

简单点解释 可以理解成:A线程刷新了ITLB以后 在读写这个页数据的时候 不会在产生0E异常 因此DTLB也就不会被刷新

好了 这就是我说的致命问题。。 也就是这个“隐藏” 是建立在线程只会读写这个页 而不会执行这个页的基础上的

我测试中发现 如果读写这个页 0E中断中会刷新DTLB DTLB刷新以后 ITLB不会被同时刷新 当刷新了DTLB的线程(读写过这个页的线程) 在执行的时候 依然会产生0E中断  

而一旦执行了这个页 0E中断中会刷新ITLB 而ITLB被刷新后 DTLB也跟着自动刷新了 在读这个页的时候 就不会在产生0E异常

对于特殊的驱动 RootKit 内核态木马 这类的程序 能进入到他们代码段执行的线程 一定是“自己”的线程 所以这样的隐藏是完全有意义的

杀毒软件也好 ARK也好 在暴力扫内存空间的时候 最多也只会尝试着读某个地址 而不可能到一个未知的虚拟地址去执行

而如果用来欺骗某个进程中的内存 麻烦就大了 假设我们想通过这种隐藏来实现过CRC 我们无法确定 被隐藏的那页内存 是否会被CRC检测的线程执行

如果就在那页里执行时对自己的内存校检 那这个隐藏根本就不会生效 我开始猜测可能是由CPU缓存有关 在BIOS中把缓存关掉了 依然还是这样 所以很明显 我失败了

我又翻出了以上两位和一些其他隐藏内存的代码进行测试 测试结果和我得到的结果是一样的

也就是说 目前我能找到的代码里 也包括我自己的代码:一旦刷新了ITLB 则DTLB也会跟着被刷新 即根本无法欺骗正在执行这个页的线程

我也查阅了一些资料和问了一些人 统一口径:TLB是硬件机制 无法人工修改表项。。

到此位置以上也算是我的全部分析了 接触win内核的时间不长 这套代码从决定写到目前为止大概十几天

在此之前我从未接触过Windows中的内存体系和分页机制相关的资料 所以以上的分析和判断 可能存在错误  

如果有问题的地方欢迎指出 不过至少能保证的是每一个判断都是由实际的测试结果得出的 没有猜测异想天开的成分在里面

发出的代码是VS2013 + WDK7.1工程  Bin可以直接在XP SP3环境下使用  在windbg中可以直接看到内存隐藏测试的输出信息

此贴最后 也算抛砖引玉 如果有哪位朋友能解决我上面提到的这个ITLB的问题 或者能提出若干的解决方案

我都愿意继续把这套代码的进程内存隐藏部分最终完善出来并共享

我的QQ是:305205  另外还建了个交流群 希望能来些编程爱好者 欢迎加入:125074547  

附件下载:
HideMemory.rar

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 3
支持
分享
最新回复 (38)
雪    币: 3
活跃值: (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
支持LZ一下。
2014-11-27 00:27
0
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
支持一下
2014-11-27 00:29
0
雪    币: 478
活跃值: (50)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
我来了  学习一下
2014-11-27 00:45
0
雪    币: 35
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
not perfect。。。。。
2014-11-27 00:49
0
雪    币: 135
活跃值: (63)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
6
============
2014-11-27 09:21
0
雪    币: 8835
活跃值: (2404)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
7
应用层的sw,参考ollybone~

最大的问题是PTE交换会影响R3~至于ITLB和DTLB的刷新,是可以通过一些技巧性的东西分开的。
2014-11-27 09:48
0
雪    币: 36
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
支持你。。。。。。。。。。。。。。
2014-11-27 09:49
0
雪    币: 6976
活跃值: (1482)
能力值: ( LV11,RANK:180 )
在线值:
发帖
回帖
粉丝
9
挺不错的, 可以进一步研究!
2014-11-27 09:57
0
雪    币: 8835
活跃值: (2404)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
10
关于进程级内存隐写:
1.32位下的PAE,DEP机制。
2.64位下的NX机制。
都实现了数据和代码的分离,做个技巧性的XXOO和对付PTE数据交换就好了~
至于什么ShadowCR3,EPT这些VT的东西,EPT更简单,主要是动态使用比较复杂。
ShadowCr3,纯粹是挑战代码逻辑。

以前写过一个,今天看楼主的帖子,突然翻出来,发出来算了。
需要应用层配合的(shadow_jmp页是应用层来创建)。
http://bbs.pediy.com/showthread.php?p=1333875
2014-11-27 09:57
0
雪    币: 239
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
感谢LZ分享
2014-11-27 09:58
0
雪    币: 56
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
如果有实现思路 欢迎进一步的详细说一下具体实现方案 光主观的说“能”与“不能” 没什么意义。
2014-11-27 10:10
0
雪    币: 8835
活跃值: (2404)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
13
老代码已发~别问我shadowCR3和EPT的事情,这玩意三言两语无法讲明白的。ShadowCr3不用VT技术的话也只能针对进程(每个进程一个独立的shadowCR3描述内核代码,参考我的在pchunter下隐藏内核hook的代码)。
EPT方式则说白了,就是自己管理TLB了,虚化的TLB,当执行时,当读取时,当写入时,三个TLB烦死~~
2014-11-27 10:15
0
雪    币: 698
活跃值: (4564)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
14
赞楼主!
2014-11-27 10:19
0
雪    币: 56
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
也就是说 在VT里 是可以虚化TLB的 但需要EPT的支持  请问是否实际测试过我上面提到的问题 也就是说 在VT下虚化TLB时 ITLB刷新是否也会导致DTLB一起刷新的问题?
2014-11-27 10:24
0
雪    币: 8835
活跃值: (2404)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
16
不会~ITLB和DTLB,你虚化成2个TLB就是2个TLB,刷一个,另一个不会该~~

不过外面代码虚拟机的基本都是一个TLB~
2014-11-27 10:25
0
雪    币: 56
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
看来我有必要找找这方面资料了解一下。。谢谢提供思路。
2014-11-27 10:31
0
雪    币: 23
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
。。。注释很完整啊,是亮点,我给你提供个更变态的隐藏思路,移型换影Vad,直接把虚拟内存页换掉,根本不用操作相关的具体页属性。在 ring3改pte或pae引起的freelinks错误,直接三精。
2014-11-27 10:37
0
雪    币: 56
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
谢谢夸奖 不过。。 您能不能整理一下逻辑 尽量叙述的完整和细致一些 我读书少 理解能力差。 您说的我真没听懂。。
2014-11-27 10:41
0
雪    币: 23
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
。。。页表怎么操作你都明白啊,大哥,VAD比页表还要向上高一层。
1、vad把PAE or PTE用二叉平衡树的方式实现虚拟地址,
2、你把自己进程内做一个读内存的hook,vad统一成正确的vad,然后再做一个假vad,当其他程序read时,不就替换了么?CE内部用的就是这个办法读取内存,所以CE读内存能力很强。
2014-11-27 10:55
0
雪    币: 56
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
这是把进程内隐藏内存的思路 活活的拆成了进程间或进程外的隐藏内存。。。严重不靠谱。。
2014-11-27 10:57
0
雪    币: 954
活跃值: (123)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
好帖子要顶起来
2014-11-27 11:02
0
雪    币: 23
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
就是进程内隐藏啊,一个进程有且只有一套vad。总不能出现多个2G内存吧。
2014-11-27 11:05
0
雪    币: 8835
活跃值: (2404)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
24
VAD只能处理跨进程内存隐藏。
如果想不跨进程就呵呵了~

另外vad自身进程读写,不用api的话,也是没用的~
2014-11-27 11:07
0
雪    币: 23
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
关键在api的hook上,即使是自身读取,一样给他移型换影,加个标志就行了。shellcode,注入等,要加标志,另外说明一点,直读页表,就无能为力喽,不过直接 能做到直接 读页表,TLB也莫有用了吧。
2014-11-27 11:16
0
游客
登录 | 注册 方可回帖
返回
//