浅析Frida Hook Android时的数据类型
2020-07-31 12:08:31 Author: bbs.pediy.com(查看原文) 阅读量:1516 收藏

背景

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

[看雪官方培训]《安卓高级研修班(网课)》9月班开始招生!顶尖技术、挑战极限、工资翻倍!


文章来源: https://bbs.pediy.com/thread-261052.htm
如有侵权请联系:admin#unsafe.sh