Reverse入门参考(一)
字数 3763 2025-09-01 11:26:03
Reverse入门参考(一) - 逆向工程基础教学文档
1. 汇编与IDA使用
1.1 汇编语言核心概念
- 本质:机器指令的助记符(MOV, ADD等),是机器码的"人类可读"形式,直接对应CPU硬件操作
- 核心任务:操作寄存器(Registers)和内存(Memory)中的数据
- 指令结构:操作码 [操作数1] [, 操作数2] (如MOV AX, 5)
- 低级性:无高级语言的变量、类型系统(需手动管理内存)、复杂控制结构(需跳转实现)
- 平台依赖:完全依赖于特定的CPU架构(x86, ARM, MIPS等)
1.2 关键硬件组件
寄存器(Registers)
- 通用寄存器(GPRs):存放数据和地址
- x86: AX, BX, CX, DX, EAX, RAX
- ARM: R0-R12
- 指令指针(IP/PC):指向下一条要执行的指令地址
- x86: EIP/RIP
- ARM: R15(PC)
- 堆栈指针(SP):指向当前栈顶
- x86: ESP/RSP
- ARM: R13(SP)
- 标志寄存器(Flags):存储上一条指令执行结果的状态
- ZF(零标志), CF(进位标志), SF(符号标志)
- 控制条件跳转(JZ, JC等)
其他组件
- 内存(Memory):RAM,按字节编址
- 总线(Bus):CPU、内存、外设间传输数据的通道
1.3 基本指令类型
数据传输
MOV dest, src:复制数据(寄存器↔寄存器,寄存器↔内存,立即数→寄存器/内存)- 注意:内存间不能直接MOV
算术运算
ADD dest, src:加法(影响标志位)SUB dest, src:减法(影响标志位)INC dest:加1DEC dest:减1MUL/IMUL:无/有符号乘法DIV/IDIV:无/有符号除法
逻辑运算
AND dest, src:按位与OR dest, src:按位或XOR dest, src:按位异或(常用清零寄存器 XOR AX, AX)NOT dest:按位取反SHL/SHR/SAL/SAR:逻辑/算术左移/右移
控制流
JMP label:无条件跳转- 条件跳转:
JE/JZ:等于/零JNE/JNZ:不等于/非零JG/JNLE:大于JL/JNGE:小于JC:进位JNC:无进位
CALL func_label:调用子程序(将下一条指令地址压栈并跳转)RET:从子程序返回(从栈顶弹出地址并跳回)
堆栈操作
PUSH src:将数据压入栈顶(SP减小)POP dest:从栈顶弹出数据到目标(SP增大)- 遵循LIFO(后进先出)原则
- 用途:
- 函数调用参数传递/局部变量存储
- 保存寄存器状态
- 中断处理
1.4 寻址方式
- 立即寻址:操作数是指令本身的一部分(常数),如
MOV AX, 42 - 寄存器寻址:操作数在寄存器中,如
ADD BX, CX - 直接寻址:操作数在内存中,地址直接给出,如
MOV AX, [0x1234](早期/特定场景) - 寄存器间接寻址:操作数地址在寄存器中,如
MOV AL, [BX](x86),LDR R0, [R1](ARM) - 寄存器相对寻址:地址=寄存器内容+偏移量,如
MOV AX, [SI + 10](x86),LDR R0, [R1, #4](ARM)- 访问数组/结构体成员常用
- 基址变址寻址:地址=基址寄存器+变址寄存器,如
MOV AX, [BX + SI](x86) - 基址变址相对寻址:地址=基址寄存器+变址寄存器+偏移量,如
MOV AX, [BX + SI + 8](x86)- 访问二维数组常用
1.5 程序结构(简化)
- 数据段(.data/.section .data):定义已初始化的全局/静态变量
DB(字节),DW(字),DD(双字),DQ(四字)
- BSS段(.bss):定义未初始化的全局/静态变量空间
RESB,RESW等
- 代码段(.text/.section .text):存放程序指令
_start:或main:通常是入口点
- 堆栈段:通常由操作系统自动分配和管理,通过SP寄存器访问
1.6 IDA使用要点
- 根据main函数入口定位
- 五六百行汇编码人肉分析是基本功
- 使用F5反编译功能查看伪代码
- 交叉引用分析(Xrefs)
2. 静态分析与动态调试
2.1 静态分析
- 主要方法:
- 使用IDA的F5功能查看伪代码
- 交叉引用分析(Xrefs)
- 字符串和函数引用分析
- 工具:
- IDA Pro
- Ghidra
- Binary Ninja
2.2 动态调试
- 本地调试:
- x64dbg/OllyDbg(Windows)
- GDB(Linux)
- 远程调试:
- Linux下使用gdbserver进行远程调试
- IDA远程调试功能
- 调试技巧:
- 断点设置
- 寄存器/内存监控
- 单步执行(Step Into/Over)
3. 迷宫(maze)逆向
- 核心思想:对数据结构BFS(广度优先搜索)和DFS(深度优先搜索)的复习应用
- 解题步骤:
- 逆向分析迷宫数据结构
- 确定起点和终点
- 分析移动规则(上下左右等)
- 编写相应代码跑一遍路径
- 考虑深度和广度对答案的影响
- 示例代码:
# 伪代码示例 def solve_maze(maze): # 实现BFS或DFS算法 pass
4. 壳与混淆
4.1 壳的概念
- 定义:程序外面的"保护层",主要分为压缩壳和加密壳
- 实质:一个子程序,在程序运行时首先取得控制权并对程序进行压缩/加密,同时隐藏程序真正的OEP(Original Entry Point)
- OEP:程序的入口点,加壳就是隐藏了OEP(或使用假的OEP)
- 关键指令:
PUSHAD:所有寄存器压栈,代表程序入口点POPAD:出栈,代表程序出口点
4.2 查壳工具
- EXEInfoPE
- PEID
- StudyPE+
- DIE(Detect It Easy)
4.3 UPX壳脱壳方法
工具脱壳
upx.exe -d <文件名>
手动脱壳(x64dbg为例)
- 使用x64dbg加载程序
- 运行至系统断点(F9)
- F7步进至
lea指令处 - 找到RSP对应位置下硬件断点
- F9运行至
pop指令处 - 步进进入源程序内部
- 使用Scylla插件脱壳:
- IAT自动搜索
- 获取导入
- 删除红叉所在行
- 转储
- 修复转储
4.4 混淆
- 常见类型:
- 函数名/变量名混淆
- 简单汇编代码混淆
- 应对策略:
- 动态调试跟踪实际执行流程
- 识别并忽略无用代码
- 关注关键数据流和控制流
5. 算法逆向
5.1 Base64
- 本质:编码方式而非加密算法
- 原理:
- 将输入数据分割成每三个字节(24位)一组
- 将24位分割为四个6位的块
- 通过查找表将6位块映射为Base64字符
- 若字节数非3的倍数,用
=填充
- 字符集:A-Z, a-z, 0-9, +, /, =
- 补位规则:
- 余数为1:二进制末尾补4个0,编码为2个Base64字符,补2个
=- 示例:"song" → "c29uZw=="
- 余数为2:二进制末尾补2个0,编码为3个Base64字符,补1个
=- 示例:"ab" → "YWI="
- 余数为1:二进制末尾补4个0,编码为2个Base64字符,补2个
- 工具:CyberChef等在线工具
5.2 RC4
- 类型:流密码(序列密码)
- 核心:密钥流生成,加解密为简单的XOR操作
- 算法流程:
初始化阶段
- 创建S盒:初始化长度为256的数组S,值为0-255
密钥调度算法(KSA)
- 遍历i从0到255
- 计算索引j = (当前j + S[i] + 密钥字节) % 256
- 密钥字节 = keyi % len(key)
- 交换S[i]和S[j]
- 目的:将密钥的随机性扩散到整个S盒
伪随机生成算法(PRGA)与加密
- 生成密钥流字节:
a. 更新索引i = (i + 1) % 256
b. 更新索引j = (j + S[i]) % 256
c. 交换S[i]和S[j]
d. 计算密钥字节k = S[(S[i] + S[j]) % 256] - 加密/解密:明/密文字节 ^ k
- 特点:若无魔改,可直接动态调试输入密文得到明文
6. 花指令基础
- 定义:故意插入的无用或误导性代码,干扰逆向分析
- 常见形式:
- 无效跳转
- 无意义计算
- 破坏性指令
- 应对方法:
- 动态调试识别实际执行路径
- 使用反汇编器的线性扫描功能
- 手动修复被破坏的代码
7. SMC(Self-Modifying Code)基础
- 定义:程序在运行时修改自身代码
- 常见用途:
- 代码保护
- 反调试
- 动态解密
- 识别特征:
- 代码段被写入
- 异常的内存访问模式
- 分析方法:
- 动态调试跟踪代码修改过程
- 内存断点监控关键代码区域
- 在修改后转储内存中的代码
8. Python逆向基础
- 常见场景:
- 逆向Python打包的exe(如PyInstaller)
- 分析pyc字节码
- 工具链:
- uncompyle6/pycdc:反编译pyc
- pyinstxtractor:解包PyInstaller生成的文件
- Decompyle++:Python字节码反编译器
- 关键点:
- Python字节码理解
- 对象模型分析
- 内置函数和模块的调用跟踪