已测环境 :
XP SP2 ' SP3
原版 OD + StrongOD 0.4.1.716
这个壳最近好像很夯. 最常见到的帖子就是 "无法下断点"
壳用的方法虽非首创, 但用得好杀伤力很大.
研究一个壳若 *无法下断点 的话, 只能在上面飞来飞去的,
这是不行的, 没办法研究的.
后面会提供一个附件(一个Crackme), 正常 Run 起会有 8 个 Thread.
Main Thread 不说, 其它全是 Anti 调试.
这时刚好可先确定你的 OD 不下任何断点时, 按 F9 能执行起来才行.
否则请先检查你的 OD.
原理可能你下面的内容边看就能边了解到了.
----
开始
----
用OD载入后要怎么弄呢? 首先我们在 KiUserExceptionDispatcher 的第 1 条
指令按 F2 设软断, 按 F9 Run
第 1 次断下时, 此时 : [ESP+8]=80000003
此时模块己 Shadow 进来, 且尚未建立 Anti 的 Thread
第 2 次断下时, 此时 : [ESP+8]=80000004
看 CPU 窗口, 记下 Dr0~3 四个值.
看 Stack 窗口, 记下 [ESP+14] 值. 此样本为 42F765
这 5 个值为这 Case 专用的.
OD 重新载入 ...
断'停在第 1 次 KiUserExceptionDispatcher , 不要乱动.
完成下面5项,就任你调试了.
(1)
在内存里找 CreateThread 的 Shadow :
了他. 请将第一条指令改成 ret 18, 如 :
shadow_CreateThread 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
我是看到 nevsayno 的贴图才想到 一个选项 一个 Thread 的, 借
他的图贴一下 :)
(2)
在内存里(跟上面是同一段的)找 GetThreadContext 的 Shadow :
Shadow_GetThreadContext 00D488DD 8BFF mov edi, edi 00D488DF 55 push ebp 00D488E0 8BEC mov ebp, esp 00D488E2 FF75 0C push dword ptr [ebp+C] 00D488E5 FF75 08 push dword ptr [ebp+8] 00D488E8 FF15 EA0ED100 call [D10EEA] 00D488EE 85C0 test eax, eax 00D488F0 0F8C 57B60000 jl 00D53F4D 00D488F6 33C0 xor eax, eax 00D488F8 40 inc eax 00D488F9 5D pop ebp 00D488FA C2 0800 ret 8 ;jmp 00E41F90 00D488FD 90 nop 00D488FE 90 nop 00D488FF 90 nop 00D48900 90 nop 00D48901 90 nop
00E41F90 50 push eax 00E41F91 8B4424 0C mov eax, [esp+C] 00E41F95 8038 10 cmp byte ptr [eax], 10 00E41F98 75 16 jnz short 00E41FB0 00E41F9A 33D2 xor edx, edx 00E41F9C 8950 04 mov [eax+4], edx ;clr Dr0~3 00E41F9F 8950 08 mov [eax+8], edx 00E41FA2 8950 0C mov [eax+C], edx 00E41FA5 8950 10 mov [eax+10], edx 00E41FA8 52 push edx 00E41FA9 6A 04 push 4 ;Index 00E41FAB E8 457C9C7B call kernel32.TlsSetValue 00E41FB0 58 pop eax 00E41FB1 C2 0800 ret 8
壳总是会判断这 TlsValue 是否等于 Dr0+Dr1+Dr2+Dr3 之 Total 值
我们在壳欲取得 Drx 的值时,将之清为 0,并设 TlsValue 为 0
至于 SetTlsValue 的 Index 应为多少才对, 很多方法可以得知.
例如: 断 Shadow 的 SetTlsValue
我这里 XP SP2 用的 Index 是 4 , XP SP3 则是 6
注: 因为这样本只有 Anti Debugger 时会调用 Shadow_GetThreadContext
所以若新代码你不想用串接的话,直接覆盖也行.
(3)
在内存里找 SetThreadContext 的 Shadow :
并将开头改为:
mov al, 1 ret 8
若不了这函数的话,到时可能我们自己调试用的硬断会被抢走.
(4)
之前我们有记下一个值为 42F765, 其实这是 VM 的 ds:[imm] 指令 :
vm.ds:[imm] 0042F763 8B01 mov eax, [ecx] ;jmp 00534FC5 0042F765 8D1C33 lea ebx, [ebx+esi] 0042F768 ^ 7E B9 jle short 0042F723 0042F76A ^ 7F CB jg short 0042F737
这里的 4 个 cmp , 为一开头我们记录下来的那 Dr0~3
00534FC5 81F9 C4754000 cmp ecx, 004075C4 00534FCB 74 18 je short 00534FE5 00534FCD 81F9 49754000 cmp ecx, 00407549 00534FD3 74 10 je short 00534FE5 00534FD5 81F9 B4744000 cmp ecx, 004074B4 00534FDB 74 08 je short 00534FE5 00534FDD 81F9 AF744000 cmp ecx, 004074AF 00534FE3 75 09 jnz short 00534FEE 00534FE5 9C pushfd ;/ 00534FE6 66:810C24 0001 or word ptr [esp], 100 ;手动产生 80000004 中断 00534FEC 9D popfd ;\ 00534FED 90 nop 00534FEE 8B01 mov eax, [ecx] ;补上原指令 00534FF0 8D1C33 lea ebx, [ebx+esi] ;补上原指令 00534FF3 ^ E9 70A7EFFF jmp 0042F768 ;跳回
原因:
壳总是会设那 4 个值到 Dr0~3, 并设 Dr7 为 33335555h
意思是只要读取那 4 处, 就会产生 80000004 的例外,
壳特意去读取时,若没发生 80000004 的话是不行的.
这就像是壳自己给自己下硬断' 给自己调试一样
当你 OD 设了断点断下来后,任何将控制权再交给 Target 的动作前,
OD 都会用 User 设的值(没用到的则是零)重新设定到 Dr0~3 ,导致
壳在该断的地方没发生中断,这就是很多人讲的:只要一断不管什么断
,就没办法正常 Run 下去了的原因.
一开头被我们删掉的 7 个 Thread 里也充满了这些对 Drx 的占用.
(5)
改 VM 的 rdtsc 指令
vm.rdtsc 原样为 :
rdtsc sub ebp, 8 mov [ebp+0], edx mov [ebp+4], eax
00437C98 8D2424 lea esp, [esp] ;改为xor eax,eax / nop 00437C9B 895500 mov [ebp], edx
原因:
壳总是利用 rdtsc.eax 来乱数做内存校验
我们有改了 ds:[imm], 而且我们可能日后会下一大堆软件断点(CC)
改了后,他就只固定去校验第0小块.
你可完全的分析此壳了.
不信你看! 试着在 401020 下硬件写入断点. 在断下几次后, 看到壳在填真正代码了, 如下 :
00401000 E8 FD FB 11 00 C3 90 90 90 90 90 90 90 90 90 90 00401010 B8 D8 21 40 00 C3 90 90 90 90 90 90 90 90 90 90 00401020 56 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00401000 E8 FDFB1100 call 00520C02 00401005 C3 ret 00401006 90 nop 00401007 90 nop 00401008 90 nop 00401009 90 nop 0040100A 90 nop 0040100B 90 nop 0040100C 90 nop 0040100D 90 nop 0040100E 90 nop 0040100F 90 nop 00401010 B8 D8214000 mov eax, 004021D8 00401015 C3 ret 00401016 90 nop
最后同样的,让我们期待大牛的脱壳脚本吧.
注: 针对 "无法下断点" 这词 :
若你当平常的壳在调试的话, 调试过程你会有二种现象 :
1. 你下了断点, 也断了下来, 也能 F9 成功 Run 起.
不要高兴, 这是因为壳尚未起动 Anti.
2. 若壳的 Anti 起动了的话, 你的断点断了下来, 但你已无法正常执行了
可能会得到 "内部错误" 之类的讯息.
3. 若内存校验被检出的话,可能直接 Crash .
听说 Shielden 欲加壳得连网才能加,所以我也没用过.
手上就只有同学给我的这个样本(Crackme),虽然我已尽量外观一点分析,但可能
也有所漏的情况.所以...若有任何错误或其它情况,请跟帖指正.
一分钱一分贷, 若嫌麻烦...花钱买滴蜡吧.
thx & 阿门!