首页
社区
课程
招聘
[原创]浅析Frida Hook Android时的数据类型
2020-7-30 21:49 15085

[原创]浅析Frida Hook Android时的数据类型

2020-7-30 21:49
15085

背景

frida被常用于android应用测试中,很多时候要对应用中的java代码进行hook,此时,常用的对象就是内置的Java对象,各种操作离不开这个内置对象,但是,除了官方网站的javascript API外,关于它的文档并不多。

 

该对象实质对应的代码在这个项目中:https://github.com/frida/frida-java-bridge/

 

在对java对象进行操作时,我们可以直接使用javascript中的数据类型,这方便了不少工作。然而有人会好奇,这是怎么实现的呢?

 

本文将对frida-java-bridge的数据类型封装进行简单的分析。

数据类型

frida-java-bridge中对于对象类型的处理位于type.js

 

type来完成对象实例和js对象的转换,有两个函数:fromJnitoJni,负责将对象在内存中的值和js中的值进行转换。在对对象进行操作,或者涉及函数调用的参数和返回值等时,转换会被调用。

 

比如说在hook到方法调用,转交给设置的implementation时,会这么处理

// class-factory.js 1617
// handleMethodInvocation中对于参数的处理
for (let i = 0; i !== numArgs; i++) {
    const t = argTypes[i];

    const value = t.fromJni(jniArgs[2 + i], env, false);
    args.push(value);

    ownedObjects.push(value);
}

// class-factory.js 1628
// handleMethodInvocation中对于implementation返回结果的处理
if (!retType.isCompatible(retval)) {
    throw new Error(`Implementation for ${methodName} expected return value compatible with ${retType.className}`);
}

let jniRetval = retType.toJni(retval, env);

再比如说在主动调用某个方法时,会这么处理

// class-factory.js 1538
// methodPrototype中invoke对应的函数中对参数的处理
for (let i = 0; i !== numArgs; i++) {
    jniArgs.push(argTypes[i].toJni(args[i], env));
}

// class-factory.js 1556
// methodPrototype中invoke对应的函数中对返回值的处理
return retType.fromJni(jniRetval, env, true);

我们再来看type中进行的对象转换,举个例子:

    fromJni (h, env, owned) {
      if (h.isNull()) {
        return null;
      }

      if (typeIsDefaultString() && unbox) {
        return env.stringFromJni(h);
      }

      return factory.cast(h, factory.use(typeName), owned);
    },
    toJni (o, env) {
      if (o === null) {
        return NULL;
      }

      if (typeof o === 'string') {
        return env.newStringUtf(o);
      }

      return o.$h;
    }

再看一些基本类型:

  boolean: {
    name: 'Z',
    type: 'uint8',
    size: 1,
    byteSize: 1,
    defaultValue: false,
    isCompatible (v) {
      return typeof v === 'boolean';
    },
    fromJni (v) {
      return !!v;
    },
    toJni (v) {
      return v ? 1 : 0;
    },
    read (address) {
      return address.readU8();
    },
    write (address, value) {
      address.writeU8(value);
    }
  },

可见frida-java-bridge在调用前后对于javascript对象进行了双向的处理,以便符合JNI调用的格式。

 

另外,一个方法的各类信息被保存在它的_p属性中,如果要获取某个方法的参数类型和返回值类型,那么可以使用以下代码:

var [methodName, classWrapper, type, retType, argTypes, handler, fallback, pendingCalls] = method._p

如果要获取某个指定Class的类型来做转换,那么可以通过Java._getType(typeName)来获得

数据类型的坑

一般情况下,这些数据类型转换的封装极大的方便了代码的编写,但是在一些容易被忽略的角落里,这些数据类型与Java中的数据类型并不一致,这就带来了一些诡异的坑。

 

比如,我们知道在java中,对象数组也被认为是java.lang.Object对象。然而,在frida-java-bridge中并不是。当你尝试去做类型转换,并把对象数组塞到一个接受Object类型的函数中去时,你会发现,类型转换居然失败了。

 

因为java.lang.Object的type是这样的:

{
    name: 'Ljava/lang/Object;',
    type: 'pointer',
    size: 1,
    defaultValue: NULL,
    isCompatible (v) {
      if (v === null) {
        return true;
      }

      if (v === undefined) {
        return false;
      }

      const isWrapper = v.$h instanceof NativePointer;
      if (isWrapper) {
        return true;
      }

      return typeof v === 'string';
    },
    fromJni (h, env, owned) {
      if (h.isNull()) {
        return null;
      }

      return factory.cast(h, factory.use('java.lang.Object'), owned);
    },
    toJni (o, env) {
      if (o === null) {
        return NULL;
      }

      if (typeof o === 'string') {
        return env.newStringUtf(o);
      }

      return o.$h;
    }
};

而一个js数组对象是没有.$h的,于是你会得到莫名其妙的报错。而Java.cast时会检查isCompatible,结果还是因为同样的原因,报错,没有办法进行转换。坑,frida认为对象数组不是对象……

 

所以……怎么做呢,只有找到对象数组对应的type,做一次toJni,然后再用目标类型的type,做一次fromJni,比如说我在XposedFridaBridge中做的:

var env = Java.vm.getEnv()
var retType = fridaMethod._p[4]
var hhmRetType = XposedBridge.handleHookedMethod.overloads[0]._p[4]

return retType.fromJni(hhmRetType.toJni(xposedResult, env), env, false)

还有什么坑呢?就是基本类型直接是javascript类型,这些类型也是没有.$h的,所以,他们也不是Object……只能手动用java.lang.Integer这样的对象进行转换了。

总结

Frida中Java对象对应的是frida-java-bridge,其中数据类型的转换是由type.js负责的,很不幸在映射的时候与java中对象并不一致。


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

收藏
点赞7
打赏
分享
最新回复 (9)
雪    币: 144
活跃值: (335)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
人在塔在 2020-7-31 19:03
2
0
谢谢分享
雪    币: 3907
活跃值: (5742)
能力值: ( LV12,RANK:200 )
在线值:
发帖
回帖
粉丝
roysue 3 2020-8-2 13:43
3
0
env.js与jni也并不一致,康康源码自己比对下就好。
https://github.com/frida/frida-java-bridge/blob/master/lib/env.js
雪    币: 482
活跃值: (1002)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
Lateautumn4 2020-8-3 11:18
4
0
感谢大佬,调试frida的时候经常会遇到.$h的问题,.$h这个指的是什么呢?
雪    币: 634
活跃值: (1503)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
Monkeylord 2020-8-3 11:50
5
0
Lateautumn4 感谢大佬,调试frida的时候经常会遇到.$h的问题,.$h这个指的是什么呢?
应该是对象在内存里的值,非基本对象的话是个对象ref的指针。
不知道这么说是否正确,我是这么理解的。
雪    币: 62
活跃值: (566)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
万里星河 2020-12-4 14:55
6
0
看不懂 fromJni 和toJni做了啥 返回值是啥呀
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
wx_星星_234 2022-1-23 22:16
7
0

在用frida hook方法时,argTypes 怎么获取的呢,可以告知一下吗?

最后于 2022-1-23 22:17 被wx_星星_234编辑 ,原因:
雪    币: 8
活跃值: (409)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
未露姓名陈某 2023-4-21 15:28
8
0
真牛逼,3年前的文章解决了我的问题
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_ldbucrik 2024-1-7 20:55
9
0
想问下楼主frida怎么把不同的线程的堆栈接起来呢
雪    币: 130
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_xsfdfkko 2024-4-4 13:53
10
0
wx_星星_234 在用frida hook方法时,argTypes 怎么获取的呢,可以告知一下吗?
大佬,解决了,我也想知道,该怎么获取argstype的类型
游客
登录 | 注册 方可回帖
返回