Kill Safengine Shadow Dll(附源码)
Kill Safengine Shadow Dll(OD插件)
/**************************************
/* 作者:半斤八兩
/* 博客:http://cnblogs.com/bjblcracked
/* 日期:2013-06-21 01:01
/**************************************
只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
端午回来在宿舍无聊上网,有一网友问se的shadow怎么绕过.之前就想研究的,正好刚放假回来无聊,就下载了个最新版 2.2.0.0 版本的加了一个壳看看 :)
0x1. 分析如何下断?
0x2. 分析如何shadow system function?
0x3. 分析为何要alloc memory 2次, 多此一举?
0x4. 分析用哪种方法处理shadow最好?
0x1. 分析如何下断?
记得第一次分析SE的时候,无论怎样下断都下不下来.后面就下到了可能是shadow了.于是就想到一个最麻烦,但又最管用的方法,搜索特征码. 比如函数 User32.MessageBoxA 就把Msg头部,的特征码复制下来,到OD的内存窗口里面去搜索.这样就能下断了.虽然方法很管用.但是,如果我们要下的断点很多的话,那样就会显得相当麻烦.下面我们来分析看看她是如何shadow的.
加壳时 [反内联API挂钩] 勾上即可 :)
0x2. 分析如何shadow system function?
老规则,首先OD载入.然后我们想一下,他shadow 无非就是重载 或者重写.我们先以作者是重载的思路来分析看看:)
重载常规的手段大概就两种.
1. 1种是读取system32 下的 DLL.
2. 另一种就是读取已加载的模块内存.
我们先试试第1种,读取 system32 的DLL,那么我们首先就是想到了 file 的那几个操作函数.
Createfile readfile. 等.
这里可能有的同学会问了,我们本身就是处理shadow的. 那file的这几个操作函数会不会是也已经shadow了?
答案是 还没有. 因为我们刚OD载入.他的程序还没来的及处理呢.所以 file 操作的这几个函数,他还是用的原始的系统函数.这点大家可以放心.
下面我们先对 createfilea/w readfile 下断. F9运行.
断下回显:
1 0012F604 0049C938 /CALL to CreateFileW from Shadow.0049C936 2 0012F608 0012FC54 |FileName = "C:\Windows\System32\ntdll.dll" 3 0012F60C 80000000 |Access = GENERIC_READ 4 0012F610 00000001 |ShareMode = FILE_SHARE_READ 5 0012F614 00000000 |pSecurity = NULL 6 0012F618 00000003 |Mode = OPEN_EXISTING 7 0012F61C 00000080 |Attributes = NORMAL 8 0012F620 00000000 \hTemplateFile = NULL
(为了方便,我们就以user32.messagebox 为例子讲解.)
Createfilew 断下来了,第一个参数显示
"C:\Windows\System32\ntdll.dll"
我们不管它,继续 F9 运行.
断下回显:
1 0012FC28 00465FA9 /CALL to CreateFileW 2 0012FC2C 0012FC54 |FileName = "C:\Windows\System32\ntdll.dll" 3 0012FC30 80000000 |Access = GENERIC_READ 4 0012FC34 00000001 |ShareMode = FILE_SHARE_READ 5 0012FC38 00000000 |pSecurity = NULL 6 0012FC3C 00000003 |Mode = OPEN_EXISTING 7 0012FC40 00000080 |Attributes = NORMAL 8 0012FC44 00000000 \hTemplateFile = NULL
我们看见SE调用两次.先不管它,继续运行.
断下回显:
1 0012FC30 00465FC7 /CALL to ReadFile from Shadow.00465FC2 2 0012FC34 000000A4 |hFile = 000000A4 (window) 3 0012FC38 01480020 |Buffer = 01480020 4 0012FC3C 0013AD40 |BytesToRead = 13AD40 (1289536.) 5 0012FC40 0012FE5C |pBytesRead = 0012FE5C 6 0012FC44 00000000 \pOverlapped = NULL
Readfile 断下来了,第三个参数是 文件大小. 我们去系统文件夹下看一下 user32.dll 有多大.?
User32 大小
792 KB (811,520 字节)
现在断下来的大小 13AD40 (1289536.) 是ntdll.dll 的.我们不用管它.继续运行.
直到是 user32的大小的时候,才停下.
在经过读取了 ntdll.dll kernelbase.dll kernel32.dll 后,现在终于在user32.dll 下断下来了.
断下回显:
1 0012FBE8 0182D53F /CALL to ReadFile from 0182D53A 2 0012FBEC 000000A4 |hFile = 000000A4 (window) 3 0012FBF0 01370020 |Buffer = 01370020 4 0012FBF4 000C6200 |BytesToRead = C6200 (811520.) 5 0012FBF8 0012FE5C |pBytesRead = 0012FE5C 6 0012FBFC 00000000 \pOverlapped = NULL
我们让他把文件读取出来,我们在 retn 0x14 处下 F2 断点. F9运行.
我们能看到user32.dll 已经读取出来了.
断下回显:
1 013C0020 4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00 MZ?........ 2 013C0030 B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ?......@....... 3 013C0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 4 013C0050 00 00 00 00 00 00 00 00 00 00 00 00 F0 00 00 00 ............?.. 5 013C0060 0E 1F BA 0E 00 B4 09 CD 21 B8 01 4C CD 21 54 68 6 013C0070 69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F is program canno 7 013C0080 74 20 62 65 20 72 75 6E 20 69 6E 20 44 4F 53 20 t be run in DOS 8 013C0090 6D 6F 64 65 2E 0D 0D 0A 24 00 00 00 00 00 00 00 mode....$....... 9 013C00A0 2E 8D 3F 12 6A EC 51 41 6A EC 51 41 6A EC 51 41 .?j霶Aj霶Aj霶A 10 013C00B0 63 94 D5 41 6B EC 51 41 6A EC 50 41 35 ED 51 41 c斦Ak霶Aj霵A5鞶A 11 013C00C0 63 94 C2 41 6D EC 51 41 63 94 C3 41 6B EC 51 41 c斅Am霶Ac斆Ak霶A 12 013C00D0 63 94 C4 41 6D EC 51 41 63 94 D2 41 17 EC 51 41 c斈Am霶Ac斠A霶A 13 013C00E0 63 94 C5 41 6B EC 51 41 63 94 C0 41 6B EC 51 41 c斉Ak霶Ac斃Ak霶A 14 013C00F0 52 69 63 68 6A EC 51 41 00 00 00 00 00 00 00 00 Richj霶A........ 15 013C0100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 16 013C0110 50 45 00 00 4C 01 04 00 2F DB 5B 4A 00 00 00 00 PE..L./踇J.... 17 013C0120 00 00 00 00 E0 00 02 21 0B 01 09 00 00 78 06 00 ....?! 18 013C0130 00 E6 05 00 00 00 00 00 C9 F7 01 00 00 10 00 00 .?.....慎....
我们还用0x1 的方法,现在来定位mesageboxa 的地方. 定位到后,
我们在命令窗口处输入da 回车. (汇编格式显示数据) 再下个F2断点.然后F9跑起来.
我们点demo上的 messagebox 按钮 .
特征码:
1 user32.MessageBoxA: 2 8BFF558BEC??????????????742464A1180000006A00FF7024??????????????????????85C0750A????????????????????6A00FF7514FF7510FF750CFF7508??????????5DC21000
刚按F9运行,OD就弹了一个类似如下的提示:
Unable to read memory of debugged process (0141DE94..0141DE97).
这是因为我们刚刚下的断点,已经不见了.为何不见了?
这不是SE的反调试,也不是OD出问题了.而是我们刚刚下断点的那块内存已经free掉了.
所以,这证实了,这一次读取的内存,还只是一个 shadow. 那么真正的实现函数在哪里?
还有,现在读取出来的已经是shadow 的函数了.他为何又要free 掉?
0x3. 分析为何要alloc memory 2次, 多此一举?
多分析几次,我们可以发现一个规律.第一次readfile 的数据.的imagebase是固定不变的
但是rva 始终固定不变,而且同user32.dll 的导出函数的 rva 保持一致.
但是程序真正跑起来后的数据的 imagebase 和 rva 都是变化的 动态的.
我个人理解的是作者这样做,就是为了让imagebase and rva 动态. 让用户更难下断.
知道了这些,那么我们只人对第一次readfile 的数据做处理. 至于第二次,他并没有readfile. 而是 new 了一内存,把第一次readfile 的数据拷贝进去. 但是这些我们都不用管它是怎么处理的呢.
因为我们只要处理他第一次shadow 数据,即可. 因为第一次的数据虽然会free.但是他后面真正跑起来的shadow 是完全原封不动的拷贝第一次的shadow 数据.所以...你懂的...
0x4. 分析用哪种方法处理shadow最好?
相信大家都知道 jmp xxx 的处理方法.我们只要在每个函数首部写上 jmp xxxx 这样即可.
但是这样就会有一个问题了.什么问题?
因为我们分析知道 rva 是固定的,但是imagebase 是动态的. 我们如何 jmp xxx?
jmp xxx 的算法是 target- current- 5 ,但是我们当前的 imagebase 是动态的.
所以 jmp xxx 想法就泡汤了.
X86 除了jcc这类直接式的转移指令,还有许多其他有意思的”间接”转移指令.
如 push xxx /retn . 我们用这条指令,就可以解决 动态 imagebase 的问题. 因为 push xxxx 不用考虑重定位.所以 我们只要打开每个函数的入口,都写上 push xxxx / retn 就可恢复 se 的 shadow了 :)
最后来个处女之作--OD插件图 -_-~
具体的实现细节,我在文章中就不说了,大家看码吧~
此插件是本人的第一个处女作.之前没有写过OD插件. 写了之后才发现OD的插件接口真是非常丰富.写起来毫无压力.但是在处理combobox 消息时,花了80%的时间 太久没有玩SDK 都不会写了 :(
本插件仅支持win7. Xp 上有些不同,此版本插件没有处理.
另外听朋友说有的OD使用此插件无效,但是朋友说换了个OD就能用了.具体原因我还没有时间看.~
源码 插件 文档 下载地址: