Petite没什么Anti-Debug的手段,只是使用SEH来指令跳转。首先把Petite本身的程序手动脱壳,然后用IDA反汇编,可以看到有两套压缩引擎,对应参数即-0是一种,-1到-9是一种。
首先来看-0模式:
.data:00418632 lea ebx, [eax+1644h]
.data:00418638 push eax
.data:00418639 add byte ptr [esp], 0Ch
.data:0041863D push eax
.data:0041863E add byte ptr [esp], 46h
.data:00418642 push eax
.data:00418643 add byte ptr [esp], 65h
.data:00418647 push eax
.data:00418648 add byte ptr [esp], 0A1h
.data:0041864C push eax
.data:0041864D add byte ptr [esp], 0BFh
.data:00418651
.data:00418651 loc_418651:
.data:00418651
.data:00418651
.data:00418651 mov ecx, [ebx]
.data:00418653 add ebx, 14h
.data:00418656 mov edx, [ebx-10h]
.data:00418659 test edx, edx
.data:0041865B jz short loc_418651
.data:0041865D mov eax, [esp+18h]
.data:00418661 lea esi, [ecx+eax]
.data:00418664 mov ebp, [esp+1Ch]
.data:00418668 mov ebp, [ebp+8]
.data:0041866B mov ecx, [ebx-4]
.data:0041866E mov edi, ebp
.data:00418670 rep movsd
.data:00418672 mov esi, ebp
.data:00418674 mov edi, [ebx-0Ch]
.data:00418677 add edi, eax
.data:00418679 push ebx
.data:0041867A push edx
.data:0041867B push edi
.data:0041867C push ebp
.data:0041867D call sub_4186AF
.data:00418682 test eax, eax
.data:00418684 jz loc_418566
.data:0041868A pop eax
.data:0041868B pop eax
.data:0041868C pop eax
.data:0041868D pop ebx
.data:0041868E jmp short loc_418651
[eax+1644h]地址指向了一个压缩数据的表,每个表项具体结构如下:
struct Petite_Section
{
DWORD compressedRVA;
DWORD uncompressedSize;
DWORD orignalRVA;
DWORD zeroSizeOrExecBit;
DWORD compressedSize;//= RealcompressedSize/4
};
zeroSizeOrExecBit的含义是最低位为是否是可执行代码段的标志位。其他位是段中后部连续为0的个数。
sub_4186AF是解压缩函数,参数有9个。前三个分别是压缩数据的地址,解压后的地址,解压后的长度。第四个参数未使用,其他参数都是常量数组。
sub_4186AF代码非常复杂,暂时没有时间研究,写脱壳程序只要把这个代码抽出来放进自己的程序里即可。
需要注意的是sub_4186AF开始的一条指令用时要去掉。
.data:004186AF push ebp
.data:004186B0 mov ebp, esp
.data:004186B2 mov [edi], al ;去掉
.data:004186B4 sub esp, 0BAD8h
.data:004186BA lea ecx, [ebp+var_8078]
当把每一个压缩块都解压之后,作者在这里有一个小trick,使得要尝试写PE的头,PE的头默认是不可写得,因此会产生一个异常,指令就跳转到在事先已经设置好的SEH地址,即开始从下面的指令执行:
.data:00419AE0 loc_419AE0:
.data:00419AE0 call loc_419B34
.data:00419AE5
.data:00419AE5 loc_419AE5:
.data:00419AE5 pop edi
.data:00419AE6 rep stosb
.data:00419AE8 popa
.data:00419AE9 popfw
.data:00419AEB add esp, 8
.data:00419AEE jmp $+5
.data:00419AF3 aThisFileHasBee db 'This file has been tampered with and',0Ah
.data:00419AF3 db 'MAY BE INFECTED BY A VIRUS!',0
.data:00419B34
.data:00419B34 loc_419B34:
.data:00419B34 xor eax, eax
.data:00419B36 pop esi
.data:00419B37 mov ebx, fs:[eax]
.data:00419B3A mov ebx, [ebx]
.data:00419B3C lea esp, [ebx-2Ah]
.data:00419B3F pop ebp
.data:00419B40 lea ecx, [esi+2CBh]
.data:00419B46 mov [ebx+4], ecx
.data:00419B49 mov large fs:0, ebx
.data:00419B50 mov edi, [esp]
.data:00419B53 push dword ptr [edi+8]
.data:00419B56 call dword ptr [ebp+7A0h]
.data:00419B5C add edi, 3Dh
.data:00419B62 push 0Eh
.data:00419B64 pop ecx
.data:00419B65 rep movsb
.data:00419B67 push dword ptr [ebx]
.data:00419B69 push esi
.data:00419B6A push edi
.data:00419B6B lea esi, [edi+155h]
.data:00419B71 mov ecx, esi
.data:00419B73 sub ecx, edi
.data:00419B75 rep stosb
.data:00419B77 pusha
.data:00419B78 jmp eax
最后的jmp eax因为eax为0,就会产生第二次异常,然后就会跳转到下面代码:
.data:00419DB0 xor eax, eax
.data:00419DB2 mov ebx, fs:[eax]
.data:00419DB5 mov ebx, [ebx]
.data:00419DB7 lea esp, [ebx-52h]
.data:00419DBA popa
.data:00419DBB
.data:00419DBB loc_419DBB:
.data:00419DBB cmp dword ptr [esi], 0
.data:00419DBE jz loc_419B7A
.data:00419DC4 mov edi, [esi+8]
.data:00419DC7 add edi, ebp
.data:00419DC9 mov ecx, [esi+0Ch]
.data:00419DCC sar ecx, 1
.data:00419DCE push ecx
.data:00419DCF jb short loc_419DE6
.data:00419DD1
.data:00419DD1 loc_419DD1:
.data:00419DD1 add edi, [esi+4]
.data:00419DD4 sar ecx, 2
.data:00419DD7 xor eax, eax
.data:00419DD9 rep stosd
.data:00419DDB pop ecx
.data:00419DDC and ecx, 3
.data:00419DDF rep stosb
.data:00419DE1 add esi, 14h
.data:00419DE4 jmp short loc_419DBB
.data:00419DE6
.data:00419DE6 loc_419DE6:
.data:00419DE6 mov ebx, [esi+4]
.data:00419DE9 sub ebx, 6
.data:00419DEC xor edx, edx
.data:00419DEE
.data:00419DEE loc_419DEE:
.data:00419DEE
.data:00419DEE cmp edx, ebx
.data:00419DF0 jge short loc_419DD1
.data:00419DF2 mov al, [edx+edi]
.data:00419DF5 inc edx
.data:00419DF6 cmp al, 0E8h
.data:00419DF8 jz short loc_419E0C
.data:00419DFA cmp al, 0E9h
.data:00419DFC jz short loc_419E0C
.data:00419DFE cmp al, 0Fh
.data:00419E00 jnz short loc_419DEE
.data:00419E02 mov al, [edx+edi]
.data:00419E05 and al, 0F0h
.data:00419E07 cmp al, 80h
.data:00419E09 jnz short loc_419DEE
.data:00419E0B inc edx
.data:00419E0C
.data:00419E0C loc_419E0C:
.data:00419E0C
.data:00419E0C mov eax, [edx+edi]
.data:00419E0F
.data:00419E0F loc_419E0F:
.data:00419E0F cmp al, 0
.data:00419E11 jnz short loc_419DEE
.data:00419E13 shr ax, 8
.data:00419E17 rol eax, 10h
.data:00419E1A xchg al, ah
.data:00419E1C add edx, 4
.data:00419E1F sub eax, edx
.data:00419E21 mov [edx+edi-4], eax
.data:00419E25 jmp short loc_419DEE
这段代码来负责压缩表需要清零的数据,然后恢复可执行代码中为了提高压缩绿而处理的跳转指令。这个算法和UPX基本一样。
接下来就会做还原OEP的一部分工作:
data:00419B84 loc_419B84:
.data:00419B84 push 0
.data:00419B86 push ebx
.data:00419B87 xor ebx, ebx
.data:00419B89 push 1234
.data:00419B8E mov ecx, [esp]
.data:00419B91
.data:00419B91 loc_419B91:
.data:00419B91 bt ebx, 0
.data:00419B95 jb short loc_419BAD
.data:00419B97 mov esi, large fs:1Ch
.data:00419B9E btr esi, 0
.data:00419BA2 add esi, large fs:22h
.data:00419BA9 inc esi
.data:00419BAA xor bx, si
.data:00419BAD
.data:00419BAD loc_419BAD:
.data:00419BAD xor bl, [ecx+edx]
.data:00419BB0 rol ebx, 3
.data:00419BB3 dec ecx
.data:00419BB4 jge short loc_419B91
.data:00419BB6 lea ecx, [eax+3Bh]
.data:00419BB9 xor [ecx], ebx
.data:00419BBB xor [ecx+4], ebx
.data:00419BBE xor [ecx+8], ebx
.data:00419BC1 xor [ecx+0Ch], ebx
Petite对PE文件OEP的做了繁琐的算法保护。首先通过自身代码的校验和来还原跳转到OEP代码,但此时最后那个JMP指令的偏移量仍然是密文,需要后面继续计算。
之后,就到了关键的Import表处理过程:
.data:00419C02 lea esi, [ebp+12345678h]
.data:00419C08 lea ecx, [ebp+800h]
.data:00419C0E mov ebx, eax
.data:00419C10
.data:00419C10 loc_419C10:
.data:00419C10 cmp dword ptr [esi], 0
.data:00419C13 jz loc_419E27
.data:00419C19 push ecx
.data:00419C1A push ecx
.data:00419C1B call dword ptr [ebp+7A4h]
.data:00419C21 test eax, eax
.data:00419C23 jnz short loc_419C36
.data:00419C25 sub esp, 4
.data:00419C28 call dword ptr [ebp+790h]
.data:00419C2E test eax, eax
.data:00419C30 jz loc_419D15
.data:00419C36
.data:00419C36 loc_419C36:
.data:00419C36 mov edi, eax
.data:00419C38 add eax, [eax+3Ch]
.data:00419C3B mov eax, [eax+78h]
.data:00419C3E push dword ptr [eax+edi+18h]
.data:00419C42 mov ecx, [eax+edi+24h]
.data:00419C46 add ecx, edi
.data:00419C48 push ecx
.data:00419C49 mov ecx, [eax+edi+20h]
.data:00419C4D add ecx, edi
.data:00419C4F push ecx
.data:00419C50 push dword ptr [eax+edi+10h]
.data:00419C54 push dword ptr [eax+edi+14h]
.data:00419C58 mov eax, [eax+edi+1Ch]
.data:00419C5C add eax, edi
.data:00419C5E push eax
.data:00419C5F push esi
.data:00419C60 mov esi, [esi]
.data:00419C62 add esi, ebp
.data:00419C64
.data:00419C64 loc_419C64:
.data:00419C64
.data:00419C64 mov eax, [esi]
.data:00419C66 test eax, eax
.data:00419C68 jz loc_419CEF
.data:00419C6E jns short loc_419C9F
.data:00419C70 bt eax, 30
.data:00419C74 jb short loc_419C9F
.data:00419C76 movzx eax, ax
.data:00419C79 sub eax, [esp+0Ch]
.data:00419C7D jb loc_419D2E
.data:00419C83 cmp eax, [esp+8]
.data:00419C87 jnb loc_419D2E
.data:00419C8D shl eax, 2
.data:00419C90 add eax, [esp+4]
.data:00419C94 mov eax, [eax]
.data:00419C96 add eax, edi
.data:00419C98 mov [esi], eax
.data:00419C9A add esi, 4
.data:00419C9D jmp short loc_419C64
.data:00419C9F
.data:00419C9F loc_419C9F:
.data:00419C9F
.data:00419C9F add eax, esi
.data:00419CA1 push eax
.data:00419CA2 push eax
.data:00419CA3 push edi
.data:00419CA4 call dword ptr [ebp+794h]
.data:00419CAA test eax, eax
.data:00419CAC jz short loc_419D2D
.data:00419CAE
.data:00419CAE loc_419CAE:
.data:00419CAE dec dword ptr [esp+28h]
.data:00419CB2 jge short loc_419CD3
.data:00419CB4 mov edx, [esp+24h]
.data:00419CB8 mov byte ptr [edx], 0E9h
.data:00419CBB sub eax, edx
.data:00419CBD sub eax, 5
.data:00419CC0 mov [edx+1], eax
.data:00419CC3 mov eax, edx
.data:00419CC5 add edx, 5
.data:00419CC8 mov [esp+24h], edx
.data:00419CCC and edx, 7
.data:00419CCF mov [esp+28h], edx
.data:00419CD3
.data:00419CD3 loc_419CD3:
.data:00419CD3 mov [esi], eax
.data:00419CD5 xchg edi, [esp]
.data:00419CD8 or ecx, 0FFFFFFFFh
.data:00419CDB xor eax, eax
.data:00419CDD repne scasb
.data:00419CDF std
.data:00419CE0 not ecx
.data:00419CE2 dec edi
.data:00419CE3 rep stosb
.data:00419CE5 pop edi
.data:00419CE6 cld
.data:00419CE7 add esi, 4
.data:00419CEA jmp loc_419C64
.data:00419CEF
.data:00419CEF loc_419CEF:
.data:00419CEF pop esi
.data:00419CF0 add esp, 18h
.data:00419CF3 mov edx, [esi]
.data:00419CF5 add edx, ebp
.data:00419CF7 lea eax, [ebx+47h]
.data:00419CFA mov ecx, [esp+4]
.data:00419CFE
.data:00419CFE loc_419CFE:
.data:00419CFE cmp dword ptr [edx], 0
.data:00419D01 jz short loc_419D15
.data:00419D03 cmp ebx, [edx]
.data:00419D05 sbb dword ptr [eax], 0
.data:00419D08 cmp [edx], ecx
.data:00419D0A sbb dword ptr [eax], 0
.data:00419D0D add edx, 4
.data:00419D10 ror dword ptr [eax], 3
.data:00419D13 jmp short loc_419CFE
.data:00419D15
.data:00419D15 loc_419D15:
.data:00419D15
.data:00419D15 mov dword ptr [esi], 0
.data:00419D1B pop edi
.data:00419D1C or ecx, 0FFFFFFFFh
.data:00419D1F xor eax, eax
.data:00419D21 repne scasb
.data:00419D23 mov ecx, edi
.data:00419D25 add esi, 4
.data:00419D28 jmp loc_419C10
Petite清除了原来程序的IID表,只使用了对应IAT的一个表,压缩后的Import表的DLL名字和这个表依次对应。如果IAT是序号的话,loader自己从模块的地址空间获取地址。如果是名字指针的话,还原时需要加上当前的RVA再减2。如果以-m1参数压缩,loader还要把某些IAT地址抽走,至于抽走哪些地址,作者使用了一个简单而巧妙的算法来实现。然后根据IAT地址的个数和是否被抽走来解密OEP。
如果PE文件中包含重定向信息,而且并没有strip,loader将执行到如下代码:
.data:00419E27 lea ecx, [ebp+12345678h]
.data:00419E2D mov edi, ecx
.data:00419E2F mov edx, ebp
.data:00419E31
.data:00419E31 loc_419E31:
.data:00419E31
.data:00419E31 mov eax, [ecx]
.data:00419E33 test eax, eax
.data:00419E35 jz short loc_419E51
.data:00419E37 cmp al, 0FFh
.data:00419E39 jnz short loc_419E45
.data:00419E3B shr eax, 8
.data:00419E3E add edx, eax
.data:00419E40 add ecx, 4
.data:00419E43 jmp short loc_419E31
.data:00419E45
.data:00419E45 loc_419E45:
.data:00419E45 inc ecx
.data:00419E46 and eax, 0FFh
.data:00419E4B add edx, eax
.data:00419E4D add [edx], ebp
.data:00419E4F jmp short loc_419E31
这个重定向表的转换也和UPX类似。
最后loader将执行到OEP,壳的执行到此结束。
-1到-9模式:
除了压缩算法不一样之外,其他都基本一样。这个压缩算法类似于UPX的算法,作者把算法做了一些变化。但是作者实现的压缩算法确实很烂,-9模式好像进入死循环了,从来就没成功过。
还原:
Petite对资源没有进行太大动作,解压缩后无需再进行重建。
对于Import表,就是把IID表清除了,需要恢复这个表,IAT也做了简单的变换。再就是抽取了DLL的名字,只要把这个数据放到它添加的Section中即可。
对于重定向表,只要得到重定向的地址,即可恢复重定向表。
对于Export表,可以原地不动,因为如果Export表被抽走,
Petite对于OEP的保护最复杂,需要耐心的写逆算法。
其他的就是做一些Section大小的优化,因为本次比赛没要求,就不去实现了。