前言 《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,并且安装:
下载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, sysdef 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/