教材:『Android Frida リバースとパケットキャプチャの実戦』 陳佳林/著
基本的な環境
- Android Studioをインストールしてadbを設定する
- root化された実マシンまたはAndroid仮想マシン
第 3 章 Frida 逆エントリ Java 層フック
3.1 フリーダの基本
3.1.3 フリーダの基礎知識
フリーダには 2 つの動作モードがあります
- CLI (コマンドラインモード) は、
コマンドラインを介してプロセスに js コードを挿入します。 - RPC モード
Python を介してプロセスに js コードを挿入します。基本的にフックには js コードを使用します。
frida がアプリを操作するには 2 つの方法があります
- spwanモードでは
アプリの起動権限がfridaに渡され、対象のアプリが起動しても
fridaによって再起動されます -fパラメータを指定することでspwanモードでアプリを動作させることができます - アタッチ モード
アプリが開始され、frida は ptrace を介してプログラムを挿入してフックを実行します。Frida はデフォルトでアタッチ モード操作アプリを使用します
3.1.4Frida IDE の設定
- ノードとnpm環境をインストールする
- git ダウンロード frida-agent-example リポジトリ
git clone https://github.com/oleavr/frida-agent-example.git 下载frida-agent-example仓库
cd frida-agent-example/
npm install
vscode を使用して開き、frida-agent-example フォルダー内にフォルダーを作成してスクリプトを作成します
3.2 frida スクリプトの使用を開始する
3.2.1 frida スクリプトの概念
例:
setTimeout(function(){
Java.perform(function(){
console.log("hello,world")
})
})
代码分析:
1. 调用setTimeout方法将匿名函数注册到js运行库中
2. 在函数中调用Java.perform方法,将匿名函数注册到App的java运行库中,并执行函数
携帯電話で frida-server を実行した後、 frid-ps -U コマンドを使用して実行中のプロセスを表示し、
frida -U -l test0.js android.process.mediaを使用してスクリプトをプロセスに挿入できます。注入後に helloworld が出力されることがわかります。
上記のパラメータでは、-U は USB デバイスを指定し、-l はインジェクション スクリプトが配置されているパスを指定します。最後の androdi.process.media はデバイス上で実行されているプロセスの名前です。
3.2.2 Java レイヤーフックの基本
1. フックに関する事前検討
Androidスタジオのプロジェクトコード
package com.example.hooktest1;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import java.util.Locale;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int x=0,y=0;
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
fun(x,y);
x++;y++;
}
}
void fun(int x,int y)
{
Log.d("sum=",String.valueOf(x+y));
}
}
実行結果: Android Studio コンソールは x+y を出力し続け、x と y は徐々に増加します。
フックスクリプトコード:
function main(){
console.log("Script loaded success")
//Java.perform是frida的api函数,可以将其中的脚本注入到java运行库,参数是一个匿名函数
//函数的主体内容是监控和修改java函数逻辑的主题内容,任何针对java层的操作都必须在这个api函数中
Java.perform(function(){
console.log("Inside java perform!")
//java.use获取hook函数所在类的类名
var Mainactivity=Java.use('com.example.hooktest1.MainActivity')
console.log("Java.use.success!")
//通过.连接函数名,比如这里的函数名是fun,表示想hookfun函数
//implementation表示实现该函数,也就是hook掉,这里可以写自己的函数
Mainactivity.fun.implementation=function(x,y)
{
console.log("x=",x,"y=",y,"x+y=",x+y) //打印参数,这里应该是函数原本没有被修改时的参数,即函数正常执行时的参数情况
var retvalue=this.fun(666,66) //再次调用原函数并且传递原本的参数fun,即重新执行原函数,在这里就可以修改参数
return retvalue //返回函数返回值,返回值最好不要修改类型,否则可能出错
}
})
}
//参数是要被执行的函数,例如传入main,表示frida注入app后立刻执行main
//setTimeout可以指定frida注入app多久之后执行函数,用于延时注入
setImmediate(main)
スクリプトをプロセスに挿入しますfrida -U -l test0.js com.example.hooktest1
挿入後、コマンド コンソールが js スクリプト関数の内容を出力することがわかります
Android Studio コマンド コンソールを見ると、次のことがわかりますコンソールが sum=: 732 を出力していることは
、スクリプト内での this.fun(666,66) の呼び出しが成功したことを示します。
2. オーバーロードされた関数のフック
わずかに変更されたコード:
package com.example.hooktest1;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import java.util.Locale;
public class MainActivity extends AppCompatActivity {
private String total="hello";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int x=0,y=0;
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
fun(x,y);
x++;y++;
Log.d("hooktest:",fun("HelloWorld!"));
}
}
void fun(int x,int y)
{
Log.d("sum=",String.valueOf(x+y));
}
String fun(String s){
return s.toLowerCase();
}
プログラム実行結果:
フックスクリプト:
//定位重载函数,使用overload即可,overload内指定重载函数参数类型,如果函数有返回值要注意返回
function main(){
console.log("Loaded sucess!")
Java.perform(function(){
console.log("Inside java perform")
var activity=Java.use("com.example.hooktest1.MainActivity")
console.log("定位activity成功")
activity.fun.overload('java.lang.String').implementation=function(x){
console.log("hook fun string=",x)
return x
}
activity.fun.overload('int','int').implementation=function(x,y){
console.log("x=",x,"y=",y)
var ret= this.fun(66,55)
return ret;
}
})
}
setImmediate(main)
フックの結果
コンソール:
3.2.3 Java層のアクティブコール
上記の実装はパッシブ コールです。つまり、関数はアプリの通常のロジックで実行されます。
アクティブ コールでは、アプリが関数を実行する必要がなく、キー関数を直接呼び出すことができます。
次に、2 つの状況を示します。
- クラス関数 (静的メソッド) Java.use を使用して関数が配置されているクラスを見つけるだけです
- インスタンス メソッド (動的メソッド) Java.choose API 関数を使用して、Java ヒープ内の指定されたクラス インスタンスを検索します。
1. アクティブな通話例 1
プログラムコード:
Secret と staticSecret の 2 つのメソッドを追加
package com.example.hooktest1;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import java.util.Locale;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int x=0,y=0;
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
fun(x,y);
x++;y++;
Log.d("hooktest:",fun("HelloWorld!"));
}
}
void fun(int x,int y)
{
Log.d("sum=",String.valueOf(x+y));
}
String fun(String s){
return s.toLowerCase();
}
void secret(){
Log.d("this is secret","find secret func!");
}
static void staticSecret(){
Log.d("this is staticSecret","Find static!");
}
}
プログラムの実行結果:
フックスクリプト:
function main(){
Java.perform(function(){
console.log("Inside java perform")
var MainActivity=Java.use("com.example.hooktest1.MainActivity")
MainActivity.staticSecret()
//动态函数主动调用
//java.choose先从内存中寻找类的实例对象,然后再调用实例对象的函数
Java.choose('com.example.hooktest1.MainActivity',{
onMatch: function(instance){
console.log("instance found",instance)
instance.secret()
},
onComplete: function(){
console.log('search Complete')
}
})
})
}
setImmediate(main)
フックの結果
インスタンス オブジェクトのクラスとアドレスが正常に出力されたことがわかります。
コンソールでは、secret と staticSecret の 2 つの関数が正常に呼び出されたことがわかります。
注: これら 2 つの関数を呼び出した結果は、frida コンソールではなく、Android Studio コンソールに表示されます。frida コンソールは、js スクリプトの内容を出力します。
2. アクティブな通話例 2
上記のプログラムコードを少し修正し、プライベート変数を2つ追加し、変数の値をフックしてみます。
package com.example.hooktest1;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import java.util.Locale;
public class MainActivity extends AppCompatActivity {
private String total="hello";
private int count=0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int x=0,y=0;
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
fun(x,y);
x++;y++;
Log.d("hooktest:",fun("HelloWorld!"));
}
}
void fun(int x,int y)
{
Log.d("sum=",String.valueOf(x+y));
}
String fun(String s){
return s.toLowerCase();
}
void secret(){
total+="call"+count;
count++;
Log.d("this is secret","find secret func!");
}
static void staticSecret(){
Log.d("this is staticSecret","Find static!");
}
}
プログラム実行結果:
フックスクリプト:
//调用secret函数,需要控制台手动进行,
function callSecretFunc(){
Java.perform(function(){
Java.choose('com.example.hooktest1.MainActivity',{
onMatch:function(instance){
instance.secret()
},
onComplete:function(){
}
})
})
}
//获取total的值
function getTotalValue(){
Java.perform(function(){
Java.choose('com.example.hooktest1.MainActivity',{
onMatch:function(instance){
console.log("find instance=",instance)
console.log("totalAddr=",instance.total)//获取类变量的值要使用.value,total本身是一个引用类型
console.log("total=",instance.total.value)
},
onComplete:function(){
console.log("search end")
}
})
})
}
setImmediate(getTotalValue)
フックの結果は
、プログラムが最初に getTotalValue 関数を呼び出して合計の値を出力することを示しています。
次に、frida コンソールでスクリプト関数 callSecretFunc() を呼び出してシークレット関数を呼び出し、合計の値を変更します。再度
getTotalValue を呼び出して、合計の値を出力します。 total の値を確認し、変更が成功したことを確認します。
要約:
- クラス内のインスタンス変数を取得するには、.valueを使用してその値を取得する必要がありますが、値を直接印刷して取得できない場合は(total自体が参照型であるため、変数の情報を取得できます)
- frida コンソールでは、プログラム関数を繰り返し呼び出すという目的を達成するために、スクリプト関数を繰り返し呼び出すことができます。
第 4 章 異議申し立てのクイックスタート
4.2 Objection のインストールと使用
4.2.1 異議のインストール
pip install -U 異議
注: frida は更新が速いため、frida の後に異議申し立てバージョンがリリースされるようにする必要があります。最新の異議申し立てバージョンは 1.11.0、対応する frida バージョンは 14.2.14 まで、frida-tools は 9.2.2 です。
4.2.2 異議申し立ての使用
1.REPLにプロセスを注入する
Objection はデフォルトで USB 経由でデバイスに接続されます。Frida のようなアプリケーション
デモンストレーション インジェクション プロセス コマンドを「設定」するために -U パラメーターを使用して USB を指定する必要はありません。次の情報を表示します。
このようにして、オブジェクトの REPL インターフェイスに正常に入ることができ、exit を入力して終了することができます。
2. 一般的なコマンド:
スペースバーによるコマンドのプロンプト
-
help 現在のコマンドの効果がわからない場合は、コマンドの前に help を使用すると、コマンドの説明情報が出力されます。
-
jobs コマンドの
jobs list には、現在のジョブが表示されます
jobs kill はジョブを削除します
-
frida コマンド
frida 情報の表示
-
メモリ ローミング関連コマンド
(1) Android フック リスト クラス
メモリ内のすべてのクラスを出力します。
(2) android フック検索クラス キーは、
キーワード キーを含むすべてのクラスを検索します。
(3) Android フック検索メソッド key は、
キーワード key を含むすべてのメソッドを検索します。
(4) android フックリスト class_methods classname
classname という名前のクラスのすべてのメソッドを表示します
(5) Android フック リスト アクティビティ (サービス レシーバー プロバイダー)
4 つの主要コンポーネントを出力し、プロセス (サービス レシーバー プロバイダー) のすべてのアクティビティをリストします。
-
フック関連コマンド
android フック ウォッチ class_method メソッド名
指定されたメソッドをフックする
継続的に更新中...