
Androidネイティブライブラリのエミュレートと、実験的なiOSエミュレーションを可能にします。
Androidネイティブライブラリと、実験的なiOSエミュレーションをエミュレートできます。
これは、ELF/MachOファイル形式とARMアセンブリを学ぶための教育用プロジェクトです。
自己責任で使用してください!
unidbgは Model Context Protocol (MCP) に対応しており、AI支援デバッグが可能です。デバッガがアクティブなときにコンソールで mcp と入力するとMCPサーバーが起動し、AIツール(例: Cursor)が接続できます。
unidbg MCP には2つの動作モードがあります:
モード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_code | レジスタ読み書き値(regs_read、prev_write)付きで命令をトレース |
trace_read / trace_write | アドレス範囲内のメモリ読み書きをトレース |
関数呼び出し
| ツール | 説明 |
|---|---|
call_function | 型付き引数(hex、string、bytes、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クラス定義(プロパティ、メソッド、プロトコル、ivars)をダンプ |
dump_gpb_protobuf | GPB protobufメッセージスキーマを.proto形式でダンプ(64ビットのみ) |
McpToolkit を使用して、それぞれが McpTool インターフェースを実装するカスタムツールを登録します。これにより、手動のif-else分岐が、クリーンで自己完結型のツールクラスに置き換わります。この時点でネイティブライブラリは完全にロードされており(JNI_OnLoad / エントリポイントは既に実行済み)、各ツールの execute() 内のコードが解析対象の関数ロジックになります。AIはカスタムツールを起動する前にブレークポイントやトレースを設定でき、プロセスを再起動することなく異なる入力に対する実行結果を検査できます。
Androidの例 — カスタムMCPツールを使用したAndroid JNIの例については、Utilities64.java を参照してください:
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の例 — カスタムMCPツールを使用したiOS IPAロードの例については、IpaLoaderTest.java を参照してください:
IpaLoader ipaLoader = new IpaLoader64(ipa, new File("target/rootfs/ipa"));
LoadedIpa loader = ipaLoader.load(this);
emulator = loader.getEmulator();
loader.callEntry();
module = loader.getExecutable();