一、目标
从错误提示上看,是 CallObjectMethod 报错,看过之前AndroidNativeEmu的同学一定知道,这个还是 jmethodID相同 的梗。
二、步骤
我在github上也给作者提issues,但是没有找到很优雅的解决方案。只要用个临时方案来解决。
在 unidbg/unidbg-android/src/main/java/com/github/unidbg/linux/android/dvm/DalvikVM.java 的第47行增加
if(name.equals("android/app/Activity"))
name = "android/app/Application";
因为在这个so里面 Activity 类根本就没有实例化,可能纯粹为了为难模拟执行吧,所以就这么硬编码一下,不过用Unidbg跑其他so的时候要把这两行代码注释掉,避免产生bug。
运行一下看结果
java.lang.UnsupportedOperationException: android/app/Application->getPackageManager()Landroid/content/pm/PackageManager;
这次提示是没有找到 Application→getPackageManager 方法
上篇文章我们说了,jni操作,作者在AbstractJni类里面实现了,实际上 getPackageManager 作者已经实现了,只是他没有响应 Application对象。
我们在 AbstractJni.java 代码中的 callObjectMethod 函数里面修改,增加 Application 对象的响应
case "android/app/Application->getPackageManager()Landroid/content/pm/PackageManager;":
case "android/content/Context->getPackageManager()Landroid/content/pm/PackageManager;":
return vm.resolveClass("android/content/pm/PackageManager").newObject(null);
再跑,报错
java.lang.UnsupportedOperationException: android/app/Application->getPackageName()Ljava/lang/String;
道理一样还是在 AbstractJni.java 代码中的 callObjectMethod 函数里面修改,增加 Application 对象的响应
case "android/app/Application->getPackageName()Ljava/lang/String;":
case "android/content/Context->getPackageName()Ljava/lang/String;": {
String packageName = vm.getPackageName();
if (packageName != null) {
return new StringObject(vm, packageName);
}
break;
case "android/app/Application->getApplicationInfo()Landroid/content/pm/ApplicationInfo;":
case "android/content/Context->getApplicationInfo()Landroid/content/pm/ApplicationInfo;":
return new ApplicationInfo(vm);
继续跑,这一次报了一个 android/content/pm/ApplicationInfo→sourceDir:Ljava/lang/String; 的错误,但是貌似可以不用修复。 然后成功的打印出了
Call [libjdbitmapkit.so]JNI_OnLoad finished, offset=120ms
开始调用
aBitmapkitUtils = vm.resolveClass("com/jingdong/common/utils/BitmapkitUtils");
DvmObject<?> strRc = aBitmapkitUtils.callStaticJniMethodObject(emulator,"getSignFromJni()(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
vm.addLocalObject(null),
vm.addLocalObject(new StringObject(vm,"asynInteface")),
vm.addLocalObject(new StringObject(vm,"{\"intefaceType\":\"asynIntefaceType\",\"skuId\":\"100008667315\"}")),
vm.addLocalObject(new StringObject(vm,"99001184062989-f460e22c02fa")),
vm.addLocalObject(new StringObject(vm,"android")),
vm.addLocalObject(new StringObject(vm,"9.2.2")));
System.out.println(strRc.getValue());
这次报错
java.lang.UnsupportedOperationException: java/lang/StringBuffer-><init>()V
我们来实现 StringBuffer ,Unidbg的好处是实现java类比较简单:
在 AbstractJni.java 代码中的 newObjectV 函数里面我们把 StringBuffer 和 Integer 都实现了:
case "java/lang/StringBuffer-><init>()V":{
return vm.resolveClass("java/lang/StringBuffer").newObject(new StringBuffer());
}
case "java/lang/Integer-><init>(I)V" :{
return vm.resolveClass("java/lang/Integer").newObject(new Integer(vaList.getInt(0)));
}
这次报错
java.lang.UnsupportedOperationException: java/lang/StringBuffer->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
我们需要在 AbstractJni.java 代码中的 callObjectMethodV 函数里实现 append和toString
case "java/lang/Integer->toString()Ljava/lang/String;":{
Integer iUse = (Integer)dvmObject.getValue();
return new StringObject(vm, Integer.toString(iUse));
}
case "java/lang/StringBuffer->toString()Ljava/lang/String;":{
StringBuffer str = (StringBuffer) dvmObject.getValue();
return new StringObject(vm,str.toString());
}
case "java/lang/StringBuffer->append(Ljava/lang/String;)Ljava/lang/StringBuffer;": {
StringBuffer str = (StringBuffer) dvmObject.getValue();
StringObject serviceName = vaList.getObject(0);
assert serviceName != null;
return vm.resolveClass("java/lang/StringBuffer").newObject(str.append(serviceName.value));
}
继续跑
st=1607417268979&sign=15db6c5b8076570b5db2407c308d282f&sv=111
这次成功了,老规矩,我们验算下,这里使用Unidbg自带的xHook
IxHook xHook = XHookImpl.getInstance(emulator); // 加载xHook,支持Import hook,文档看https://github.com/iqiyi/xHook
xHook.register("libjdbitmapkit.so", "gettimeofday", new ReplaceCallback() { // hook libttEncrypt.so的导入函数strlen
Pointer pointer1 = null;
@Override
public HookStatus onCall(Emulator<?> emulator, HookContext context, long originFunction) {
pointer1 = context.getPointerArg(0);
// String str = pointer.getString(0);
System.out.println("gettimeofday");
// context.push(str);
return HookStatus.RET(emulator, originFunction);
}
@Override
public void postCall(Emulator<?> emulator, HookContext context) {
// Pointer pointer = context.getPointerArg(0);
if(pointer1 != null){
// 这里把 时间写死
byte[] buf = {(byte)0x91,(byte)0x50,(byte)0xc4,(byte)0x5f,(byte)0x15,(byte)0x97,(byte)0x09,(byte)0x00};
pointer1.write(0,buf,0,8);
}
// ByteBuffer tv_sec = pointer1.getByteBuffer(0,8);
System.out.println("gettimeofday Ok"); // + context.pop() ); // + ", ret=" + context.getIntArg(0));
}
}, true);
最后结果
st=1606701201628&sign=59039230dc2e1ea27a4f250d9ec81b8c&sv=111
和我们之前算的是一样的。
三、总结
Unidbg在模拟java类上有优势,感觉AndroidNativeEmu更像个优雅的Demo,而Unidbg还在一直更新,实用价值可能更大一点。
关注微信公众号,最新技术干货实时推送