逆向脱壳——基本知识

基本知识

加壳程序

  • 1、壳是什么?
  • 加壳是可执行程序资源压缩,是保护文件的常用手段。加壳过的程序可以直接运行,但是不能查看源代码,要经过脱壳才可以查看源代码。
  • 2、加壳的原理是什么?壳是怎么运作的?
  • 加壳是利用特殊的算法,对EXE、DLL文件里的资源进行压缩、加密。类似WINZIP 的效果,只不过这个压缩之后的文件,可以独立运行,解压过程完全隐蔽,都在内存中完成。它们附加在原程序上通过Windows加载器载入内存后,先于原始程序执行,得到控制权,执行过程中对原始程序进行解密、还原,还原完成后再把控制权交还给原始程序,执行原来的代码部分。加上外壳后,原始程序代码在磁盘文件中一般是以加密后的形式存在的,只在执行时在内存中还原,这样就可以比较有效地防止破解者对程序文件的非法修改,同时也可以防止程序被静态反编译。
  • 简单来说,壳可以完成代码的压缩和实时解压执行,加壳程序可以正常执行,但是看不到原来的代码,反编译的时候看不懂。
  • 3、壳的分类
  • 壳的类型通常分为压缩壳和加密壳两类。
  • 压缩壳的特点是减小软件体积大小,加密保护不是重点。
  • 加密壳种类比较多,不同的壳侧重点不同,一些壳单纯保护程序,另一些壳提供额外的功能,如提供注册机制、使用次数、时间限制等。

脱壳

  • 1、对特定的壳可以用专门的脱壳软件进行脱壳。
  • 2、也可以手动脱壳

OD

反汇编工具OD=OllyDebug,OllyDbg 是一种具有可视化界面的 32 位汇编-分析调试器。它的特别之处在于可以在没有源代码时解决问题,并且可以处理其它编译器无法解决的难题。

手动脱壳的基本思路

  • 1、用软件查壳,可用PEID或者ExeinfoPE查壳。
  • 2、用进行OD动态反汇编,找到程序真正的入口。
  • 3、用OD插件或者loadPE脱壳。

OEP

  • OEP:(Original Entry Point),程序的入口点。软件加壳一般隐藏了程序真实的OEP(或者用了假的OEP), 我们需要寻找程序真正的OEP,才可以完成脱壳。
    一般加壳程序在使用Ollydbg等动态调试工具时,会停在壳的预处理块。即处在对于程序原始代码块的解压或解密操作之前,在运行完程序自脱壳模块后,会停留在程序加壳之前的OEP位置,此时是dump程序的最佳时期。脱壳时在真实OEP处下int3断点,就可以捕捉到程序代码段完全恢复的状态。因此,寻找加壳程序的正确OEP,也成了手动脱壳时的第一要务。

OPE的基本特征

VC6

  • 区段特征

  • 入口特征

  • EBP为基指针(Base Pointer)寄存器,用它可直接存取堆栈中的数据;

  • ESP为堆栈指针(Stack Pointer)寄存器,用它只可访问栈顶。

  • push ebp;保存ebp内容以便调用完后恢复;此时esp <- esp - 4

  • mov ebp esp; 保存调用前栈顶地址以便调用完后恢复,用ebp来保存当前esp。

VS2008和VS2013

  • 入口特征

  • 第一行是call XXXX

  • 第二行是 jmp XXXX

  • call里面是push ebp mov ebp esp

常见语言的入口点:

VB:

004012D4 > 68 54474000 push QQ个性网.00404754
004012D9 E8 F0FFFFFF call <jmp.&MSVBVM60.#100>
004012DE 0000 add byte ptr ds:[eax],al
004012E0 0000 add byte ptr ds:[eax],al
004012E2 0000 add byte ptr ds:[eax],al
004012E4 3000 xor byte ptr ds:[eax],al
004012E6 0000 add byte ptr ds:[eax],al
004012E8 48 dec eax

delphi:

004A5C54 > 55 push ebp
004A5C55 8BEC mov ebp,esp
004A5C57 83C4 F0 add esp,-10
004A5C5A B8 EC594A00 mov eax,openpro.004A59EC

BC++:

00401678 > /EB 10 jmp short btengine.0040168A
0040167A |66:623A bound di,dword ptr ds:[edx]
0040167D |43 inc ebx
0040167E |2B2B sub ebp,dword ptr ds:[ebx]
00401680 |48 dec eax
00401681 |4F dec edi
00401682 |4F dec edi
00401683 |4B dec ebx
00401684 |90 nop
00401685 -|E9 98005400 jmp 00941722
0040168A \A1 8B005400 mov eax,dword ptr ds:[54008B]
0040168F C1E0 02 shl eax,2
00401692 A3 8F005400 mov dword ptr ds:[54008F],eax
00401697 52 push edx
00401698 6A 00 push 0
0040169A E8 99D01300 call <jmp.&KERNEL32.GetModuleHandleA>
0040169F 8BD0 mov edx,eax

VC++:

0040A41E > 55 push ebp
0040A41F 8BEC mov ebp,esp
0040A421 6A FF push -1
0040A423 68 C8CB4000 push 跑跑排行.0040CBC8
0040A428 68 A4A54000 push <jmp.&MSVCRT._except_handler3>
0040A42D 64:A1 00000000 mov eax,dword ptr fs:[0]
0040A433 50 push eax
0040A434 64:8925 0000000>mov dword ptr fs:[0],esp
0040A43B 83EC 68 sub esp,68
0040A43E 53 push ebx
0040A43F 56 push esi
0040A440 57 push edi

MASM(汇编):

004035C9 > 6A 00 push 0
004035CB E8 A20A0000 call <jmp.&kernel32.GetModuleHandleA>
004035D0 A3 5B704000 mov dword ptr ds:[40705B],eax
004035D5 68 80000000 push 80
004035DA 68 2C754000 push 11.0040752C
004035DF FF35 5B704000 push dword ptr ds:[40705B]
004035E5 E8 820A0000 call <jmp.&kernel32.GetModuleFileNameA>
004035EA E8 87070000 call 11.00403D76
004035EF 6A 00 push 0
004035F1 68 0B364000 push 11.0040360B
004035F6 6A 00 push 0
004035F8 6A 64 push 64
004035FA FF35 5B704000 push dword ptr ds:[40705B]

手动脱壳方法

  • 方法1:单步跟踪

  • 方法2:ESP定律法

  • 方法3:2次内存镜像法

  • 方法4:一步直达法

常用汇编

1.MOV 传送字或字节.
2.MOVSX 先符号扩展,再传送.
3.MOVZX 先零扩展,再传送.
4.PUSH 把字压入堆栈.
5.POP 把字弹出堆栈.
6.PUSHA 把AX,CX,DX,BX,SP,BP,SI,DI依次压入堆栈.
7.POPA 把DI,SI,BP,SP,BX,DX,CX,AX依次弹出堆栈.
8.JMP 无条件转移指令
9.CALL 过程调用
10.RET/RETF过程返回.
11.2>条件转移指令 (短转移,-128到+127的距离内)
( 当且仅当(SF XOR OF)=1时,OP1<OP2 )
12.JA/JNBE 不小于或不等于时转移.
13.JAE/JNB 大于或等于转移.
14.JB/JNAE 小于转移.
15.JBE/JNA 小于或等于转移.
以上四条,测试无符号整数运算的结果(标志C和Z).
16.JG/JNLE 大于转移.
17.JGE/JNL 大于或等于转移.
18.JL/JNGE 小于转移.
19.JLE/JNG 小于或等于转移.
以上四条,测试带符号整数运算的结果(标志S,O和Z).
20.JE/JZ 等于转移.
21.JNE/JNZ 不等于时转移.
22.JC 有进位时转移.
23.JNC 无进位时转移.
24JNO 不溢出时转移.
25.JNP/JPO 奇偶性为奇数时转移.
26.JNS 符号位为 "0" 时转移.
27.JO 溢出转移.
28.JP/JPE 奇偶性为偶数时转移.
29.JS 符号位为 "1" 时转移.
30.3>循环控制指令(短转移)
31.LOOP CX不为零时循环.
32.LOOPE/LOOPZ CX不为零且标志Z=1时循环.
33.LOOPNE/LOOPNZ CX不为零且标志Z=0时循环.
34.JCXZ CX为零时转移.
35.JECXZ ECX为零时转移.

寄存器相关知识

  • EAX : 是"累加器"(accumulator), 它是很多加法乘法指令的缺省寄存器。

  • ECX : 是计数器(counter), 是重复(REP)前缀指令和LOOP指令的内定计数器。

  • EDX : 总是被用来放整数除法产生的余数。

  • EBX : 是"基地址"(base)寄存器, 在内存寻址时存放基地址。

  • ESP : 专门用作堆栈指针,被形象地称为栈顶指针,堆栈的顶部是地址小的区域,压入堆栈的数据越多,ESP也就越来越小。在32位平台上,ESP每次减少4字节。

  • EBP : 是"基址指针"(BASE POINTER), 它最经常被用作高级语言函数调用的"框架指针"(frame pointer).

push ebp ;保存当前ebp
mov ebp,esp ;EBP设为当前堆栈指针
sub esp, xxx ;预留xxx字节给函数临时变量.
  • ESI/EDI:分别叫做"源/目标索引寄存器"(source/destination index),因为在很多字符串操作指令中, DS:ESI指向源串,而ES:EDI指向目标串.

  • EIP: 寄存器存放下一个CPU指令存放的内存地址,当CPU执行完当前的指令后,从EIP寄存器中读取下一条指令的内存地址,然后继续执行。

  • ES:附加段寄存器

  • CS:代码段寄存器

  • SS:堆栈段寄存器

  • DS:数据段寄存器

  • FS:标志段寄存器

  • GS:全局寄存器

posted @ 2017-06-03 21:27  君知らない  阅读(3879)  评论(0编辑  收藏  举报