抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

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;

/* compiled from: BL */
/* loaded from: classes.dex */
public final class LibBili {
public static final int a = 0;
public static final int b = 1;

/* renamed from: c reason: collision with root package name */
public static final int f14890c = 0;
public static final int d = 1;
public static final int e = 2;
public static final int f = 3;

/* compiled from: BL */
@Retention(RetentionPolicy.SOURCE)
/* loaded from: classes.dex */
public @interface a {
}

/* compiled from: BL */
@Retention(RetentionPolicy.SOURCE)
/* loaded from: classes.dex */
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);
}

// target
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
// add
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()); // dvm object

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_TRUEJNI_FALSE
    • 引用类型: 包括字符串、字节数组、以及 Java 对象,使用对应的 DvmObject 封装。
  • 引用类型的封装逻辑: 对于引用类型(如 Stringbyte[]),通过 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)用于获取第indexobject参数,如果是 int 、long 等类型需要使用对应的getintArggetlongArg等等。

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();
// 不要忘了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,有两种方法:

  1. SignedQuery是样本自定义的类库,可以通过resolveClass(className).newObject处理。

  2. 由于将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);
    //转成unidbg所使用的类型
    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

评论