frida基础学习(1)
1.环境搭建
pc安装frida
frida-tools要选择与frida相匹配版本
1 2
| pip3 install frida==版本号 pip3 install frida-tools==版本号
|
Android运行相应版本frida-server,版本要与frida版本一致,且选择对应系统架构
https://github.com/frida/frida/releases
下载解压后push到Android端tmp目录下,运行,即可开始使用frida进行操作
1 2 3
| adb push frida-server /data/local/tmp ./frida-server adb forward tcp:27042 tcp:27043
|
在Android逆向过程中,Frida存在两种操作模式:一种是通过命令行直接将JavaScript脚本注入进
程中,对进程进行操作,称为CLI(命令行)模式;另一种是使用Python进行JavaScript脚本的注入工
作,实际对进程进行操作的还是JavaScript脚本,这种操作模式称为RPC模式。两种模式本质上是一样
的,最终执行Hook工作的都是JavaScript脚本,而且核心执行注入工作的还是Frida本身,只是RPC模
式在对复杂数据的处理上可以通过RPC传输给Python脚本来进行,这样有利于减少被注入进程的性能损
耗,在大规模调用中更加普遍。
Frida操作App的方式有两种。第一种是spwan模式,简而言之就是将启动App的权利交由Frida来
控制。采用这个模式时,即使目标App已经启动,在使用Frida注入程序时还是会重新启动App。在CLI
模式中,Frida通过加上-f参数指定包名以spwan模式操作App。第二种是attach模式,建立在目标App
已经启动的情况下,Frida通过ptrace注入程序从而执行Hook的操作。在CLI模式中,如果不添加-f参
数,则默认会通过attach模式注入App。
2.frida脚本入门
2.1认识frida脚本
Frida脚本就是利用Frida动态插桩框架,使用Frida导出的API和方法对内存空间里的对象方法进行
监视、修改或者替换的一段代码。Frida的API是用JavaScript实现的,所以可以充分利用JavaScript的匿
名函数优势以及大量的Hook(钩子函数)和回调函数的API。
下例为注入android.process.media进程打印一条“hello world”
1 2 3 4 5 6 7
| setTimeout( function () { Java.perform(function () { console.log("hello world") }) } )
|
1
| frida -U xx.xx.xx -l xx.js
|
2.2 java层hook基础
例1:下例为一简单hook练习:测试app会定期输出值至日志中,注入脚本hook此app中部分函数打印、修改输出值
app MainActivity:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.example.fridatest; import androidx.appcompat.app. AppCompatActivity; import android.os.Bundle; import android.util.Log; public class MainActivity extends AppCompatActivity{ @Override protected void onCreate(Bundle savedInstancestate){ super.onCreate(savedInstancestate); setContentView(R.layout.activity_main);while(true){ try { Thread.sleep(1000); } catch (InterruptedException e){ e.printStackTrace(); } fun(50,30); } } void fun(int x,int y){ Log.d("r0ysue.sum",String.valueOf(x+y)); }
}
|
js脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function main() { console.log("success") Java.perform(function () { console.log("inside") var MainActivity = Java.use('com.example.fridatest.MainActivity') console.log("Java.Use.successfuly") MainActivity.fun.implementation = function(x,y){ console.log("x=>",x,"y=>",y) var ret_value=this.fun(x,y); return ret_value } }) } setImmediate(main)
|
adb logcat查看输出
hook后修改x,y的值
例2:当函数使用重载时,可在要hook的函数名后指定重载的函数进行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
| package com.example.fridatest2;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle; import android.util.Log;
public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } fun(50, 30); Log.d("r0ysue.string", fun("LoweRcAsE Me ! ! ! ! ! ! ! ! ! ")); } }
void fun(int x, int y) { Log.d("r0ysue.sum", String.valueOf(x + y)); }
String fun(String x) { return x.toLowerCase(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| //hook js function main() { console.log("script success") Java.perform(function () { console.log("inside") var MainAcitivity = Java.use('com.example.fridatest2.MainActivity') console.log("java success") MainAcitivity.fun.overload('int','int').implementation =function (x,y) { //指定函数 console.log("x=>",x,",y=>",y) var ret_value =this.fun(2,5); return ret_value } }) } setImmediate(main)
|
adb logcat查看输出
hook后修改x,y的值
2.3 java层主动调用
主动调用就是强制调用一个函数去执行。相对地,被动调用是由App按照正常逻辑去执行函数,函数的执行完全依靠与用户交互完成程序逻辑进而间接调用到关键函数,而主动调用则可以直接调用关键函数,主动性更强。
例3:下例增加了两个secret()函数:一个是没有static修饰的secret实例方法,一个是有static关键字修饰的staticSecret类方法
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
| package com.example.fridatest3;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle; import android.util.Log;
public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } fun(50, 30); Log.d("r0ysue.string", fun("LoweRcAsE Me ! ! ! ! ! ! ! ! ! ")); } }
void fun(int x, int y) { Log.d("r0ysue.sum", String.valueOf(x + y)); }
String fun(String x) { return x.toLowerCase(); } void Secret(){ Log.d("r0ysue.secret","this is secret func"); } static void staticSecret(){ Log.d("r0ysue.secret","this is static secret func"); } }
|
logcat查看日志
js脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| function main() { console.log("script success") Java.perform(function () { console.log("inside") //静态函数主动调用 var MainActivity = Java.use('com.example.fridatest3.MainActivity') MainActivity.staticSecret() //动态函数主动调用 Java.choose('com.example.fridatest3.MainActivity', { onMatch:function(instance){ console.log('instance found',instance) instance.Secret() }, onComplete:function(){ console.log('search complete') } }) }) } setImmediate(main)
|
hook后主动调用函数