逆向题的最小分析闭环
逆向题的最小分析闭环
本文适合
打开 IDA/Ghidra 后容易被函数数量吓住,或者只会截图反编译结果但写不出还原脚本的逆向入门学习者。学完你能:用"运行 -> 字符串 -> 交叉引用 -> 输入变换 -> 比较条件 -> 验证脚本"完成逆向题最小闭环。
一句话判断
逆向题不是读懂整个程序,而是找到输入如何进入、如何被变换、最后和什么条件比较。
最小闭环:
只要能回答"输入经过什么变换、和什么比较、怎么还原或绕过",就已经抓住主线。
题目中常见信号
优先判断:直接比较或校验函数
起手动作:strings 后找交叉引用
优先判断:壳、混淆或静态链接
起手动作:查壳、熵、入口点、动态运行
优先判断:明文或变换后比较
起手动作:断比较函数,看参数
优先判断:可逆字节变换
起手动作:提取常量,写逆运算脚本
优先判断:校验函数入口
起手动作:从调用点回溯输入
优先判断:Java 层转 native 层
起手动作:JADX 看入口,IDA/Ghidra 看 .so
优先判断:语言特征明显
起手动作:先用对应工具恢复符号或伪代码
核心概念
要问的问题:程序从哪里读用户数据
证据:stdin、argv、文件、网络、Intent
要问的问题:成功/失败分支在哪里
证据:字符串交叉引用、条件跳转、返回值
要问的问题:输入被怎么处理
证据:循环、查表、异或、加密、hash
要问的问题:程序拿什么做比较
证据:硬编码数组、资源、网络返回值
要问的问题:怎样进入正确分支
证据:比较相等、返回 0、校验通过
要问的问题:分析是否能复现
证据:输出正确输入或 patch 后走成功分支
逆向时不要把"看懂某个函数"当目标;目标是还原可验证的输入到输出关系。
最小分析流程
- 运行程序:记录提示、输入格式、成功/失败输出和崩溃现象。
- 识别文件类型:
file、Detect It Easy、JADX、dnSpy、Ghidra 等按类型选。 - 扫字符串:找
flag、correct、wrong、password、URL、算法名。 - 找交叉引用:从成功/失败字符串定位判断逻辑,而不是从入口逐行读。
- 确定输入变量:看
scanf/read/fgets/argv/EditText等输入如何传入校验函数。 - 提取变换和常量:记录循环、数组、密钥、表、比较函数。
- 写最小脚本验证:能还原输入、枚举候选、模拟校验或 patch 成功分支。
- 复核运行结果:把脚本输出喂回程序,确认不是误读反编译。
静态记录模板:
输入变量:
定位函数:
变换过程:
目标常量:
比较方式:
成功条件:
验证方式:最小验证示例
1. 从字符串定位主线
file ./challenge
strings ./challenge | grep -iE "flag|correct|wrong|pass|success"判断:
出现 Correct/Wrong -> 在反汇编器里找交叉引用
只有随机字符串 -> 继续看壳、加密常量或动态输出
出现算法名 AES/MD5/Base64 -> 转算法识别和参数定位2. 比较函数断点
break strcmp
break memcmp
run断下后记录:
参数 1:用户输入或变换后缓冲区
参数 2:目标字符串或常量地址
返回值:是否影响成功分支如果比较函数从未断下,说明校验可能是手写循环、hash、加密后比较或被静态链接改名。
3. 可逆字节变换还原
target = [0x34, 0x26, 0x26, 0x21, 0x27, 0x3e, 0x2c, 0x35]
password = ''.join(chr(x ^ 0x55) for x in target)
print(password)判断:
脚本输出能让程序进入成功分支 -> 逆向闭环成立
脚本输出乱码或失败 -> 检查数组顺序、字符有符号性、长度和终止符常见利用 / 解题路线
路线总览:
适用场景:成功/失败提示明显
关键动作:从字符串回到判断函数
适用场景:strcmp/memcmp 类校验
关键动作:动态读取比较两边
适用场景:xor/add/sub/rol/查表
关键动作:提取常量并写反向逻辑
适用场景:AES/TEA/RC4/MD5/Base64
关键动作:找 key/iv/nonce/目标 hash
适用场景:只需验证成功路径或取资源
关键动作:改条件跳转,确认 flag 生成位置
适用场景:运行时值难静态看清
关键动作:Frida/GDB/x64dbg 打印参数
适用场景:多轮条件、符号表达式
关键动作:Z3/angr 建模验证
平台专项按文件类型进入:
- APK:[[APK逆向基础]]、[[JNI深入]]
- Python:[[Python字节码逆向]]
- .NET:[[DotNET逆向基础]]
- Go/Rust:[[Go与Rust逆向特征]]
- WASM:[[WebAssembly逆向]]
- VM/混淆:[[VM虚拟机逆向]]、[[控制流平坦化]]
常见失败原因
可能原因:从入口逐行读,没有定位点
排查动作:先运行、扫字符串、找交叉引用
可能原因:常量顺序、字节符号或终止符错
排查动作:打印中间值,对照调试器内存
可能原因:反调试、动态解密或输入格式差异
排查动作:断输入函数和比较函数确认运行时值
可能原因:有壳、混淆、加密字符串或 stripped
排查动作:查壳、动态运行、断 API、找解密循环
可能原因:flag 运行时生成或二次解密
排查动作:继续回溯成功分支后的数据来源
可能原因:key/iv/mode/padding 或 endian 错
排查动作:记录参数来源,写小样例验证
迷你案例
程序运行后提示 Enter password:,输入 test 返回 Wrong password!。先用 strings 找到 Correct! The flag is:,在 Ghidra 中查看交叉引用,定位到 main 里调用 check_password。
进入 check_password 后发现循环:每个输入字符先异或 0x55,再与硬编码数组比较。把数组抄出后写脚本逆向异或,得到候选 password。把候选输入程序后进入成功分支。
记录闭环:
输入点:stdin
定位点:Correct/Wrong 字符串交叉引用
变换:input[i] ^ 0x55
目标:硬编码数组
验证:脚本还原 password,程序输出 Correct
结论:分析闭环成立这比"我大概看懂了 check 函数"更可靠,因为最终脚本和程序输出互相验证。