Frida05 - 高级API用法

参考文档

api-caller.com/2019/03/30/...

frida.re/docs/javasc...

数组打印

测试代码:

ini 复制代码
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() 方法很简单:

javascript 复制代码
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

ini 复制代码
var dexPath = "/data/local/tmp/r0gson.dex";
Java.openClassFile(dexPath).load();

已经有大佬编译好了的dex,我们可以直接用:

github.com/r0ysue/Andr...

为了避免类重复,还特意换了包名,具体可看:

bbs.kanxue.com/thread-2591...

重新写脚本:

javascript 复制代码
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)

看下输出结果:

ini 复制代码
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}

构造数组

有时候为了替换返回值,需要一个数组类型,可以使用如下方式,构造一个数组:

ini 复制代码
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 ]));

类型转换

测试代码:

ini 复制代码
Father father = new Father();
Son son = new Son();
Father father2 = new Son();

脚本:

javascript 复制代码
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 () { }

})

输出结果:

vbnet 复制代码
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 方法来做到。

测试代码:

java 复制代码
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();

脚本代码:

javascript 复制代码
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

与开发时的写法是一样的:

ini 复制代码
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 方法名处理

比如说

arduino 复制代码
int ֏(int x) {
    return x + 100;
}

甚至有一些不可视, 所以可以先编码打印出来, 再用编码后的字符串去 hook。

javascript 复制代码
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;
}
相关推荐
许彰午6 分钟前
# 政务表单动态建表?运行时DDL引擎,前端拖完字段后端直接建
java·前端·后端·架构·政务
San30.8 分钟前
前端渲染:从 CSR、SSR 到同构与手写 Vite+React SSR 实践
前端·react.js·前端框架
三声三视9 分钟前
React 19 正式发布!17 个新特性深度解析与迁移指南(2026 实战版)
前端·javascript·reactjs·react
滴滴答答哒11 分钟前
c#将平铺列表转换为树形结构(支持孤儿节点作为独立根节点)
java·前端·c#
雨季mo浅忆12 分钟前
第四项目梳理
前端·面试·vue2
a11177612 分钟前
三维地图可视化 ThreeJS vue 开源项目
前端·javascript·vue.js
接着奏乐接着舞。2 小时前
部署BFF与前端的踩坑与经验记录
前端·node.js
小李子呢02119 小时前
前端八股CSS(2)---动画的实现方式
前端·javascript
GreenTea11 小时前
从 Claw-Code 看 AI 驱动的大型项目开发:2 人 + 10 个自治 Agent 如何产出 48K 行 Rust 代码
前端·人工智能·后端