简介:这份资源是华中科技大学网络空间安全学院逆向工程分析技术实验的完整存档,面向正在修读逆向工程、软件安全相关课程的高校学生,以及希望动手练习逆向分析基础技能的初学者。包内共57个文件,约6.11MB,以35张png实验截图、6个xml配置、2份md说明文档为主,另含exe、i64、apk、dex、so、arsc、py等逆向分析常见样本与脚本,覆盖从静态分析到Android逆向的典型实验场景。资源包含源码与说明书,可自行修改,方便对照实验步骤复现分析过程。目前已有165人学习下载。读者可借助其中的实验样本、操作截图与说明文档,理解逆向分析的基本流程、工具使用与关键思路,适合作为课程实验参考或自主练习的素材。
1. 逆向工程实验包拆解:从静态分析到 APK 脱壳的完整复现路径
很多人对逆向工程的第一印象是「拿 IDA 打开 exe 找 flag」,但真正上手才发现,卡住你的往往不是逆向思路,而是环境跑不起来、文件打不开、脚本报错。这份华中科技大学网络空间安全学院的逆向工程分析技术实验存档,包含两个实验的完整材料:一个 Windows 平台的 PE 逆向实验(含.i64数据库、.exe样本、Python 脚本和答案文件),一个 Android 平台的 APK 逆向实验(含反编译后的classes.dex、AndroidManifest.xml、资源文件和打包 zip)。所有源码和说明书都可以自行修改,适合课程设计参考、实验复现和自学练手。如果你正在找一份能直接跑通的逆向工程课程实验,这份资源省去了从零搭环境的折腾。
2. 实验一 PE 逆向:从 i64 数据库到 Python 求解脚本
2.1 文件构成与工具链选型
拿到Experient-1.i64和Experient-1.exe这两个文件,第一反应应该是确认工具链。.i64是 IDA Pro 64 位数据库文件,意味着原作者用的是 IDA 64 位版本做的静态分析。.exe是待分析的 PE 样本,1.py是配套的求解脚本,ans.txt是预期输出结果。
为什么选 IDA 而不是 Ghidra?在这个实验场景下,IDA 的.i64数据库直接保存了分析状态——函数命名、注释、交叉引用、结构体定义都在里面。你打开.i64就能看到原作者的分析痕迹,相当于拿到了一份带批注的答案。Ghidra 虽然免费,但没法直接加载.i64格式,你得从.exe重新分析,效率差很多。
常见做法是:先用 IDA 打开.i64看分析结果,再用 Python 脚本验证求解逻辑。如果你没有 IDA Pro,可以用 IDA Free 打开.exe重新分析,但要注意 Free 版不支持 64 位反编译,只能看汇编。
2.2 用 IDA 加载 i64 数据库并定位关键函数
打开 IDA,File → Open选择Experient-1.i64。加载完成后,先看左侧函数列表窗口(Functions window),按地址排序,找到main或start入口。如果函数名被 stripped 了,就找entry point附近的调用链。
定位关键逻辑的常用手法:
- 在 Strings window(
Shift+F12)里搜索可疑字符串,比如flag、correct、wrong、input等 - 双击字符串跳到引用位置,用
X键查看交叉引用 - 在反编译窗口(
F5)里看伪代码,重点关注条件跳转和循环结构
// 典型的逆向题伪代码结构(示意) int __cdecl main(int argc, const char **argv, const char **envp) { char input[32]; printf("Enter flag: "); scanf("%s", input); if ( check_flag(input) ) // 关键判断函数 puts("Correct!"); else puts("Wrong!"); return 0; }上面这段伪代码是逆向题里最常见的结构。核心逻辑在check_flag函数里,你需要跟进这个函数,看它怎么处理输入、怎么比较、比较的目标值是什么。在 IDA 里双击函数名就能跳进去。
参数说明:input是用户输入缓冲区,check_flag的返回值决定成败。实际实验中,check_flag可能做了异或、移位、查表等操作,你需要逐步还原。
2.3 Python 脚本还原求解逻辑
1.py是配套的求解脚本。先看它的结构:
# 1.py 典型结构(根据实验内容推断) # 逆向工程实验一求解脚本 def decode(encoded_bytes): """根据逆向分析结果还原 flag""" result = [] for i, b in enumerate(encoded_bytes): # 这里的操作取决于 IDA 里看到的变换逻辑 # 比如异或、加减、查表等 result.append(b ^ 0x41) # 示例:异或密钥 return bytes(result) if __name__ == "__main__": # 从 exe 中提取的密文数据 cipher = [0x32, 0x27, 0x36, 0x20, 0x21] # 示例数据 flag = decode(cipher) print(f"Flag: {flag.decode()}")逻辑说明:脚本的核心是把 IDA 里分析出的变换逻辑用 Python 重写一遍。decode函数接收密文字节数组,按逆向出的算法逐步还原。参数encoded_bytes是从 exe 的.data段或.rdata段提取的常量数组,你需要在 IDA 里找到这些数据的地址和长度。
实际操作步骤:
- 在 IDA 里找到比较用的常量数组,右键 →
Export data导出为.py或.txt - 把导出的数据粘贴到
1.py的cipher变量里 - 根据 IDA 伪代码修改
decode函数里的变换逻辑 - 运行
python 1.py,对比ans.txt验证结果
注意:如果
1.py里的逻辑和 IDA 里看到的不一致,以 IDA 为准。脚本可能是半成品,需要你自己补全。
3. 实验二 APK 逆向:从反编译资源到 dex 分析
3.1 APK 解包后的目录结构与关键文件
实验二的目录结构很清晰:
AliCrackme/ ├── AndroidManifest.xml # 应用清单,声明权限、组件、入口 Activity ├── classes.dex # Dalvik 字节码,核心逻辑所在 ├── resources.arsc # 编译后的资源索引表 ├── res/ # 资源文件目录(布局、字符串、图片等) ├── META-INF/ # 签名信息 ├── lib/ # native 库(如有) └── AliCrackme.zip # 原始 APK 打包AndroidManifest.xml是入口,先看它确定MainActivity是哪个类。classes.dex是重头戏,所有 Java/Kotlin 代码编译后都在这里。resources.arsc和res/目录负责 UI 和字符串资源。
常见做法是:先用apktool反编译得到 smali 代码,再用jadx或dex2jar + jd-gui得到 Java 源码。两种方式各有优劣——smali 更接近底层,Java 源码可读性更好但可能丢失细节。
3.2 用 jadx 反编译 classes.dex 定位校验逻辑
jadx 是目前最顺手的 dex 反编译工具,支持命令行和 GUI。命令行方式:
# 用 jadx 反编译 APK jadx -d output_dir AliCrackme.apk # 或者只反编译 dex jadx -d output_dir classes.dex反编译完成后,在output_dir/sources/下找到MainActivity.java。典型的 Crackme 校验逻辑长这样:
// MainActivity.java 典型校验逻辑(示意) public class MainActivity extends Activity { public void onCheckClick(View v) { String input = editText.getText().toString(); if (checkPassword(input)) { Toast.makeText(this, "Correct!", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "Wrong!", Toast.LENGTH_SHORT).show(); } } private boolean checkPassword(String input) { // 核心校验逻辑 // 可能是字符串比较、哈希校验、native 调用等 return input.equals(buildExpectedString()); } }逻辑说明:onCheckClick是按钮点击回调,checkPassword是核心校验函数。你需要跟进checkPassword,看它怎么构造期望值。如果它调用了 native 方法(native boolean checkPassword(String)),那就需要分析lib/下的.so文件。
参数说明:input是用户输入的密码字符串,buildExpectedString()可能做了拼接、异或、Base64 解码等操作。jadx 的反编译结果通常能直接看懂,遇到混淆的变量名(如a、b、c)就结合上下文推断。
3.3 资源文件与 AndroidManifest 的辅助定位
有时候校验逻辑不在 dex 里,而是藏在资源文件中。比如字符串比较的目标值可能定义在res/values/strings.xml里。用apktool反编译后可以直接看:
# 用 apktool 反编译 APK apktool d AliCrackme.apk -o AliCrackme_decoded # 查看 strings.xml cat AliCrackme_decoded/res/values/strings.xmlAndroidManifest.xml里重点关注:
package属性:应用包名application标签下的android:name:自定义 Application 类activity标签下的android:name:入口 Activitymeta-data标签:可能藏有密钥或配置
提示:如果
AndroidManifest.xml是二进制格式(直接解压 APK 得到的就是二进制),用apktool或AXMLPrinter2转成可读文本。
4. 避坑与常见问题排查
4.1 IDA 打开 i64 报版本不兼容
现象:双击.i64文件,IDA 提示「database version mismatch」或直接闪退。
原因:.i64数据库和 IDA 版本强绑定。原作者用的 IDA 版本和你本地的不一致,高版本数据库无法在低版本 IDA 中打开。
解决:换用同版本或更高版本的 IDA。如果实在没有,就用 IDA 打开.exe重新分析,虽然丢失了原作者的注释,但核心逻辑还在。另一个办法是用 IDA 的File → Produce file → Dump database to IDC导出为 IDC 脚本,但前提是你能打开。
4.2 Python 脚本运行报编码错误
现象:python 1.py报UnicodeDecodeError或SyntaxError: Non-UTF-8 code。
原因:脚本里可能包含中文注释,而文件编码不是 UTF-8。Windows 下默认用 GBK 编码保存,跨平台运行时就会出问题。
解决:用 VS Code 或 Notepad++ 把1.py转成 UTF-8 编码。或者在脚本开头加# -*- coding: utf-8 -*-。如果报的是SyntaxError,检查 Python 版本——Python 2 和 Python 3 的 print 语法不同,这份实验大概率是 Python 3。
4.3 jadx 反编译后代码不全
现象:jadx 打开 APK 后,某些类显示「// decompilation failed」或只有空壳。
原因:代码被混淆了,或者用了 jadx 不支持的字节码特性。也可能是 APK 做了加固,dex 被加密了。
解决:换用dex2jar + jd-gui组合试试。如果还是不行,就看 smali 代码(用apktool d反编译)。smali 虽然可读性差,但不会丢失逻辑。遇到加固的 APK,需要先脱壳——常见工具是 Frida 或 Xposed,但这超出了本实验的范围。
4.4 APK 反编译后资源文件乱码
现象:res/values/strings.xml打开全是乱码,或者resources.arsc无法解析。
原因:直接解压 APK 得到的resources.arsc是二进制格式,不是文本。AndroidManifest.xml同理。
解决:用apktool反编译,它会自动把二进制 XML 转成可读文本。如果apktool报错,试试apktool --force或更新到最新版本。另一个工具是AXMLPrinter2,专门转二进制 XML。
4.5 实验答案对不上
现象:按照脚本跑出来的结果和ans.txt不一致。
原因:可能是脚本里的常量数据没更新,或者 IDA 里看到的逻辑和脚本实现有偏差。也可能是ans.txt本身就是错的(原作者留的坑)。
解决:以 IDA 和 jadx 里看到的实际逻辑为准,逐步调试。在 Python 脚本里加 print 输出中间结果,对比 IDA 里的运行时数据。如果实在对不上,检查样本文件是否完整——.exe可能被截断,.dex可能被修改过。
5. 进阶技巧:用 Frida 动态验证静态分析结果
静态分析再仔细,也可能漏掉运行时才暴露的逻辑。比如 APK 里的校验函数可能依赖运行时生成的密钥,或者 PE 样本在特定条件下才触发关键分支。这时候就需要动态分析来交叉验证。
Frida 是目前最顺手的动态插桩工具。以 APK 实验为例,假设你在 jadx 里看到checkPassword函数,想确认它的实际输入输出:
// frida hook checkPassword 函数 Java.perform(function() { var MainActivity = Java.use('com.example.alicrackme.MainActivity'); MainActivity.checkPassword.implementation = function(input) { console.log('[*] checkPassword called'); console.log('[*] input: ' + input); var result = this.checkPassword(input); // 调用原函数 console.log('[*] result: ' + result); return result; }; });逻辑说明:Java.perform是 Frida 的入口,确保在 Java 虚拟机加载完成后执行。Java.use加载目标类,implementation替换原函数。在替换函数里,你可以打印参数、修改返回值、甚至完全重写逻辑。
参数说明:com.example.alicrackme.MainActivity需要替换成实际的包名和类名(从AndroidManifest.xml里查)。input是原函数的参数,this.checkPassword(input)调用原始实现,避免破坏原有逻辑。
运行方式:
# 前提:设备已 root,frida-server 已运行 frida -U -f com.example.alicrackme -l hook.js --no-pause-U表示 USB 设备,-f表示启动应用,-l加载脚本,--no-pause表示不暂停等待。
对于 PE 实验,可以用 x64dbg 做动态调试。在 IDA 里找到关键地址,在 x64dbg 里下断点,运行样本,观察寄存器和内存变化。静态分析和动态调试结合,基本能覆盖所有情况。
注意:Frida 需要 root 环境,如果没有真机,可以用模拟器(如 Genymotion 或 Android Studio 自带的 AVD)。模拟器要选 x86 架构,frida-server 也要对应架构版本。
从那以后我每次做逆向实验,都强制走一遍「静态定位 → 脚本还原 → 动态验证」的流程。静态分析给你全局视野,脚本还原验证逻辑理解,动态调试兜底运行时行为。三管齐下,基本不会翻车。希望帮到你。
本文还有配套的精品资源,点击获取