镇北看雪 发表于 2020-8-26 02:28

AntiCrack Protector 1.0x -> RISCO Software Inc脱壳详解

本帖最后由 镇北看雪 于 2020-8-26 16:57 编辑

# 写在前面
***
ACProtect 1.x的壳,此壳所用的防脱手段有Anti Debug,Anti Dump,stolen bytes以及检测检测断点和IAT加密等。在脱此壳的时候可以利用OD脚本辅助完成一些操作,下面我们就一起来看看这个壳。

# 分析工具
***
* OD
* ImportRE
* LordPE
*运行环境虚拟机 Windows XP

# 去除外壳
***
脱壳一般分三个步骤,寻找OEP,dump程序,重建输入表。
## 寻找OEP
我们用OD加载程序后直接运行,结果程序直接结束运行。


所以我们怀疑应该是有反调试存在,我们利用插件HideOD配置好反反调试选项,然后运行程序发现程序没有结束运行。



接着我们发现程序有多处异常,所以我们利用最后一次异常法来寻找OEP。我们多次Shift+F9后使程序运行起来后打开日志记录窗口,发现最后一次异常发生的地址是0x47108E。



我们重新用OD加载程序,然后多次Shift+F9后来到最后一次异常处。



然后我们对主模块的text代码段设置内存访问断点。然后我们Shift+F9运行程序,程序停在了OEP处。但是我们发现OEP处的代码并不是正常的入口点代码,所以壳应该是采用了stolen bytes手段,此OEP为假的OEP。



我们需要找到被窃取的代码并将其恢复,进一步获取程序真正的OEP。

### 解决stolen bytes

我们重新加载程序来到最后一次异常处,在异常处理程序下断点并来到异常处理程序中。单步往下跟我们发现其调用ZwContext()函数的context参数的eip为0x471090,刚好为刚刚发生异常时的下一条指令的地址。



我们在此地址处下断点,运行程序来到此处。我们设置跟踪暂停条件为`push ebp 和 popad`即遇到这两条指令就停止跟踪。



设置好之后我们Ctrl+F11跟踪步入,结果我们发现指令太对很长时间之后程序还是没有停止。我们需要换一种思路。因为我们是为了找到被窃取的代码,而在壳代码空间中执行被窃取的代码之前一定会进行popad恢复寄存器环境,所以我们利用ESP定律来寻找被窃取的代码。首先我们重新加载程序查看push后栈指针的值,我们发现执行完push后esp为0x12FFA4。



我们再次运行程序来到最后一次异常发生后的下一条指令处,我们对0x12FFA4下硬件访问断点。然后我们运行程序,发现程序在执行了一条popad指令后停了下来。



而且我们发现在执行完popad指令程序停下来后,下一条即将执行的指令为push ebp,很像程序真正的入口点代码。我们向下运行验证此处是否为被窃取的代码,F9运行数次后其到了假的OEP跳转处,所以说明此处就是被窃取的代码。



我们分析执行完第一次popad后的被窃取的入口点代码,我们发现在执行完`push ebpmov ebp,esp push -0x1`后会继续pushad保存寄存器环境等到要跳到假的OEP时在popad。`push ebpmov ebp,esp push -0x1`此代码也就是被窃取的代码。对应的机器码为:
```
55 8B EC 6A FF
```


```
00485AF2    61            popad
00485AF3    55            push ebp
00485AF4    8BEC            mov ebp,esp
00485AF6    6A FF         push -0x1
00485AF8    90            nop
00485AF9    60            pushad
00485AFA    60            pushad
00485AFB    E8 00000000   call 00485B00                            ; UnPackMe.00485B00
00485B00    5E            pop esi
00485B01    83EE 06         sub esi,0x6
00485B04    B9 35000000   mov ecx,0x35
00485B09    29CE            sub esi,ecx
00485B0B    BA FDD5D6E3   mov edx,0xE3D6D5FD
00485B10    C1E9 02         shr ecx,0x2
00485B13    83E9 02         sub ecx,0x2


```

有以上代码我们还可以知道:
1.在从外壳的EP到原程序的OEP过程中可能不止一次pushad,popad 只要保证堆栈平衡就行。
2.stolen bytes的入口点代码不一定要紧挨着假的OEP,有可能中间会执行一些垃圾指令。

我们接着运行程序来到假的OEP处,将被窃取的指令的机器码粘贴到假的OEP上方。最后得到真正的OEP的RVA为:0x271B0



## DUMP程序
利用LordPE工具对正在调试的程序进行完整转存。



## 重建输入表
我们首先查找程序的IAT在哪里。我们在入口点并没有直接看到API的调用指令,但是我们发现可以指令。
```
004271D6    FF15 DC0A4600   call Xdword ptr ds:         


```
我们在运行来到0x460ADC处,然后发现此处实际是进行API调用。我们得出程序的IAT被加密,IAT地址被替换为壳代码自己的地址,然后让壳代码来调用API。



我们这里可以利用OD脚本将地址写回到原处,还可以逆向壳代码的IAT重定向逻辑,通过修改关键跳转来达到跳过IAT重定向的目的。
如果采用OD脚本,则脚本代码如下:

```
start:                                       
mov dwAddress1,0460974

word1:
mov dwAddress2,,4
mov dwNum1,,4
mov dwNum2,,4
xor dwNum1,dwNum2
mov ,dwNum1

add dwAddress1,4
cmp dwAddress1,0460BA0
jbe word1

mov dwAddress1,0460BFC

word2:
mov dwAddress2,,4
mov dwNum1,,4
mov dwNum2,,4
xor dwNum1,dwNum2
mov ,dwNum1

add dwAddress1,4
cmp dwAddress1,0460E84
jbe word2
```

如果采用逆向IAT重定向逻辑的方法,我们需要重新加载程序。因为由上面分析可得IAT地址0x460ADC的内容会被重定向,所以我们对此处下内存写入断点。运行程序我们可以来到壳代码的IAT加密处,代码主要逻辑如下。
```
004742A7    8985 20404000             mov dword ptr ss:,eax
004742AD    C785 24404000 00000000    mov dword ptr ss:,0x0
004742B7    8B95 28404000             mov edx,dword ptr ss:      ; edx = 0x00400000
004742BD    8B06                      mov eax,dword ptr ds:               ; eax = IID的RVA( INT )
004742BF    0BC0                      or eax,eax
004742C1    75 07                     jnz X004742CA                            ; if INT不为空
004742C3    90                        nop
004742C4    90                        nop
004742C5    90                        nop
004742C6    90                        nop
004742C7    8B46 10                   mov eax,dword ptr ds:
004742CA    03C2                      add eax,edx                              ; eax = INT RVA + 0x00400000
004742CC    0385 24404000             add eax,dword ptr ss:      ; eax = eax + 4
004742D2    8B18                      mov ebx,dword ptr ds:
004742D4    8B7E 10                   mov edi,dword ptr ds:          ; edi = IID的FirstThunk字段de RVA
004742D7    03FA                      add edi,edx                              ; edi = edi + 0x00400000
004742D9    03BD 24404000             add edi,dword ptr ss:      ; edi = edi + 4
004742DF    85DB                      test ebx,ebx
004742E1    0F84 FA000000             je 004743E1
004742E7    F7C3 00000080             test ebx,0x80000000
004742ED    75 1D                     jnz X0047430C
004742EF    90                        nop
004742F0    90                        nop
004742F1    90                        nop
004742F2    90                        nop
004742F3    03DA                      add ebx,edx                              ; ebx = ebx + 0x00400000
004742F5    83C3 02                   add ebx,0x2                              ; ebx = ebx + 2
004742F8    56                        push esi
004742F9    57                        push edi
004742FA    50                        push eax
004742FB    8BF3                      mov esi,ebx                              ; 得到API的名称字符串
004742FD    8BFB                      mov edi,ebx
004742FF    AC                        lods byte ptr ds:
00474300    C0C0 03                   rol al,0x3
00474303    AA                        stos byte ptr es:
00474304    803F 00                   cmp byte ptr ds:,0x0
00474307^ 75 F6                     jnz X004742FF
00474309    58                        pop eax
0047430A    5F                        pop edi
0047430B    5E                        pop esi
0047430C    81E3 FFFFFF0F             and ebx,0xFFFFFFF
00474312    53                        push ebx
00474313    FFB5 20404000             push dword ptr ss:
00474319    FF95 68C24100             call Xdword ptr ss:      ; GetProcAddress( )
0047431F    3B9D 28404000             cmp ebx,dword ptr ss:
00474325    7C 0F                     jl X00474336
00474327    90                        nop
00474328    90                        nop
00474329    90                        nop
0047432A    90                        nop
0047432B    60                        pushad
0047432C    2BC0                      sub eax,eax
0047432E    8803                      mov byte ptr ds:,al               ; 清除,破坏INT。使其为空
00474330    43                        inc ebx
00474331    3803                      cmp byte ptr ds:,al
00474333^ 75 F9                     jnz X0047432E
00474335    61                        popad
00474336    0BC0                      or eax,eax
00474338^ 0F84 2EFFFFFF             je 0047426C                              ; 如果GetProcAddress( )获得地址失败则跳转
0047433E    3B85 78C24100             cmp eax,dword ptr ss:
00474344    75 0A                     jnz X00474350
00474346    90                        nop
00474347    90                        nop
00474348    90                        nop
00474349    90                        nop
0047434A    8D85 CB454000             lea eax,dword ptr ss:
00474350    56                        push esi
00474351    FFB5 20404000             push dword ptr ss:         ; 判断DLL的基地址是否为0x7C800000
00474357    5E                        pop esi                                  ; 0x7C800000地址为kernel32.dll的基地址
00474358    39B5 E9204000             cmp dword ptr ss:,esi      ; if (esi == 0x7C800000)   就跳转
0047435E    74 15                     je X00474375
00474360    90                        nop
00474361    90                        nop
00474362    90                        nop
00474363    90                        nop                                    ; 判断DLL的基地址是否为0x77D10000
00474364    39B5 ED204000             cmp dword ptr ss:,esi      ; 0x77D10000地址为USER32.dll的基地址
0047436A    74 09                     je X00474375                           ; if (esi == 0x77D10000)   就跳转
0047436C    90                        nop
0047436D    90                        nop
0047436E    90                        nop
0047436F    90                        nop
00474370    EB 60                     jmp X004743D2
00474372    90                        nop
00474373    90                        nop
00474374    90                        nop
00474375    80BD 87A34000 00          cmp byte ptr ss:,0x0       ; if ( ss: == 0)就跳转
0047437C    74 54                     je X004743D2
0047437E    90                        nop
0047437F    90                        nop
00474380    90                        nop
00474381    90                        nop
00474382    EB 07                     jmp X0047438B
00474384    90                        nop
00474385    90                        nop
00474386    90                        nop
00474387    0100                      add dword ptr ds:,eax
00474389    0000                      add byte ptr ds:,al
0047438B    8BB5 ED404000             mov esi,dword ptr ss:
00474391    83C6 0D                   add esi,0xD
00474394    81EE C71F4000             sub esi,0x401FC7
0047439A    2BF5                      sub esi,ebp
0047439C    83FE 00                   cmp esi,0x0
0047439F    7F 31                     jg X004743D2                           ; UnPackMe.004743D2
004743A1    90                        nop
004743A2    90                        nop
004743A3    90                        nop
004743A4    90                        nop
004743A5    8BB5 ED404000             mov esi,dword ptr ss:
004743AB    53                        push ebx
004743AC    50                        push eax
004743AD    0F31                      rdtsc
004743AF    8BD8                      mov ebx,eax
004743B1    58                        pop eax
004743B2    33C3                      xor eax,ebx
004743B4    C606 68                   mov byte ptr ds:,0x68
004743B7    8946 01                   mov dword ptr ds:,eax
004743BA    C746 05 81342400          mov dword ptr ds:,0x243481
004743C1    895E 08                   mov dword ptr ds:,ebx
004743C4    C646 0C C3                mov byte ptr ds:,0xC3
004743C8    5B                        pop ebx
004743C9    8BC6                      mov eax,esi
004743CB    8385 ED404000 0D          add dword ptr ss:,0xD
004743D2    5E                        pop esi
004743D3    8907                      mov dword ptr ds:,eax               ; 修正输入地址表IAT的值
004743D5    8385 24404000 04          add dword ptr ss:,0x4      ; 指向下一个IAT地址项
004743DC^ E9 D6FEFFFF               jmp 004742B7


```

由上述壳代码的IAT加密逻辑可得,修改1的je跳转为jmp,或者修改2的je跳转为jmp。从而跳过IAT的重定向


需要注意的是MessageBoxA( )函数被特殊对待,其IAT地址被篡改为了0x46E5C8,我们需要手动帮其更改。
```
0047433E    3B85 78C24100             cmp eax,dword ptr ss:      ; if (eax == MessageBoxA)
00474344    75 0A                     jnz X00474350                            ; 不跳转
00474346    90                        nop
00474347    90                        nop
00474348    90                        nop
00474349    90                        nop
0047434A    8D85 CB454000             lea eax,dword ptr ss:      ; eax = 0x46E5C8
00474350    56                        push esi

```

我们在OD中Ctrl + G搜索MessageBoxA得到其地址为0x77D507EA,找到USER32.dll对应的被篡改的IAT地址,将其更改为0x77D507EA。




然后我们运行程序来到假的OEP处,我们可以看到IAT已经修复完成。



然后我们用工具ImportRE来重建输入表,输入OEP的RVA为0x271B0,IAT的RVA为0x60818.获取输入表并截切掉无效的数据,最终得到IAT正确的数据。


我们修复前面dump的文件后,运行修复后的文件结果程序崩溃了。我们用OD打开程序单步来到崩溃处,发现崩溃在一处跳转处,此跳转调到0x17XXXX地址处。


而程序并无0x17XXXX区块,说明此地址应该是壳动态申请的,而将壳脱去后此动态地址的区段没有被dump。以此达到反dump的目的。


### 解决Anti dump

指令字节数相同。所以我们可以用这几个需要执行的指令的自己码替代jmp跳转。(如果这里待执行指令字节数大于jmp指令,我们就需要另行自己寻找空间来存放这些指令)


这里我们用OD脚本来完成这项操作,脚本代码如下非常简单。
```
start:

mov s,0167d58
mov d,046c0f5
mov c,016862d



word:
mov ss,,1
mov ,ss,1
inc d
inc s
cmp s,c
jbe word

ret
```

运行脚本之后jmp指令就会被替换为待执行指令然后我们在dump,之后在修复dump。



最后我们运行修复的程序,运行成功。



# 总结
***
此壳主要还是在解决Anti dump和stolen bytes上。其中还要注意对壳代码MessageBox的特殊对待,如果没有去逆向壳的IAT加密逻辑而直接将函数地址粘贴到IAT处就会忽略MessageBox的修复。

镇北看雪 发表于 2020-8-26 03:04

米粒米粒 发表于 2020-8-26 03:02
是这个论坛的爱盘吗

就是那个论坛的爱盘,实在没有也可以直接百度

米粒米粒 发表于 2020-8-26 02:58

请问楼主图中的工具在哪里下载

镇北看雪 发表于 2020-8-26 03:00

米粒米粒 发表于 2020-8-26 02:58
请问楼主图中的工具在哪里下载

爱盘,看雪工具箱都可以

米粒米粒 发表于 2020-8-26 03:02

镇北看雪 发表于 2020-8-26 03:00
爱盘,看雪工具箱都可以

是这个论坛的爱盘吗

lingyinGR 发表于 2020-8-26 05:40

感谢分享!

andyyang886 发表于 2020-8-26 09:47

I just want to know WTF

gzwzj 发表于 2020-8-26 10:33

脱壳是我感觉最难的破解手段,太难跟了

727426900 发表于 2020-8-26 11:00

是这个论坛的爱盘吗

镇北看雪 发表于 2020-8-26 11:15

727426900 发表于 2020-8-26 11:00
是这个论坛的爱盘吗

就是论坛的爱盘
页: [1] 2
查看完整版本: AntiCrack Protector 1.0x -> RISCO Software Inc脱壳详解