05 初识加壳脱壳
(很久以前的学习记录,放到博客上来)
1.IsDebuggerPresent()函数(CheckRemoteDebuggerPresent())
确定调用进程是否由用户模式的调试器调试.
确定调用进程是否由用户模式的调试器调试.
#include "stdafx.h"
#include<windows.h>
#include<stdio.h>
int main()
{
if (IsDebuggerPresent())
{
printf("Debugger!\n");
}
else
{
printf("no debugger\n");
}
return 0;
}
VS2015中运行:
ollydbg中运行:
--------------------------------------------------------------------------------------------------
2.加壳与脱壳
源代码如下:
#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
if (argc < 2) {
fprintf(stderr, "$packed.exe <password>\n");
return 1;
}
if (IsDebuggerPresent()) {
printf("on debugger\n");
return -1;
}
else {
if (strcmp(argv[1], "unpacking") == 0) {
printf("correct!\n");
}
else {
printf("auth error\n");
return -1;
}
}
getchar();
return 0;
}
exe文件(未加壳)拖到IDA中反汇编:
.text:00411830 ; int __cdecl main(int argc, char **argv)
.text:00411830 _main proc near ; CODE XREF: j__mainj
.text:00411830
.text:00411830 var_C0 = byte ptr -0C0h
.text:00411830 argc = dword ptr 8
.text:00411830 argv = dword ptr 0Ch
.text:00411830
.text:00411830 push ebp
.text:00411831 mov ebp, esp
.text:00411833 sub esp, 0C0h
.text:00411839 push ebx
.text:0041183A push esi
.text:0041183B push edi
.text:0041183C lea edi, [ebp+var_C0]
.text:00411842 mov ecx, 30h
.text:00411847 mov eax, 0CCCCCCCCh
.text:0041184C rep stosd
.text:0041184E cmp [ebp+argc], 2
.text:00411852 jge short loc_41187D
.text:00411854 push offset _Format ; "$packed.exe <password>\n"
.text:00411859 mov esi, esp
.text:0041185B push 2
.text:0041185D call ds:__imp____acrt_iob_func
.text:00411863 add esp, 4
.text:00411866 cmp esi, esp
.text:00411868 call j___RTC_CheckEsp
.text:0041186D push eax ; _Stream
.text:0041186E call j__fprintf
.text:00411873 add esp, 8
.text:00411876 mov eax, 1
.text:0041187B jmp short loc_4118F6
.text:0041187D ; ---------------------------------------------------------------------------
.text:0041187D
.text:0041187D loc_41187D: ; CODE XREF: _main+22j
.text:0041187D mov esi, esp
.text:0041187F call ds:__imp__IsDebuggerPresent@0 ; IsDebuggerPresent()
.text:00411885 cmp esi, esp
.text:00411887 call j___RTC_CheckEsp
.text:0041188C test eax, eax
.text:0041188E jz short loc_4118A4
.text:00411890 push offset aOnDebugger ; "on debugger\n"
.text:00411895 call j__printf
.text:0041189A add esp, 4
.text:0041189D or eax, 0FFFFFFFFh
.text:004118A0 jmp short loc_4118F6
.text:004118A2 ; ---------------------------------------------------------------------------
.text:004118A2 jmp short loc_4118E5
.text:004118A4 ; ---------------------------------------------------------------------------
.text:004118A4
.text:004118A4 loc_4118A4: ; CODE XREF: _main+5Ej
.text:004118A4 push offset Str2 ; "unpacking"
.text:004118A9 mov eax, 4
.text:004118AE shl eax, 0
.text:004118B1 mov ecx, [ebp+argv]
.text:004118B4 mov edx, [ecx+eax]
.text:004118B7 push edx ; Str1
.text:004118B8 call j__strcmp
.text:004118BD add esp, 8
.text:004118C0 test eax, eax
.text:004118C2 jnz short loc_4118D3
.text:004118C4 push offset aCorrect ; "correct!\n"
.text:004118C9 call j__printf
.text:004118CE add esp, 4
.text:004118D1 jmp short loc_4118E5
.text:004118D3 ; ---------------------------------------------------------------------------
.text:004118D3
.text:004118D3 loc_4118D3: ; CODE XREF: _main+92j
.text:004118D3 push offset aAuthError ; "auth error\n"
.text:004118D8 call j__printf
.text:004118DD add esp, 4
.text:004118E0 or eax, 0FFFFFFFFh
.text:004118E3 jmp short loc_4118F6
.text:004118E5 ; ---------------------------------------------------------------------------
.text:004118E5
.text:004118E5 loc_4118E5: ; CODE XREF: _main+72j
.text:004118E5 ; _main+A1j
.text:004118E5 mov esi, esp
.text:004118E7 call ds:__imp__getchar
.text:004118ED cmp esi, esp
.text:004118EF call j___RTC_CheckEsp
.text:004118F4 xor eax, eax
.text:004118F6
.text:004118F6 loc_4118F6: ; CODE XREF: _main+4Bj
.text:004118F6 ; _main+70j ...
.text:004118F6 pop edi
.text:004118F7 pop esi
.text:004118F8 pop ebx
.text:004118F9 add esp, 0C0h
.text:004118FF cmp ebp, esp
.text:00411901 call j___RTC_CheckEsp
.text:00411906 mov esp, ebp
.text:00411908 pop ebp
.text:00411909 retn
.text:00411909 _main endp
这段汇编代码开起来简明清晰。
接下来用UPX为exe文件加壳(用cmd命令行执行的时候被“拒绝访问”了,最后直接拿exe文件往upx.exe里拖,压缩成功了):
压缩过的exe文件在IDA中的流程视图:
里面的代码也变成这样的了(截取) :
UPX1:004204C0 ; ---------------------------------------------------------------------------
UPX1:004204C2 align 8
UPX1:004204C8
UPX1:004204C8 loc_4204C8: ; CODE XREF: start:loc_4204D9j
UPX1:004204C8 mov al, [esi]
UPX1:004204CA inc esi
UPX1:004204CB mov [edi], al
UPX1:004204CD inc edi
UPX1:004204CE
UPX1:004204CE loc_4204CE: ; CODE XREF: start+B6j
UPX1:004204CE ; start+CDj
UPX1:004204CE add ebx, ebx
UPX1:004204D0 jnz short loc_4204D9
UPX1:004204D2
UPX1:004204D2 loc_4204D2: ; CODE XREF: start+10j
UPX1:004204D2 mov ebx, [esi]
UPX1:004204D4 sub esi, 0FFFFFFFCh
UPX1:004204D7 adc ebx, ebx
UPX1:004204D9
UPX1:004204D9 loc_4204D9: ; CODE XREF: start+20j
UPX1:004204D9 jb short loc_4204C8
UPX1:004204DB mov eax, 1
UPX1:004204E0
UPX1:004204E0 loc_4204E0: ; CODE XREF: start+3Fj
UPX1:004204E0 ; start+4Aj
UPX1:004204E0 add ebx, ebx
UPX1:004204E2 jnz short loc_4204EB
UPX1:004204E4 mov ebx, [esi]
UPX1:004204E6 sub esi, 0FFFFFFFCh
UPX1:004204E9 adc ebx, ebx
UPX1:004204EB
UPX1:004204EB loc_4204EB: ; CODE XREF: start+32j
UPX1:004204EB adc eax, eax
UPX1:004204ED add ebx, ebx
UPX1:004204EF jnb short loc_4204E0
UPX1:004204F1 jnz short loc_4204FC
UPX1:004204F3 mov ebx, [esi]
UPX1:004204F5 sub esi, 0FFFFFFFCh
UPX1:004204F8 adc ebx, ebx
UPX1:004204FA jnb short loc_4204E0
(现在想要去解压缩,因为upx命令行输入总是被拒绝访问,解决无果,无奈发现好像存在带有窗口界面的upx,按百度排序的第一个下载——全是一群流氓,捆绑一堆软件安装,百度流氓头子!最后下载的upx还总是error,最后在360上查了下,发现360竟然有这款软件,UPX Easy GUI,今天感谢360的良心!)
Depreess脱壳后的exe文件不如原来那样清晰直观了:
流程试图:
反汇编代码(截取):
--------------------------------------------------------------------------------------------------------
3.尝试手动脱壳
在ollydbg中打开upx加壳过的exe文件,开头是这样的:
首先PUSHAD将所有寄存器的值复制都栈中,F8单步执行,不远处进入了一个人循环:
(PUSHAD指令在堆栈中按顺序压入下列寄存器:EAX,ECX,EDX,EBX,ESP,EBP,ESI和EDI.
POPAD指令则是PUSHAD指令的逆操作。POPAD指令按照与上面相反的顺序依次弹出寄存器的值。
顺序为 EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX.)
这里的逻辑就是从esi中向edi中拷贝数据,
还可以从窗口中看到edi的地址是从00FC03F0开始的。
向下找到 POPAD命令,F2设置断点,F9执行到断点处。
00FC056B . 61 POPAD
00FC056C . 8D4424 80 LEA EAX,DWORD PTR SS:[ESP-80]
00FC0570 > 6A 00 PUSH 0
00FC0572 . 39C4 CMP ESP,EAX
00FC0574 .^75 FA JNZ SHORT Pack.00FC0570
00FC0576 . 83EC 80 SUB ESP,-80
00FC0579 .-E9 CD0AFFFF JMP Pack.00FB104B
执行到JMP Pack.00FB104B,
跳转到了:
这时打开菜单栏上Plug_in > OllyDump > Dump debugged process,进行脱壳,但是......多歧路今安在啊!总是无法读内存,显示“Bad DOS Signature”,我也很无奈,尚未解决。
按道理, pushad和popad之间的代码就是解压缩,在解压缩之前,先将寄存器状态保存到栈,解压缩结束之后在从栈中读取,恢复寄存器状态,从而运行真正的代码。
所以手动脱壳,关键在于找到 解压结束,程序开始的瞬间。
(现在想要去解压缩,因为upx命令行输入总是被拒绝访问,解决无果,无奈发现好像存在带有窗口界面的upx,按百度排序的第一个下载——全是一群流氓,捆绑一堆软件安装,百度流氓头子!最后下载的upx还总是error,最后在360上查了下,发现360竟然有这款软件,UPX Easy GUI,今天感谢360的良心!)
Depreess脱壳后的exe文件不如原来那样清晰直观了:
流程试图:
反汇编代码(截取):
.text:0041104B ; [00000005 BYTES: COLLAPSED FUNCTION start. PRESS CTRL-NUMPAD+ TO EXPAND]
.text:00411050 ; [00000005 BYTES: COLLAPSED FUNCTION j_wcscpy_s. PRESS CTRL-NUMPAD+ TO EXPAND]
.text:00411055 ; ---------------------------------------------------------------------------
.text:00411055 jmp sub_414E40
.text:0041105A
.text:0041105A ; =============== S U B R O U T I N E =======================================
.text:0041105A
.text:0041105A ; Attributes: thunk
.text:0041105A
.text:0041105A sub_41105A proc near ; CODE XREF: .text:00411C1Ap
.text:0041105A ; .text:00411C4Ap
.text:0041105A jmp sub_411BF0
.text:0041105A sub_41105A endp
.text:0041105A
.text:0041105F
.text:0041105F ; =============== S U B R O U T I N E =======================================
.text:0041105F
.text:0041105F ; Attributes: thunk
.text:0041105F
.text:0041105F sub_41105F proc near ; CODE XREF: .text:00411CA1p
.text:0041105F ; .text:00411CC5p ...
.text:0041105F jmp sub_413650
.text:0041105F sub_41105F endp
.text:0041105F
.text:00411064
.text:00411064 ; =============== S U B R O U T I N E =======================================
.text:00411064
.text:00411064 ; Attributes: thunk
.text:00411064
.text:00411064 sub_411064 proc near ; CODE XREF: sub_412D00+6p
.text:00411064 ; .text:00412D63p ...
.text:00411064 jmp sub_414820
.text:00411064 sub_411064 endp
.text:00411064
.text:00411069
.text:00411069 ; =============== S U B R O U T I N E =======================================
.text:00411069
.text:00411069 ; Attributes: thunk
.text:00411069
.text:00411069 sub_411069 proc near ; CODE XREF: sub_411BA0+1Ap
.text:00411069 jmp sub_4123C0
.text:00411069 sub_411069 endp
.text:00411069 --------------------------------------------------------------------------------------------------------
3.尝试手动脱壳
在ollydbg中打开upx加壳过的exe文件,开头是这样的:
00FC03F0 > $ 60 PUSHAD
00FC03F1 . BE 00E0FB00 MOV ESI,Pack.00FBE000
00FC03F6 . 8DBE 0030FEFF LEA EDI,DWORD PTR DS:[ESI+FFFE3000]
00FC03FC . 57 PUSH EDI
00FC03FD . 83CD FF OR EBP,FFFFFFFF
00FC0400 > EB 10 JMP SHORT Pack.00FC0412
首先PUSHAD将所有寄存器的值复制都栈中,F8单步执行,不远处进入了一个人循环:
(PUSHAD指令在堆栈中按顺序压入下列寄存器:EAX,ECX,EDX,EBX,ESP,EBP,ESI和EDI.
POPAD指令则是PUSHAD指令的逆操作。POPAD指令按照与上面相反的顺序依次弹出寄存器的值。
顺序为 EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX.)
00FC0408 > 8A06 MOV AL,BYTE PTR DS:[ESI]
00FC040A . 46 INC ESI
00FC040B . 8807 MOV BYTE PTR DS:[EDI],AL
00FC040D . 47 INC EDI
00FC040E > 01DB ADD EBX,EBX
00FC0410 . 75 07 JNZ SHORT Pack.00FC0419
00FC0412 > 8B1E MOV EBX,DWORD PTR DS:[ESI]
00FC0414 . 83EE FC SUB ESI,-4
00FC0417 . 11DB ADC EBX,EBX
00FC0419 >^72 ED JB SHORT Pack.00FC0408
这里的逻辑就是从esi中向edi中拷贝数据,
向下找到 POPAD命令,F2设置断点,F9执行到断点处。
00FC056B . 61 POPAD
00FC056C . 8D4424 80 LEA EAX,DWORD PTR SS:[ESP-80]
00FC0570 > 6A 00 PUSH 0
00FC0572 . 39C4 CMP ESP,EAX
00FC0574 .^75 FA JNZ SHORT Pack.00FC0570
00FC0576 . 83EC 80 SUB ESP,-80
00FC0579 .-E9 CD0AFFFF JMP Pack.00FB104B
执行到JMP Pack.00FB104B,
跳转到了:
00FB104B E9 50100000 JMP Pack.00FB20A0
00FB1050 E9 553D0000 JMP Pack.00FB4DAA ; JMP to ucrtbase.wcscpy_s
按道理, pushad和popad之间的代码就是解压缩,在解压缩之前,先将寄存器状态保存到栈,解压缩结束之后在从栈中读取,恢复寄存器状态,从而运行真正的代码。
所以手动脱壳,关键在于找到 解压结束,程序开始的瞬间。