对于安卓逆向传统方法是 脱壳→分析代码→写脚本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 扮演“总工程师”的角色,指挥不同的工具完成特定任务。
你需要先将这些开源工具集成到你的本地环境,并注册到 Trae/Claude 中。
配置 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"
}
}
}
这是让 AI 自动化的关键。你需要编写几个自定义的 Skills(Python 脚本),让 AI 能够调用。
Skill 1: reverse_engineer_app (全链路分析技能)
/api/sign)get_source, search_method) 找到发起请求的 Java 类。Skill 2: analyze_native_algorithm (IDA 深度分析技能)
decompile, get_asm) 获取伪代码。Skill 3: generate_unidbg_starter (Unidbg 模拟生成技能)
Skill 4: write_mitmproxy_script (自动化脚本生成技能)
mitmproxy 脚本。为了让这套系统跑起来,你的 System Prompt 需要非常明确地定义工作流。
"你是一名顶尖的 Android 逆向工程师,拥有 Jadx、IDA Pro、Frida 和 Unidbg 的操作权限。你的任务是帮助用户分析 App 的加签逻辑并生成抓包脚本。
工作流规则:
generate_unidbg_starter Skill 生成模拟执行代码。1.下载jadx并对其进行安装
https://github.com/skylot/jadx
2.配置与项目同级,不需要使用的时候,就把缓存删了

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

2.安装jadx-ai-mcp插件

插件安装成功:

查看插件状态:


当前目录:

我使用的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,不想混在一起用的话,可以自定义一个智能体

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

2.trae打开apk的目录窗口 提示词输入:
借助jadx-mcp工具,分析这个APP的网络通信报文的加解密流程以及分析 AndroidManifest.xml 存在哪些安全风险(进行自动化分析)
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库。
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.LibraryResolver;
import com.github.unidbg.Module;
import com.github.unidbg.arm.backend.BackendFactory;
import com.github.unidbg.linux.android.AndroidElfLoader;
import com.github.unidbg.linux.android.dvm.DalvikVM;
import com.github.unidbg.linux.android.dvm.DvmClass;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.spi.LibraryFile;
import com.github.unidbg.utils.Inspector;
import java.io.File;
public class UnidbgExample {
public static void main(String[] args) {
// 初始化模拟器
AndroidEmulator emulator = AndroidEmulator.builder().setProcessName("com.example.app").build();
Memory memory = emulator.getMemory();
memory.setLibraryResolver(new DefaultLibraryResolver());
// 加载本地库
File soFile = new File("path/to/libnative-lib.so");
Module module = emulator.loadLibrary(soFile);
// 打印模块信息
module.callInitFunction(emulator);
System.out.println("Module loaded: " + module);
}
}
假设我们有一个函数 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());
在逆向工程中,很多时候我们需要模拟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);
}
}
假设我们有一个复杂的函数 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> {
// 扩展接口
}