frida-tools チートシート

接続可能なプロセスやデバイスを一覧表示したり、プロセスを終了させたりします。

コマンド説明使用例
frida-ps実行中のプロセスを一覧表示します。接続先を指定できます。
# ローカルマシン上のプロセス一覧
frida-ps
# USB接続されたデバイス上のプロセス一覧
frida-ps -U
# リモートデバイス上のプロセス一覧
frida-ps -R
# アプリケーションのプロセスのみ表示 (iOS/Android)
frida-ps -Ua
# インストールされているアプリケーション一覧 (iOS/Android)
frida-ps -Uai
frida-ls-devicesFridaが認識しているデバイスを一覧表示します。
frida-ls-devices
frida-kill指定したプロセスを終了させます。
# プロセスID (PID) を指定して終了 (ローカル)
frida-kill 1234
# USBデバイス上のプロセスIDを指定して終了
frida-kill -U 5678
# リモートデバイス上のプロセスIDを指定して終了
frida-kill -R 9012

ターゲットへの接続と起動

frida コマンドを使用して、既存のプロセスにアタッチしたり、新しいプロセスを起動してインジェクトしたりします。

オプション説明使用例
-U / --usbUSB経由で接続されたデバイスを指定します。frida -U -n Twitter
-R / --remoteリモートのfrida-server(デフォルト: localhost:27042)に接続します。host:port の指定も可能です。frida -R -p 1234
frida -R 192.168.1.100:12345 -f com.example.app
-H HOST / --host HOSTリモートのfrida-serverのホストとポートを指定します。-R と似ていますが、より明示的です。frida -H 10.0.0.5:27042 -f com.example.app
-f FILE / --file FILE指定したアプリケーションを起動し、メインスレッドが実行される前にインジェクトします。(例: Androidのパッケージ名、iOSのバンドルID、実行ファイルパス)frida -U -f com.android.settings
frida -U -f com.apple.Preferences
frida -f /bin/ls
-n NAME / --attach-name NAME指定した名前のプロセスにアタッチします。frida -U -n WeChat
frida -n explorer.exe
-p PID / --attach-pid PID指定したプロセスID (PID) のプロセスにアタッチします。frida -p 1337
--no-pause-f オプションでアプリを起動する際に、初期状態で一時停止させずに即座に実行を開始します。frida -U -f com.example.test --no-pause
-l SCRIPT / --load SCRIPT指定したJavaScriptファイルを読み込んで実行します。frida -U -n Twitter -l myscript.js
-e CODE / --eval CODE指定したJavaScriptコードを直接実行します。REPLに入る代わりにコード実行後に終了します。frida -U -f com.app.id -e "console.log('Hello from Frida!');"
--runtime=qjs|v8使用するJavaScriptランタイムを指定します (デフォルトはV8)。QJSは軽量です。frida -U -f com.example.app --runtime=qjs -l script.js
--realm=native|emulatedネイティブプロセスまたはエミュレートされたプロセスにアタッチするかどうかを指定します。frida -U --realm=emulated -n "emulator-process"
-f, -n, -p は通常、どれか1つを指定します。接続先デバイスに応じて -U, -R, -H を組み合わせます。

関数トレース (frida-trace)

特定の関数呼び出しをトレースします。引数や戻り値、呼び出し元の情報を表示できます。

オプション説明使用例
-i "function"指定した関数をトレース対象に含めます。globパターンが使用可能です。
# open関数をトレース
frida-trace -U -i "open" -p 1234
# libc.so 内の全ての関数をトレース
frida-trace -U -i "libc.so!*" -f com.example.app
# 特定のクラスのメソッドをトレース (Objective-C)
frida-trace -U -i "-[NSString UTF8String]" -f com.apple.AppStore
# 特定のクラスの全てのメソッドをトレース (Objective-C)
frida-trace -U -i "-[MyClass *]" -f com.example.myapp
# 特定のクラスのメソッドをトレース (Java)
frida-trace -U -i "Java:com.example.MainActivity.onCreate" -f com.example.app
# 特定のJavaクラスの全メソッドをトレース
frida-trace -U -i "Java:com.example.SecretClass.*" -f com.example.app
-I "module"指定したモジュール内のすべての関数をトレース対象に含めます。
# libcommon.so 内の全関数をトレース
frida-trace -U -I "libcommon.so" -f com.example.app
-x "function"指定した関数をトレース対象から除外します。-i-I と組み合わせて使用します。
# libc.so の関数をトレースするが、read と write は除外
frida-trace -U -i "libc.so!*" -x "libc.so!read" -x "libc.so!write" -p 1234
-X "module"指定したモジュール内のすべての関数をトレース対象から除外します。
# 全ての関数をトレースするが、libSystem.B.dylib は除外 (macOS/iOS)
frida-trace -f /Applications/Calculator.app -i "*" -X "libSystem.B.dylib"
-a "module!offset"モジュール名とオフセットで関数を指定してトレースします。シンボルがない場合に有効です。
# mylib.so のベースアドレス + 0x1234 の位置にある関数をトレース
frida-trace -U -a "mylib.so!0x1234" -f com.example.app
-T / --include-importsインポートされた関数もトレース対象に含めます (通常はエクスポートされた関数のみ)。
frida-trace -U -i "recv" -T -f com.example.networkapp
-m "objc_method"Objective-C のメソッドをトレースします。-i "-[Class method]"-i "+[Class method]" と同等です。
# -[NSURLRequest initWithURL:] をトレース
frida-trace -U -m "-[NSURLRequest initWithURL:]" -f com.apple.mobilesafari
-M "objc_class"指定したObjective-Cクラスのすべてのメソッドをトレースします。
# MyViewController クラスの全メソッドをトレース
frida-trace -U -M MyViewController -f com.example.myapp
-j "java_method"Javaのメソッドをトレースします。* をワイルドカードとして使用できます。クラス名とメソッド名を指定します。
# java.io.File クラスのコンストラクタをトレース
frida-trace -U -j 'java.io.File.$init' -f com.example.app
# com.example.Utils クラスの全てのメソッドをトレース
frida-trace -U -j 'com.example.Utils.*' -f com.example.app
# 特定のオーバーロードされたメソッドをトレース (シグネチャ指定)
frida-trace -U -j 'com.example.MyClass.myMethod(int, java.lang.String)' -f com.example.app
-J "java_class"指定したJavaクラスのすべてのメソッドとコンストラクタをトレースします。
# android.security.keystore.KeyGenParameterSpec クラスの全メソッドをトレース
frida-trace -U -J 'android.security.keystore.KeyGenParameterSpec' -f com.android.settings
-o output.txtトレース結果を指定したファイルに出力します。
frida-trace -U -i "write" -p 1234 -o trace_output.log
-h / --helpヘルプメッセージを表示します。
frida-trace -h
トレース対象が多すぎるとパフォーマンスに影響が出ることがあります。対象を適切に絞り込むことが重要です。

インタラクティブ操作 (Frida REPL)

frida コマンドでアタッチ後、対話的にJavaScript APIを実行してターゲットプロセスを操作します。

基本的なREPLコマンド

コマンド説明
%resume-f で起動時に一時停止しているプロセスを再開します。
%reload-l でロードしたスクリプトを再読み込みします。
%load script.js新しいスクリプトファイルをロードします。
%unload現在ロードされているスクリプトをアンロードします。
%exitFrida REPLを終了します(プロセスはデタッチされます)。
(JavaScriptコード)任意のJavaScriptコードを実行します。

JavaScript API の基本例

REPL内でよく使われるAPIの例です。

// モジュール一覧表示
Process.enumerateModules().forEach(function(m){ console.log(JSON.stringify(m)); });
// 特定モジュールのエクスポート関数一覧表示
Module.enumerateExports('libc.so').forEach(function(exp){ console.log(exp.name + ': ' + exp.address); });
// モジュールのベースアドレス取得
var baseAddr = Module.findBaseAddress('libnative-lib.so');
console.log('Base address: ' + baseAddr);
// アドレスから関数ポインタ作成
var myFunc = new NativeFunction(baseAddr.add(0x1234), 'int', ['int', 'pointer']);
// 関数呼び出し
var result = myFunc(10, Memory.allocUtf8String('hello'));
console.log('Result: ' + result);
// Javaクラスの利用 (Android)
if (Java.available) { Java.perform(function() { var StringClass = Java.use('java.lang.String'); var myString = StringClass.$new('Hello from Java!'); console.log(myString.toUpperCase()); var MainActivity = Java.use('com.example.app.MainActivity'); // 静的メソッド呼び出し var result = MainActivity.staticMethod(5); console.log('Static method result: ' + result); // インスタンス取得 (例: 既存のインスタンスを探す) Java.choose('com.example.app.MainActivity', { onMatch: function(instance) { console.log('Found instance: ' + instance); // インスタンスメソッド呼び出し console.log(instance.getSomeValue()); instance.setSomeValue(100); }, onComplete: function() { console.log('Instance search complete.'); } }); });
}
// Objective-C クラス/メソッドの利用 (iOS/macOS)
if (ObjC.available) { var NSAutoreleasePool = ObjC.classes.NSAutoreleasePool; var pool = NSAutoreleasePool.alloc().init(); // メモリ管理のため try { var NSString = ObjC.classes.NSString; var nsString = NSString.stringWithString_('Hello from ObjC!'); console.log(nsString.UTF8String()); // C文字列に変換して表示 var UIAlertController = ObjC.classes.UIAlertController; if (UIAlertController) { // クラスメソッド呼び出し var alert = UIAlertController.alertControllerWithTitle_message_preferredStyle_( NSString.stringWithString_('Frida Alert'), NSString.stringWithString_('Hello from Frida!'), 1 // UIAlertControllerStyleAlert ); console.log('Alert Controller created: ' + alert.$ivars.message); // インスタンス変数アクセス (注意: 非公開API) } } finally { pool.release(); }
}

関数フックと書き換え (JavaScript API)

Interceptor.attachJava.use, ObjC.classes を使用して、関数の呼び出しを監視したり、引数や戻り値を改変したりします。

ネイティブ関数 (Interceptor)

// libc の open 関数をフック
var openPtr = Module.findExportByName('libc.so', 'open');
Interceptor.attach(openPtr, { onEnter: function(args) { // args[0] は 첫 번째 인수 (pathname) this.filePath = args[0].readUtf8String(); console.log('open() called with path: ' + this.filePath); // 引数の書き換え例: 特定ファイルを別のファイルにリダイレクト // if (this.filePath.includes('target.txt')) { // args[0] = Memory.allocUtf8String('/data/local/tmp/redirected.txt'); // console.log('Redirecting open() to /data/local/tmp/redirected.txt'); // } }, onLeave: function(retval) { // retval は戻り値 (ファイルディスクリプタ) console.log('open("' + this.filePath + '") returned: ' + retval.toInt32()); // 戻り値の書き換え例: エラーを返す // if (this.filePath.includes('secret.txt')) { // console.log('Forcing open() to fail for secret.txt'); // retval.replace(-1); // EACCES などに対応する errno を返すのがより正確 // } }
});
// 特定アドレスの関数をフック (オフセット指定)
var moduleBase = Module.findBaseAddress('libnative-lib.so');
var targetAddr = moduleBase.add(0x1A2B); // 対象関数のアドレス
Interceptor.attach(targetAddr, { onEnter: function(args) { console.log('Function at ' + targetAddr + ' called.'); console.log('Arg0: ' + args[0].toInt32() + ', Arg1: ' + args[1].readPointer().readUtf8String()); }, onLeave: function(retval) { console.log('Function at ' + targetAddr + ' returned: ' + retval); }
});
// 関数の実装を置き換え (Interceptor.replace)
var mallocPtr = Module.findExportByName(null, 'malloc'); // null は全モジュール検索
var myMalloc = new NativeCallback(function(size) { console.log('malloc() called with size: ' + size); // 元の malloc を呼び出す必要はない。ここで独自のアロケータを実装したり、 // 固定のポインタを返したりできる。ここでは単純にログだけ表示して 0 を返す。 return ptr(0); // NULL を返す例
}, 'pointer', ['size_t']);
// replace する場合、元の関数を呼び出したいなら Interceptor.attach を使う
Interceptor.replace(mallocPtr, myMalloc);
// 注意: replace は危険を伴う。元の実装を完全に置き換えるため、
// プログラムの動作に重大な影響を与える可能性がある。

Java メソッド (Android)

Java.perform(function() { // 特定クラスのメソッドをフック var TargetClass = Java.use('com.example.app.CryptoUtils'); TargetClass.encrypt.implementation = function(data) { console.log('CryptoUtils.encrypt() called with data: ' + data); // 元のメソッドを呼び出す var result = this.encrypt(data); console.log('CryptoUtils.encrypt() returned: ' + result); // 戻り値を書き換える例 // var modifiedResult = "MODIFIED_" + result; // console.log('Returning modified result: ' + modifiedResult); // return modifiedResult; return result; }; // オーバーロードされたメソッドのフック (シグネチャ指定) TargetClass.processData.overload('int', 'java.lang.String').implementation = function(num, str) { console.log('processData(int, String) called with num=' + num + ', str=' + str); // 引数を書き換える例 // var modifiedStr = str + "_hooked"; // console.log('Calling original with modified string: ' + modifiedStr); // return this.processData(num, modifiedStr); return this.processData(num, str); }; // コンストラクタのフック var FileClass = Java.use('java.io.File'); FileClass.$init.overload('java.lang.String').implementation = function(path) { console.log('new File("' + path + '")'); // コンストラクタ呼び出しを書き換えるのは複雑な場合がある // 基本的には元のコンストラクタを呼び出す return this.$init(path); }; // メソッドの実装を完全に乗っ取る (元のメソッドを呼び出さない) TargetClass.getSecretKey.implementation = function() { console.log('getSecretKey() called, returning fake key!'); var StringClass = Java.use('java.lang.String'); return StringClass.$new("FAKE_SECRET_KEY_FROM_FRIDA"); };
});

Objective-C メソッド (iOS/macOS)

if (ObjC.available) { try { // クラスメソッドをフック var NSURL = ObjC.classes.NSURL; Interceptor.attach(NSURL['+ URLWithString:'].implementation, { onEnter: function(args) { // args[0] は self (クラスオブジェクト), args[1] は selector // args[2] が 첫 번째 인수 (NSString *) this.urlString = ObjC.Object(args[2]).toString(); console.log('[NSURL URLWithString:@"' + this.urlString + '"]'); // 引数の書き換え // if (this.urlString.startsWith('http://example.com')) { // var newUrlString = ObjC.classes.NSString.stringWithString_('https://safe.example.com'); // args[2] = newUrlString; // console.log('Rewriting URL to https://safe.example.com'); // } }, onLeave: function(retval) { // retval は戻り値 (NSURL *) console.log('[NSURL URLWithString:] returned: ' + ObjC.Object(retval)); } }); // インスタンスメソッドをフック var LAContext = ObjC.classes.LAContext; // ローカル認証 (Face ID/Touch ID) if (LAContext) { Interceptor.attach(LAContext['- evaluatePolicy:localizedReason:reply:'].implementation, { onEnter: function(args) { console.log('[LAContext evaluatePolicy...]'); // args[2] は policy (LAPolicy) // args[3] は localizedReason (NSString *) // args[4] は reply (block) this.replyBlock = new ObjC.Block(args[4]); // ブロックを取得 var originalReply = this.replyBlock.implementation; // 元のブロック実装 // フックした認証を常に成功させる例 console.log('Forcing authentication success!'); // 新しい実装を持つブロックを作成して呼び出す var newReply = new ObjC.Block(function(success, error) { console.log('Original reply block would have received: success=' + success + ', error=' + error); // 常に success = YES, error = nil で元のコールバックを呼び出す originalReply(true, null); }, 'void', ['bool', 'pointer']); // 元の reply block を置き換える (ここでは直接呼び出す) newReply(true, null); // ダミーの値で呼び出す(上書きされる) // 重要: 元のメソッドの実行をスキップするために reply block を置き換える場合、 // 元のメソッド呼び出し自体を避ける必要がある。 // Interceptor.replace を使うか、onEnter で早期リターンするなど。 // ここでは単純化のため、元のメソッドが呼ばれる前提で reply block を操作している。 // 実際には Interceptor.replace の方が安全な場合がある。 // 元の reply block のポインタを書き換えてしまうことも可能だが、複雑。 // args[4] = newReply.handle; }, onLeave: function(retval) { // このメソッドは非同期なので onLeave はあまり意味がない console.log('[LAContext evaluatePolicy...] returned (async)'); } }); // Interceptor.replace を使った認証バイパス例 (より直接的) // Interceptor.replace(LAContext['- evaluatePolicy:localizedReason:reply:'].implementation, // new NativeCallback(function(self, sel, policy, reason, reply) { // console.log('[REPLACED LAContext evaluatePolicy...] - Forcing success!'); // var replyBlock = new ObjC.Block(reply); // // 認証成功として即座にコールバックを呼び出す // replyBlock.implementation(true, null); // }, 'void', ['pointer', 'pointer', 'long', 'pointer', 'pointer']) // ); } else { console.log('LAContext not available.'); } } catch (e) { console.error('Error setting up ObjC hooks: ' + e); }
} else { console.log('Objective-C runtime not available.');
}

メモリ操作

ターゲットプロセスのメモリを読み書きしたり、特定のパターンを検索したりします。

// 特定アドレスのメモリ読み込み
var addr = ptr('0x12345678'); // 対象アドレス
// 4バイト整数として読み込み
var valueInt = Memory.readInt(addr);
console.log('Int at ' + addr + ': ' + valueInt);
// ポインタとして読み込み
var valuePtr = Memory.readPointer(addr);
console.log('Pointer at ' + addr + ': ' + valuePtr);
// UTF-8文字列として読み込み (長さ指定)
var valueStr = Memory.readUtf8String(addr, 64); // 最大64バイト読む
console.log('String at ' + addr + ': ' + valueStr);
// バイト列として読み込み (ArrayBuffer)
var valueBytes = Memory.readByteArray(addr, 16); // 16バイト読み込み
console.log('Bytes at ' + addr + ': ' + hexdump(valueBytes, { ansi: true }));
// 特定アドレスへのメモリ書き込み
var targetAddr = Module.findExportByName('libtarget.so', 'global_flag');
if (targetAddr) { // 32ビット整数値を書き込む Memory.writeInt(targetAddr, 1); console.log('Wrote 1 to ' + targetAddr); // 文字列を書き込む var strAddr = Memory.allocUtf8String('Modified by Frida'); // グローバル変数が文字列へのポインタだと仮定して書き換え // Memory.writePointer(targetAddr, strAddr); // console.log('Wrote string pointer to ' + targetAddr); // バイト列を書き込む Memory.writeByteArray(targetAddr.add(8), [0xDE, 0xAD, 0xBE, 0xEF]); console.log('Wrote bytes to ' + targetAddr.add(8));
} else { console.log('Could not find global_flag export.');
}
// メモリ確保
var buffer = Memory.alloc(1024); // 1024バイト確保
console.log('Allocated buffer at: ' + buffer);
// 確保したメモリに書き込み
Memory.writeUtf8String(buffer, 'Hello from allocated memory!');
console.log(Memory.readUtf8String(buffer));
// メモリ検索 (Memory.scan)
var moduleName = 'libnative-lib.so';
var searchPattern = '48 8d 3d ?? ?? ?? ?? 48 8d 15'; // x86_64 の LEA 命令などの一部 (?? はワイルドカード)
var module = Process.findModuleByName(moduleName);
if (module) { console.log('Scanning module ' + moduleName + ' (' + module.size + ' bytes) for pattern: ' + searchPattern); Memory.scan(module.base, module.size, searchPattern, { onMatch: function(address, size) { console.log('Pattern found at address: ' + address); // マッチした場所の周辺をダンプ // console.log(hexdump(address, { length: 64, ansi: true })); // ここで見つかったアドレスに対して Interceptor.attach などができる // return 'stop'; // 最初に見つかった時点でスキャンを停止する場合 }, onError: function(reason) { console.error('Memory scan error: ' + reason); }, onComplete: function() { console.log('Memory scan complete.'); } });
} else { console.log('Module ' + moduleName + ' not found.');
}
// Java ヒープ検索 (Android)
if (Java.available) { Java.perform(function() { console.log('Searching for instances of java.lang.String on the heap...'); Java.choose('java.lang.String', { onMatch: function(instance) { // 大量のインスタンスが見つかる可能性があるので注意 if (instance.length() > 50) { // 例: 50文字以上の文字列のみ表示 console.log('Found String instance: "' + instance + '"'); } }, onComplete: function() { console.log('Heap search complete.'); } }); console.log('\nSearching for instances of com.example.SecretData...'); Java.choose('com.example.SecretData', { onMatch: function(instance) { console.log('Found SecretData instance: ' + instance); // インスタンスのフィールドにアクセス console.log(' - key: ' + instance.key.value); console.log(' - value: ' + instance.data.value); }, onComplete: function() { console.log('SecretData search complete.'); } }); });
}

高度な機能

より複雑な解析や操作を行うための機能です。

Stalker (コードトレース)

スレッドの実行を追跡し、実行された命令レベルの情報を収集します。パフォーマンスへの影響が大きいですが、詳細な分析が可能です。

// 特定のスレッドの実行をトレース
var threadId = Process.getCurrentThreadId(); // 現在の REPL スレッド ID (通常はフック対象スレッドを指定)
// 例: 特定関数が呼ばれたらそのスレッドのトレースを開始する
var targetFuncPtr = Module.findExportByName(null, 'sensitive_function');
Interceptor.attach(targetFuncPtr, { onEnter: function(args) { var currentTid = this.threadId; console.log('sensitive_function called in thread ' + currentTid + '. Starting Stalker.'); // このスレッドの追跡を開始 Stalker.follow(currentTid, { events: { call: true, // 関数呼び出しをトレース ret: false, // リターンはトレースしない (ノイズが多い場合) exec: false, // 個々の命令はトレースしない (非常に多い) block: false, // 基本ブロックの実行はトレースしない compile: false // コンパイルイベントはトレースしない }, // 特定の呼び出しイベントが発生したときに呼ばれるコールバック onCallSummary: function(summary) { // summary オブジェクトには呼び出し先と呼び出し元の情報が含まれる // console.log(JSON.stringify(summary)); }, // より詳細な情報が必要な場合 (カスタムコールバック) // transform: function (iterator) { // var instruction = iterator.next(); // do { // // instruction オブジェクトで命令の詳細を取得 // // console.log(instruction.address + ': ' + instruction.mnemonic + ' ' + instruction.opStr); // // 特定の命令や呼び出しを検知したら何かする // if (instruction.mnemonic === 'call') { // // ... // } // iterator.keep(); // 命令を実行させるために必要 // } while ((instruction = iterator.next()) !== null); // }, // onReceive: function (events) { // Stalker.follow で収集されたイベントデータを受け取る (上記 transform とは排他) // var decodedEvents = Stalker.parse(events); // console.log(JSON.stringify(decodedEvents)); // } }); // 一定時間後にストークを停止する例 setTimeout(function() { console.log('Stopping Stalker for thread ' + currentTid); Stalker.unfollow(currentTid); // 必要なら収集したデータを処理 // Stalker.flush(); // キューに残っているデータを強制的に送信 }, 5000); // 5秒間トレース }, onLeave: function(retval) { // Stalker の停止は非同期で行うことが多い }
});
// 注意: Stalker.follow は対象スレッドを一時停止させ、コードを書き換えるため、
// 著しいパフォーマンス低下や、場合によってはクラッシュを引き起こす可能性があります。
// イベントフィルタリングや対象範囲の限定が重要です。

RPC (Remote Procedure Call)

Fridaスクリプト内で関数を定義し、それを外部のPythonスクリプトなどから呼び出すことができます。データのやり取りや複雑な制御に便利です。

Frida スクリプト側 (agent.js)

// Python から呼び出せる関数を定義してエクスポート
function addNumbers(a, b) { console.log('[Agent] addNumbers called with:', a, b); return a + b;
}
function getDeviceInfo() { console.log('[Agent] getDeviceInfo called'); var info = { platform: Process.platform, arch: Process.arch, pid: Process.id, mainModule: Process.enumerateModules()[0].name }; // Java/ObjC API なども利用可能 if (Java.available) { try { Java.performNow(function() { var Build = Java.use('android.os.Build'); var VERSION = Java.use('android.os.Build$VERSION'); info.androidSdk = VERSION.SDK_INT.value; info.deviceModel = Build.MODEL.value; }); } catch (e) { info.javaError = e.message; } } return info;
}
// 非同期処理を含む関数の例 (結果を Promise で返す)
function readFileContent(path) { console.log('[Agent] readFileContent called for path:', path); return new Promise(function(resolve, reject) { try { var file = new File(path, 'r'); var content = file.readAllText(); file.close(); console.log('[Agent] Read ' + content.length + ' bytes.'); resolve(content); } catch (e) { console.error('[Agent] Error reading file:', e.message); reject(e.message); // エラーメッセージを返す } });
}
// 関数をエクスポート
rpc.exports = { add: addNumbers, deviceInfo: getDeviceInfo, readFile: readFileContent // Promise を返す関数もエクスポート可能
};
console.log('[Agent] RPC exports ready.');
// Pythonからの呼び出しを待機
// (このスクリプト自体は特にループなどは不要)

Python 側 (control.py)

import frida
import sys
import time
def on_message(message, data): """ Fridaスクリプトからのメッセージを受信したときのコールバック """ if message['type'] == 'send': print(f"[Frida Agent] {message['payload']}") elif message['type'] == 'error': print(f"[Frida Error] {message['stack']}") else: print(f"[Frida Message] {message}")
# ターゲットプロセスにアタッチ (ここでは例として実行中の 'cat' にアタッチ)
# 実際には frida-ps などで PID や名前を確認して指定する
target_process = "cat" # または PID, Android/iOS アプリ名など
try: # USBデバイス上のプロセスにアタッチする場合 # device = frida.get_usb_device(timeout=1) # session = device.attach(target_process) # ローカルプロセスにアタッチする場合 session = frida.attach(target_process)
except frida.ProcessNotFoundError: print(f"Error: Process '{target_process}' not found.") sys.exit(1)
except frida.TimedOutError: print("Error: Timed out waiting for USB device.") sys.exit(1)
except Exception as e: print(f"Error attaching to process: {e}") sys.exit(1)
# エージェントスクリプトを読み込む
script_path = "agent.js"
try: with open(script_path, "r", encoding="utf-8") as f: script_code = f.read()
except FileNotFoundError: print(f"Error: Script file '{script_path}' not found.") session.detach() sys.exit(1)
script = session.create_script(script_code)
# メッセージハンドラを設定
script.on('message', on_message)
# スクリプトをロード
print("[Python] Loading script...")
script.load()
print("[Python] Script loaded. Accessing RPC exports...")
# RPC関数が利用可能になるまで少し待つ (状況による)
time.sleep(1)
# エクスポートされた関数を呼び出す
try: # 単純な同期呼び出し result_add = script.exports.add(10, 25) print(f"[Python] RPC call add(10, 25) returned: {result_add}") # 情報を取得する同期呼び出し dev_info = script.exports.device_info() print("[Python] RPC call deviceInfo() returned:") for key, value in dev_info.items(): print(f" - {key}: {value}") # Promise を返す非同期関数を呼び出す (Python側では同期的に待機) # 存在しないファイルを読み込もうとしてエラーを発生させる例 try: print("[Python] Calling RPC readFile('/path/to/nonexistent/file.txt')...") content = script.exports.read_file('/path/to/nonexistent/file.txt') print(f"[Python] RPC readFile() returned: {len(content)} bytes") except frida.InvalidOperationError as e: # スクリプト側で reject(errorMessage) された場合、 # Python側では frida.InvalidOperationError が発生し、 # エラーメッセージが取得できる。 print(f"[Python] RPC readFile() failed as expected: {e}") # 存在するファイルを読み込む例 (ターゲット環境に依存) # 例: Android の場合 # target_file = "/data/local/tmp/mydata.txt" # try: # print(f"[Python] Calling RPC readFile('{target_file}')...") # content = script.exports.read_file(target_file) # print(f"[Python] RPC readFile('{target_file}') returned:\n{content[:200]}...") # 最初の200文字表示 # except frida.InvalidOperationError as e: # print(f"[Python] RPC readFile('{target_file}') failed: {e}")
except frida.RPCException as e: print(f"[Python] RPC Error: {e}")
except AttributeError: print("[Python] Error: RPC exports not available yet or script error.")
except Exception as e: print(f"[Python] An unexpected error occurred: {e}")
finally: # スクリプトをアンロードし、セッションをデタッチ print("[Python] Unloading script and detaching...") try: script.unload() session.detach() except frida.TransportError: print("[Python] Target process may have already terminated.") except Exception as e: print(f"[Python] Error during cleanup: {e}")
print("[Python] Finished.")

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です