Frida05 - 高级API用法

发布时间:2023年12月18日

参考文档

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
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。