这里整理了逆向中最基础的知识,计算机基础,特别有帮助
学习链条
1 | 进制 / 字长 / 端序 |
一、进制介绍与进制转换
常见进制
- 二进制(BIN):0、1,基数为 2
- 十进制(DEC):0~9,基数为 10
- 十六进制(HEX):0
9、AF,基数为 16
权重
每一位数字代表的大小叫“权重”,从右往左,第 0 位权重是 基数^0。
| 进制 | 第0位 | 第1位 | 第2位 | 第3位 | 第4位 |
|---|---|---|---|---|---|
| 十进制 | 1 | 10 | 100 | 1000 | 10000 |
| 二进制 | 1 | 2 | 4 | 8 | 16 |
| 十六进制 | 1 | 16 | 256 | 4096 | 65536 |
转十进制
每位数字 × 对应权重,再相加。
例:
1 | 11111₂ = 1×16 + 1×8 + 1×4 + 1×2 + 1×1 = 31₁₀ |
十进制转其他进制
除以基数取余数,倒着读。
例:31 转二进制
1 | 31 ÷ 2 = 15 余 1 |
二进制 ↔ 十六进制
1 位十六进制 = 4 位二进制。
| HEX | BIN | HEX | BIN |
|---|---|---|---|
| 0 | 0000 | 8 | 1000 |
| 1 | 0001 | 9 | 1001 |
| 2 | 0010 | A | 1010 |
| 3 | 0011 | B | 1011 |
| 4 | 0100 | C | 1100 |
| 5 | 0101 | D | 1101 |
| 6 | 0110 | E | 1110 |
| 7 | 0111 | F | 1111 |
例:
1 | 11111₂ → 0001 1111 → 1F₁₆ |
二、字长
- 比特(bit):一个二进制位,最基本单位。
- 字节(byte):内存的基本单位,1 byte = 8 bit。
- 字(WORD):x86 中为 2 字节 = 16 位。
- 双字(DWORD):4 字节 = 32 位。
- 四字(QWORD):8 字节 = 64 位。
- Int:有32位有64位
注意:广义上“字长”指 CPU 一次能处理的位数,比如 32 位、64 位。
这里说的 WORD / DWORD / QWORD 是 Windows / x86 语境下的定义。
一个字节 01011010:
1 | 高位 0101 低位 1010 |
三、端序(字节序)

注意正负号
端序指多字节数据在内存中的存放顺序。
以 0x12345678 为例,它在内存里占 4 个字节:
1 | 高字节 → 12 34 56 78 ← 低字节 |
大端序(Big Endian)
高位字节放在低地址,低位字节放在高地址。
内存从低到高:
1 | 12 34 56 78 |
符合人类阅读习惯,网络协议常用大端序。
小端序(Little Endian)
低位字节放在低地址,高位字节放在高地址。
内存从低到高:
1 | 78 56 34 12 |
x86 / x64 电脑常用小端序。
记忆口诀
- 大端:高放低,低放高(高位字节在低地址)
- 小端:低放低,高放高(低位字节在低地址)
四、寄存器
寄存器是 CPU 内部的高速存储,比内存快得多。逆向时看汇编,主要就是看寄存器怎么变。
x86(32 位)常用寄存器
| 寄存器 | 名称 | 常见用途 |
|---|---|---|
| EAX | 累加器 | 函数返回值、算术运算 |
| EBX | 基址寄存器 | 通用,有时存指针 |
| ECX | 计数器 | 循环计数、this 指针(C++) |
| EDX | 数据寄存器 | 乘除法高位、函数第三个参数 |
| ESI | 源变址 | 字符串/内存复制源 |
| EDI | 目的变址 | 字符串/内存复制目的 |
| EBP | 栈基址指针 | 指向当前栈帧底部 |
| ESP | 栈顶指针 | 始终指向栈顶 |
| EIP | 指令指针 | 指向下一条要执行的指令 |
x64(64 位)常用寄存器
x64 把 x86 的寄存器扩展到 64 位,并新增 R8~R15。
| 64 位 | 低 32 位 | 低 16 位 | 低 8 位 |
|---|---|---|---|
| RAX | EAX | AX | AL |
| RBX | EBX | BX | BL |
| RCX | ECX | CX | CL |
| RDX | EDX | DX | DL |
| RSI | ESI | SI | SIL |
| RDI | EDI | DI | DIL |
| RBP | EBP | BP | BPL |
| RSP | ESP | SP | SPL |
| R8~R15 | R8D~R15D | R8W~R15W | R8B~R15B |
逆向时看到
AL、AX、EAX、RAX,其实是同一个寄存器的不同宽度。
标志寄存器
- x86:EFLAGS
- x64:RFLAGS
常用标志位:
| 标志 | 名称 | 含义 |
|---|---|---|
| ZF | 零标志 | 结果为 0 时置 1 |
| SF | 符号标志 | 结果为负时置 1 |
| OF | 溢出标志 | 有符号溢出时置 1 |
| CF | 进位标志 | 无符号进位/借位时置 1 |
| PF | 奇偶标志 | 低 8 位中 1 的个数为偶数时置 1 |
cmp、test后跟jz、jnz、jg等跳转,就是看这些标志位。
五、栈
栈是一块从高地址向低地址生长的内存区域。
ESP/RSP始终指向栈顶。PUSH让栈顶下移(ESP 减小),POP让栈顶上移(ESP 增大)。
PUSH / POP
1 | push eax ; esp -= 4, [esp] = eax |
x64 下 push rax 是 rsp -= 8。
栈帧
函数调用时,会在栈上开辟一块区域,叫栈帧。
典型 x86 栈帧:
1 | 高地址 |
标准函数开头:
1 | push ebp |
函数结尾:
1 | mov esp, ebp |
或者用 leave; ret 等价。
栈溢出
如果局部变量写入超过分配大小,就会覆盖 EBP、返回地址,这就是栈溢出漏洞。CTF PWN 题的核心之一。
六、逆向常用指令速查
| 指令 | 含义 |
|---|---|
| mov | 传送 |
| lea | 取地址 |
| push / pop | 入栈 / 出栈 |
| call | 调用函数 |
| ret | 返回 |
| jmp | 无条件跳转 |
| jz / je | 为零/相等跳转 |
| jnz / jne | 非零/不等跳转 |
| cmp | 比较 |
| test | 按位与,常用来判断是否为 0 |
| add / sub | 加 / 减 |
| inc / dec | 自增 / 自减 |
| and / or / xor | 位运算 |
| shl / shr | 左移 / 右移 |
| nop | 空指令 |
七、CTF 逆向常见题型和套路
逆向题的核心一句话:
看懂程序在干什么,找到它判断 flag 的逻辑,然后逆回去。
不同题型只是“藏 flag 的方式”不同。
按 flag 存放位置分类
1. 明文 flag
最简单。flag 直接以字符串形式存在程序里。
特征:
- 扔进反编译工具或
strings就能看到flag{...}或moectf{...}
套路:
1 | strings challenge | grep -i flag |
工具:strings、Ghidra、010 Editor
2. 简单异或 / 加减加密
flag 存在程序里,但每个字节被异或或加减了一个固定值。
特征:
- 反汇编里出现
xor、add、sub循环 - 有一段密文数组
套路:
- 找到密文数组和运算逻辑
- 写脚本逆运算
例:
1 | // 加密逻辑 |
工具:Python、Ghidra
3. 逐字符比较
程序把输入的每个字符和正确 flag 逐位比较。
特征:
- 大量
cmp指令 - 或者
if (input[i] != target[i])结构
套路:
- 找到比较逻辑
- 把目标数组提取出来
- 直接转成字符串
变种:比较顺序被打乱,需要按索引还原。
4. 校验和 / 哈希
程序对输入做哈希,和固定值比较。
特征:
- 出现
md5、sha1、crc32等常量或函数 - 比较的是一个 16/32/64 字节的值
套路:
- 如果是弱哈希(CRC32、简单异或和),可以爆破或反推
- 如果是 MD5/SHA,通常需要已知明文或字典
- 有时候是分块哈希,逐块爆破
按保护/难度分类
5. 无壳无混淆(入门题)
特征:
- 直接能反编译出接近源码的伪代码
套路:
- 反编译 → 看主逻辑 → 写解密脚本
6. 加壳
特征:
- 程序入口点奇怪
- 反编译工具打开看到少量代码 + 一堆数据
PEiD、DIE检测出壳名(UPX、ASPack 等)
套路:
- UPX:
upx -d challenge.exe - 其他壳:手动脱壳(ESP 定律、内存 dump)
- 脱完再扔反编译工具
工具:DIE、PEiD、UPX、Scylla
7. 花指令
特征:
- 反汇编出现大量无意义跳转、垃圾字节
- 反编译报错或伪代码混乱
套路:
- 手动 patch 掉花指令(nop 掉垃圾跳转)
- 或用脚本批量清理
- 找到真实入口后重新分析
8. 控制流平坦化(OLLVM)
特征:
- 一个巨大的
while(1) + switch结构 - 状态变量决定下一步走哪
套路:
- 找状态变量
- 动态调试跟踪状态变化
- 或用反混淆工具(deflat、D810)
工具:D810 插件、Angr、动态调试
9. 虚拟机保护(VM)
特征:
- 有一段“解释器”循环
- 自定义字节码
- 大量
switch或跳转表
套路:
- 找到 VM 的入口
- 分析每条 handler 干什么
- 还原字节码语义
- 写反汇编器或直接模拟执行
难度:高,CTF 里属于压轴题。
按语言/平台分类
10. C/C++ 逆向
最常见。看反编译伪代码即可。
注意:
- C++ 有虚表、
this指针、名字修饰 - STL 容器在汇编里很啰嗦
11. Python 逆向
特征:
- 文件是
.pyc,或打包成 exe - exe 里有
python3x.dll
套路:
.pyc→uncompyle6/decompyle3/pycdc- 打包 exe →
pyinstxtractor提取 → 再反编译 - 有混淆 → 动态调试或手动还原
工具:uncompyle6、pycdc、pyinstxtractor、PyLingual
12. Java 逆向
特征:
.jar或.class文件
套路:
jd-gui、CFR、Procyon反编译- 混淆过的用动态调试(JDB)
13. .NET 逆向
特征:
.exe用 DIE 检测出 .NET
套路:
dnSpy直接反编译 + 调试 + 改代码- 混淆过的用
de4dot去混淆
14. Android 逆向
特征:
.apk文件
套路:
jadx反编译 Javaapktool解包资源- so 层用反编译工具
- 动态调试用 Frida
按输入方式分类
16. 静态输入 flag
程序启动读参数或标准输入,直接比较。
套路:静态分析即可。
17. 多阶段校验
程序分多步校验,每步给一部分 flag。
套路:
- 逐段分析
- 或者动态调试,每步跟进去
18. 需要满足约束条件
flag 的每个字符满足一组方程/不等式。
特征:
- 大量 if 判断
- 或者数学运算
套路:
- 提取约束
- 用约束求解器求解
例:
1 | # 伪代码,使用约束求解器 |
工具:Angr
19. 需要爆破
flag 空间小,可以直接枚举。
套路:
- 写 C/Python 爆破脚本
- 或用 Angr 符号执行
按技巧分类
20. 反调试
特征:
IsDebuggerPresent、ptrace、NtQueryInformationProcess- 检测到调试器就输出假 flag 或崩溃
套路:
- patch 掉检测
- 或 hook 掉 API
- 或用 ScyllaHide 插件
21. 动态解密
特征:
- 代码段在运行时才解密
- 静态看不到真实逻辑
套路:
- 在解密完成后 dump 内存
- 找 OEP,dump 再修 IAT
22. 自修改代码
特征:
- 程序运行时修改自己的指令
套路:
- 动态跟踪
- 或模拟执行
23. 算法逆向
特征:
- 出现加密算法(AES、RC4、TEA、XXTEA、Base64)
- 但可能改过常量或轮数
套路:
- 识别算法(看常量:AES 的 S 盒、TEA 的 delta
0x9E3779B9) - 确认是否有魔改
- 写解密脚本
常见算法常量:
| 算法 | 特征常量 |
|---|---|
| TEA / XXTEA | 0x9E3779B9 |
| AES | S 盒、0x1B |
| RC4 | 256 字节初始化 |
| Base64 | ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ |
| MD5 | 0x67452301、0xEFCDAB89 |
24. 迷宫 / 走图题
特征:
- 一个大数组
- 输入是 WASD 或方向
- 有起点终点
套路:
- 提取地图数组
- 找起点终点
- BFS/DFS 找路径
- 路径转成输入
工具:Python + BFS
25. 数独 / 棋盘题
特征:
- 9×9 数组
- 输入是填数
套路:
- 提取初始盘
- 写数独求解器
- 输出填法
通用解题流程
1 | 1. 看文件类型 |
常用工具速查
| 用途 | 工具 |
|---|---|
| 查壳/查类型 | DIE、PEiD、file |
| 静态反编译 | Ghidra、Binary Ninja |
| 动态调试 | GDB + pwndbg、lldb |
| Python 反编译 | uncompyle6、pycdc、PyLingual |
| Java 反编译 | jd-gui、CFR |
| .NET 反编译 | dnSpy、de4dot |
| Android | jadx、apktool、Frida |
| 符号执行 | Angr |
| 脱壳 | UPX、Scylla |
| 十六进制 | 010 Editor、HxD |
新手建议
- 先跑一遍程序,看它要什么输入、输出什么。
- 优先看字符串,很多题 flag 或提示直接暴露。
- 用反编译工具先看主逻辑,不要一上来就啃汇编。
- 遇到算法先搜常量,大概率是已知算法。
- 卡住了就动态调试,静态看不清就下断点跟。
- 多写脚本,Python 是逆向最好的朋友。
- 做完记录套路,CTF 题重复率很高,笔记比刷题更重要。
小结
| 题型 | 核心思路 |
|---|---|
| 明文 flag | strings |
| 异或/加减 | 写逆运算 |
| 逐字符比较 | 提数组 |
| 哈希校验 | 爆破或反推 |
| 加壳 | 脱壳再分析 |
| 花指令 | patch 清理 |
| OLLVM | 动态跟踪 + 反混淆 |
| VM | 分析 handler |
| Python/Java/.NET | 用对应反编译器 |
| 约束求解 | 约束求解器 |
| 反调试 | patch/hook |
| 算法逆向 | 识别常量 + 写解密 |
| 迷宫 | BFS/DFS |
八、Python 逆向实战
Python 逆向在 CTF 里非常常见,核心就三类:
.pyc文件:直接反编译- PyInstaller 打包的 exe:解包出
.pyc再反编译 - 混淆/加密的 Python 代码:动态调试或字节码分析
判断文件类型
1 | file challenge |
常见输出:
| 输出 | 含义 |
|---|---|
Python script, ASCII text |
明文 Python 源码 |
python 3.x byte-compiled |
.pyc 文件 |
PE32 executable |
Windows exe,可能是 PyInstaller |
ELF 64-bit |
Linux 可执行文件,可能是打包的 Python |
查壳:
1 | die challenge.exe |
如果 DIE 显示 PyInstaller,就走 PyInstaller 解包流程。
.pyc 文件反编译
1. 查看 Python 版本
.pyc 文件开头 4 个字节是 magic number,对应 Python 版本。
1 | xxd -l 4 challenge.pyc |
也可以用 Python 查看:
1 | import importlib.util |
常见 magic:
| Python 版本 | magic(十六进制) |
|---|---|
| 3.8 | 550d0d0a |
| 3.9 | 610d0d0a |
| 3.10 | 6f0d0d0a |
| 3.11 | a70d0d0a |
| 3.12 | cb0d0d0a |
版本不对,反编译会失败。
2. 反编译工具
| 工具 | 支持版本 | 安装 |
|---|---|---|
| uncompyle6 | Python 2.7 ~ 3.8 | pip install uncompyle6 |
| decompyle3 | Python 3.7 ~ 3.8 | pip install decompyle3 |
| pycdc | 较广,支持 3.11+ | 编译安装 |
| PyLingual | 在线,支持 3.11+ | 网页版 |
用法:
1 | uncompyle6 -o . challenge.pyc |
如果 uncompyle6 报错,先确认 Python 版本。
3. 反汇编字节码
反编译失败时,用 dis 模块看字节码:
1 | import dis |
关注:
co_consts:常量,可能有密文co_names:变量名、函数名co_code:原始字节码
PyInstaller 打包的 exe
1. 解包
用 pyinstxtractor:
1 | python pyinstxtractor.py challenge.exe |
会生成 challenge.exe_extracted 文件夹。
进去找主文件,通常是一个无后缀文件或 .pyc。
2. 修复文件头
PyInstaller 提取出的 .pyc 可能缺少 magic number,需要补上。
详见本博客 python逆向 这篇文章
然后再用 uncompyle6 或 pycdc 反编译。
3. 常用工具
| 工具 | 用途 |
|---|---|
| pyinstxtractor | 解包 PyInstaller |
| pyinstxtractor-ng | 新版,支持更多版本 |
| uncompyle6 | 反编译 |
| pycdc | 反编译 |
| PyLingual | 在线反编译 |
混淆与加密
1. PyArmor
特征:
- 代码里有
pyarmor字样 - 大量
__pyarmor__变量 - 字符串加密
套路:
- 动态调试,在解密后 dump 代码
- 用
sys.settrace跟踪 - 找解密函数,手动还原
2. 变量名混淆
特征:
- 变量名是
_0x1234、a1、b2之类
套路:
- 根据逻辑重命名
- 用 IDE 重构
3. 控制流平坦化
特征:
- 大量
while True+if跳转 - 状态变量控制流程
套路:
- 动态跟踪状态变量
- 或写脚本还原
4. 代码加密
特征:
exec(base64.b64decode(...))eval(zlib.decompress(...))
套路:
- 把
exec换成print - 或手动解码
例:
1 | import base64, zlib |
实战例子:简单异或校验
假设有 challenge.pyc,反编译后得到:
1 | flag = input('Input: ') |
解密脚本:
1 | enc = [0x12, 0x34, 0x56, 0x78] |
常用命令速查
1 | # 判断文件 |
新手建议
- 先
file和strings,判断类型和找提示。 .pyc先看 magic,版本不对就换 Python。- PyInstaller 先解包,再反编译。
- 反编译失败就用
dis,看字节码手动还原。 - 遇到
exec/eval,先替换成print。 - 多写 Python 脚本,解密、爆破都靠它。
