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/