Android中使用Frida hook方法

日期 2017-09-12
Android中使用Frida hook方法

强大的动态注入框架 Frida,看官网介绍几乎支持所有平台,对ART的支持也不错,而且通过python脚本很方便的注入代码,我们看看在Android下如何使用,注意手机必须有Root权限。

首先简单的看一遍官方文档,根据介绍先安装,依赖python3

sudo pip install --user frida

我使用的macOS,开始没有加sudo,安装完了一直没有frida-ps等命令出现,加上sudo就有了

连接手机,到https://github.com/frida/frida/releases 下载frida-server,根据情况选择android-arm 版本,
我下载的https://github.com/frida/frida/releases/download/10.5.14/frida-server-10.5.14-android-arm.xz 这个版本push到手机并启动

xz -d frida-server-10.5.14-android-arm.xz
adb push frida-server-10.5.14-android-arm /data/local/tmp/
adb shell "chmod 755 /data/local/tmp/frida-server"


adb shell su -C "/data/local/tmp/frida-server &" &
或者
adb shell
su
cd /data/local/tmp
./frida-server &

然后在终端中执行

frida-ps -U

应该就会看到如下进程列表了

PID  Name
----- ----------
30141 adbd
23825 android.process.acore
11070 android.process.media
....

下面,我们根据 https://www.frida.re/docs/examples/android/ 给出的示例来hook方法,
保存位 cty.py 代码如下

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
// 要hook的类名
var MainActivity = Java.use('com.example.seccon2015.rock_paper_scissors.MainActivity');

// Whenever button is clicked
MainActivity.onClick.implementation = function (v) {
// Show a message to know that the function got called
send('onClick');

// Call the original onClick handler
// 执行原始方法代码
this.onClick(v);

// Set our values after running the original onClick handler
//修改MainActivity中属性的值
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));
};
});
"""

# 要注入的进程名或者pid
# 方法可以参考https://github.com/frida/frida-python/blob/master/src/frida/core.py
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()

注意修改进程名称和类名,然后执行

python3 cty.py

代码不多很简单,其实我们可以用这个作为模板,只需要修改进程名和jscode就可以了。
下面是几个常用的示例


Java.perform(function () {


var Activity = Java.use('android.app.Activity');
var Toast = Java.use('android.widget.Toast');

var Bundle = Java.use('android.os.Bundle');

var PackageInfo = Java.use('android.content.pm.PackageInfo');


//得到context
var currentApplication = Java.use('android.app.ActivityThread').currentApplication();
var context = currentApplication.getApplicationContext();

// hook所有activity的onResume方法
Activity.onResume.implementation = function () {
console.log('onResume --> '+this);

console.log('onResume --> context '+context);
this.onResume();
}

// hook所有activity的onCreate方法,因为onCreate方法有多种参数,所以需要指定参数类型来确定要hook的方法
Activity.onCreate.overload('android.os.Bundle').implementation = function (c) {
console.log('onCreate --> '+this);
this.onCreate(c);

//1.调用静态方法,首先使用Java.use,后面makeText方法的参数保持一致
//Toast.makeText(this,"onCreate",Toast.LENGTH_SHORT).show();
Toast.makeText(this,"onCreate ----",Toast.LENGTH_SHORT.value).show();

//2. 读取静态属性
//int code = Activity.RESULT_OK;
var code = Activity.RESULT_OK.value

//3.调用实例方法,并new一个对象
//在intent中添加一个bundle
//Bundle bundle=new Bundle();
//bundle.putString("key1","value1");
// activity.getIntent().putExtra("testBundle",bundle);
var bundle = Bundle.$new();
//调用实例方法需要使用call
Bundle.putString.call(bundle,'key1','value1')
//也可以指定具体的参数类型
//Bundle.putString.overload('java.lang.String','java.lang.String').call(bundle,'key1','value1')
this.getIntent().putExtra('testBundle',bundle)

//activity.getIntent().getBundleExtra("testBundle");
var outB = this.getIntent().getBundleExtra('testBundle')
console.log(outB);

//4.读取实例对象的属性值,对于得到的对象,需要使用Java.cast()方法转换后才可以使用
// PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(),0);
// String pkg = packageInfo.packageName;
var t = this.getPackageManager().getPackageInfo(this.getPackageName(),0);
var packageInfo = Java.cast(t.$handle, PackageInfo);
var pkg = packageInfo.packageName.value
console.log(pkg)

}
});

参数类型注意事项

  • 基本类型可以简写,比如byte -> B, int -> I, boolean -> Z …
  • 对于数组类型,可以添加前缀[,例如int[] -> [I, String[] -> [Ljava.lang.String
  • 泛型在编译后会擦除,所以在使用时不需要加泛型类型
    具体简写类型可以参考JVM规范

更多java api可以通过https://github.com/frida/frida-java/blob/master/index.js 找到
比如

//获取android版本号,比如 7.1.1
Java.androidVersion
//是否在主线程
Java.isMainThread()


Java.scheduleOnMainThread(function () {
send('main thread');
});

对Frida的介绍不多,

参考
https://github.com/frida/frida
https://github.com/frida/frida-python
https://github.com/frida/frida-java
https://github.com/dweinstein/awesome-frida
http://www.jianshu.com/p/ca8381d3e094