Bootstrap

【Android安全】Frida安装| Frida Java hook| Frida命令参数

Frida安装

objection与Frida版本对应

注意,只有安装frida-tools之后,才能使用frida --version命令

可行的版本组合1(到20220804为止的最新版):

pip install frida==15.2.2
发布时间:2022 07 21

pip install frida-tools==11.0.0

Objection安装的前置条件:
python版本 > 3.4
pip版本 > 9.0

pip install objection==1.11.0


可行的版本组合2:
pip install frida==12.5.9
发布时间:28 May 2019

pip install frida-tools==2.1.1
发布时间:15 Aug 2019


pip install frida==14.2.2
发布时间:20 Dec 2020

pip install frida-tools==9.2.0
发布时间:11 Feb 2021

参考:
https://blog.csdn.net/qq_39799322/article/details/124017424


Frida 代码自动补全

需要安装Frida-gum

先将vscode安装好,
然后将nodejs安装好,
接着就是使用命令,如果不想安装全局那就使用

npm  i  @types/frida-gum

如果想随便那个文件夹都有这个提示,那就

npm  i -g @types/frida-gum

在这里插入图片描述
在这里插入图片描述


打印backtrace时删除末尾的换行

var bt = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());
bt = bt.substr(0, bt.length - 1);
send("Backtrace:" + bt);

例如:

var hook_class = Java.use('java.io.FileInputStream');

hook_class.$init.overload('java.io.File').implementation = function (arg1) {
    var bt = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());
    bt = bt.substr(0, bt.length - 1);

    console.log(" ");
    send('1');
    send("Backtrace:" + bt);
    send('arg1 : ' + arg1);
    send("return");

    return this.$init(arg1);
}

byte[]打印为16进制数形式

function byteArrayToHex(byteArray) {
    var ByteString = Java.use("com.android.okhttp.okio.ByteString");
	return ByteString.of(byteArray).hex();
}

示例代码:

Java.perform(function () {
	var class_name = 'java.io.DataOutputStream';
	var hook_class = Java.use(class_name);
	hook_class.write.overload('[B', 'int', 'int').implementation = function (arg1, arg2, arg3) {
        var bt = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());
        
        if(bt.indexOf("csns.aH") != -1)
        // if(bt.indexOf("bdpg") != -1) 
        // if (1) 
        {
            send(class_name + ' hooked');
            send("Backtrace:" + bt);
            send('arg1 : ' + arg1);
            send('arg1 : ' + byteArrayToHex(arg1));
            send('arg2 : ' + arg2);
            send('arg3 : ' + arg3);
            send("return");
            return this.write(arg1, arg2, arg3);
        }
        else
        {
            return this.write(arg1, arg2, arg3);
        }
	}
});

function byteArrayToHex(byteArray) {
    var ByteString = Java.use("com.android.okhttp.okio.ByteString");
	return ByteString.of(byteArray).hex();
}
[*] arg1 : 10,-103,1,10,32,-2,-11,85
[*] arg1 : 0a99010a20fef555
[*] arg2 : 0
[*] arg3 : 4096
[*] return

byte[]打印为String形式

 function byteToString(arr) {
	if(typeof arr === 'string') {
		return arr;
	}
	var str = '',
		_arr = arr;
	for(var i = 0; i < _arr.length; i++) {
		var one = _arr[i].toString(2),
			v = one.match(/^1+?(?=0)/);
		if(v && one.length == 8) {
			var bytesLength = v[0].length;
			var store = _arr[i].toString(2).slice(7 - bytesLength);
			for(var st = 1; st < bytesLength; st++) {
				store += _arr[st + i].toString(2).slice(2);
			}
			str += String.fromCharCode(parseInt(store, 2));
			i += bytesLength - 1;
		} else {
			str += String.fromCharCode(_arr[i]);
		}
	}
	return str;
}

Frida打印类的成员变量

打印类的a成员变量:

send('this.a : ' + this.a.value);

有相同方法名的成员变量

如果有方法和变量重名,则在访问变量时加上_
例如:

var class_name = 'csns';
var hook_class = Java.use(class_name);

hook_class.k.overload('[B', 'int', 'int').implementation = function (arg1,arg2,arg3)
{
    send('this.a : ' + this._a.value);
	return this.k(arg1,arg2,arg3);
}

参考:
https://github.com/liyansong2018/FridaHookAndroid

设置成员变量的值

//主动调用静态方法
var clazz = Java.use("类名");
 //设置静态成员变量
clazz.static_var.value = something;

包名填写无误,但找不到进程

process = frida.get_usb_device().attach('com.demo.sysintegrity')
script = process.create_script(jscode)

报错 unable to find process with name ‘xxx’:

frida.ProcessNotFoundError: unable to find process with name 'com.demo.sysintegrity'

原因:某些版本的Frida,需要写App名称(或者进程PID),而不能写包名

解决:查看APP名称:frida-ps -Uai
在这里插入图片描述

process = frida.get_usb_device().attach('HMSSafetyDetectSample')
script = process.create_script(jscode)

frida常用参数

  • -U, connect to USB device

  • -f FILE, --file=FILE spawn FILE

  • -j JAVA_METHOD, --include-java-method=JAVA_METHOD
    include JAVA_METHOD 插桩某个java方法

  • -J JAVA_METHOD, --exclude-java-method=JAVA_METHOD
    exclude JAVA_METHOD 排除某个java方法

  • -F, attach to frontmost application
    例如在iOS设备上打开淘宝app,然后执行frida -UF -l frida-script.js
    frida-script.js中无需指定hook的app
    frida也能自动找到最前台的app进程,并将js代码注入
    在这里插入图片描述

spawn hook

#coding=utf-8

import frida,importlib, sys
importlib.reload(sys)

def on_message(message, data):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)

jscode = """
Java.perform(function () {
	
    var class_name ='com.baidu.protect.StubApplication';
	var hook_class = Java.use(class_name);
	hook_class.attachBaseContext.overload('android.content.Context').implementation = function (arg1) {
	var bt = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());
	send('');
	send(class_name + " hooked");
	console.log("Backtrace:" + bt);

	// send('arg1 : ' + byteToString(arg1));
	// send('arg2 : ' + arg2);
	// send('arg3 : ' + arg3);

	send("return");

	return this.write(arg1);
	}

});

 function byteToString(arr) {
	if(typeof arr === 'string') {
		return arr;
	}
	var str = '',
		_arr = arr;
	for(var i = 0; i < _arr.length; i++) {
		var one = _arr[i].toString(2),
			v = one.match(/^1+?(?=0)/);
		if(v && one.length == 8) {
			var bytesLength = v[0].length;
			var store = _arr[i].toString(2).slice(7 - bytesLength);
			for(var st = 1; st < bytesLength; st++) {
				store += _arr[st + i].toString(2).slice(2);
			}
			str += String.fromCharCode(parseInt(store, 2));
			i += bytesLength - 1;
		} else {
			str += String.fromCharCode(_arr[i]);
		}
	}
	return str;
}

function utf8ByteToUnicodeStr(utf8Bytes){
    var unicodeStr ="";
    for (var pos = 0; pos < utf8Bytes.length;){
        var flag= utf8Bytes[pos];
        var unicode = 0 ;
        if ((flag >>>7) === 0 ) {
            unicodeStr+= String.fromCharCode(utf8Bytes[pos]);
            pos += 1;

        } else if ((flag &0xFC) === 0xFC ){
            unicode = (utf8Bytes[pos] & 0x3) << 30;
            unicode |= (utf8Bytes[pos+1] & 0x3F) << 24;
            unicode |= (utf8Bytes[pos+2] & 0x3F) << 18;
            unicode |= (utf8Bytes[pos+3] & 0x3F) << 12;
            unicode |= (utf8Bytes[pos+4] & 0x3F) << 6;
            unicode |= (utf8Bytes[pos+5] & 0x3F);
            unicodeStr+= String.fromCharCode(unicode) ;
            pos += 6;

        }else if ((flag &0xF8) === 0xF8 ){
            unicode = (utf8Bytes[pos] & 0x7) << 24;
            unicode |= (utf8Bytes[pos+1] & 0x3F) << 18;
            unicode |= (utf8Bytes[pos+2] & 0x3F) << 12;
            unicode |= (utf8Bytes[pos+3] & 0x3F) << 6;
            unicode |= (utf8Bytes[pos+4] & 0x3F);
            unicodeStr+= String.fromCharCode(unicode) ;
            pos += 5;

        } else if ((flag &0xF0) === 0xF0 ){
            unicode = (utf8Bytes[pos] & 0xF) << 18;
            unicode |= (utf8Bytes[pos+1] & 0x3F) << 12;
            unicode |= (utf8Bytes[pos+2] & 0x3F) << 6;
            unicode |= (utf8Bytes[pos+3] & 0x3F);
            unicodeStr+= String.fromCharCode(unicode) ;
            pos += 4;

        } else if ((flag &0xE0) === 0xE0 ){
            unicode = (utf8Bytes[pos] & 0x1F) << 12;;
            unicode |= (utf8Bytes[pos+1] & 0x3F) << 6;
            unicode |= (utf8Bytes[pos+2] & 0x3F);
            unicodeStr+= String.fromCharCode(unicode) ;
            pos += 3;

        } else if ((flag &0xC0) === 0xC0 ){ //110
            unicode = (utf8Bytes[pos] & 0x3F) << 6;
            unicode |= (utf8Bytes[pos+1] & 0x3F);
            unicodeStr+= String.fromCharCode(unicode) ;
            pos += 2;

        } else{
            unicodeStr+= String.fromCharCode(utf8Bytes[pos]);
            pos += 1;
        }
    }
    return unicodeStr;
}

"""

device = frida.get_usb_device()
pid = device.spawn("com.example.test")
device.resume(pid)
# time.sleep(1)
process = device.attach(pid)


script = process.create_script(jscode)
script.on('message', on_message)
print('[*] Running hooking')
script.load()
sys.stdin.read()

Frida打印Map内容

方法:p0.s.a(java.util.Map) : void
想打印其参数内容

// p0.s.a(java.util.Map) : void
var class_name5 = 'p0.s';
var hook_class5 = Java.use(class_name5);
hook_class5.a.overload('java.util.Map').implementation = function (arg1) {
    var bt = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());
    
    // if(bt.indexOf("csns.aH") != -1)
    // if(bt.indexOf("bdpg") != -1) 
    if (1) 
    {
        send('---------------------------------------------------');
        send(class_name5 + ' hooked');
        console.log(class_name5 + " Backtrace:" + bt);
        // send(class_name5 + ' arg1 : ' + arg1);

        var keys = arg1.keySet();
        var iterator = keys.iterator();
        while (iterator.hasNext()) {
          var k = iterator.next();
          console.log(k + " : " + arg1.get(k));
        }
        
        send(class_name5 + " return");
        return this.a(arg1);
    }
    else
    {
        return this.a(arg1);
    }
} 

frida-trace

启动com.example.test,hook 所有方法签名带有 “attachBase” 的java方法:

frida-trace -U -f com.example.test --runtime=v8 -j '*!*attachBase*/isu'

注意'*!*attachBase*/isu'
s:in their signature
i:ignoring case
u:only searching in user-defined classes
!用来分隔MODULE和OFFSET,例如"gdi32full.dll!ExtTextOutW"
*代表匹配任意内容

Native Hook

参见:https://editor.csdn.net/md/?articleId=129849293


关于so分析中的Memory.readByteArray

不要这么写:

console.log("\\n"+Memory.readByteArray(ptr(0x7751a537ec),128));

就是不要在Memory.readByteArray前加"\n",会报错
可以用send
也可以单独将"\n"输出

关于frida中的数据类型

https://bbs.pediy.com/thread-261052-1.htm

一些参考资料

https://zyzling.gitee.io/2020/05/12/Frida%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/

https://onejane.github.io/2021/05/06/frida%E6%B2%99%E7%AE%B1%E8%87%AA%E5%90%90%E5%AE%9E%E7%8E%B0/

;