Frida05 - 高级API用法
2023-12-18 14:00:53
参考文档
https://api-caller.com/2019/03/30/frida-note/
https://frida.re/docs/javascript-api/#frida
数组打印
测试代码:
private?static?class?Bean?{
????String?a;
????int?b;
????float?c;
}
private?void?test()?{
????Bean[]?beans?=?new?Bean[3];
????beans[0]?=?new?Bean();
????beans[0].a?=?"a";
????beans[0].b?=?1;
????beans[0].c?=?1f;
????beans[1]?=?new?Bean();
????beans[1].a?=?"b";
????beans[1].b?=?2;
????beans[1].c?=?2f;
????beans[2]?=?new?Bean();
????beans[2].a?=?"c";
????beans[2].b?=?2;
????beans[2].c?=?3f;
????Arrays.toString(beans);
}
想要 hook?Arrays.toString()
?方法很简单:
function?main()?{
????Java.perform(function?()?{
????????Java.use("java.util.Arrays").toString.overload('[Ljava.lang.Object;').implementation?=?function?(x)?{
????????????var?result?=?this.toString(x);
????????????console.log("params=",?x);
????????????console.log("result=",?result)
????????????return?result
????????}
????})
}
setImmediate(main)
输出的结果很多:
可以看到,输出的数组里是一个对象,那有没有什么好办法将对象转成字符串显示出来呢?
答案就是Gson,所以使用开发的思想来做逆向是很重要的。
项目里面不一定引入了Gson,所以我们需要自己编译一个gons库,放到手机里面,然后使用frida加载一下就可以使用了。
加载外部DEX
var?dexPath?=?"/data/local/tmp/r0gson.dex";
Java.openClassFile(dexPath).load();
已经有大佬编译好了的dex,我们可以直接用:
https://github.com/r0ysue/AndroidSecurityStudy/blob/master/FRIDA/r0gson.dex.zip
为了避免类重复,还特意换了包名,具体可看:
https://bbs.kanxue.com/thread-259186.htm
重新写脚本:
function?main()?{
????Java.perform(function?()?{
????????Java.use("java.util.Arrays").toString.overload('[Ljava.lang.Object;').implementation?=?function?(x)?{
????????????var?result?=?this.toString(x);
????????????if?(x.length?>?0?&&?x[0].getClass().getName()?==?"com.example.demo2.MainActivity$Bean")?{
????????????????Java.openClassFile("/data/local/tmp/r0gson.dex").load();
????????????????const?gsonClass?=?Java.use('com.r0ysue.gson.Gson');
????????????????for?(var?i=0;?i<x.length;?i++)?{
????????????????????console.log("entry=",?gsonClass.$new().toJson(x[i]));
????????????????}
????????????}
????????????return?result;
????????}
????})
}
setImmediate(main)
看下输出结果:
Spawned?`com.example.demo2`.?Resuming?main?thread!??????????????????????
[Pixel::com.example.demo2]->?entry=?{"a":"a","b":1,"c":1.0}
entry=?{"a":"b","b":2,"c":2.0}
entry=?{"a":"c","b":2,"c":3.0}
构造数组
有时候为了替换返回值,需要一个数组类型,可以使用如下方式,构造一个数组:
const?values?=?Java.array('int',?[?1003,?1005,?1007?]);
const?JString?=?Java.use('java.lang.String');
const?str?=?JString.$new(Java.array('byte',?[?0x48,?0x65,?0x69?]));
类型转换
测试代码:
Father?father?=?new?Father();
Son?son?=?new?Son();
Father?father2?=?new?Son();
脚本:
Java.choose('com.example.demo2.Father',?{
????onMatch:?function?(instance)?{
????????console.log('found?instance',?instance);
????????var?son?=?Java.cast(instance,?Java.use('com.example.demo2.Son'))
????????console.log('cast?instance',?son.test());
????},?onComplete:?function?()?{?}
})
Java.choose('com.example.demo2.Son',?{
????onMatch:?function?(instance)?{
????????console.log('found?instance',?instance);
????????var?father?=?Java.cast(instance,?Java.use('com.example.demo2.Father'))
????????console.log('cast?instance',?father.test());
????},?onComplete:?function?()?{?}
})
输出结果:
found?instance?com.example.demo2.Father@daf1aad
found?instance?com.example.demo2.Son@c7d41e2
cast?instance?Son
found?instance?com.example.demo2.Son@2341973
cast?instance?Son
Error:?Cast?from?'com.example.demo2.Father'?to?'com.example.demo2.Son'?isn't?possible
????at?cast?(frida/node_modules/frida-java-bridge/lib/class-factory.js:131)
????at?cast?(frida/node_modules/frida-java-bridge/index.js:270)
????at?onMatch?(/demo2.js:20)
????at?_chooseObjectsArtPreA12?(frida/node_modules/frida-java-bridge/lib/class-factory.js:298)
????at?<anonymous>?(frida/node_modules/frida-java-bridge/lib/class-factory.js:250)
????at?vt?(frida/node_modules/frida-java-bridge/lib/android.js:573)
符合直觉,子类可以转成父类类型,但是调用的方法还是子类的。
注册一个类
有时候,我们想做一个AOPhook的时候,就需要实现一个接口,我们可以使用 registerClass 方法来做到。
测试代码:
public?interface?IBook?{
????String?id();
????int?size();
????boolean?test(int?input);
}
//?-------------------------
public?class?SimpleBook?implements?IBook?{
????@Override
????public?String?id()?{
????????return?UUID.randomUUID().toString();
????}
????@Override
????public?int?size()?{
????????return?100;
????}
????@Override
????public?boolean?test(int?input)?{
????????Log.e("SimpleBook",?"input?=?"?+?input);
????????return?false;
????}
}
//?-------------------------
IBook?book?=?new?SimpleBook();
脚本代码:
Java.registerClass({
????name:?'com.example.demo2.SimpleBook2',
????implements:?[Java.use('com.example.demo2.IBook')],
????fields:?{
????????proxy:?'com.example.demo2.IBook',
????},
????methods:?{
????????'<init>':?[{
????????????returnType:?'void',
????????????argumentTypes:?['com.example.demo2.IBook'],
????????????implementation:?function?(proxy)?{
????????????????this.$super.$init();
????????????????this.proxy.value?=?proxy;
????????????}
????????}],
????????id()?{
????????????return?this.proxy.value.id();
????????},
????????size()?{
????????????return?this.proxy.value.size();
????????},
????????test(input)?{
????????????this.proxy.value.test(input);
????????????return?true;
????????},
????}
})
Java.choose('com.example.demo2.MainActivity',?{
????onMatch:?function?(instance)?{
????????console.log('found?MainActivity?instance',?instance);
????????var?oldBook?=?instance.book.value;
????????instance.book.value?=?Java.use('com.example.demo2.SimpleBook2').$new(oldBook);
????????console.log('book?test?id?=?',?instance.book.value.id());
????????console.log('book?test?size?=?',?instance.book.value.size());
????????console.log('book?test?result?=?',?instance.book.value.test(1));
????},?onComplete:?function?()?{?}
})
这里我们注册了一个类,com.example.demo2.SimpleBook2,并且实现了一个代理类,将 MainActivity 的字段替换之后,我们就可以让代理类来托管逻辑,做很多操作。
打印Map
与开发时的写法是一样的:
var?result?=?"";
var?keyset?=?map.keySet();
var?it?=?keyset.iterator();
while(it.hasNext()){
????var?keystr?=?it.next().toString();
????var?valuestr?=?map.get(keystr).toString();
????result?+=?valuestr;
}
Non-ASCII 方法名处理
比如说
int??(int?x)?{
????return?x?+?100;
}
甚至有一些不可视, 所以可以先编码打印出来, 再用编码后的字符串去 hook。
var?methods?=?Java.use('com.example.demo2.MainActivity').class.getDeclaredMethods();
for?(var?i?in?methods)?{
????console.log('origin?method?name?->?'?+?methods[i].toString());
????console.log('encode?method?name?->'?+?encodeURIComponent(methods[i].toString().replace(/^.*?\.([^\s\.\(\)]+)\(.*?$/,?"$1")));
}
Java.use('com.example.demo2.MainActivity')[decodeURIComponent("%D6%8F")].implementation?=?function()?{
????console.log('method?invoke');
????return?200;
}
文章来源:https://blog.csdn.net/a5right/article/details/135059753
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!