逆向工程基础

关于逆向工程

逆向工程(Reverse Engineering,简称RE)

代码逆向工程(Reverse Code Engineering,简称RCE)

逆向分析法

  • 静态分析法
    • 在不执行代码文件的情形下,对代码进行静态分析的一种方法。
  • 动态分析法
    • 在程序文件的执行过程中对代码进行动态分析

Hex Editor是一个简单易用的工具,使用它可以轻松地把二进制文件转换为十六进制文件。

image-20220710181629406

“打补丁”与“破解”

对应用程序文件或进程内存内容的更改被称为“打补丁”(Patch),“破解”(Crack)与其含义类似

逆向分析Hello World!程序

OllyDbg: http://www.ollydbg.de

OllyDbg是一种强大的Win32调试工具

image-20220710182151607

入口点

EP是Windows可执行文件(EXE、DLL、SYS等)的代码入口点,是执行应用程序时最先执行的代码的起始位置,它依赖于CPU。

调试器停止的地点即为HelloWorld.exe执行的起始地址,它是一段EP (EntryPoint,入口点)代码。

地址:进程的虚拟内存地址(Virtual Address, VA)
指令:IA32 (或x86) CPU指令
反汇编代码:将OP code转换为便于查看的汇编指令
注释:调试器添加的注释(根据选项不同,显示的注释略有不同)

OllyDbg基本指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
F2:				设置断点/取消断点

F7: 单步步入

F8: 单步执行

Ctrl + F2: 重新调试

Ctrl+G: 跳转到指定地址(查看代码时使用,非运行时命令)

F9: 运行(遇到断点时暂停)

Ctrl+F9: 执行函数代码内的命令,直到遇到RETN命令,用于跳出函数体

F4: 执行到光标所在位置(直接转到要调试的位置)

; 添加注释

: 添加标签

* 显示当前EIP(命令指针)位置

- 显示上一个光标位置

Enter: 若光标处有CALL/JMP等指令,则跟踪并显示相关地址(运行时不可用,简单查看函数内容时非常有用)

空格键: 编辑汇编代码

设置“大本营”的四种方法

1.Goto命令

执行Go to(Ctrl+G)命令

image-20220710195834481

2.设置断点

在OllyDbg菜单栏中依次选择View-Breakpoints选项(快捷键(ALT+B)),打开Breakpoints对话框,列出代码中设置的断点

image-20220710200136557

在断点列表中双击某个断点会直接跳转到相应位置。

3.注释

按键盘上的“;”键可以在指定地址处添加注释,还可以通过查找命令找到它。

在鼠标右键菜单中依次选择Search for-User defined comment,这样就能看到用户输入的所有注释

image-20220710200340518

双击相应注释,光标将自动定位到相应位置。

4.标签

单击鼠标右键,依次选择Search forlUser defined labels菜单即可打开User defined labels窗口,该窗口列出了用户设置的标签

在User defined labels窗口中双击某个标签,光标即移动到相应位置。

image-20220710200640747

image-20220710200714534

快速查找指定代码的四种方法

1.代码执行法

2.字符串检索法

鼠标右键菜单-Search for-All referenced text strings

image-20220710201316743

双击字符串,光标定位到使用该字符串的指令处

VC++中, static字符串会被默认保存为Unicode码形式, static字符串是指在程序内部被硬编码(Hard Coding)的字符串。

API检索法(1):在调用代码中设置断点

鼠标右键菜单-Search for-All intermodular calls

image-20220710201618956

双击它,光标即定位到调用它的地址处

API检索法(2):在API代码中设置断点

鼠标右键菜单-Search for-Name in all calls

在OllyDbg菜单栏中依次选择View-Memory菜单(快捷键Alt+M),打开内存映射窗口。内存映射窗口中显示了一部分HelloWorld.exe进程内存。在图底部的方框中可以看到,USER32库被加载到了内存。

image-20220710202412666

使用OllyDbg中的Name in all modules命令可以列出被加载的DLL文件中提供的所有API。使用Name in all modules命令打开All names窗口,单击Name栏目按名称排序,通过键盘敲出MessageBoxW后,光标会自动定位到MessageBoxW上

image-20220710203219441

双击MessageBoxW函数后就会显示其代码,它实现于USER32.dll库中

image-20220710203502389

模块中的地址和本程序的地址是完全不同的。

修改字符串的两种方法

1.直接修改字符串缓冲区

在Dump窗口中按Ctrl+G快捷键执行Go to命令,在弹出窗口中输入地址进入字符串缓冲区。然后使用鼠标选中地址处的字符串,按Ctrl+E快捷键打开编辑窗口

image-20220710220035154

注意:
若新字符串长度大于原有字符串,执行覆盖操作时可能损坏字符串后面的数据,所以一定要小心。特别是字符串后面有非常重要的数据时,覆盖操作导致数据损坏就会引发程序内存引用错误。

新字符串的长度不应比原字符串长。

保存更改到可执行文件

选中更改后的“Hello Reversing”字符串,单击鼠标右键,在弹出的菜单中选择Copy to executable file菜单

image-20220710220428077

在弹出的Hex窗口中单击鼠标右键,选择Save file菜单,在Save file as对话框中输人文件名“Hello Reversing.exe”后保存为.exe可执行文件。

2.在其他内存区域新建字符串并传递给消息函数

image-20220710220905979

image-20220710220921112

image-20220710220933154

image-20220710220950573





小端序标记法

字节序

image-20220713005301379

字节型: 大小端的字节顺序都是一样的。

大端:低地址放数据的高位

小端:高地址放数据的低位

OD中查看小端序

image-20220713005536736



IA-32寄存器基本讲解

什么是CPU寄存器

寄存器是CPU内部用来存放数据的一些小型存储区域



基本程序运行寄存器

通用寄存器( General Purpose Registers, 32位, 8个)
段寄存器( Segment Registers, 16位, 6个)
程序状态与控制寄存器( Program Status and Control Registers, 32位, 1个)
指令指针寄存器(Instruction Pointer, 32位, 1个)

image-20220713005855404

image-20220713005916672


EAX:(0~31)32位
AX:(0~15)EAX的低16位
AH:(8~15)AX的高8位
AL:(0-7)AX的低8位


循环命令(LOOP)中,ECX用来循环计数(loop count),每执行一次循环,ECX都会减1。EAX一般用在函数返回值中,所有Win32API函数都会先把返回值保存到EAX再返回。


Win32 API函数在内部会使用ECX与EDX



EBP:(SS段中栈内数据指针)扩展基址指针寄存器
ESI:(字符串操作源指针)源变址寄存器
EDI:(字符串操作目标指针)目的变址寄存器
ESP:(SS段中栈指针)栈指针寄存器


ESP指示栈区域的栈顶地址

EBP表示栈区域的基地址,函数被调用时保存ESP的值,函数返回时再把值返回ESP,保证栈不会崩溃,这称为栈帧技术。


ESI和EDI与特定指令(LODS、STOS、REP、MOVS等)一起使用,主要用于内存复制。



段寄存器

段内存记录在SDT(Segment Descriptor Table,段描述符表)

段寄存器就持有这些SDT的索引(index)。

段寄存器有6各个:CS 、SS、DS、ES、FS、GS 每个寄存器大小为16位,2个字节。

每个段寄存器指向的段描述符(Segment Descriptor)与虚拟内存结合,形成一个线性地址(Linear Address),借助分页技术,线性地址最终被转换为实际的物理地址(Physical Address)。


CS: Code Segment,代码段寄存器
SS: Stack Segment,栈段寄存器
DS: Data Segment,数据段寄存器
ES: Extra (Data) Segment,附加(数据)段寄存器
FS: Data Segment,数据段寄存器
GS: Data Segment,数据段寄存器


image-20220713013432002


程序调试中会经常用到FS寄存器,它用于计算SEH(Structured Exception Handler,结构化异常处理机制)、TEB(Thread Environment Block,线程环境块)、PEB(Process Environment Block,进程环境块)等地址



程序状态与控制寄存器

EFLAGS:Flag Register,标志寄存器

其大小为4个字节(32位)

EFLAGS寄存器的每位都有意义,每位的值或为1或为0,代表On/Off或True/False。

ZF(Zero Flag,零标志)

OF(Overflow Flag,溢出标志)

CF(Carry Flag,进位标志)

image-20220713014111043

ZF
若运算结果为0,则其值为1(True),否则其值为0(False)。
OF
有符号整数(signed integer)溢出时, OF值被置为1。此外, MSB (Most Significant Bit,
最高有效位)改变时,其值也被设为1。
CF
无符号整数(unsigned integer)溢出时,其值也被置为1。



指令指针寄存器

EIP:Instruction Pointer,指令指针寄存器

程序运行时,CPU会读取EIP中一条指令的地址,传送指令到指令缓冲区后,EIP寄存器的值自动增加,增加的大小即是读取指令的字节大小。

不能直接修改EIP的值,只能通过其他指令间接修改




(1)暂时保存函数内的局部变量。
(2)调用函数时传递参数。
(3)保存函数返回后的地址。

栈其实是一种数据结构,它按照FILO(First In Last Out,后进先出)的原则存储数据。

image-20220714011408035

栈顶指针(ESP)初始状态指向栈底端。

执行PUSH命令将数据压入栈时,栈顶指针就会上移到栈顶端。

执行POP命令从栈中弹出数据时,若栈为空,则栈顶指针重新移动到栈底端。

栈是一种由高地址向低地址扩展的数据结构

栈是逆向扩展的

从栈中弹出数据后,ESP随之向下移动。

向栈压入数据时,栈顶指针减小,向低地址移动;从栈中弹出数据时,栈顶指针增
加,向高地址移动。

汇编指令

image-20220714012232945

函数参数压入栈时是从最后一个参数开始压入的。

image-20220714012419678