Unidbg 的基本使用(四) 学习uniodbg的第四天
样本是bilibili.apk
任务介绍 JADX
反编译 apk,找到com.bilibili.nativelibrary.LibBili
类,其中的 s 方法是本篇的目标函数。它是一个静态方法,参数是 Map,返回值是 SignedQuery 对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 package com.bilibili.nativelibrary;import androidx.annotation.NonNull;import com.bilibili.lib.media.resource.PlayIndex;import java.io.UnsupportedEncodingException;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.security.InvalidKeyException;import java.util.Arrays;import java.util.Map;import java.util.SortedMap;import java.util.TreeMap;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;public final class LibBili { public static final int a = 0 ; public static final int b = 1 ; public static final int f14890c = 0 ; public static final int d = 1 ; public static final int e = 2 ; public static final int f = 3 ; @Retention(RetentionPolicy.SOURCE) public @interface a { } @Retention(RetentionPolicy.SOURCE) public @interface b { } static { com.getkeepsafe.relinker.c.c(PlayIndex.G); } private static native String a (String str) ; private static native String ao (String str, int i2, int i3) ; private static native IvParameterSpec b (String str) throws InvalidKeyException; public static byte [] b(String str, byte [] bArr) throws InvalidKeyException { try { byte [] bytes = str.getBytes("UTF-8" ); return com.bilibili.nativelibrary.a.a(new SecretKeySpec (Arrays.copyOf(bytes, 16 ), "AES" ), b(str), bArr); } catch (UnsupportedEncodingException | Exception unused) { return bArr; } } public static byte [] c(String str, byte [] bArr) throws InvalidKeyException { try { byte [] bytes = str.getBytes("UTF-8" ); return com.bilibili.nativelibrary.a.b(new SecretKeySpec (Arrays.copyOf(bytes, 16 ), "AES" ), b(str), bArr); } catch (UnsupportedEncodingException | Exception unused) { return bArr; } } @Deprecated public static String d () { return e("android" ); } public static String e (String str) { return a(str); } public static String f (String str, int i2, int i3) { return ao(str, i2, i3); } public static SignedQuery g (Map<String, String> map) { return s(map == null ? new TreeMap () : new TreeMap (map)); } public static native int getCpuCount () ; @Deprecated public static native int getCpuId () ; public static SignedQuery h (Map<String, String> map, int i2, int i3) { TreeMap treeMap; if (map == null ) { treeMap = new TreeMap (); } else { treeMap = new TreeMap (map); } return so(treeMap, i2, i3); } public static SignedQuery i (Map<String, String> map, @NonNull byte [] bArr) { TreeMap treeMap; if (map == null ) { treeMap = new TreeMap (); } else { treeMap = new TreeMap (map); } return so(treeMap, bArr); } static native SignedQuery s (SortedMap<String, String> sortedMap) ; static native SignedQuery so (SortedMap<String, String> sortedMap, int i2, int i3) ; static native SignedQuery so (SortedMap<String, String> sortedMap, byte [] bArr) ; }
LibBili类里的 g 函数使用了它
1 2 3 public static SignedQuery g (Map<String, String> map) { return s(map == null ? new TreeMap () : new TreeMap (map)); }
对 g 做 Hook,参考代码如下
1 2 3 4 5 6 7 8 9 10 Java .perform (function ( ) { let LibBili = Java .use ("com.bilibili.nativelibrary.LibBili" ); let Map = Java .use ('java.util.HashMap' ); LibBili ["g" ].implementation = function (map ) { console .log ('g is called' + ', ' + 'map: ' + Java .cast (map, Map )); let ret = this .g (map); console .log ('g ret value is ' + ret); return ret; }; })
输出很多,随便选择一条
1 2 g is called, map : {build=6180500 , mobi_app=android, channel=shenma069, appkey=1d8b6e7d45233436, s_locale=zh-Hans _CN, c_locale=zh-Hans _CN, platform=android, statistics={"appId" :1 ,"platform" :3 ,"version" :"6.18.0" ,"abtest" :"" }} g ret value is appkey=1d8b6e7d45233436&build=6180500 &c_locale=zh-Hans _CN&channel=shenma069&mobi_app=android&platform=android&s_locale=zh-Hans _CN&statistics=%7B%22appId%22 %3A1%2C%22platform%22 %3A3%2C%22version%22 %3A%226.18 .0 %22 %2C%22abtest%22 %3A%22 %22 %7D&ts=1735285105 &sign=0f5acc626dbc75c422023515e360b04c
使用frida主动调用 s 函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function callS ( ){ Java .perform (function ( ) { let LibBili = Java .use ("com.bilibili.nativelibrary.LibBili" ); let TreeMap = Java .use ("java.util.TreeMap" ); var map = TreeMap .$new(); map.put ("build" , "6180500" ); map.put ("mobi_app" , "android" ) map.put ("channel" , "shenma069" ) map.put ("appkey" , "1d8b6e7d45233436" ) map.put ("s_locale" , "zh_CN" ) let result = LibBili .s (map); console .log ("ret:" +result.toString ()); }) }
结果如下
1 ret:appkey=1d8b6e7d45233436&build=6180500&channel=shenma069&mobi_app=android&s_locale=zh_CN&ts=1735285285&sign=8a88956be47023309124d5b45a33d0b4
接下来使用unidbg复现 s 函数的调用。
初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package com.Bili;import com.github.unidbg.AndroidEmulator;import com.github.unidbg.Emulator;import com.github.unidbg.arm.backend.Unicorn2Factory;import com.github.unidbg.file.FileResult;import com.github.unidbg.file.IOResolver;import com.github.unidbg.linux.android.AndroidEmulatorBuilder;import com.github.unidbg.linux.android.AndroidResolver;import com.github.unidbg.linux.android.dvm.AbstractJni;import com.github.unidbg.linux.android.dvm.DalvikModule;import com.github.unidbg.linux.android.dvm.DvmClass;import com.github.unidbg.linux.android.dvm.VM;import com.github.unidbg.memory.Memory;import java.io.File;public class NativeLibrary extends AbstractJni implements IOResolver { private final AndroidEmulator emulator; private final DvmClass LibBili; private final VM vm; public NativeLibrary () { emulator = AndroidEmulatorBuilder .for32Bit() .addBackendFactory(new Unicorn2Factory (true )) .setProcessName("tv.danmaku.bili" ) .build(); Memory memory = emulator.getMemory(); memory.setLibraryResolver(new AndroidResolver (23 )); vm = emulator.createDalvikVM(new File ("unidbg-android/src/test/resources/bili/bilibili.apk" )); vm.setJni(this ); vm.setVerbose(true ); emulator.getSyscallHandler().addIOResolver(this ); DalvikModule dm = vm.loadLibrary("bili" , true ); LibBili = vm.resolveClass("com.bilibili.nativelibrary.LibBili" ); dm.callJNI_OnLoad(emulator); } public static void main (String[] args) { NativeLibrary nativeLibrary = new NativeLibrary (); } @Override public FileResult resolve (Emulator emulator, String pathname, int oflags) { System.out.println("lilac open:" +pathname); return null ; } }
因为担心可能存在的文件访问,所以这里继承了 IOResolver,补文件访问的相关处理留个坑:
如果爆红内存不足可以补上多线程:
[main]W/libc: pthread_create failed: clone failed: Out of memory
1 2 3 4 emulator.getBackend().registerEmuCountHook(10000 ); emulator.getSyscallHandler().setVerbose(true ); emulator.getSyscallHandler().setEnableThreadDispatcher(true );
Unidbg 的日志输出中可以看到s
方法,它来自于动态注册。s(Ljava/util/SortedMap;)Lcom/bilibili/nativelibrary/SignedQuery;
就是待会儿调用它的签名。
1 2 3 4 5 6 7 8 9 10 11 12 lilac open:/dev/__properties__ lilac open:/proc/stat JNIEnv->FindClass(com/bilibili/nativelibrary/LibBili) was called from RX@0x12001b7b [libbili.so]0x1b7b JNIEnv->RegisterNatives(com/bilibili/nativelibrary/LibBili, RW@0x1200b004 [libbili.so]0xb004 , 8 ) was called from RX@0x12001b8f [libbili.so]0x1b8f RegisterNative(com/bilibili/nativelibrary/LibBili, a(Ljava/lang/String;)Ljava/lang/String;, RX@0x12001c7d [libbili.so]0x1c7d ) RegisterNative(com/bilibili/nativelibrary/LibBili, ao(Ljava/lang/String;II)Ljava/lang/String;, RX@0x12001c83 [libbili.so]0x1c83 ) RegisterNative(com/bilibili/nativelibrary/LibBili, b(Ljava/lang/String;)Ljavax/crypto/spec/IvParameterSpec;, RX@0x12001c91 [libbili.so]0x1c91 ) RegisterNative(com/bilibili/nativelibrary/LibBili, s(Ljava/util/SortedMap;)Lcom/bilibili/nativelibrary/SignedQuery;, RX@0x12001c97 [libbili.so]0x1c97 ) RegisterNative(com/bilibili/nativelibrary/LibBili, so(Ljava/util/SortedMap;II)Lcom/bilibili/nativelibrary/SignedQuery;, RX@0x12001c9d [libbili.so]0x1c9d ) RegisterNative(com/bilibili/nativelibrary/LibBili, so(Ljava/util/SortedMap;[B)Lcom/bilibili/nativelibrary/SignedQuery;, RX@0x12001cab [libbili.so]0x1cab ) RegisterNative(com/bilibili/nativelibrary/LibBili, getCpuCount()I, RX@0x12001cb3 [libbili.so]0x1cb3 ) RegisterNative(com/bilibili/nativelibrary/LibBili, getCpuId()I, RX@0x12001cb7 [libbili.so]0x1cb7 )
发起调用 前面的例子里,参数和返回值都是基本类型、字符串、字节数组,直接传入就可以就调用,这里比较特殊,是个 map
完整的学一下 Unidbg 中 JNI 对象的表示法,首先根据JNI
标准,JNI
方法的返回值,以及JAVA
传递到Native
的对象,都是JObject
,其中最常用的一些类目还做了细分,具体情况如下。
jclass
是类对象,jstring
是字符串,jarray
以及其细分是各种各样的数组,jthrowable
是 JNI 异常处理中使用到的对象。
在 Unidbg 中,做了几乎完整的等价模拟和映射。
JNI 类型
描述
Unidbg 映射
jobject
通用的 Java 对象
DvmObject
jclass
类对象
DvmClass
jstring
字符串对象
StringObject
jarray
通用数组类型
BaseArray
jobjectArray
对象数组
ArrayObject
jbyteArray
字节数组
ByteArray
jshortArray
短整型数组
ShortArray
jintArray
整型数组
IntArray
jthrowable
异常处理对象
模拟后处理
Unidbg 实现了对上述 JNI 对象的几乎完整映射,通过封装 Java 对象为 DvmObject
系列的子类,实现了 Native 层与 Java 层的无缝交互。
在补 JNI 调用时,如果返回值是 String,需要封装为 StringObject,比如前文就有一个片段,返回了字符串。
1 2 3 4 5 case "java/lang/Class->getSimpleName()Ljava/lang/String;" :{ String className = ((DvmClass) dvmObject).getClassName(); String[] name = className.split("/" ); return new StringObject (vm, name[name.length - 1 ]); }
callJniMethod
函数逻辑Unidbg 提供了 callXXX
系列方法来调用 JNI 函数,如 callJniMethod
是核心的函数调用方法。其具体逻辑如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 if (args != null ) { for (Object arg : args) { if (arg instanceof Boolean) { list.add((Boolean) arg ? VM.JNI_TRUE : VM.JNI_FALSE); continue ; } else if (arg instanceof Hashable) { list.add(arg.hashCode()); if (arg instanceof DvmObject) { vm.addLocalObject((DvmObject<?>) arg); } continue ; } else if (arg instanceof DvmAwareObject || arg instanceof String || arg instanceof byte [] || arg instanceof short [] || arg instanceof int [] || arg instanceof float [] || arg instanceof double [] || arg instanceof Enum) { DvmObject<?> obj = ProxyDvmObject.createObject(vm, arg); list.add(obj.hashCode()); vm.addLocalObject(obj); continue ; } list.add(arg); } }
(1) 找到函数地址
callJniMethod
首先通过方法签名找到对应的 Native 函数地址:
1 UnidbgPointer fnPtr = objectType.findNativeFunction(emulator, method);
该方法通过 findNativeFunction
从当前 DvmClass 中解析出 Native 函数地址。
(2) 函数入参处理
进入核心的参数处理逻辑,主要包括以下几个步骤:
添加默认的 JNIEnv 和对象引用: 根据 JNI 标准,JNIEnv
和调用对象 (Jobject/Jclass
) 是默认参数:
1 2 3 List<Object> list = new ArrayList <>(10 ); list.add(vm.getJNIEnv()); list.add(thisObj.hashCode());
基本类型与引用类型的处理: 根据参数类型分为两类:
基本类型 : 如 Boolean
被转换为 JNI_TRUE
或 JNI_FALSE
。
引用类型 : 包括字符串、字节数组、以及 Java 对象,使用对应的 DvmObject
封装。
引用类型的封装逻辑: 对于引用类型(如 String
或 byte[]
),通过 ProxyDvmObject.createObject
方法封装为对应的 DvmObject
子类:
1 2 3 DvmObject <?> obj = ProxyDvmObject .createObject (vm, arg);list.add (obj.hashCode ()); vm.addLocalObject (obj);
(3) 调用 Native 函数
完成参数组装后,调用 Native 函数:
1 return Module.emulateFunction(emulator, fnPtr.peer, list.toArray());
但事实上,createObject除了可以处理 string、array 这样的普通对象,也可以处理 map 或其他对象类型。来看ProxyDvmObject 对象的构造逻辑,它会对类的父类以及接口类做解析,嵌套式的getObjectType,最后resolveClass再newObject得到最终的DvmObject。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private ProxyDvmObject (VM vm, Object value) { super (getObjectType(vm, value.getClass()), value); } private static DvmClass getObjectType (VM vm, Class<?> clazz) { Class<?> superClass = clazz.getSuperclass(); DvmClass[] interfaces = new DvmClass [clazz.getInterfaces().length + (superClass == null ? 0 : 1 )]; int i = 0 ; if (superClass != null ) { interfaces[i++] = getObjectType(vm, superClass); } for (Class<?> cc : clazz.getInterfaces()) { interfaces[i++] = getObjectType(vm, cc); } return vm.resolveClass(clazz.getName().replace('.' , '/' ), interfaces); }
因此我们手动调用它处理 map
1 2 3 4 5 6 7 8 9 10 11 public String callS () { TreeMap<String, String> map = new TreeMap <>(); map.put("build" , "6180500" ); map.put("mobi_app" , "android" ); map.put("channel" , "shenma069" ); map.put("appkey" , "1d8b6e7d45233436" ); map.put("s_locale" , "zh_CN" ); DvmObject<?> mapObject = ProxyDvmObject.createObject(vm, map); String ret = LibBili.callStaticJniMethodObject(emulator, "s(Ljava/util/SortedMap;)Lcom/bilibili/nativelibrary/SignedQuery;" , mapObject).getValue().toString(); return ret; }
总结一下补 JNI 的形式要求
基本类型直接传递
字符串、字节数组等基本对象直接传递,其内部会做封装,也可以自己调用new StringObject(vm, str)
、new ByteArray(vm, value)
等。
JDK 标准库对象,如 HashMap、JSONObject 等,使用ProxyDvmObject.createObject(vm, value)
处理。
非 JDK 标准库对象,如 Android Context、SharedPreference 等,使用vm.resolveClass(vm,className).newObject(value)
处理。
补环境 main函数进行调用:
1 System.out.println("call s result: " + nativeLibrary.callS());
报错
1 2 3 4 5 6 JNIEnv-> GetStaticMethodID(com/bilibili/nativelibrary/SignedQuery.r(Ljava/util/Map;)Ljava/lang/String;) => 0xf79a08d7 was called from RX@0x12004753[libbili.so]0x4753 Find native function Java_com_bilibili_nativelibrary_LibBili_s => RX@0x12001c97[libbili.so]0x1c97 [16:12:12 005] WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987312, svcNumber=0x121, PC=unidbg@0xfffe02a4, LR=RX@0x12006697[libbili.so]0x6697, syscall=null java.lang.UnsupportedOperationException: java/util/Map->isEmpty()Z at com.github.unidbg.linux.android.dvm.AbstractJni.callBooleanMethod(AbstractJni.java:598) at com.github.unidbg.linux.android.dvm.AbstractJni.callBooleanMethod(AbstractJni.java:588)
在callBooleanMethod方法中补充Map.isEmpty()方法,isEmpty 用于判断 Map 是否为空,取出这个 dvmObject 对应的 map 对象,调用它的 isEmpty 方法
1 2 3 4 5 6 7 8 9 @Override public boolean callBooleanMethod (BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) { switch (signature){ case "java/util/Map->isEmpty()Z" : { return ((Map)dvmObject.getValue()).isEmpty(); } } return super .callBooleanMethod(vm, dvmObject, signature, varArg); }
接着报错:
1 2 3 4 5 JNIEnv->NewStringUTF("appkey" ) was called from RX@0x12003019 [libbili.so]0x3019 [16 :15 :49 772 ] WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538 ) - handleInterrupt intno=2 , NR=-452987312 , svcNumber=0x11e , PC=unidbg@0xfffe0274 , LR=RX@0x120064dd [libbili.so]0x64dd , syscall=null java.lang.UnsupportedOperationException: java/util/Map->get(Ljava/lang/Object;)Ljava/lang/Object; at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethod(AbstractJni.java:933 ) at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethod(AbstractJni.java:867 )
同样实现它。不以V
结尾的 JNI 函数通过 varArg 取参数,以V
结尾的 JNI 函数中通过 varList 取参数。
getObjectArg(index)
用于获取第index
个object
参数,如果是 int 、long 等类型需要使用对应的getintArg
、getlongArg
等等。
1 2 3 4 5 6 7 8 9 10 11 12 @Override public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) { switch (signature){ case "java/util/Map->get(Ljava/lang/Object;)Ljava/lang/Object;" :{ Map map = (Map) dvmObject.getValue(); Object key = varArg.getObjectArg(0 ).getValue(); return ProxyDvmObject.createObject(vm, map.get(key)); } } return super .callObjectMethod(vm, dvmObject, signature, varArg); }
继续运行,报错
1 2 3 4 5 6 JNIEnv->NewStringUTF("1735287490" ) was called from RX@0x12003471 [libbili.so]0x3471 [16 :18 :10 927 ] WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538 ) - handleInterrupt intno=2 , NR=-452987368 , svcNumber=0x11e , PC=unidbg@0xfffe0274 , LR=RX@0x1200659d [libbili.so]0x659d , syscall=null java.lang.UnsupportedOperationException: java/util/Map->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethod(AbstractJni.java:933 ) at com.Bili.NativeLibrary.callObjectMethod(NativeLibrary.java:70 ) at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethod(AbstractJni.java:867 )
同样实现它
1 2 3 4 5 6 case "java/util/Map->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;" :{ Map map = (Map) dvmObject.getValue(); Object key = varArg.getObjectArg(0 ).getValue(); Object value = varArg.getObjectArg(1 ).getValue(); return ProxyDvmObject.createObject(vm, map.put(key, value)); }
接着报错:
1 2 3 4 5 6 [16 :19 :25 116 ] WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538 ) - handleInterrupt intno=2 , NR=-452987096 , svcNumber=0x16e , PC=unidbg@0xfffe0774 , LR=RX@0x12003077 [libbili.so]0x3077 , syscall=null java.lang.UnsupportedOperationException: com/bilibili/nativelibrary/SignedQuery->r(Ljava/util/Map;)Ljava/lang/String; at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethod(AbstractJni.java:433 ) at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethod(AbstractJni.java:422 ) at com.github.unidbg.linux.android.dvm.DvmMethod.callStaticObjectMethod(DvmMethod.java:54 ) at com.github.unidbg.linux.android.dvm.DalvikVM$111. handle(DalvikVM.java:1765 )
这是样本自定义的函数,在 JADX 中看它的代码逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 static String r (Map<String, String> map) { if (!(map instanceof SortedMap)) { map = new TreeMap (map); } StringBuilder sb = new StringBuilder (256 ); for (Map.Entry<String, String> entry : map.entrySet()) { String key = entry.getKey(); if (!TextUtils.isEmpty(key)) { sb.append(b(key)); sb.append(ContainerUtils.KEY_VALUE_DELIMITER); String value = entry.getValue(); sb.append(value == null ? "" : b(value)); sb.append(ContainerUtils.FIELD_DELIMITER); } } int length = sb.length(); if (length > 0 ) { sb.deleteCharAt(length - 1 ); } if (length == 0 ) { return null ; } return sb.toString(); }
将其实现拷贝到 Unidbg 里,导入 SortedMap
TextUtils 是 Android FrameWork 中用于处理文本的工具类,TextUtils.isEmpty 和 string.isEmpty 功能类似,这里直接替换。
b 方法是SignedQuery
类里另一个方法,我们干脆把SignedQuery
以及ContainerUtils
这两个类从 JADX 中拷贝过来,,以免遗漏,在这个过程中发现依赖cv.m
。
其中cv.m
字段的值为 15,直接硬编码处理一下,替换为15。
然后再重写callStaticObjectMethod方法,实现SignedQuery.r()
方法。
1 2 3 4 5 6 7 8 9 10 @Override public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) { switch (signature){ case "com/bilibili/nativelibrary/SignedQuery->r(Ljava/util/Map;)Ljava/lang/String;" :{ Map map = (Map) varArg.getObjectArg(0 ).getValue(); return new StringObject (vm, SignedQuery.r(map)); } } return super .callStaticObjectMethod(vm, dvmClass, signature, varArg); }
产生新的报错:
1 2 3 4 5 6 JNIEnv->NewStringUTF("7b9bb7ea1bb602d6a4cb1830c8a096df" ) was called from RX@0x120031a5 [libbili.so]0x31a5 [16 :47 :20 874 ] WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538 ) - handleInterrupt intno=2 , NR=-452987096 , svcNumber=0x118 , PC=unidbg@0xfffe0214 , LR=RX@0x120031cb [libbili.so]0x31cb , syscall=null java.lang.UnsupportedOperationException: com/bilibili/nativelibrary/SignedQuery-><init>(Ljava/lang/String;Ljava/lang/String;)V at com.github.unidbg.linux.android.dvm.AbstractJni.newObject(AbstractJni.java:753 ) at com.github.unidbg.linux.android.dvm.AbstractJni.newObject(AbstractJni.java:733 )
须重写newObject函数以完善SignedQuery的构造函数
1 2 3 4 5 6 7 8 9 10 11 @Override public DvmObject<?> newObject(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) { switch (signature){ case "com/bilibili/nativelibrary/SignedQuery-><init>(Ljava/lang/String;Ljava/lang/String;)V" :{ String arg1 = varArg.getObjectArg(0 ).getValue().toString(); String arg2 = varArg.getObjectArg(1 ).getValue().toString(); return vm.resolveClass("com/bilibili/nativelibrary/SignedQuery" ).newObject(new SignedQuery (arg1, arg2)); } } return super .newObject(vm, dvmClass, signature, varArg); }
最终需要将SignedQuery对象转成unidbg能接受的obj,有两种方法:
SignedQuery是样本自定义的类库,可以通过resolveClass(className).newObject
处理。
由于将SignedQuery类导入到了本地环境中,因此可以使用ProxyDvmObject.createObject(vm, obj);
构造出对象。
1 2 3 4 5 6 String str1 = (String) varArg.getObjectArg(0 ).getValue(); String str2 = (String) varArg.getObjectArg(1 ).getValue(); SignedQuery obj = new SignedQuery (str1, str2); return ProxyDvmObject.createObject(vm, obj);
结果:
1 call s result: appkey=1d8b6e7d45233436&build=6180500 &channel=shenma069&mobi_app=android&s_locale=zh_CN&ts=1735289412 &sign=16e98ec4f7c59790ea76ebd142f5b1e4