
简介这份资源是华中科技大学网络空间安全学院逆向工程分析技术实验的完整存档面向正在修读逆向工程、软件安全或移动安全课程的高校学生以及需要完成同类课程设计的学习者。包内共57个文件约6.11MB以35张png实验截图、6个xml配置、2份md说明文档为主另含exe与i64可执行样本、apk与dex安卓逆向目标、so动态库、rsa与py脚本等覆盖PE静态分析、IDA调试、安卓APK反编译等典型实验环节。资源附有说明书源码与实验材料均可自行修改便于对照复现实验流程、理解逆向分析思路并完成报告撰写。目前已有165人学习下载适合作为课程实验参考与逆向入门练手素材。1. 逆向工程分析技术实验从一份华科课件包说起拿到「华中科技大学网络空间安全学院-逆向工程分析技术实验-内含源码和说明书」这个包时多数人第一反应是解压、翻目录、找 README然后卡在第一步——环境跑不起来。这不是个例。逆向工程分析技术实验的核心价值不在于它来自哪所学校而在于它把「静态分析 动态调试 反混淆」这条链路压缩成了可复现的实验任务。源码和说明书的存在意味着你可以改参数、换样本、重跑流程而不是对着一个黑匣子猜。适合谁适合已经会写 C/Python、能看懂汇编片段、但没系统做过逆向实验的从业者。如果你正在找一份能直接上手改的逆向工程实验素材这个包的结构和思路值得拆开看。下面按「实验环境怎么搭 → 静态分析怎么做 → 动态调试怎么跑 → 反混淆怎么落地 → 避坑 → 进阶验证」的顺序推。2. 实验环境搭建与源码结构拆解2.1 先看清包里有什么源码、说明书、样本三件套这类实验包通常由三部分组成实验指导书说明书、待分析的二进制样本或源码、以及辅助脚本。说明书负责告诉你每个实验的目标和步骤源码是你需要逆向或修改的对象辅助脚本一般是 Python 写的自动化分析工具。拿到包后不要急着跑先做一件事用tree或find把目录结构打印出来确认文件类型分布。# 查看目录结构排除无关文件 find . -maxdepth 3 -type f | head -50 # 统计文件类型分布 find . -type f -name *.c -o -name *.py -o -name *.exe -o -name *.elf | awk -F. {print $NF} | sort | uniq -c逻辑说明第一条命令限制深度为 3避免输出过长第二条按扩展名统计快速判断包里是源码为主还是二进制为主。参数说明-maxdepth 3控制递归深度head -50防止刷屏。如果发现.exe或.elf文件占多数说明实验偏动态调试如果.c和.py占多数说明偏静态分析和脚本改写。常见做法是先把说明书通读一遍标记出每个实验对应的样本文件和预期输出。我一般会在说明书里用不同颜色标注三类信息环境依赖、操作步骤、验证标准。环境依赖包括编译器版本、Python 包、调试器版本操作步骤是你要执行的命令序列验证标准是实验成功的判据比如某个内存地址的值、某个函数的返回值。2.2 环境依赖的版本对齐别让工具链成为第一个坑逆向工程实验对环境版本极其敏感。说明书里写的gcc 9.4和你机器上的gcc 13可能导致汇编输出完全不同。常见做法是用 Docker 或 conda 隔离环境。如果说明书没有指定版本按以下原则选32 位样本用gcc -m3264 位样本用默认Python 脚本统一用python3.8以上但注意pwntools和capstone的版本兼容性。# 创建隔离环境以 conda 为例 conda create -n re_lab python3.9 conda activate re_lab pip install capstone pwntools angr z3-solver # 验证关键库版本 python -c import capstone; print(capstone.__version__) python -c import angr; print(angr.__version__)逻辑说明capstone用于反汇编pwntools用于二进制交互angr用于符号执行z3-solver用于约束求解。参数说明python3.9是兼容性较好的版本angr对 Python 版本有上限要求太新的版本可能装不上。如果安装失败优先降级angr到 9.2.x 系列。提示不要用系统自带的 Python 直接装这些库依赖冲突会让你后悔药都没得吃。隔离环境是逆向实验的第一道保险。2.3 编译与反编译工具链的最小验证环境搭好后用一个最小样本验证工具链是否正常。写一个简单的 C 程序编译后反汇编确认能看到预期的汇编代码。// test.c - 最小验证样本 #include stdio.h int check(int x) { if (x 0x1234) return 1; return 0; } int main() { int a; scanf(%d, a); if (check(a)) printf(pass\n); else printf(fail\n); return 0; }gcc -o test test.c -O0 objdump -d test | grep -A 20 check逻辑说明-O0关闭优化保证汇编与源码逻辑对应objdump -d反汇编grep -A 20定位check函数。参数说明如果换成-O2check可能被内联你就找不到独立函数了。这一步的目的是确认「编译 → 反汇编 → 阅读」这条链路通畅。如果objdump输出乱码检查是否用了-m32但缺少 32 位库。3. 静态分析从源码到反汇编的对照阅读3.1 用 objdump 和 Ghidra 建立第一份反汇编视图静态分析的第一步是拿到可读的反汇编。objdump适合快速查看Ghidra 适合深度分析。我一般先用objdump定位关键函数再用 Ghidra 看反编译伪代码。# 生成完整反汇编文件 objdump -d -M intel test test.asm # 提取所有函数符号 objdump -t test | grep F .text逻辑说明-M intel指定 Intel 语法比 ATT 语法更易读-t输出符号表grep F .text过滤出代码段函数。参数说明如果样本被 strip 过符号表为空需要用 Ghidra 的自动分析功能恢复函数边界。Ghidra 的用法是新建项目 → 导入样本 → 自动分析 → 在 Symbol Tree 里找main或entry。常见做法是先用 Ghidra 的Decompiler窗口看伪代码再对照Listing窗口的汇编。如果伪代码里有undefined4或in_EAX这类变量说明类型推断失败需要手动定义函数签名。3.2 关键函数的识别字符串引用与交叉引用逆向工程的核心技能之一是快速定位关键函数。最有效的方法是找字符串引用。比如样本里有pass和fail顺着引用就能找到判断逻辑。# 用 capstone pefile 提取字符串引用以 PE 为例 import pefile from capstone import * pe pefile.PE(test.exe) for section in pe.sections: if b.rdata in section.Name: data section.get_data() # 查找字符串偏移 idx data.find(bpass) if idx ! -1: print(fString pass at RVA: {section.VirtualAddress idx:#x})逻辑说明pefile解析 PE 结构定位.rdata段中的字符串section.VirtualAddress idx计算出字符串的 RVA。参数说明如果是 ELF 文件改用pyelftoolsidx是段内偏移加上虚拟地址才是内存地址。拿到 RVA 后在 Ghidra 里按G跳转然后看交叉引用XREF就能找到引用该字符串的指令。注意字符串可能被加密或混淆直接搜不到。这时候需要先跑动态调试在内存里 dump 字符串。3.3 控制流还原if-else 和 switch 的汇编模式静态分析绕不开控制流还原。if-else在汇编里通常是cmpjccswitch是跳转表。以check函数为例x 0x1234编译后是cmp DWORD PTR [rbp-0x4], 0x1234 jne 0x4005b6逻辑说明cmp比较jne不相等则跳转。参数说明[rbp-0x4]是局部变量x的栈地址。如果是switch你会看到jmp QWORD PTR [rax*80x4006a0]这样的间接跳转后面跟一个地址表。还原方法是在 Ghidra 里右键跳转表 →Data→Address Table自动解析出所有 case 目标。常见误用是看到jne就以为是if其实也可能是循环的退出条件。判断依据是跳转目标是否在当前位置之前。向前跳是循环向后跳是分支。4. 动态调试用 GDB 和 x64dbg 验证静态推断4.1 GDB 断点设置与内存查看的最小命令集动态调试是验证静态分析结论的唯一手段。GDB 的核心命令不多但组合起来很灵活。gdb -q ./test # 在 check 函数下断点 (gdb) break check (gdb) run # 输入测试值 (gdb) info registers rdi (gdb) x/4x $rsp (gdb) disassemble /r $pc逻辑说明break check在函数入口下断run启动程序info registers rdi查看第一个参数System V AMD64 调用约定x/4x $rsp查看栈顶 4 个 16 进制值disassemble /r $pc反汇编当前指令及机器码。参数说明/r显示原始字节用于确认指令编码$pc是程序计数器。如果样本是 32 位参数在栈上用x/4x $esp查看。常见做法是在cmp指令前下断然后单步执行观察标志位变化。4.2 用 pwntools 脚本化调试流程手动敲 GDB 命令效率低用 pwntools 可以脚本化。from pwn import * context.log_level debug p process(./test) # 发送测试输入 p.sendline(b4660) # 0x1234 4660 # 接收输出 print(p.recvall())逻辑说明process启动本地程序sendline发送输入recvall读取全部输出。参数说明4660是0x1234的十进制。如果输出pass说明静态分析判断的0x1234是正确的。如果输出fail检查是否大小端或输入格式有问题。提示pwntools 的context.arch要设置正确否则p32/p64会打包错误。AMD64 用context.arch amd64i386 用i386。4.3 反调试对抗识别 ptrace 和时序检测真实样本往往带反调试。最常见的是ptrace(PTRACE_TRACEME, 0, 0, 0)如果已经被调试则返回 -1。// 反调试检测示例 #include sys/ptrace.h if (ptrace(PTRACE_TRACEME, 0, 0, 0) -1) { printf(debugger detected\n); return 1; }逻辑说明PTRACE_TRACEME允许父进程跟踪自己如果已经被调试调用失败。参数说明绕过方法是在 GDB 里catch syscall ptrace然后修改返回值。或者在ptrace调用前下断强制$rax 0。时序检测是另一种反调试通过rdtsc测量指令执行时间。如果时间过长说明被单步调试。绕过方法是跳过检测或修改比较阈值。5. 反混淆与自动化分析从 angr 到符号执行5.1 用 angr 求解约束以 check 函数为例angr 是符号执行框架适合自动求解输入。以check函数为例目标是找到让check返回 1 的输入。import angr import claripy proj angr.Project(./test, auto_load_libsFalse) # 创建符号变量 arg claripy.BVS(arg, 32) # 构造初始状态从 main 开始 state proj.factory.entry_state() # 设置标准输入为符号变量 state.posix.stdin.content (arg, 4) simgr proj.factory.simulation_manager(state) # 探索到 pass 分支 simgr.explore(findlambda s: bpass in s.posix.dumps(1)) if simgr.found: found simgr.found[0] print(found.solver.eval(arg, cast_tobytes))逻辑说明claripy.BVS创建 32 位符号变量state.posix.stdin.content将符号变量绑定到标准输入simgr.explore探索到输出包含pass的状态found.solver.eval求解具体值。参数说明auto_load_libsFalse避免加载系统库拖慢速度cast_tobytes输出原始字节。如果 angr 跑不出结果常见原因是路径爆炸。解决方法是限制探索深度或添加avoid条件。5.2 符号执行的边界什么时候不该用 angrangr 不是万能的。以下场景不适合样本包含大量系统调用或文件 IO样本使用复杂加密算法样本有反符号执行检测。我一般先用静态分析缩小范围只在关键判断函数上用 angr。如果 5 分钟内没结果就换手动分析。注意angr 对 Windows 样本的支持弱于 Linux ELF。如果是 PE 文件优先用 x64dbg 手动调试。5.3 自动化脚本的工程化日志、超时、结果校验把分析脚本写成可复用的工具而不是一次性代码。关键要素日志记录、超时控制、结果校验。import logging import signal logging.basicConfig(levellogging.INFO, format%(asctime)s %(levelname)s %(message)s) def timeout_handler(signum, frame): raise TimeoutError(analysis timeout) signal.signal(signal.SIGALRM, timeout_handler) signal.alarm(300) # 5 分钟超时 try: # 分析逻辑 result solve_constraint() logging.info(fresult: {result}) except TimeoutError: logging.error(analysis timed out) finally: signal.alarm(0)逻辑说明signal.alarm(300)设置 5 分钟超时timeout_handler抛出异常finally取消定时器。参数说明300是秒数根据样本复杂度调整。结果校验可以用已知输入验证比如0x1234应该输出pass。6. 避坑与常见问题排查6.1 反汇编地址与源码行号对不上现象GDB 里list显示的行号和objdump的地址不匹配。原因编译时开了优化或者调试信息被 strip。解决重新用-O0 -g编译确保.debug_info段存在。如果样本不可重编译用 Ghidra 的Debug视图手动映射。6.2 angr 报 unsat 但手动分析有解现象angr 求解返回 unsat但手动跟踪发现输入确实能让程序输出pass。原因符号变量绑定错误或者路径约束遗漏。解决检查state.posix.stdin.content是否正确设置用simgr.active查看当前状态数添加state.options.add(angr.options.ZERO_FILL_UNCONSTRAINED_MEMORY)避免未约束内存导致误判。6.3 GDB 断点不生效现象break check后run程序直接跑完断点没触发。原因样本是 PIE位置无关可执行文件函数地址在运行时才确定。解决用break *check或先start再下断或者在 GDB 里set disable-randomization on关闭 ASLR。6.4 反调试导致程序行为异常现象直接运行正常GDB 里运行输出错误。原因ptrace反调试检测到调试器。解决在 GDB 里catch syscall ptrace收到信号后return强制返回 0或者用LD_PRELOAD劫持ptrace函数。6.5 字符串搜索找不到关键提示现象strings命令没有输出pass或fail。原因字符串被加密或动态生成。解决在 GDB 里对printf下断运行到断点后x/s $rdi查看实际参数字符串或者用ltrace跟踪库函数调用。7. 进阶验证用差分分析确认补丁效果逆向工程的最终验证是修改样本并观察行为变化。以check函数为例把0x1234改成0x5678重新编译确认只有输入0x5678时输出pass。这一步能验证你对判断逻辑的理解是否准确。# 修改源码后重新编译 sed -i s/0x1234/0x5678/ test.c gcc -o test_patched test.c -O0 # 用 pwntools 验证 python3 -c from pwn import * p process(./test_patched) p.sendline(b22136) # 0x5678 22136 print(p.recvall()) 逻辑说明sed替换常量gcc重新编译pwntools发送新值验证。参数说明22136是0x5678的十进制。如果输出pass说明补丁生效。更进阶的做法是二进制补丁用hexedit或radare2直接修改指令中的立即数不重新编译。radare2的命令是wx写入字节wa写入汇编。比如把cmp eax, 0x1234改成cmp eax, 0x5678需要修改机器码中的立即数字节。验证方法适用场景工具风险源码修改重编译有源码gcc pwntools低二进制补丁无源码radare2 / hexedit中可能破坏指令对齐内存补丁动态调试GDB / x64dbg高仅运行时有效符号执行验证复杂约束angr低但可能超时我一般先用源码修改验证逻辑再用二进制补丁验证对机器码的理解。两步都通过才说明静态分析和动态调试的结论一致。这套流程跑熟之后拿到新的逆向工程实验包你就能在半小时内判断出它的难度和可改空间。希望帮到你。本文还有配套的精品资源点击获取