前言

《Android应用安全防护和逆向分析》这本书也快速过完了(好像略了很多,但问题不大,只是为了了解而已)。理论方面暂时就这样了,接下来打算刷刷攻防世界的题,学习Frida,还有再看看时间能不能学下fuzzing技术(感觉内容挺多,慢慢来,暂定这三个,争取一个月内完成)。

Frida

Frida是个轻量级so级别的hook框架,它可以帮助逆向人员对指定的进程的so模块进行分析。它主要提供了功能简单的python接口和功能丰富的js接口,使得hook函数和修改so编程化,值得一提的是接口中包含了主控端与目标进程的交互接口,由此我们可以即时获取信息并随时进行修改。使用frida可以获取进程的信息(模块列表,线程列表,库导出函数),可以拦截指定函数和调用指定函数,可以注入代码,总而言之,使用frida我们可以对进程模块进行手术刀式剖析。

它主要的工作方式是将脚本库注入到目标进程,在目标进程执行脚本。这里需要注意的是,它是将脚本库注入到已经启动的进程,但并不是说,对于进程初始化所做的动作,frida无能为力,frida提供了一个接口spawn,可以启动并暂时挂起进程,然后待我们布置好hook代码后再恢复进程运行。

除了用于脚本编程的接口外,frida还提供了一些简单的工具,比如查看进程列表,追踪某个库函数等。分别是:frida-trace,frida-ps,frida,frida-discover。(相应源码地址:https://github.com/frida/frida-python)

环境

win10

python 3.8

pip version 19.2.3

Frida官网:https://www.frida.re/

Frida源码:https://github.com/frida

Frida下载:https://github.com/frida/frida/releases

安装

首先安装python和pip,并且安装:

1
pip install frida-tools

下载frida,这里因为是用x86的32位模拟器,所以下载frida-server-12.9.7-android-x86.xz。然后将其用adb push进手机,赋予运行权限并运行。

测试:运行后在windows下的dos界面输入frida-ps -U显示当前进程则完成安装。

例子

出处

一个简单地石头剪刀布app,需要赢1000次才能获得flag。jeb中代码如下:

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
public class MainActivity extends Activity implements View$OnClickListener {
class com.example.seccon2015.rock_paper_scissors.MainActivity$1 implements Runnable {
com.example.seccon2015.rock_paper_scissors.MainActivity$1(MainActivity arg1) {
MainActivity.this = arg1;
super();
}

public void run() {
View v0 = MainActivity.this.findViewById(0x7F0C0052);
if(MainActivity.this.n - MainActivity.this.m == 1) {
++MainActivity.this.cnt;
((TextView)v0).setText("WIN! +" + String.valueOf(MainActivity.this.cnt));
}
else if(MainActivity.this.m - MainActivity.this.n == 1) {
MainActivity.this.cnt = 0;
((TextView)v0).setText("LOSE +0");
}
else if(MainActivity.this.m == MainActivity.this.n) {
((TextView)v0).setText("DRAW +" + String.valueOf(MainActivity.this.cnt));
}
else if(MainActivity.this.m < MainActivity.this.n) {
MainActivity.this.cnt = 0;
((TextView)v0).setText("LOSE +0");
}
else {
++MainActivity.this.cnt;
((TextView)v0).setText("WIN! +" + String.valueOf(MainActivity.this.cnt));
}

if(1000 == MainActivity.this.cnt) {
((TextView)v0).setText("SECCON{" + String.valueOf((MainActivity.this.cnt + MainActivity.this.calc()) * 107) + "}");
}

MainActivity.this.flag = 0;
}
}

Button P;
Button S;
int cnt;
int flag;
private final Handler handler;
int m;
int n;
Button r;
private final Runnable showMessageTask;

static {
System.loadLibrary("calc");
}

public MainActivity() {
super();
this.cnt = 0;
this.handler = new Handler();
this.showMessageTask = new com.example.seccon2015.rock_paper_scissors.MainActivity$1(this);
}

public native int calc() {
}

public void onClick(View arg11) {
int v9 = 3;
int v8 = 2;
if(this.flag != 1) {
this.flag = 1;
this.findViewById(0x7F0C0052).setText("");
View v2 = this.findViewById(0x7F0C0050);
View v3 = this.findViewById(0x7F0C0051);
this.m = 0;
this.n = new Random().nextInt(v9);
String[] v1 = new String[v9];
v1[0] = "CPU: Paper";
v1[1] = "CPU: Rock";
v1[v8] = "CPU: Scissors";
((TextView)v3).setText(v1[this.n]);
if(arg11 == this.P) {
((TextView)v2).setText("YOU: Paper");
this.m = 0;
}

if(arg11 == this.r) {
((TextView)v2).setText("YOU: Rock");
this.m = 1;
}

if(arg11 == this.S) {
((TextView)v2).setText("YOU: Scissors");
this.m = v8;
}

this.handler.postDelayed(this.showMessageTask, 1000);
}
}

protected void onCreate(Bundle arg2) {
super.onCreate(arg2);
this.setContentView(0x7F040018);
this.P = this.findViewById(0x7F0C004D);
this.S = this.findViewById(0x7F0C004F);
this.r = this.findViewById(0x7F0C004E);
this.P.setOnClickListener(((View$OnClickListener)this));
this.r.setOnClickListener(((View$OnClickListener)this));
this.S.setOnClickListener(((View$OnClickListener)this));
this.flag = 0;
}
}

不难得出cnt是赢的次数,n-m==1即算我们赢。

这样只需要修改cnt为999,并且让n-m==1成立,即可打印出flag。

hook代码如下:

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
import frida, sys
def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)

jscode = """
Java.perform(function () {
// Function to hook is defined here
var MainActivity = Java.use('com.example.seccon2015.rock_paper_scissors.MainActivity');

// Whenever button is clicked
var 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));
};
});
"""

process = frida.get_usb_device().attach('com.example.seccon2015.rock_paper_scissors')
script = process.create_script(jscode)
script.on('message', on_message)
print('[*] Running CTF')
script.load()
sys.stdin.read()

直接python运行该脚本即可。

其中onClick.call(this, v);是JavaScript中的用法,代表用该onClick代替this(也就是将原本的函数载入)。(参考:https://blog.csdn.net/qq_35810838/article/details/87824081)

接着再修改n, m, cnt的值即可。并且由于是多线程,所以运行一次即可完成。

关于Frida的api介绍可翻阅官方文档。

参考文章

Frida https://tool.pediy.com/index-detail-188.htm

[原创]Frida从入门到入门—安卓逆向菜鸟的frida食用说明-『Android安全』-看雪安全论坛 https://bbs.pediy.com/thread-226846.htm

Android | Frida • A world-class dynamic instrumentation framework https://frida.re/docs/examples/android/

JavaScript API | Frida • A world-class dynamic instrumentation framework https://frida.re/docs/javascript-api/