今天我們要來練習用 frida 在 Android 上做動態調試
逆之呼吸壹之型 - 一般函式
先來寫個簡單的範例 APP
有一個按扭和一個輸入欄,輸入名字之後,按下按鈕,就會顯示 Hello 加上你輸入的名字
不會寫 APP 的小朋友可以先去 youtube 上找教學,有一大堆
MainActivity.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.example.myapplication;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;public class MainActivity extends AppCompatActivity { @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button sayButton = findViewById(R.id.sayButton); sayButton.setOnClickListener(new View .OnClickListener() { @Override public void onClick (View v) { EditText somethingEditText = findViewById(R.id.somethingEditText); TextView resultTextView = findViewById(R.id.resultTextView); String something = somethingEditText.getText().toString(); resultTextView.setText(say(something)); } }); } String say (String something) { return "Hello " + something; } }
python 負責呼叫 frida api 做注入,javascript 是被注入進去做事的
我們的目標是 hook 函式 say
,並在原本的輸出文字後面加上 !!!
hook.py 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import fridadef on_message (message, payload ): print (message) device = frida.get_usb_device() pid = device.spawn(["com.example.myapplication" ]) session = device.attach(pid) with open ("script.js" ) as f: script = session.create_script(f.read()) script.on("message" , on_message) script.load() device.resume(pid) input ()
script.js 1 2 3 4 5 6 7 Java .perform (() => { main = Java .use ("com.example.myapplication.MainActivity" ) main.say .implementation = function (something ) { var ret = this .say (something) return ret + '!!!' } })
逆之呼吸貳之型 - 重載函式
改一下範例 APP,多加上一個接受數字做輸入的 say
函式
接收到數字後,就輸出 Hello 加上輸入的數字的平方
MainActivity.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package com.example.myapplication;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;public class MainActivity extends AppCompatActivity { @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button sayButton = findViewById(R.id.sayButton); sayButton.setOnClickListener(new View .OnClickListener() { @Override public void onClick (View v) { EditText somethingEditText = findViewById(R.id.somethingEditText); TextView resultTextView = findViewById(R.id.resultTextView); String something = somethingEditText.getText().toString(); try { Integer number = Integer.parseInt(something); resultTextView.setText(say(number)); } catch (NumberFormatException e) { resultTextView.setText(say(something)); } } }); } String say (String something) { return "Hello " + something; } String say (Integer number) { number = number * number; return "Hello " + number.toString(); } }
兩個 say
都是一樣的名字,所以 hook 的時候要用 overload
去區分,overload
參數放的是目標函式輸入參數的型態
這次我們在新的 say
函式的輸出文字後面加上 ???
hook.py
跟上一個例子一樣就不再貼一次了
script.js 1 2 3 4 5 6 7 8 9 10 11 Java .perform (() => { main = Java .use ("com.example.myapplication.MainActivity" ) main.say .overload ("java.lang.String" ).implementation = function (something ) { var ret = this .say (something) return ret + '!!!' } main.say .overload ("java.lang.Integer" ).implementation = function (number ) { var ret = this .say (number) return ret + '???' } })
逆之呼吸參之型 - 隱藏函式
再改一下範例 APP,多加上一個變數 secret
和一個函式 getSecret
,在 onCreate
裡面會給 secret
一個隨機值,我們的目標就是找出這個值
MainActivity.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package com.example.myapplication;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;public class MainActivity extends AppCompatActivity { private int secret; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); secret = (int ) (Math.random() * 100 ); Button sayButton = findViewById(R.id.sayButton); sayButton.setOnClickListener(new View .OnClickListener() { @Override public void onClick (View v) { EditText somethingEditText = findViewById(R.id.somethingEditText); TextView resultTextView = findViewById(R.id.resultTextView); String something = somethingEditText.getText().toString(); try { Integer number = Integer.parseInt(something); resultTextView.setText(say(number)); } catch (NumberFormatException e) { resultTextView.setText(say(something)); } } }); } String say (String something) { return "Hello " + something; } String say (Integer number) { number = number * number; return "Hello " + number.toString(); } int getSecret () { return secret; } }
因為我們的目標不是自己 new 一個物件出來抓 secret
,這樣就只是一個我們自己就可以生成的隨機值而已
我們是要找出目前已經存在的那個 instance 的 secret
,在實際例子中可能就會是一組隨機生成的密碼之類的
所以我們要用到 Java.choose
去抓 instance,抓到 instance 後,可以
instance.getSecret()
呼叫函式搞定
instance.secret.value
存取變數搞定
然後用 send
可以把資料傳到 python 端的 on_message
做處理
script.js 1 2 3 4 5 6 7 8 9 Java .perform (function ( ) { Java .choose ("com.example.myapplication.MainActivity" , { onMatch : function (instance ) { send (instance.getSecret ()) send (instance.secret .value ) }, onComplete : function ( ) {} }) })
疑難雜症
Q : 有函式 a
跟變數 a
同名怎麼辦 ?
A : a
存取函式,_a
存取變數
https://github.com/hookmaster/frida-all-in-one
How to access class member variable if there’s a member function called the same name?