允许您模拟 Android 原生库,以及实验性的 iOS 模拟。
这是一个教育项目,用于更深入地了解 ELF/MachO 文件格式和 ARM 汇编。
使用风险自负!
unidbg 支持 模型上下文协议 (MCP) 进行 AI 辅助调试。当调试器激活时,在控制台输入 mcp 即可启动 MCP 服务器,AI 工具(例如 Cursor)可以连接该服务器。
unidbg MCP 有两种运行模式:
模式 1:断点调试 —— 附加调试器并运行你的代码。当命中断点时,Breaker.debug() 会暂停模拟器——在控制台输入 mcp 启动 MCP 服务器,让 AI 协助分析。所有调试工具均可用(寄存器、内存、反汇编、单步执行、跟踪等)。恢复执行后,若再次命中断点,调试器会再次暂停。当执行完成且未命中断点时,进程退出,MCP 关闭。
Debugger debugger = emulator.attach();
debugger.addBreakPoint(address);
// run your emulation logic — debugger pauses when breakpoint is hit
模式 2:自定义工具(可重复) —— 使用 McpToolkit 注册自定义工具,让 AI 使用不同参数重新运行目标函数。原生库仅加载一次;每次执行后进程保持存活,MCP 继续保持活动状态以便下一次运行。
McpToolkit toolkit = new McpToolkit();
toolkit.addTool(new McpTool() {
@Override public String name() { return "encrypt"; }
@Override public String description() { return "Run encryption"; }
@Override public String[] paramNames() { return new String[]{"input"}; }
@Override public void execute(String[] params) {
String input = params.length > 0 ? params[0] : "default";
// call encryption with input
}
});
toolkit.run(emulator.attach());
当调试器中断时,在控制台输入 mcp(或 mcp 9239 指定端口)。然后在 Cursor MCP 设置中添加:
{
"mcpServers": {
"unidbg-mcp-server": {
"url": "http://localhost:9239/sse"
}
}
}
状态与信息
| 工具 | 描述 |
|---|---|
check_connection | 模拟器状态:Family、架构、后端能力、isRunning、已加载模块 |
list_modules / get_module_info | 列出已加载模块,获取包括导出符号数量和依赖项在内的详细信息 |
list_exports | 列出模块的导出/动态符号,支持可选过滤和 C++ 符号还原 |
find_symbol | 按名称查找符号,或在地址处查找最近的符号 |
get_threads | 列出模拟器中的所有线程/任务 |
寄存器与反汇编
| 工具 | 描述 |
|---|---|
get_registers / get_register / set_register | 读写 CPU 寄存器 |
disassemble | 对指定地址的指令进行反汇编(分支目标自动标注符号名称) |
assemble | 将指令文本汇编为机器码 |
get_callstack | 获取当前调用堆栈(回溯) |
内存
| 工具 | 描述 |
|---|---|
read_memory / write_memory | 读写原始内存字节 |
read_string / read_std_string | 读取 C 字符串或 C++ std::string(带 SSO 检测) |
read_pointer | 读取带符号解析的指针链 |
read_typed | 以类型化值读取内存(int8–int64、float、double、pointer) |
search_memory | 在内存中搜索字节模式,支持范围/权限过滤 |
list_memory_map | 列出所有内存映射及其权限 |
allocate_memory / free_memory / list_allocations | 分配(malloc/mmap)可附带初始数据,释放并跟踪内存块 |
patch | 将汇编后的指令写入内存 |
断点与执行
| 工具 | 描述 |
|---|---|
add_breakpoint / add_breakpoint_by_symbol / add_breakpoint_by_offset | 按地址、符号或模块+偏移量添加断点 |
remove_breakpoint / list_breakpoints | 移除或列出断点(含反汇编) |
continue_execution | 恢复执行。使用 poll_events 等待 breakpoint_hit 或 execution_completed |
step_over / step_into / step_out | 单步跳过、单步进入(N 条指令)或单步跳出函数 |
next_block | 在下一个基本块处中断(仅限 Unicorn) |
step_until_mnemonic | 在下一个匹配助记符的指令处中断,例如 bl、ret(仅限 Unicorn) |
poll_events | 轮询 breakpoint_hit、execution_completed、trace 事件 |
跟踪
| 工具 | 描述 |
|---|---|
trace_code | 跟踪指令及其寄存器读写值(regs_read、prev_write) |
trace_read / trace_write | 跟踪指定地址范围内的内存读/写 |
函数调用
| 工具 | 描述 |
|---|---|
call_function | 按地址调用原生函数,支持类型化参数(十六进制、字符串、字节、null)。返回值带符号解析和内存预览 |
call_symbol | 按模块 + 符号名称调用导出函数,例如 libc.so + malloc |
仅限 iOS(当 Family=iOS 时可用)
| 工具 | 描述 |
|---|---|
inspect_objc_msg | 检查 objc_msgSend 调用:显示接收者类名和选择器,例如 -[NSString length] |
get_objc_class_name | 获取给定地址处对象的 ObjC 类名(纯内存解析,不改变状态) |
dump_objc_class | 导出 ObjC 类定义(属性、方法、协议、实例变量) |
dump_gpb_protobuf | 以 .proto 格式导出 GPB protobuf 消息结构(仅限 64 位) |
使用 McpToolkit 注册自定义工具,每个工具实现 McpTool 接口。这用简洁且自包含的工具类取代了手动 if-else 分发。此时原生库已完全加载(JNI_OnLoad / 入口点已执行),因此每个工具 execute() 内的代码就是要分析的目标函数逻辑。AI 可以在触发自定义工具之前设置断点和跟踪,然后检查不同输入下的执行结果,而无需重启进程。
Android 示例 —— 参见 Utilities64.java,这是一个带自定义 MCP 工具的 Android JNI 示例:
DalvikModule dm = vm.loadLibrary(new File("libtmessages.29.so"), true);
dm.callJNI_OnLoad(emulator);
cUtilities = vm.resolveClass("org/telegram/messenger/Utilities");
McpToolkit toolkit = new McpToolkit();
toolkit.addTool(new McpTool() {
@Override public String name() { return "aesCbc"; }
@Override public String description() { return "Run AES-CBC encryption on input data"; }
@Override public String[] paramNames() { return new String[]{"input"}; }
@Override public void execute(String[] params) {
byte[] input = params.length > 0 ? params[0].getBytes() : new byte[16];
aesCbcEncryptionByteArray(input);
}
});
toolkit.addTool(new McpTool() {
@Override public String name() { return "aesCtr"; }
@Override public String description() { return "Run AES-CTR decryption on input data"; }
@Override public String[] paramNames() { return new String[]{"input"}; }
@Override public void execute(String[] params) {
byte[] input = params.length > 0 ? params[0].getBytes() : new byte[16];
aesCtrDecryptionByteArray(input);
}
});
toolkit.addTool(new McpTool() {
@Override public String name() { return "pbkdf2"; }
@Override public String description() { return "Run PBKDF2 key derivation"; }
@Override public String[] paramNames() { return new String[]{"password", "iterations"}; }
@Override public void execute(String[] params) {
String password = params.length > 0 ? params[0] : "123456";
int iterations = params.length > 1 ? Integer.parseInt(params[1]) : 100000;
pbkdf2(password.getBytes(), iterations);
}
});
toolkit.run(emulator.attach());
iOS 示例 —— 参见 IpaLoaderTest.java,这是一个带自定义 MCP 工具的 iOS IPA 加载示例:
IpaLoader ipaLoader = new IpaLoader64(ipa, new File("target/rootfs/ipa"));
LoadedIpa loader = ipaLoader.load(this);
emulator = loader.getEmulator();
loader.callEntry();
module = loader.getExecutable();
McpToolkit toolkit = new McpToolkit();
toolkit.addTool(new McpTool() {
@Override public String name() { return "dumpClass"; }
@Override public String description() { return "Dump an ObjC class definition by name"; }
@Override public String[] paramNames() { return new String[]{"className"}; }
@Override public void execute(String[] params) {
String className = params.length > 0 ? params[0] : "AppDelegate";
IClassDumper classDumper = ClassDumper.getInstance(emulator);
System.out.println("dumpClass(" + className + "):\n" + classDumper.dumpClass(className));
}
});
toolkit.addTool(new McpTool() {
@Override public String name() { return "readVersion"; }
@Override public String description() { return "Read the TelegramCoreVersionString from the executable"; }
@Override public void execute(String[] params) {
Symbol sym = module.findSymbolByName("_TelegramCoreVersionString");
if (sym != null) {
Pointer pointer = UnidbgPointer.pointer(emulator, sym.getAddress());
if (pointer != null) {
System.out.println("_TelegramCoreVersionString=" + pointer.getString(0));
}
}
}
});
toolkit.run(emulator.attach());