|
4 | 4 | * [补环境](#奇技淫巧) |
5 | 5 | * [0x01. JNI以及相关API](#0x01-JNI以及相关API) |
6 | 6 | - [构造最基本Context实例](#构造最基本Context实例) |
| 7 | + - [resolveClass解释](#resolveClass解释) |
7 | 8 | - [Context实例的getClass方法](#Context实例的getClass方法) |
8 | 9 | - [返回PID](#返回PID) |
9 | 10 | - [使用java的api返回一个对象](#使用java的api返回一个对象) |
10 | 11 | - [VarArg的使用](#VarArg的使用) |
11 | 12 | - [VaList的使用](#VaList的使用) |
| 13 | + - [newObject](#newObject) |
12 | 14 | * [0x02. 文件访问](#0x02-文件访问) |
13 | 15 | * [实体路径](#实体路径) |
14 | 16 | * [代码方式](#代码方式) |
| 17 | + * [0x03. 加载Unidbg中不支持的SO](#0x03-加载Unidbg中不支持的SO) |
| 18 | + * |
15 | 19 |
|
16 | 20 | <!-- /code_chunk_output --> |
17 | 21 |
|
@@ -54,6 +58,106 @@ public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String |
54 | 58 | return super.callObjectMethodV(vm, dvmObject, signature, vaList); |
55 | 59 | } |
56 | 60 | ``` |
| 61 | +#### resolveClass解释 |
| 62 | + |
| 63 | +[原文链接](https://t.zsxq.com/RBujayn) |
| 64 | + |
| 65 | +```java |
| 66 | +@Override |
| 67 | +public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) { |
| 68 | + switch (signature){ case "android/app/ActivityThread->getApplication()Landroid/app/Application;":{ |
| 69 | + return vm.resolveClass("android/app/Application", vm.resolveClass("android/content/ContextWrapper", |
| 70 | + vm.resolveClass("android/content/Context"))).newObject(signature); |
| 71 | + } |
| 72 | + } |
| 73 | + return super.callObjectMethodV(vm, dvmObject, signature, vaList); |
| 74 | +} |
| 75 | +``` |
| 76 | + |
| 77 | +- callObjectMethodV或者之前的getStaticObjectField,返回的都是jobject,Unidbg中如何构造一个Jobject? |
| 78 | + |
| 79 | + jString在Unidbg中被封装成了StringObject,除此之外,下图中标准和的jobject也都封装好了。 |
| 80 | + |
| 81 | + Unidbg源码如下:`unidbg-android/src/main/java/com/github/unidbg/linux/android/dvm/array/` |
| 82 | + |
| 83 | + <img src="pic/02.png" style="zoom:50%;" /> |
| 84 | + |
| 85 | +- 那么Unidbg中没有的Jobject该怎么处理呢,比如返回**ctrip.android.security.a**的实例? |
| 86 | + |
| 87 | + ```java |
| 88 | + return vm.resolveClass("ctrip/android/security/a").newObject(null); |
| 89 | + ``` |
| 90 | + |
| 91 | + 类名全路径中“.”替换成”/“,传入resolveClass,即创建了目标类,newObject即创建实例,newObject 的参数为null即可,这样就创建了一个目标类的实例jobject。除此之外,我们一般还会将signature签名 填入newObject,用于区分jobject,方便后续分别处理。 |
| 92 | + |
| 93 | + ```java |
| 94 | + return vm.resolveClass("ctrip/android/security/a").newObject(signature); |
| 95 | + ``` |
| 96 | + |
| 97 | + Application在Unidbg中并没有封装,所以按照上面的说法,Application对象应该这么表示 |
| 98 | + |
| 99 | + ```java |
| 100 | + return vm.resolveClass("android/app/Application").newObject(signature); |
| 101 | + ``` |
| 102 | + |
| 103 | + 拆开来看 |
| 104 | + |
| 105 | + ```java |
| 106 | + DvmClass context = vm.resolveClass("android/content/Context"); |
| 107 | + DvmClass ContextWrapper = vm.resolveClass("android/content/ContextWrapper", context); |
| 108 | + DvmClass Application = vm.resolveClass("android/app/Application",ContextWrapper); |
| 109 | + return Application.newObject(signature); |
| 110 | + ``` |
| 111 | + |
| 112 | + resolveClass其实是个可变参数方法,如果有第二个参数,那它代表我们创建的类其父类。 所以实际上这里是明确了继承关系。 |
| 113 | + |
| 114 | + <img src="pic/03.png"> |
| 115 | + |
| 116 | +- 为什么我们要在Unidbg中表明这个继承关系? 假如不填写继承关系,查看报错: |
| 117 | + |
| 118 | + ```java |
| 119 | + switch (signature) { |
| 120 | + case "android/app/ActivityThread->getApplication()Landroid/app/Application;": { |
| 121 | + DvmClass Application = vm.resolveClass("android/app/Application"); |
| 122 | + return Application.newObject(signature); |
| 123 | + } |
| 124 | + ``` |
| 125 | + |
| 126 | + <img src="pic/04.png"> |
| 127 | + |
| 128 | + 查看526行代码: |
| 129 | + |
| 130 | + ```java |
| 131 | + DvmClass dvmClass = dvmObject == null ? null : dvmObject.getObjectType(); |
| 132 | + DvmMethod dvmMethod = dvmClass == null ? null : dvmClass.getMethod(jmethodID.toIntPeer()); |
| 133 | + if (dvmMethod == null) { |
| 134 | + throw new BackendException("dvmObject=" + dvmObject + ", dvmClass=" + dvmClass + ", jmethodID=" + jmethodID); |
| 135 | + } |
| 136 | + ``` |
| 137 | + |
| 138 | + 说明dvmMethod没有找到,方法找不到,methodID肯定有问题,我们发现了一个问题,对象是Application实例,但MethodID是从Context类找来的,为什么能这样做 呢?Application是Context的子孙类,getContentResolver是Context中的抽象方法,JAVA允许这么玩儿。Unidbg处理为什么出现问题了呢? |
| 139 | + |
| 140 | + 首先,Unidbg中执行GetMethodID方法时建立了一个对应关系,Context的methodMap中存储了这个对应关系,而CallObjectMethodV时,因为对象是Application实例,所以它去Application的methodMap里乱找一通,发现啥都没有,自然就报错了。但从Unidbg的代码逻辑中我们发现,当找不到方法时,它会去父类以及超类的methodMap中去寻找,当我们写如下代码时: |
| 141 | + |
| 142 | + ```java |
| 143 | + DvmClass context = vm.resolveClass("android/content/Context"); |
| 144 | + DvmClass ContextWrapper = vm.resolveClass("android/content/ContextWrapper", context); |
| 145 | + DvmClass Application = vm.resolveClass("android/app/Application",ContextWrapper); |
| 146 | + return Application.newObject(signature); |
| 147 | + ``` |
| 148 | + |
| 149 | + Unidbg先在Application自己的methodMap里找,找不到的话去ContextWrapper的methodMap里 找,最后在Context的methodMap里找,除此之外,它还会在接口类里找(resolveClass的第三个参数 开始代表所实现的接口类)。因此,AbstractJNI中给Application补齐了继承链,就是担心ID在超类的 methodMap里。 |
| 150 | + |
| 151 | + 在我们自己分析样本时,可以不这么复杂,每次需要resolveClass+newObject声明jObject时候,就别管 什么继承,就当它没爹,但如果报错就直接检索methodID,看是哪个超类生成的ID,别管它在”**真实代码逻辑**“中是太爷爷、爷爷还是爸爸, 直接安排它做爸爸。这里直接省略了`ContextWrapper` |
| 152 | + |
| 153 | + ```java |
| 154 | + case "android/app/ActivityThread->getApplication()Landroid/app/Application;": { |
| 155 | + DvmClass context = vm.resolveClass("android/content/Context"); |
| 156 | + DvmClass Application = vm.resolveClass("android/app/Application", context); |
| 157 | + return Application.newObject(signature); |
| 158 | + } |
| 159 | + ``` |
| 160 | + |
57 | 161 | #### Context实例的getClass方法 |
58 | 162 |
|
59 | 163 | ```java |
@@ -167,6 +271,42 @@ public DvmObject<?> callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String |
167 | 271 | } |
168 | 272 | ``` |
169 | 273 |
|
| 274 | +#### newObject |
| 275 | + |
| 276 | +```java |
| 277 | +@Override |
| 278 | +public DvmObject<?> newObject(BaseVM vm, DvmClass dvmClass, String signature, |
| 279 | +VarArg varArg) { |
| 280 | + switch (signature){ |
| 281 | + case "java/lang/Throwable-><init>()V":{ |
| 282 | + return vm.resolveClass("java/lang/Throwable").newObject(new Throwable()); |
| 283 | + } |
| 284 | + case "java/io/ByteArrayOutputStream-><init>()V":{ |
| 285 | + return vm.resolveClass("java/io/ByteArrayOutputStream").newObject(new ByteArrayOutputStream()); |
| 286 | +} } |
| 287 | + return super.newObject(vm, dvmClass, signature, varArg); |
| 288 | +} |
| 289 | +``` |
| 290 | + |
| 291 | +另一种方式: |
| 292 | + |
| 293 | +```java |
| 294 | +@Override |
| 295 | +public DvmObject<?> newObject(BaseVM vm, DvmClass dvmClass, String signature, |
| 296 | +VarArg varArg) { |
| 297 | + switch (signature){ |
| 298 | + case "java/lang/Throwable-><init>()V":{ |
| 299 | + return dvmClass.newObject(new Throwable()); |
| 300 | + } |
| 301 | + case "java/io/ByteArrayOutputStream-><init>()V":{ |
| 302 | + return dvmClass.newObject(new ByteArrayOutputStream()); |
| 303 | +} } |
| 304 | + return super.newObject(vm, dvmClass, signature, varArg); |
| 305 | +} |
| 306 | +``` |
| 307 | + |
| 308 | + |
| 309 | + |
170 | 310 | ### 0x02. 文件访问 |
171 | 311 |
|
172 | 312 | #### 实体路径 |
@@ -210,5 +350,47 @@ public DvmObject<?> callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String |
210 | 350 | } |
211 | 351 | ``` |
212 | 352 |
|
| 353 | +### 0x03. 加载Unidbg中不支持的SO |
| 354 | + |
| 355 | +- [CSDN-样本八](https://blog.csdn.net/qq_38851536/article/details/118024298) [知识星球-样本二](https://t.zsxq.com/RBujayn) |
| 356 | + |
| 357 | +- 查看Unidbg 支持哪些so: `unidbg-android/src/main/resources/android/sdk23/lib` |
| 358 | + |
| 359 | + > 为什么Unidbg不内置支持所有系统SO的加载? |
| 360 | + > |
| 361 | + > 1. 大部分SO中主要的依赖项,就是Unidbg已经支持的这些,即已经够用了。 |
| 362 | + > 2. 把Android系统中全部SO都成功加载进Unidbg虚拟内存中,既是很大的工作量,又会占用过多内存。 |
| 363 | + > 3. 另一个原因是,比如libandroid.so,其依赖SO实在太多了,想顺利加载整个SO确确实实是个苦差事。 |
| 364 | + |
| 365 | +- 如果SO的依赖项中有Unidbg不支持的系统SO,怎么办? |
| 366 | + |
| 367 | + > 首先Unidbg会给予提示:比如libnative-lib.so load dependency libandroid.so failed |
| 368 | + > |
| 369 | + > 然后去网上搜下是不是系统库,如果是APK本身自带的SO,手动加载一下即可,并默认执行其init函数,并调用JNIOnLoad, |
| 370 | + > 注意:依赖项必须先加载 |
| 371 | + > |
| 372 | + > 其次,尽管SO加载了Unidbg不支持的SO,但有可能我们的目标函数并没有使用到这个系统SO,这种情况下就不用理会,当作不存在就行。 |
| 373 | + |
| 374 | +- 但如果目标函数使用到了这个系统SO,那就麻烦了,我们就得直面这个问题,一般有两种处理办法。 |
| 375 | + |
| 376 | + 1. Patch/Hook 这个不支持的SO所使用的函数 |
| 377 | + |
| 378 | + 2. 使用Unidbg VirtualModule |
| 379 | + |
| 380 | + > VirtualModule是Unidbg为此种情况所提供的官方解决方案,并在代码中提供了两个示例,路径:unidbg-android/src/main/java/com/github/unidbg/virtualmodule/android |
| 381 | + |
| 382 | + 比如libandroid.so可以使用`AndroidModule`,只实现了libandroid中这几个常用的导出函数。 |
| 383 | + |
| 384 | + ```java |
| 385 | + vm = emulator.createDalvikVM(new File("unidbg-android/src/test/java/com/lession8/demo2.apk")); |
| 386 | + // 注册libandroid.so虚拟模块 |
| 387 | + new AndroidModule(emulator, vm).register(memory); |
| 388 | + |
| 389 | + DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/java/com/lession8/readassets.so"), true); |
| 390 | + module = dm.getModule(); |
| 391 | + ``` |
| 392 | + |
| 393 | + 需要注意,一定要在样本SO加载前加载它,道理也很简单,系统SO肯定比用户SO加载的早。VirtualModule并不是一种真正意义上的加载SO,它本质上也是Hook,只不过实现了SO中少数几个函数罢了。 |
213 | 394 |
|
| 395 | + 需要注意的是,VirtualModule并不是一种真正意义上的加载SO,它本质上也是Hook,只不过实现了SO中少数几个函数罢了。 |
214 | 396 |
|
0 commit comments