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:加1
  • DEC dest:减1
  • MUL/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 寻址方式

  1. 立即寻址:操作数是指令本身的一部分(常数),如MOV AX, 42
  2. 寄存器寻址:操作数在寄存器中,如ADD BX, CX
  3. 直接寻址:操作数在内存中,地址直接给出,如MOV AX, [0x1234](早期/特定场景)
  4. 寄存器间接寻址:操作数地址在寄存器中,如MOV AL, [BX](x86), LDR R0, [R1](ARM)
  5. 寄存器相对寻址:地址=寄存器内容+偏移量,如MOV AX, [SI + 10](x86), LDR R0, [R1, #4](ARM)
    • 访问数组/结构体成员常用
  6. 基址变址寻址:地址=基址寄存器+变址寄存器,如MOV AX, [BX + SI](x86)
  7. 基址变址相对寻址:地址=基址寄存器+变址寄存器+偏移量,如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(深度优先搜索)的复习应用
  • 解题步骤
    1. 逆向分析迷宫数据结构
    2. 确定起点和终点
    3. 分析移动规则(上下左右等)
    4. 编写相应代码跑一遍路径
    5. 考虑深度和广度对答案的影响
  • 示例代码
    # 伪代码示例
    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为例)

  1. 使用x64dbg加载程序
  2. 运行至系统断点(F9)
  3. F7步进至lea指令处
  4. 找到RSP对应位置下硬件断点
  5. F9运行至pop指令处
  6. 步进进入源程序内部
  7. 使用Scylla插件脱壳:
    • IAT自动搜索
    • 获取导入
    • 删除红叉所在行
    • 转储
    • 修复转储

4.4 混淆

  • 常见类型
    • 函数名/变量名混淆
    • 简单汇编代码混淆
  • 应对策略
    • 动态调试跟踪实际执行流程
    • 识别并忽略无用代码
    • 关注关键数据流和控制流

5. 算法逆向

5.1 Base64

  • 本质:编码方式而非加密算法
  • 原理
    1. 将输入数据分割成每三个字节(24位)一组
    2. 将24位分割为四个6位的块
    3. 通过查找表将6位块映射为Base64字符
    4. 若字节数非3的倍数,用=填充
  • 字符集:A-Z, a-z, 0-9, +, /, =
  • 补位规则
    • 余数为1:二进制末尾补4个0,编码为2个Base64字符,补2个=
      • 示例:"song" → "c29uZw=="
    • 余数为2:二进制末尾补2个0,编码为3个Base64字符,补1个=
      • 示例:"ab" → "YWI="
  • 工具:CyberChef等在线工具

5.2 RC4

  • 类型:流密码(序列密码)
  • 核心:密钥流生成,加解密为简单的XOR操作
  • 算法流程

初始化阶段

  1. 创建S盒:初始化长度为256的数组S,值为0-255

密钥调度算法(KSA)

  1. 遍历i从0到255
  2. 计算索引j = (当前j + S[i] + 密钥字节) % 256
  3. 交换S[i]和S[j]
  4. 目的:将密钥的随机性扩散到整个S盒

伪随机生成算法(PRGA)与加密

  1. 生成密钥流字节:
    a. 更新索引i = (i + 1) % 256
    b. 更新索引j = (j + S[i]) % 256
    c. 交换S[i]和S[j]
    d. 计算密钥字节k = S[(S[i] + S[j]) % 256]
  2. 加密/解密:明/密文字节 ^ k
  • 特点:若无魔改,可直接动态调试输入密文得到明文

6. 花指令基础

  • 定义:故意插入的无用或误导性代码,干扰逆向分析
  • 常见形式
    • 无效跳转
    • 无意义计算
    • 破坏性指令
  • 应对方法
    • 动态调试识别实际执行路径
    • 使用反汇编器的线性扫描功能
    • 手动修复被破坏的代码

7. SMC(Self-Modifying Code)基础

  • 定义:程序在运行时修改自身代码
  • 常见用途
    • 代码保护
    • 反调试
    • 动态解密
  • 识别特征
    • 代码段被写入
    • 异常的内存访问模式
  • 分析方法
    • 动态调试跟踪代码修改过程
    • 内存断点监控关键代码区域
    • 在修改后转储内存中的代码

8. Python逆向基础

  • 常见场景
    • 逆向Python打包的exe(如PyInstaller)
    • 分析pyc字节码
  • 工具链
    • uncompyle6/pycdc:反编译pyc
    • pyinstxtractor:解包PyInstaller生成的文件
    • Decompyle++:Python字节码反编译器
  • 关键点
    • Python字节码理解
    • 对象模型分析
    • 内置函数和模块的调用跟踪
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 :加1 DEC dest :减1 MUL/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(深度优先搜索)的复习应用 解题步骤 : 逆向分析迷宫数据结构 确定起点和终点 分析移动规则(上下左右等) 编写相应代码跑一遍路径 考虑深度和广度对答案的影响 示例代码 : 4. 壳与混淆 4.1 壳的概念 定义 :程序外面的"保护层",主要分为压缩壳和加密壳 实质 :一个子程序,在程序运行时首先取得控制权并对程序进行压缩/加密,同时隐藏程序真正的OEP(Original Entry Point) OEP :程序的入口点,加壳就是隐藏了OEP(或使用假的OEP) 关键指令 : PUSHAD :所有寄存器压栈,代表程序入口点 POPAD :出栈,代表程序出口点 4.2 查壳工具 EXEInfoPE PEID StudyPE+ DIE(Detect It Easy) 4.3 UPX壳脱壳方法 工具脱壳 手动脱壳(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=" 工具 :CyberChef等在线工具 5.2 RC4 类型 :流密码(序列密码) 核心 :密钥流生成,加解密为简单的XOR操作 算法流程 : 初始化阶段 创建S盒:初始化长度为256的数组S,值为0-255 密钥调度算法(KSA) 遍历i从0到255 计算索引j = (当前j + S[ i ] + 密钥字节) % 256 密钥字节 = key i % 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字节码理解 对象模型分析 内置函数和模块的调用跟踪