【Androidリバース】フリーダ入門と共通メモ
予備知識
フックとは何ですか?
フックは「フック」と訳され、メソッド/関数をフックすることを指し、その後、やりたいことを実行できるようになります。実際、いくつかのツールを使用して、メソッド/関数の呼び出しを Java およびネイティブ レベルで「フック」することも、それをモニタリングとして直接理解することもでき、メソッドの呼び出し、メソッドのパラメータの受け渡し、および変更されたメソッドの戻りをモニタリングできます。 .値など
フックの機能
これらのツールを理解することは、問題の特定、デバッグ、さらには一部のアプリのクラックや変更をより適切に行うのに役立ちます。
一般的なリバースツール
ツール/方法 | 効果 | Java層 | ネイティブ層 | SDKの統合 | ルートが必要ですか | 述べる | 他の |
---|---|---|---|---|---|---|---|
JADX | 逆コンパイルしてソースコードを表示する | サポート | サポートしません | なし | なし | 強化された APK を最初に解凍する必要があります | |
apktool | APKを逆コンパイル/再コンパイルする | なし | なし | なし | なし | メールコードを変更する | |
エピック/サンドフック | アプリにはコードフック用の SDK が統合されています | サポート | サポート | 必要 | 不要 | Xped フレームワーク、さまざまなシステム API に一貫性がないため、調整する必要がある | |
xposed プラグイン | xused フレームワークと統合された Android 環境をフックする | サポート | サポートしません | 不要 | 必要 | root が必要、その他は上記と同じ | |
フリーダ | root化されたAndroid環境では、java/nativeに対してバイナリフックを直接実行します。 | サポート | サポート | 不要 | 必要 | root環境、Python環境が必要 |
フリーダを使い始める
概要と参考ノート
Frida は、Java/ネイティブ レイヤ フックをサポートし、ルート環境を必要とするバイナリ フック フレームワークです。バイナリ メモリ フックであるため、SDK を統合したり、プロセスを再起動したりせずに、現在のプロセスを直接フックできます。
事前環境構成
- Python3とpip環境をインストールする
- pip を使用して frida ライブラリとツールをインストールする
// 安装特定版本 pip install frida==版本号
pip install frida
pip install frida-tools
// 网络不好使用镜像库
pip install firda -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
// 查看当前frida版本
frida --version
- エミュレータを使用する場合、ポートフォワーディング(デフォルトポート)
adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043
- 現在のデバイス/シミュレータのアーキテクチャに従って、公式 Web サイトから対応する frida-server をダウンロードし、デバイス/シミュレータのディレクトリにプッシュして、実行権限を付与します。
# 这里放于 /data/local/tmp
adb push E:\frida-server /data/local/tmp/frida-server
# 进入adb shell
adb shell
# 以管理员权限访问
su
# 进入frida-server目录
cd /data/local/tmp
# 提供权限
chmod 777 frida-server
# 运行frida-server
./frida-server
- frida-server を実行した後、この時点ではウィンドウを開いたままにし、新しいウィンドウを開いて成功したかどうかを確認します。
# 命令成功输出进程列表
frida-ps -U
# 根据包名连接目标进程
frida -U -f com.xxx.xxx
フックを実行する
- Python スクリプトはフックを実行します。サンプルコードは次のとおりです。
import frida, sys
def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
jscode = """
Java.perform(() => {
// Function to hook is defined here
const MainActivity = Java.use('com.example.seccon2015.rock_paper_scissors.MainActivity');
// Whenever button is clicked
const onClick = MainActivity.onClick;
onClick.implementation = function (v) {
// Show a message to know that the function got called
send('onClick');
// Call the original onClick handler
onClick.call(this, v);
// Set our values after running the original onClick handler
this.m.value = 0;
this.n.value = 1;
this.cnt.value = 999;
// Log to the console that it's done, and we should have the flag!
console.log('Done:' + JSON.stringify(this.cnt));
};
});
"""
# pid or package name
process = frida.get_usb_device().attach(13347)
script = process.create_script(jscode)
script.on('message', on_message)
print('[*] Running CTF')
script.load()
sys.stdin.read())
script = process.create_script(jscode)
script.on('message', on_message)
print('[*] Running CTF')
script.load()
sys.stdin.read()
- jsスクリプトを直接挿入する
frida -U [pid|packagename] -l test.js
一般的に使用されるフック スクリプト/API
- テストコードクラス
public class TestStaticClass {
public static int count = 0;
private static String TAG = "TestStaticClass";
public static String getCountString(){
Log.i(TAG, "call testMethod");
return "count:" + count;
}
public static void addCount(){
count++;
}
public static String getCountString(int input){
return "getCountString:" + input;
}
public static String getCountString(int[] input){
return "getCountString:" + Arrays.toString(input);
}
public void testStack(){
Log.i(TAG, "call testStack");
}
}
- Javaクラスを取得して新しいオブジェクトを構築する
方法 | 意味 | 他の |
---|---|---|
$new | 新しいオブジェクト | |
$init | コンストラクタ |
const JavaString = Java.use('java.lang.String');
var exampleString1 = JavaString.$new('Hello World, this is an example string in Java.');
-
クラスおよび静的関数のメソッド呼び出しを取得する
Java.perform(() => { const TestStaticClass = Java.use("com.hjl.nativetest.TestStaticClass"); TestStaticClass.count.value = 1; //访问静态变量 TestStaticClass.addCount(); //hook静态函数直接调用 });
-
フックメソッドはメソッド値を出力し、戻り値を変更します。
Java.perform(() => { // 获取类 const TestStaticClass = Java.use("com.hjl.nativetest.TestStaticClass"); // 获取方法 const getCountString = TestStaticClass.getCountString; // hook方法 getCountString.implementation = function () { // 当方法调用时 send('call getCountString'); // 调用原方法获取结果 var result = getCountString.call(this); console.log("getCountString:" + result); // 返回自定义的结果 return "hook return String"; }; // 获取重载方法 // 基础类型直接填,数组以类似JNI的签名形式如[I ,类型填完整类 const getCountString2 = TestStaticClass.getCountString.overload('int') getCountString2.implementation = function (data) { // 打印原始输入参数 send('call getCountString:' + data); // 调用原方法获取结果 var result = getCountString2.call(this,data); console.log("getCountString:" + result); // 返回自定义的结果 return "hook return String"; }; });
-
コールスタックを取得する
Java.perform(() => { const TestStaticClass = Java.use("com.hjl.nativetest.TestStaticClass"); const Exception = Java.use('java.lang.Exception'); const Log = Java.use('android.util.Log'); const testStack = TestStaticClass.testStack testStack.implementation = function () { console.log(stackTraceHere()); }; function stackTraceHere() { return Log.getStackTraceString(Exception.$new()); } });