一、目标
这次的报错是
Could not find class 'com/jingdong/jdsdk/widget/ToastUtils' for JNIEnv.
二、步骤
这个简单,再增加一个 ToastUtils 类
class ToastUtils(metaclass=JavaClassDef, jvm_name='com/jingdong/jdsdk/widget/ToastUtils'):
def __init__(self):
pass
@java_method_def(name='longToast',args_list=["jstring"], signature='(Ljava/lang/String;)V')
def longToast(self, mu, *args, **kwargs):
logger.info("longToast %r" % args)
pass
emulator.java_classloader.add_class(ToastUtils)
跑一下,耶!!! JNI_OnLoad 跑通了,
下面开始来构造参数调用我们的目标 getSignFromJni
getSignFromJni函数我们之前分析过,它一共有6个参数,第一个参数是Context,后面5个都是String
Context正好原作者有实现,(实际在本例中Context只是传进去了,并没有使用)
调用方法如下:
activity_Th = ActivityThread()
result = emulator.call_symbol(lib_module, 'Java_com_jingdong_common_utils_BitmapkitUtils_getSignFromJni',emulator.java_vm.jni_env.address_ptr,0x00, activity_Th.getSystemContext(emulator),"asynInteface", '{"intefaceType":"asynIntefaceType","skuId":"100008667315"}', "99001184062989-f460e22c02fa", "android", "9.2.2")
logger.info("Response from JNI call: %s" % result.toString(emulator))
为什么前面多两个参数呢? 我们看看 IDA出来的伪码,比java调用的多两个参数,第一个是 JNIEnv *env, 第二个是 jobject obj
int __fastcall Java_com_jingdong_common_utils_BitmapkitUtils_getSignFromJni(JNIEnv *a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
{
return sub_127E4(a1, a3, a4, a5, a6, a7, a8);
}
JNIEnv类型实际上代表了Java环境,通过这个JNIEnv* 指针,就可以对Java端的代码进行操作。例如,创建Java类中的对象,调用Java对象的方法,获取Java对象中的属性等等。JNIEnv的指针会被JNI传入到本地方法的实现函数中来对Java端的代码进行操作。
参数:jobject obj的解释:
如果native方法不是static的话,这个obj就代表这个native方法的类实例
如果native方法是static的话,这个obj就代表这个native方法的类的class对象实例(static方法不需要类实例的,所以就代表这个类的class对象)
这两个参数是所有的JNI函数都有的。
好了,继续跑
这次要增加java/lang/StringBuffer
这时候就体现 unidbg 的优势了,增加 StringBuffer 这种java类,用java来写,太TMD的简单了。用python就不那么好过了。
class StringBuffer(metaclass=JavaClassDef, jvm_name='java/lang/StringBuffer'):
def __init__(self):
self._str = ''
pass
@java_method_def(name='<init>', signature='()V', native=False)
def init(self, emu):
pass
@java_method_def(name='append', args_list=["jstring"], signature='(Ljava/lang/String;)Ljava/lang/StringBuffer;', native=False)
def append(self, emu,*args, **kwargs):
logger.info("append %r" % args)
pyobj = JNIEnv.jobject_to_pyobject(args[0])
self._str += pyobj
return self
@java_method_def(name='toString', signature='()Ljava/lang/String;', native=False)
def toString(self, emu):
logger.info("toString %r" % self._str)
return String(self._str)
这里我们实现了 append 和 toString 这两个函数。
再依葫芦画瓢增加好 java/lang/Integer 和 java/lang/String
再跑一下, 成功了
三、总结
本次教程,一共介绍了
- 增加类
- 增加成员函数
- 填jmethodID相同的坑
- 实现函数的功能
- 增加字段(成员变量)
- 补齐jni函数 最后 构造参数来调用Native函数。
其中最难的部分就是 补齐jni函数 和 实现java类 。 这里需要对Android开发和java开发的熟悉,在实现java类方面 unidbg 有天然的优势。而 填jmethodID相同的坑 就要靠经验和细心了,再就是绝不放弃的精神。
做逆向,七分靠运气,三分靠努力,诸君共勉
关注微信公众号,最新技术干货实时推送