プロセスとデバイスの操作
接続可能なプロセスやデバイスを一覧表示したり、プロセスを終了させたりします。
コマンド | 説明 | 使用例 |
---|---|---|
frida-ps | 実行中のプロセスを一覧表示します。接続先を指定できます。 |
|
frida-ls-devices | Fridaが認識しているデバイスを一覧表示します。 |
|
frida-kill | 指定したプロセスを終了させます。 |
|
ターゲットへの接続と起動
frida
コマンドを使用して、既存のプロセスにアタッチしたり、新しいプロセスを起動してインジェクトしたりします。
オプション | 説明 | 使用例 |
---|---|---|
-U / --usb | USB経由で接続されたデバイスを指定します。 | 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パターンが使用可能です。 |
|
-I "module" | 指定したモジュール内のすべての関数をトレース対象に含めます。 |
|
-x "function" | 指定した関数をトレース対象から除外します。-i や -I と組み合わせて使用します。 |
|
-X "module" | 指定したモジュール内のすべての関数をトレース対象から除外します。 |
|
-a "module!offset" | モジュール名とオフセットで関数を指定してトレースします。シンボルがない場合に有効です。 |
|
-T / --include-imports | インポートされた関数もトレース対象に含めます (通常はエクスポートされた関数のみ)。 |
|
-m "objc_method" | Objective-C のメソッドをトレースします。-i "-[Class method]" や -i "+[Class method]" と同等です。 |
|
-M "objc_class" | 指定したObjective-Cクラスのすべてのメソッドをトレースします。 |
|
-j "java_method" | Javaのメソッドをトレースします。* をワイルドカードとして使用できます。クラス名とメソッド名を指定します。 |
|
-J "java_class" | 指定したJavaクラスのすべてのメソッドとコンストラクタをトレースします。 |
|
-o output.txt | トレース結果を指定したファイルに出力します。 |
|
-h / --help | ヘルプメッセージを表示します。 |
|
トレース対象が多すぎるとパフォーマンスに影響が出ることがあります。対象を適切に絞り込むことが重要です。
インタラクティブ操作 (Frida REPL)
frida
コマンドでアタッチ後、対話的にJavaScript APIを実行してターゲットプロセスを操作します。
基本的なREPLコマンド
コマンド | 説明 |
---|---|
%resume | -f で起動時に一時停止しているプロセスを再開します。 |
%reload | -l でロードしたスクリプトを再読み込みします。 |
%load script.js | 新しいスクリプトファイルをロードします。 |
%unload | 現在ロードされているスクリプトをアンロードします。 |
%exit | Frida 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.attach
や Java.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.")