【安卓逆向】Frida Hook 動態調試

今天我們要來練習用 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 frida

def 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 後,可以

  1. instance.getSecret() 呼叫函式搞定
  2. 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()) // call function
send(instance.secret.value) // access vairable
},
onComplete: function () {}
})
})

疑難雜症

Q : 有函式 a 跟變數 a 同名怎麼辦 ?
A : a 存取函式,_a 存取變數


  1. https://github.com/hookmaster/frida-all-in-one
  2. How to access class member variable if there’s a member function called the same name?

【安卓逆向】Frida Hook 動態調試

http://oalieno.github.io/2020/02/16/android-frida/

Author

oalieno

Posted on

2020-02-16

Updated on

2023-11-15

Licensed under

Comments