好友
阅读权限 20
听众
最后登录 1970-1-1
本帖最后由 xiaohang99 于 2017-4-12 22:36 编辑
最近又在论坛上看到不少人求Safengine Shielden (以下称SE)的脱壳 方法,其实之前论坛里的几位大牛都已经放出过不少牛文,虽然这些牛文是对应老版本的SE的,文中附带的代码可能已经失效,但是里面的方法确实在新版本中依然有效的,可能大神们对自己思路的阐述过于跳跃了,所以难免让新人们有些跟不上。在这里,我把我对大牛们的思路理解放上来,以供参考吧。 SE 2.3.8.0 目前已知的最大反调试手段就是 anti 断点,主要是 anti 了硬件断点和 int3 软断点,加壳后无法对被加壳的程序下断,客观上提高了脱壳的难度,所以第一步是要想办法把 anti 断点去掉。
我的思路
int3 软断点的 anti 必然是基于内存效验的方法( int3 软断点需要修改内存代码),对壳代码进行 hash 效验保护,如若发现内存有被修改,则结束进程。如果要 patch 掉内存 hash 效验,必须知道 hash 代码的位置,由于 SE 的垃圾混淆代码很多,更本不可能通过搜索的方式获取,最好的办法就是用硬件断点下一个读取断,所以问题就是要先修复硬件断点。
对于硬件断点, SE 采用的是先通过 SetThreadContext 设置四个 DRx 寄存器为 1byte 读取特定地址的断点,然后通过在虚拟机 中构造读取特定地址以产生异常,再用 SEH handler 处理异常,并检测 DRx 寄存器的值。如果该产生异常的时候没有产生异常,又或者 DRx 的值不为先前 SetThreadContext 设定的值,那么就退出进程。
对于内存断点反 anti ,暂时没有思路,还需要继续研究。
处理过程
先获取 SE 对 DRx 设置的值。
1 、先在 KiUserExceptionDispatcher 下断
2 、断下两次后记录
[Asm] 纯文本查看 复制代码
[esp+0x14] = 0012F7FC 00E8A7CA unpackme.00E8A7CA //异常产生的位置,就是后面的vm:ds[imm]位置
3 、 CPU 窗口中显示调试寄存器
DR0 =00E57016
DR1 =00E57000
DR2 =00E57004
DR3 = 00E57008
DR7 = 33330555
Patch Shadow_CreateThread
由于 SE 会产生数个线程来进行反调试检测,内存完整性检测等,为了减少干扰项,把 SE 创建的检测线程先 patch 掉,不让他启动,可能会对后面的程序运行有副作用,但是便于我一开始的脱壳调试。
PS: SE 会把 NTDLL.dll 和 krenel32.dll 中的一些代码 Shadow 到一段自己分配的空间中,所以需要利用内存搜索的方法,搜索特定代码找到这些被 Shadow 的 API 。
先在 KiUserExceptionDispatcher下断 第一次断下后,搜索特征代码
#8BFF558BECFF751CFF7518FF7514FF7510FF750CFF75086AFFE8????????5DC21800#
搜索到的代码:
[Asm] 纯文本查看 复制代码
00D1FB55 8BFF mov edi, edi ;改成 ret 18
00D1FB57 55 push ebp
00D1FB58 8BEC mov ebp, esp
00D1FB5A FF75 1C push dword ptr [ebp+1C]
00D1FB5D FF75 18 push dword ptr [ebp+18]
00D1FB60 FF75 14 push dword ptr [ebp+14]
00D1FB63 FF75 10 push dword ptr [ebp+10]
00D1FB66 FF75 0C push dword ptr [ebp+C]
00D1FB69 FF75 08 push dword ptr [ebp+8]
00D1FB6C 6A FF push -1
00D1FB6E E8 D9FDFFFF call 00D1F94C
00D1FB73 5D pop ebp
00D1FB74 C2 1800 ret 18
修改函数开头位置为 ret1800D1FB55 8BFF mov edi, edi ; 改成 ret 18
Patch Shadow_GetThreadContext
SE 会调用 Shadow_GetThreadContext 来检测是否存在调试软件下的硬件断点,如果有就会退出进程。
在刚才搜索到 Shadow_CreateThread的内存段内搜索特征代码:
#8BFF558BECFF750CFF7508FF15????????85C00F8C??????0033C0405DC208009090909090#
搜索到的代码:
[Asm] 纯文本查看 复制代码
01888ABF 8BFF mov edi, edi
01888AC1 55 push ebp
01888AC2 8BEC mov ebp, esp
01888AC4 FF75 0C push dword ptr [ebp+C]
01888AC7 FF75 08 push dword ptr [ebp+8]
01888ACA FF15 9E088501 call dword ptr [185089E] //获取这里地址
01888AD0 85C0 test eax, eax
01888AD2 0F8C 89B20000 jl 01893D61
01888AD8 33C0 xor eax, eax
01888ADA 40 inc eax
01888ADB 5D pop ebp
01888ADC C2 0800 retn 8
如果直接修改以上代码会被 SE 检测到,我的方法是 inline hook 函数调用 KiFastSystemCall 之前的地址(壳另外加载了一块内存)
[Asm] 纯文本查看 复制代码
01888ACA FF15 9E088501 call dword ptr [185089E] //获取这里地址
//获取到的地址标准的ntdll.ZwGetContextThread调用
0169C597 B8 55000000 mov eax, 55 //服务号
0169C59C BA 00005901 mov edx, 00390000 //这里改成我自己分配的空间
0169C5A1 FFE2 jmp edx//跳到我自己分配的空间执行
0169C5A3 C2 0800 retn 8
//我构造的代码
[Asm] 纯文本查看 复制代码
00390000 B8 55000000 mov eax, 55
00390005 BA 0003FE7F mov edx, 7FFE0300
0039000A FF12 call dword ptr [edx]
0039000C 50 push eax
0039000D 8B4424 0C mov eax, dword ptr [esp+C]
00390011 8038 10 cmp byte ptr [eax], 10
00390014 75 16 jnz short 0039002C
00390016 33D2 xor edx, edx
00390018 8950 04 mov dword ptr [eax+4], edx
0039001B 8950 08 mov dword ptr [eax+8], edx
0039001E 8950 0C mov dword ptr [eax+C], edx
00390021 8950 10 mov dword ptr [eax+10], edx
00390024 52 push edx
00390025 6A 06 push 6
00390027 E8 299C477C call kernel32.TlsSetValue
0039002C 58 pop eax
0039002D C2 0800 retn 8
壳总是会判断这 TlsValue 是否等于 Dr0+Dr1+Dr2+Dr3 之 Total 值
我们在壳欲取得 Drx 的值时,将之清为 0 ,并设 TlsValue 为 0
至于 SetTlsValue 的 Index 应为多少才对 , 很多方法可以得知 .
例如断 Shadow 的 SetTlsValue ,XP SP2 用的 Index 是 4 , XP SP3 则是 6
Hook ExecuteHandler2
这里是重头戏,反 anti 硬件断点必须从这里开始,由于壳会检测异常信息中的 DRx 寄存器,所以我们必须在 SE 的 SEH handler 被调用前,给 _CONTEXT 结构中的 DRx 赋予之前获得的特定值。
_CONTEXT 结构如下:
typedef struct _CONTEXT
{
DWORD ContextFlags;
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;
FLOATING_SAVE_AREA FloatSave;
DWORD SegGs;
DWORD SegFs;
DWORD SegEs;
DWORD SegDs;
DWORD Edi;
DWORD Esi;
DWORD Ebx;
DWORD Edx;
DWORD Ecx;
DWORD Eax;
DWORD Ebp;
DWORD Eip;
DWORD SegCs;
DWORD EFlags;
DWORD Esp;
DWORD SegSs;
}CONTEXT;
typedef struct _FLOATING_SAVE_AREA
{
ULONG ControlWord;
ULONG StatusWord;
ULONG TagWord;
ULONG ErrorOffset;
ULONG ErrorSelector;
ULONG DataOffset;
ULONG DataSelector;
UCHAR RegisterArea[80];
ULONG Cr0NpxState;
} FLOATING_SAVE_AREA, *PFLOATING_SAVE_AREA;
#558BECFF750C5264FF350000000064892500000000FF7514FF7510FF750CFF75#
搜索到的代码 :
[Asm] 纯文本查看 复制代码
.text:7C923282 push ebp
.text:7C923283 mov ebp, esp
.text:7C923285 push [ebp+arg_4]
.text:7C923288 push edx ; 如果定义的异常处理handle中出错,这个handle是最终处理者
.text:7C923289 push large dword ptr fs:0
.text:7C923290 mov large fs:0, esp
.text:7C923297 push [ebp+arg_C]
.text:7C92329A push [ebp+arg_8]
.text:7C92329D push [ebp+arg_4]
.text:7C9232A0 push [ebp+arg_0]
.text:7C9232A3 mov ecx, [ebp+arg_10]
.text:7C9232A6 call ecx ; _except_handler(
.text:7C9232A6 ; struct _EXCEPTION_RECORD *ExceptionRecord,
.text:7C9232A6 ; void * EstablisherFrame,
.text:7C9232A6 ; struct _CONTEXT *ContextRecord,
.text:7C9232A6 ; void * DispatcherContext )
.text:7C9232A8 mov esp, large fs:0
.text:7C9232AF pop large dword ptr fs:0
.text:7C9232B6 mov esp, ebp
.text:7C9232B8 pop ebp
.text:7C9232B9 retn 14h
[Asm] 纯文本查看 复制代码
[/font][/color]
[color=black]/[/color][color=black]/ hook executehandler2
7C99AFC0 E8 00000000 call 7C99AFC5
7C99AFC5 5B pop ebx
7C99AFC6 83EB 05 sub ebx, 5
7C99AFC9 81EB 00060000 sub ebx, 600 //减出myexceptionhandle的地址
7C99AFCF 3B8B FC0F0000 cmp ecx, dword ptr [ebx+FFC] //比较是否是SE的SEHhandler
7C99AFD5 75 02 jnz short 7C99AFD9
7C99AFD7 8BCB mov ecx, ebx //是的话就HOOK,否则放过
7C99AFD9 33DB xor ebx, ebx
7C99AFDB FFD1 call ecx
7C99AFDD 64:8B25 0000000>mov esp, dword ptr fs:[0]
7C99AFE4 64:8F05 0000000>pop dword ptr fs:[0]
7C99AFEB 8BE5 mov esp, ebp
7C99AFED 5D pop ebp
7C99AFEE C2 1400 retn 14
E8 00 00 00 00 5B 83 EB 05 81 EB 00 0600 00 3B 8B FC 0F 00 00 75 02 8B CB 33 DB FF D1 64 8B 25 00 00 00 00 64 8F 05 00 00 00 00 8B E55D C2 14
[Asm] 纯文本查看 复制代码
// myexceptionhandle
00401000 60 pushad
00401001 E8 00000000 call 00401006
00401006 5B pop ebx
00401007 83EB 06 sub ebx, 6
0040100A 81C3 00080000 add ebx, 800
00401010 8BFB mov edi, ebx
00401012 8B7424 24 mov esi, dword ptr [esp+24] //pExceptionRecord
00401016 B9 06000000 mov ecx, 6
0040101B F3:A5 rep movs dword ptr es:[edi], dword ptr [esi]
0040101D 8BFB mov edi, ebx
0040101F 83C7 30 add edi, 30
00401022 8B7424 2C mov esi, dword ptr [esp+2C] //pContextRecord
00401026 B9 18000000 mov ecx, 18
0040102B F3:A5 rep movs dword ptr es:[edi], dword ptr [esi]
0040102D 8B83 90000000 mov eax, dword ptr [ebx+90]
00401033 8D8B A0000000 lea ecx, dword ptr [ebx+A0]
00401039 C1E0 02 shl eax, 2
0040103C 03C8 add ecx, eax
0040103E 8B7424 24 mov esi, dword ptr [esp+24]
00401042 8B46 0C mov eax, dword ptr [esi+C]
00401045 8901 mov dword ptr [ecx], eax
00401047 FF83 90000000 inc dword ptr [ebx+90]
0040104D 8D7B 30 lea edi, dword ptr [ebx+30]
00401050 8B83 E8070000 mov eax, dword ptr [ebx+7E8] //这里考虑可以在hook setthreadcontext中赋值[/color]
[color=black]
015A0056 8947 04 mov dword ptr [edi+4], eax
015A0059 8B83 EC070000 mov eax, dword ptr [ebx+7EC]
015A005F 8947 08 mov dword ptr [edi+8], eax
015A0062 8B83 F0070000 mov eax, dword ptr [ebx+7F0]
015A0068 8947 0C mov dword ptr [edi+C], eax
015A006B 8B83 F4070000 mov eax, dword ptr [ebx+7F4]
015A0071 8947 10 mov dword ptr [edi+10], eax
015A0074 8B83 F8070000 mov eax, dword ptr [ebx+7F8]
015A007A 8947 18 mov dword ptr [edi+18], eax
015A007D 8B4424 30 mov eax, dword ptr [esp+30]
015A0081 8B4C24 28 mov ecx, dword ptr [esp+28]
015A0085 8D7B 30 lea edi, dword ptr [ebx+30]
015A0088 50 push eax
015A0089 57 push edi
015A008A 51 push ecx
015A008B 53 push ebx
015A008C 8B8B FC070000 mov ecx, dword ptr [ebx+7FC]
015A0092 33C0 xor eax, eax
015A0094 33DB xor ebx, ebx
015A0096 33FF xor edi, edi
015A0098 33F6 xor esi, esi
015A009A FFD1 call ecx
015A009C 61 popad
015A009D C3 retn
60 E8 00 00 00 00 5B 83 EB 06 81 C3 0008 00 00 8B FB 8B 74 24 24 B9 06 00 00 00 F3 A5 8B FB 83 C7 30 8B 74 24 2C B9 18 00 00 00 F3 A58B 83 90 00 00 00 8D 8B A0 00 00 00 C1 E0 02 03 C8 8B 74 24 24 8B 46 0C 89 01 FF 83 90 00 00 008D 7B 30 8B 83 E8 07 00 00 89 47 04 8B 83 EC 07 00 00 89 47 08 8B 83 F0 07 00 00 89 47 0C 8B 83F4 07 00 00 89 47 10 8B 83 F8 07 00 00 89 47 18 8B 44 24 30 8B 4C 24 28 8D 7B 30 50 57 51 53 8B8B FC 07 00 00 33 C0 33 DB 33 FF 33 F6 FF D1 61 C3 00 00 Inline Hook ExecuteHandler2 Hook 在这里 .text:7C9232A6 call ecx 改 成 Jmp to // hook executehandler2
Patch vm:ds[imm]
这个地址是前面已经找到的,由于暂时还没找到 HASH 检测的代码位置,所以不能直接改代码,我的方法是,用脚本在这个位置上设置一个硬件执行断点在,当脚本断下后,脚本自动修改代码跳转到我们自己分配的空间内执行一段代码,在代码的开头立即修复之前被脚本改掉的代码,然后产生一个 single step 异常给系统,再执行之前由于改成跳转没有执行到的程序源码后跳转到原程序下一行继续。
这个方法是确定是要被占用一个硬件断点。
以下是我构造的代码:
[Asm] 纯文本查看 复制代码
//跳转来自0E8A7C8
00390100 60 pushad
00390101 BE 00023900 mov esi, 390200
00390106 BF C8A7E800 mov edi, 0E8A7C8
0039010B B9 05000000 mov ecx, 5
00390110 F3:A4 rep movs byte ptr es:[edi], byte ptr [esi] //还原ds[imm]被我修改用来跳转的代码
00390112 61 popad
00390113 9C pushfd \
00390114 66:810C24 0001 or word ptr [esp], 100 3条指令用来产生一个80000004 的 EXCEPTION_SINGLE_STEP
0039011A 9D popfd / 就是通过符号位中的单步标记来产生一个异常
0039011B 8B10 mov edx, dword ptr [eax] --读取数值,由于硬件执行断点的那行没有执行
0039011D - E9 A8A6AF00 jmp 乔巴.00E8A7CA --返回执行下一行代码
Patch Shadow_SetThreadContext
为了防止硬件断点被覆盖,这个 Shadow 函数必须 Patch 掉,当然,如果前面的部分没有 Patch 而直接来改这个函数, SE 就不会获得需要检测的异常,造成程序的退出。
1 、搜索代码
通过之前 patch Shadow_GetThreadContext 获得的调用 KiFastSystemCall 前代码区域,查找调用服务号为 0D5 的调用,
修改代码 只需要把跳入 KiFastSystemCall的位置直接 nop掉就行
FFE2 jmp edx// 这里是跳入 KiFastSystemCall ,直接 nop
Patch 掉 HASH 计算
通过上面几步,现在我可以下硬件断点了,能断下来的第一件事,当然是找 HASH 效验代码。
在前面 vm:ds[imm]的位置后面几行下个硬件读取断点 断下之后:
[Asm] 纯文本查看 复制代码
[font=Calibri][color=#000000].sedata:00E6323F push esi
.sedata:00E63240[/color][/font]
[font=Calibri][color=#000000].sedata:00E63240 loc_E63240: ; CODE XREF: .sedata:00E6324Ej
.sedata:00E63240 movzx esi, byte ptr [ecx+edx]
.sedata:00E63244 rol eax, 7 //断在这里
.sedata:00E63247 xor eax, esi
.sedata:00E63249 inc ecx[/color][/font]
[font=Calibri][color=#000000].sedata:00E6324A cmp ecx, [esp+8]
.sedata:00E6324E jb short loc_E63240
.sedata:00E63250 pop esi
.sedata:00E63251 jmp short locret_E6325C
Run Trace 后 获得整个循环体,直接找到跳出循环的位置 来到这里:
[Asm] 纯文本查看 复制代码
00E63247 Main xor eax, esi ; FL=PZ, EAX=F4E889ED, ECX=00093F9F, ESI=000000D0[/font][/color]
[color=#000000][font=Calibri]00E63250 Main pop esi ; ESP=0012FEC8, ESI=F4E889ED
00E63251 Main jmp short 00E6325C
00E6325C Main retn 4 ; ESP=0012FED0 //hash 值保存在eax
重新加载几次之后,发现 HASH计算的参数和结果是不变的,我只能说,呵呵
检测开始位置: 0E7C91D
检测大小: 0093F9F
返回 EAX : F4E889ED
//------ Fix HASH------
[Asm] 纯文本查看 复制代码
00E63240 B8 ED89E8F4 mov eax, F4E889ED
00E63245 90 nop
00E63246 90 nop
00E63247 90 nop
00E63248 90 nop
00E63249 90 nop
00E6324A 90 nop
00E6324B 90 nop
00E6324C 90 nop
00E6324D 90 nop
00E6324E 90 nop
00E6324F 90 nop
免费评分
查看全部评分
本帖被以下淘专辑推荐:
· 优秀逆向文 | 主题: 238, 订阅: 93
· 教程 | 主题: 123, 订阅: 88