首页
论坛
课程
招聘
雪    币: 559
活跃值: 活跃值 (644)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝

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

2020-7-30 21:49 2054

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

2020-7-30 21:49
2054

背景

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中对象并不一致。



HWS计划·2020安全精英夏令营来了!我们在华为松山湖欧洲小镇等你

最新回复 (4)
雪    币: 153
活跃值: 活跃值 (38)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
人在塔在 活跃值 2020-7-31 19:03
2
0
谢谢分享
雪    币: 2600
活跃值: 活跃值 (257)
能力值: (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
雪    币: 222
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
Lateautumn4 活跃值 2020-8-3 11:18
4
0
感谢大佬,调试frida的时候经常会遇到.$h的问题,.$h这个指的是什么呢?
雪    币: 559
活跃值: 活跃值 (644)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
Monkeylord 活跃值 2020-8-3 11:50
5
0
Lateautumn4 感谢大佬,调试frida的时候经常会遇到.$h的问题,.$h这个指的是什么呢?
应该是对象在内存里的值,非基本对象的话是个对象ref的指针。
不知道这么说是否正确,我是这么理解的。
游客
登录 | 注册 方可回帖
返回