一、前言
对于安卓逆向传统方法是 脱壳→分析代码→写脚本hook→编写自动化加解密脚本。当前的AI方法是先借助ida mcp+jadx mcp进行分析。再让AI编写frida脚本去hook。通过AI写处加解密脚本,并让AI写出mitmproxy自动化请求重放的脚本。
当前利用“Trae + MCP + Skills + Unidbg” 组合构建一个 AI 驱动的全自动移动安全分析流水线(AI-Powered Mobile Security Pipeline)。以下是基于你提供的工具链(IDA MCP, Jadx MCP, Frida MCP, Unidbg)构建的一套 AI 自动化逆向安卓解决方案。
二、核心架构:AI 逆向流水线 (The Stack)
这套系统的核心逻辑是让 AI 扮演“总工程师”的角色,指挥不同的工具完成特定任务。
| 1. 静态分析 (Java) | Jadx MCP | 分析调用链,定位关键 Java 函数,寻找 JNI 入口 | zinja-coder/jadx-ai-mcp |
| 2. 静态分析 (Native) | IDA MCP | 分析 .so 文件,还原算法逻辑,识别加密/签名特征 | mrexodia/ida-pro-mcp |
| 3. 动态验证 (Runtime) | Frida MCP | Hook Java/Native 层,获取运行时参数(Key, IV, Data) | 1193776794/frida-mcp |
| 4. 算法还原 (Emulation) | Unidbg | 模拟执行 Native 函数,解决无法直接 Python 化的复杂逻辑 | zhkl0228/unidbg |
| 5. 控制中枢 | Trae + Skills | 编写 Python 脚本,整合上述工具的输出,生成最终 API | 你的核心逻辑 |
三、分步实施方案
1.第一步:环境搭建 (Setup)
你需要先将这些开源工具集成到你的本地环境,并注册到 Trae/Claude 中。
- Python >= 3.10
- IDA Pro 8.3+ (用于 Native 分析)
- Jadx-GUI (用于 Java 分析)
- Frida Server (在手机或模拟器上)
- Android Studio (用于运行 Unidbg)
配置 MCP Servers: 需要将三个 MCP 服务器配置到你的 Trae 对应配置文件中:
{ "mcpServers": { "frida-agent": { "command": "frida-mcp" } } }
{ "mcpServers": { "jadx-mcp-server": { "command": "C:\\\\python3\\\\python.exe", "args": [ "E:\\\\mcp\\\\jadx-mcp-server-6.3\\\\jadx_mcp_server.py", "–jadx-port", "8650" ] } } }
{ "mcpServers": { "ida-pro-mcp": { "url": "http://127.0.0.1:13337/mcp" } } }
2.第二步:编写 Trae Skills (核心逻辑)
这是让 AI 自动化的关键。你需要编写几个自定义的 Skills(Python 脚本),让 AI 能够调用。
Skill 1: reverse_engineer_app (全链路分析技能)
- 输入: APK 路径,目标 URL (如 /api/sign)
- AI 动作:
- 调用 Jadx MCP (get_source, search_method) 找到发起请求的 Java 类。
- 识别出是 Java 实现还是 JNI (Native) 实现。
- 如果是 Native,提取 .so 文件名和函数名。
Skill 2: analyze_native_algorithm (IDA 深度分析技能)
- 输入: .so 文件路径,函数名
- AI 动作:
- 调用 IDA MCP (decompile, get_asm) 获取伪代码。
- Prompt 关键点: "请分析这段代码,指出哪些是 AES/MD5/RSA,哪些是自定义魔改算法。如果是标准算法,请直接给出 Python 实现;如果是魔改,请标记出关键的 Magic Number。"
Skill 3: generate_unidbg_starter (Unidbg 模拟生成技能)
- 输入: .so 文件,函数签名,参数类型
- AI 动作:
- 基于 Unidbg 模板,自动生成一个 Java 项目代码,用于加载该 .so 并调用该函数。
- 原理: 利用 AI 的代码生成能力,将 IDA 分析出的参数类型(int, char*, struct)自动转换为 Unidbg 的调用代码。
Skill 4: write_mitmproxy_script (自动化脚本生成技能)
- 输入: 算法逻辑(Python 实现或 Unidbg 调用逻辑)
- AI 动作:
- 生成 mitmproxy 脚本。
- 实现:抓包 -> 修改 Request -> 调用 Python 算法计算签名 -> 注入 Header -> 发送。
- 侦察阶段: 使用 Jadx MCP 分析 APK,找到网络请求入口,判断签名是在 Java 层还是 Native (.so) 层。
- 分析阶段:
- Java 层: 直接阅读代码,生成 Python 版本。
- Native 层: 使用 IDA MCP 加载对应的 .so 文件,分析函数逻辑。
- 验证阶段: 使用 Frida MCP Hook 关键函数,打印输入输出,确认算法逻辑。
- 还原阶段:
- 标准算法: 使用 Python (pycryptodome) 直接重写。
- 复杂/魔改算法: 调用 generate_unidbg_starter Skill 生成模拟执行代码。
- 交付阶段: 编写 mitmproxy 脚本,集成上述逻辑,实现一键重放。
4.AI 提示词 (Prompt) 设计
为了让这套系统跑起来,你的 System Prompt 需要非常明确地定义工作流。
"你是一名顶尖的 Android 逆向工程师,拥有 Jadx、IDA Pro、Frida 和 Unidbg 的操作权限。你的任务是帮助用户分析 App 的加签逻辑并生成抓包脚本。
工作流规则:
四、jadx1、jadx安装与配置
1.下载jadx并对其进行安装
https://github.com/skylot/jadx

2.配置与项目同级,不需要使用的时候,就把缓存删了

2.jadx-ai-mcp安装与配置
1.下载jadx-ai-mcp和jadx-mcp-server
https://github.com/zinja-coder/jadx-ai-mcp
下面两个文件都要下载,一个安装在jadx上,一个是用于启动mcp联动大模型。

2.安装jadx-ai-mcp插件

插件安装成功:

查看插件状态:


3.jadx-mcp-server安装和配置
当前目录:

我使用的Python版本3.11.2

使用pip3对其进行安装:
pip3 install -r requirements.txt
4、trae配置MCP
查看运行jadx-ai-mcp的python.exe路径

添加MCP
{ "mcpServers": { "jadx-mcp-server": { "command": "C:\\\\python3\\\\python.exe", "args": [ "E:\\\\mcp\\\\jadx-mcp-server-6.3\\\\jadx_mcp_server.py", "–jadx-port", "8650" ] } } }

配置成功

MCP默认在 Builder with MCP中

如果有多个MCP,不想混在一起用的话,可以自定义一个智能体

4、jadx+mcp+trae使用
要正常使用,需要我们新建项目或者选择一个文件夹,并在@里选择需要得智能体

2.trae打开apk的目录窗口 提示词输入:
借助jadx-mcp工具,分析这个APP的网络通信报文的加解密流程以及分析 AndroidManifest.xml 存在哪些安全风险(进行自动化分析)
五、Unidbg
Unidbg是一个基于QEMU的Android Native层模拟框架,能够帮助我们在不依赖实际设备的情况下进行逆向分析。
1.安装Java JDK
在开始之前,需要确保安装了Java JDK,因为Unidbg是用Java编写的。
访问Oracle官网下载适合您操作系统的Java JDK版本。

2.安装IDEA
IntelliJ IDEA是一个强大的Java IDE,它将帮助您更容易地编写和调试代码。
访问IntelliJ IDEA官网下载社区版或专业版。
下载安装程序后,双击运行并一路选择“下一步”完成安装。

2.下载与运行Unidbg
从GitHub上获取Unidbg源代码
https://github.com/zhkl0228/Unidbg
3.加载Unidbg项目
对下载的Unidbg ZIP文件进行解压
打开IntelliJ IDEA,选择“Open”或“Import Project”,然后选择到Unidbg项目所在的文件夹。
进行maven正确的配置

六、找到并执行测试文件MainActivity
在项目的文件目录中,找到示例测试文件MainActivity。
右击MainActivity,选择“Run ‘MainActivity.main()’”。
注意测试文件中的.so的路径


完成以上步骤后,如果一切顺利,您的Unidbg环境就设置完成了,您现在可以开始利用此环境来模拟ARM应用程序了。
一、准备工作
在开始之前,我们需要确保以下环境准备就绪:
二、基本使用方法
首先,我们创建一个基本的Unidbg项目来加载并模拟一个Android Native库。
2. 调用本地函数
假设我们有一个函数 int add(int a, int b),我们可以通过Unidbg来调用它并获取返回值
//找到函数的地址 long addFuncAddr = module.findSymbolByName("add").getAddress(); // 设置参数并调用 int a = 2; int b = 3; Number result = emulator.getBackend().emulate(addFuncAddr, null, new int[]{a,b}); System.out.println("Result of add: " + result.intValue());
三、实例讲解
1. 模拟JNI调用
在逆向工程中,很多时候我们需要模拟Java层和Native层之间的JNI调用。Unidbg提供了强大的DalvikVM来模拟这一过程。
public class JNIExample { public static void main(String[] args) { AndroidEmulator emulator = AndroidEmulator.builder().setProcessName("com.example.app").build(); VM vm = emulator.createDalvikVM(new File("path/to/apkfile.apk")); // 加载本地库 Module module = emulator.loadLibrary(new File("path/to/libnative-lib.so"), true); // 获取JNIEnv指针 long jniEnv = vm.getJNIEnv(); // 模拟JNI调用 DvmClass nativeClass = vm.resolveClass("com/example/app/NativeMethods"); nativeClass.callStaticJniMethodInt(emulator, "nativeAdd(II)I", 2, 3); } }
2. 逆向复杂函数
假设我们有一个复杂的函数 int complexCalculation(int x, int y),我们需要逆向分析其内部逻辑。
public class ComplexCalculation { public static void main(String[] args) { AndroidEmulator emulator = AndroidEmulator.builder().setProcessName("com.example.app").build(); Module module = emulator.loadLibrary(new File("path/to/libnative-lib.so")); // 找到函数地址 long calcFuncAddr = module.findSymbolByName("complexCalculation").getAddress(); // 设置参数 int x = 5; int y = 7; // 调用函数并分析返回值 Number result = emulator.getBackend().emulate(calcFuncAddr, null, new int[]{x, y}); System.out.println("Result of complexCalculation: " + result.intValue()); // 使用Inspector工具查看寄存器和内存状态 Inspector.inspect(emulator.getBackend().reg_read_unicorn(ArmConst.UC_ARM_REG_R0), "R0"); } }
3.完整的脚本
package com.unidbg.analysis; import com.github.unidbg.AndroidEmulator; import com.github.unidbg.Module; import com.github.unidbg.arm.backend.Unicorn2Backend; import com.github.unidbg.debugger.BreakPoint; import com.github.unidbg.debugger.Debugger; import com.github.unidbg.file.FileResult; import com.github.unidbg.file.IOResolver; import com.github.unidbg.hook.hookzz.HookZz; import com.github.unidbg.linux.android.AndroidEmulatorBuilder; import com.github.unidbg.linux.android.AndroidResolver; import com.github.unidbg.linux.android.dvm.*; import com.github.unidbg.linux.file.SimpleFileIO; import com.github.unidbg.memory.Memory; import com.github.unidbg.pointer.UnidbgPointer; import com.github.unidbg.utils.Inspector; import com.sun.jna.Pointer; import unicorn.ArmConst; import java.io.File; import java.io.PrintStream; import java.util.Arrays; /** * Unidbg SO文件分析工具 * 功能:加载Android Native库,调用导出函数,Hook监控,内存分析 * * @author Unidbg Analysis Tool */ public class SoAnalysisTool { // 配置参数 private static final String SO_FILE_PATH = "libnative-lib.so"; private static final String APK_FILE_PATH = "app.apk"; private static final String PACKAGE_NAME = "com.example.app"; private static final int SDK_VERSION = 23; // Unidbg核心组件 private final AndroidEmulator emulator; private final VM dalvikVM; private final Memory memory; private Module targetModule; /** * 构造函数 – 初始化模拟环境 */ public SoAnalysisTool() { System.out.println("[初始化] 创建Android模拟器…"); // 1. 创建模拟器(64位ARM架构) emulator = AndroidEmulatorBuilder .for64Bit() .setProcessName(PACKAGE_NAME) .addBackendFactory(new Unicorn2Backend.BackendFactory()) .build(); // 2. 获取内存管理器 memory = emulator.getMemory(); // 3. 设置系统库解析器(Android SDK 23) memory.setLibraryResolver(new AndroidResolver(SDK_VERSION)); // 4. 创建Dalvik虚拟机 File apkFile = new File(APK_FILE_PATH); if (apkFile.exists()) { dalvikVM = emulator.createDalvikVM(apkFile); System.out.println("[初始化] 使用APK文件: " + APK_FILE_PATH); } else { dalvikVM = emulator.createDalvikVM(); System.out.println("[初始化] 未找到APK文件,创建空虚拟机"); } // 5. 开启详细日志 dalvikVM.setVerbose(true); System.out.println("[初始化] 模拟器初始化完成\\n"); } /** * 加载SO文件 * * @param soPath SO文件路径 * @return 加载的模块 */ public Module loadSoFile(String soPath) { System.out.println("[加载] 开始加载SO文件: " + soPath); File soFile = new File(soPath); if (!soFile.exists()) { throw new RuntimeException("SO文件不存在: " + soPath); } // 加载SO文件 targetModule = emulator.loadLibrary(soFile); // 调用初始化函数(JNI_OnLoad) targetModule.callInitFunction(emulator); // 打印模块信息 printModuleInfo(); System.out.println("[加载] SO文件加载成功\\n"); return targetModule; } /** * 方式1:通过函数地址直接调用 * * @param functionName 函数名 * @param args 参数列表 * @return 返回值 */ public Number callFunctionByAddress(String functionName, Object… args) { System.out.println("[调用] 通过地址调用函数: " + functionName); // 查找符号 long funcAddr = targetModule.findSymbolByName(functionName).getAddress(); System.out.println("[调用] 函数地址: 0x" + Long.toHexString(funcAddr)); // 准备参数 int[] intArgs = Arrays.stream(args) .filter(arg -> arg instanceof Integer) .mapToInt(arg -> (Integer) arg) .toArray(); // 调用函数 Number result = emulator.getBackend().emulate(funcAddr, null, intArgs); System.out.println("[调用] 返回值: " + result); return result; } /** * 方式2:通过JNI模拟调用 * * @param className Java类名 * @param methodName 方法名(带签名) * @param args 参数 * @return 返回值 */ public Object callFunctionByJNI(String className, String methodName, Object… args) { System.out.println("[JNI调用] 类名: " + className); System.out.println("[JNI调用] 方法: " + methodName); // 解析Java类 DvmClass dvmClass = dalvikVM.resolveClass(className); if (dvmClass == null) { throw new RuntimeException("无法解析类: " + className); } // 调用静态JNI方法 Object result = dvmClass.callStaticJniMethodObject(emulator, methodName, args); System.out.println("[JNI调用] 返回值: " + result); return result; } /** * 调用带基本类型返回值的JNI方法 * * @param className Java类名 * @param methodName 方法名(带签名) * @param args 参数 * @return int返回值 */ public int callJNIIntMethod(String className, String methodName, Object… args) { System.out.println("[JNI调用] 调用int返回方法: " + methodName); DvmClass dvmClass = dalvikVM.resolveClass(className); if (dvmClass == null) { throw new RuntimeException("无法解析类: " + className); } int result = dvmClass.callStaticJniMethodInt(emulator, methodName, args); System.out.println("[JNI调用] 返回值: " + result); return result; } /** * Hook函数 – 监控函数调用 * * @param functionName 要Hook的函数名 */ public void hookFunction(String functionName) { System.out.println("[Hook] 设置Hook点: " + functionName); long funcAddr = targetModule.findSymbolByName(functionName).getAddress(); // 使用HookZz框架进行Hook HookZz hookZz = HookZz.getInstance(emulator); hookZz.hook(funcAddr, new HookZz.HookCallback() { @Override public void preCall(Emulator<?> emulator, HookZz.RegContext context, HookEntryInfo info) { System.out.println("\\n[Hook] ===== 进入函数: " + functionName + " ====="); // 打印参数(ARM64:X0-X7为参数寄存器) for (int i = 0; i < 8; i++) { long regValue = emulator.getBackend().reg_read(i).longValue(); System.out.printf("[Hook] 参数 X%d: 0x%x (%d)\\n", i, regValue, regValue); } } @Override public void postCall(Emulator<?> emulator, HookZz.RegContext context, HookEntryInfo info) { // 获取返回值(X0寄存器) long retValue = emulator.getBackend().reg_read(0).longValue(); System.out.println("[Hook] 返回值: 0x" + Long.toHexString(retValue) + " (" + retValue + ")"); System.out.println("[Hook] ===== 退出函数: " + functionName + " =====\\n"); } }); System.out.println("[Hook] Hook设置完成\\n"); } /** * 设置断点调试 * * @param functionName 函数名 */ public void setBreakpoint(String functionName) { System.out.println("[调试] 设置断点: " + functionName); long funcAddr = targetModule.findSymbolByName(functionName).getAddress(); Debugger debugger = emulator.attach(); debugger.addBreakPoint(funcAddr); System.out.println("[调试] 断点已设置,地址: 0x" + Long.toHexString(funcAddr)); } /** * 追踪函数调用(指令级追踪) * * @param functionName 函数名 * @param startAddress 开始追踪的地址偏移 * @param endAddress 结束追踪的地址偏移 */ public void traceFunction(String functionName, long startAddress, long endAddress) { System.out.println("[追踪] 开始追踪函数: " + functionName); long baseAddr = targetModule.base; long start = baseAddr + startAddress; long end = baseAddr + endAddress; emulator.getBackend().hook_add_new(new AbstractCodeHook() { @Override public void hook(Backend backend, long address, int size, Object user) { long offset = address – baseAddr; System.out.printf("[追踪] 0x%x (偏移: 0x%x)\\n", address, offset); // 读取指令 byte[] code = new byte[4]; backend.mem_read(address, code); System.out.printf("[追踪] 指令: %02x %02x %02x %02x\\n", code[0], code[1], code[2], code[3]); } }, start, end, null); System.out.println("[追踪] 追踪范围: 0x" + Long.toHexString(start) + " – 0x" + Long.toHexString(end)); } /** * 读取内存数据 * * @param address 内存地址 * @param size 读取大小 * @return 字节数组 */ public byte[] readMemory(long address, int size) { byte[] data = new byte[size]; emulator.getBackend().mem_read(address, data); return data; } /** * 写入内存数据 * * @param address 内存地址 * @param data 要写入的数据 */ public void writeMemory(long address, byte[] data) { emulator.getBackend().mem_write(address, data); System.out.println("[内存] 写入 " + data.length + " 字节到地址 0x" + Long.toHexString(address)); } /** * 读取字符串 * * @param address 字符串地址 * @return 字符串内容 */ public String readString(long address) { Pointer pointer = UnidbgPointer.pointer(emulator, address); if (pointer != null) { return pointer.getString(0); } return null; } /** * 打印模块信息 */ public void printModuleInfo() { if (targetModule == null) { System.out.println("[信息] 模块未加载"); return; } System.out.println("\\n========== 模块信息 =========="); System.out.println("名称: " + targetModule.name); System.out.println("基址: 0x" + Long.toHexString(targetModule.base)); System.out.println("大小: " + targetModule.size + " bytes"); System.out.println("入口点: 0x" + Long.toHexString(targetModule.entryPoint)); // 打印导出函数 System.out.println("\\n导出函数列表:"); targetModule.getSymbols().stream() .filter(sym -> sym.getName() != null && !sym.getName().isEmpty()) .limit(20) // 限制输出20个 .forEach(sym -> System.out.printf(" – %s (0x%x)\\n", sym.getName(), sym.getAddress())); System.out.println("=============================\\n"); } /** * 打印寄存器状态 */ public void printRegisters() { System.out.println("\\n========== 寄存器状态 =========="); // ARM64寄存器 String[] regNames = {"X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7", "X8", "X9", "X10", "X11", "X12", "X13", "X14", "X15", "X16", "X17", "X18", "X19", "X20", "X21", "X22", "X23", "X24", "X25", "X26", "X27", "X28", "FP", "LR", "SP", "PC"}; for (int i = 0; i < regNames.length; i++) { long value = emulator.getBackend().reg_read(i).longValue(); System.out.printf("%-4s: 0x%016x (%d)\\n", regNames[i], value, value); } System.out.println("===============================\\n"); } /** * 导出分析报告 * * @param filename 报告文件名 */ public void exportAnalysisReport(String filename) { System.out.println("[报告] 生成分析报告: " + filename); try (PrintStream ps = new PrintStream(new File(filename))) { ps.println("SO文件分析报告"); ps.println("=============="); ps.println("文件: " + SO_FILE_PATH); ps.println("基址: 0x" + Long.toHexString(targetModule.base)); ps.println("大小: " + targetModule.size); ps.println("\\n导出函数:"); targetModule.getSymbols().stream() .filter(sym -> sym.getName() != null && !sym.getName().isEmpty()) .forEach(sym -> ps.printf(" %s @ 0x%x\\n", sym.getName(), sym.getAddress())); System.out.println("[报告] 报告生成成功"); } catch (Exception e) { System.err.println("[报告] 生成失败: " + e.getMessage()); } } /** * 清理资源 */ public void cleanup() { System.out.println("\\n[清理] 释放资源…"); try { if (emulator != null) { emulator.close(); } System.out.println("[清理] 资源释放完成"); } catch (Exception e) { System.err.println("[清理] 错误: " + e.getMessage()); } } /** * 主函数 – 完整分析流程示例 */ public static void main(String[] args) { SoAnalysisTool analyzer = null; try { // 1. 初始化分析工具 analyzer = new SoAnalysisTool(); // 2. 加载SO文件 analyzer.loadSoFile(SO_FILE_PATH); // 3. 设置Hook监控(可选) analyzer.hookFunction("complexCalculation"); analyzer.hookFunction("add"); // 4. 方式1:直接调用函数 System.out.println("\\n=== 测试1: 直接调用函数 ==="); Number addResult = analyzer.callFunctionByAddress("add", 10, 20); System.out.println("10 + 20 = " + addResult.intValue()); // 5. 方式2:通过JNI调用 System.out.println("\\n=== 测试2: JNI调用 ==="); int jniResult = analyzer.callJNIIntMethod( "com/example/app/NativeMethods", "nativeAdd(II)I", 100, 200 ); System.out.println("JNI调用结果: " + jniResult); // 6. 分析复杂函数 System.out.println("\\n=== 测试3: 复杂函数分析 ==="); Number complexResult = analyzer.callFunctionByAddress("complexCalculation", 5, 7); System.out.println("complexCalculation(5,7) = " + complexResult); // 7. 打印寄存器状态 analyzer.printRegisters(); // 8. 导出分析报告 analyzer.exportAnalysisReport("so_analysis_report.txt"); } catch (Exception e) { System.err.println("分析过程中出现错误: " + e.getMessage()); e.printStackTrace(); } finally { // 9. 清理资源 if (analyzer != null) { analyzer.cleanup(); } } } } /** * 简单的代码Hook基类 */ abstract class AbstractCodeHook implements com.github.unidbg.arm.backend.CodeHook { @Override public void hook(Backend backend, long address, int size, Object user) { // 由子类实现 } } /** * Backend接口定义 */ interface Backend extends com.github.unidbg.arm.backend.Backend { // 扩展接口 } /** * 模拟器接口扩展 */ interface Emulator<T extends Backend> extends com.github.unidbg.Emulator<T> { // 扩展接口 }
来自为知笔记(Wiz)



