这篇文章上次修改于 1130 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
原文 -> https://blog.csdn.net/qq_38851536/article/details/118000259
前言
这是SO逆向入门实战教程的第七篇,总共会有十三篇,十三个实战
- 一个较好的综合性样本,本篇抛砖引玉分析一下
准备
这是我们的目标方法
参数1是203
参数2是一个对象数组
- 9b69f861-e054-4bc4-9daf-d36ae205ed3e (String)
- GET /aggroup/homepage/display __xxxxx(byte数组形式)
- 2 (int包装类)
unidbg模拟执行
首先搭一个架子
package com.lession10;
import com.github.unidbg.linux.android.dvm.AbstractJni;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.api.PackageInfo;
import com.github.unidbg.linux.android.dvm.api.Signature;
import com.github.unidbg.linux.android.dvm.array.ArrayObject;
import com.github.unidbg.linux.android.dvm.array.ByteArray;
import com.github.unidbg.linux.android.dvm.wrapper.DvmInteger;
import com.github.unidbg.memory.Memory;
import java.io.File;
public class NBridge extends AbstractJni {
private final AndroidEmulator emulator;
private final VM vm;
private final Module module;
NBridge(){
emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.meituan").build();
final Memory memory = emulator.getMemory(); // 模拟器的内存操作接口
memory.setLibraryResolver(new AndroidResolver(23)); // 设置系统类库解析
vm = emulator.createDalvikVM(new File("unidbg-android\\src\\test\\java\\com\\lession7\\mt.apk")); // 创建Android虚拟机
vm.setVerbose(true); // 设置是否打印Jni调用细节
DalvikModule dm = vm.loadLibrary(new File("unidbg-android\\src\\test\\java\\com\\lession7\\libmtguard.so"), true);
module = dm.getModule(); //
vm.setJni(this);
dm.callJNI_OnLoad(emulator);
}
public static void main(String[] args) {
NBridge test = new NBridge();
}
}
从日志看,主要做了函数的动态绑定,接下来执行目标方法
package com.lession7;
import com.github.unidbg.linux.android.dvm.AbstractJni;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.api.PackageInfo;
import com.github.unidbg.linux.android.dvm.api.Signature;
import com.github.unidbg.linux.android.dvm.array.ArrayObject;
import com.github.unidbg.linux.android.dvm.array.ByteArray;
import com.github.unidbg.linux.android.dvm.wrapper.DvmInteger;
import com.github.unidbg.memory.Memory;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
public class NBridge extends AbstractJni {
private final AndroidEmulator emulator;
private final VM vm;
private final Module module;
NBridge(){
emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.meituan").build();
final Memory memory = emulator.getMemory(); // 模拟器的内存操作接口
memory.setLibraryResolver(new AndroidResolver(23)); // 设置系统类库解析
vm = emulator.createDalvikVM(new File("unidbg-android\\src\\test\\java\\com\\lession7\\mt.apk")); // 创建Android虚拟机
vm.setVerbose(true); // 设置是否打印Jni调用细节
DalvikModule dm = vm.loadLibrary(new File("unidbg-android\\src\\test\\java\\com\\lession7\\libmtguard.so"), true);
module = dm.getModule(); //
vm.setJni(this);
dm.callJNI_OnLoad(emulator);
}
public static void main(String[] args) {
NBridge test = new NBridge();
test.main203();
}
public String main203(){
List<Object> list = new ArrayList<>(10);
list.add(vm.getJNIEnv()); // 第一个参数是env
list.add(0); // 第二个参数,实例方法是jobject,静态方法是jclazz,直接填0,一般用不到
list.add(203);
StringObject input2_1 = new StringObject(vm, "9b69f861-e054-4bc4-9daf-d36ae205ed3e");
ByteArray input2_2 = new ByteArray(vm, "GET /aggroup/homepage/display __r0ysue".getBytes(StandardCharsets.UTF_8));
DvmInteger input2_3 = DvmInteger.valueOf(vm, 2);
vm.addLocalObject(input2_1);
vm.addLocalObject(input2_2);
vm.addLocalObject(input2_3);
// 完整的参数2
list.add(vm.addLocalObject(new ArrayObject(input2_1, input2_2, input2_3)));
Number number = module.callFunction(emulator, 0x5a38d, list.toArray())[0];
return vm.getObject(number.intValue()).getValue().toString();
};
}
有两个注意点
- 参数如何构造
- 我没有用unidbg封装的方式去call,其实那样可以简单一些,但是!地址!yyds!
非常快的就报错退出了
这个时候使用JNItrace + Frida Call的方式看一下真实样本中的JNI执行流
/* TID 12169 */
134794 ms [+] JNIEnv->GetObjectArrayElement
134794 ms |- JNIEnv* : 0xe385fdc0
134794 ms |- jobjectArray : 0xd1c48a40
134794 ms |- jsize : 0
134794 ms |= jobject : 0x11
134794 ms ----------------------------Backtrace----------------------------
134794 ms |-> 0xc64dfeab: libmtguard.so!0x5beab (libmtguard.so:0xc6484000)
/* TID 12169 */
135008 ms [+] JNIEnv->GetObjectArrayElement
135008 ms |- JNIEnv* : 0xe385fdc0
135008 ms |- jobjectArray : 0xd1c48a40
135008 ms |- jsize : 1
135008 ms |= jobject : 0x21
135008 ms ----------------------------Backtrace----------------------------
135008 ms |-> 0xc64df07b: libmtguard.so!0x5b07b (libmtguard.so:0xc6484000)
/* TID 12169 */
135223 ms [+] JNIEnv->GetObjectArrayElement
135223 ms |- JNIEnv* : 0xe385fdc0
135223 ms |- jobjectArray : 0xd1c48a40
135223 ms |- jsize : 2
135223 ms |= jobject : 0x35
135223 ms ----------------------------Backtrace----------------------------
135223 ms |-> 0xc64df555: libmtguard.so!0x5b555 (libmtguard.so:0xc6484000)
/* TID 12169 */
135434 ms [+] JNIEnv->GetObjectClass
135434 ms |- JNIEnv* : 0xe385fdc0
135434 ms |- jobject : 0x35
135434 ms |= jclass : 0x49
135434 ms ----------------------------Backtrace----------------------------
135434 ms |-> 0xc64df24d: libmtguard.so!0x5b24d (libmtguard.so:0xc6484000)
/* TID 12169 */
135602 ms [+] JNIEnv->GetMethodID
135602 ms |- JNIEnv* : 0xe385fdc0
135602 ms |- jclass : 0x49
135602 ms |- char* : 0xc6569650
135602 ms |: intValue
135602 ms |- char* : 0xc6569659
135602 ms |: ()I
135602 ms |= jmethodID : 0x708253b0 { intValue()I }
135602 ms ----------------------------Backtrace----------------------------
135602 ms |-> 0xc64df321: libmtguard.so!0x5b321 (libmtguard.so:0xc6484000)
/* TID 12169 */
135700 ms [+] JNIEnv->ExceptionCheck
135700 ms |- JNIEnv* : 0xe385fdc0
135700 ms |= jboolean : 0 { false }
135700 ms ----------------------------Backtrace----------------------------
135700 ms |-> 0xc64efa1f: libmtguard.so!0x6ba1f (libmtguard.so:0xc6484000)
/* TID 12169 */
135797 ms [+] JNIEnv->CallIntMethodV
135797 ms |- JNIEnv* : 0xe385fdc0
135797 ms |- jobject : 0x35
135797 ms |- jmethodID : 0x708253b0 { intValue()I }
135797 ms |- va_list : 0xd1c489bc
135797 ms |= jint : 2
135797 ms ----------------------------Backtrace----------------------------
135797 ms |-> 0xc64e0369: libmtguard.so!0x5c369 (libmtguard.so:0xc6484000)
/* TID 12169 */
135991 ms [+] JNIEnv->GetStringUTFChars
135991 ms |- JNIEnv* : 0xe385fdc0
135991 ms |- jstring : 0x11
135991 ms |- jboolean* : 0x0
135991 ms |= char* : 0xc804d4b8
135991 ms ----------------------------Backtrace----------------------------
135991 ms |-> 0xc64eb2c1: libmtguard.so!0x672c1 (libmtguard.so:0xc6484000)
/* TID 12169 */
136088 ms [+] JNIEnv->ReleaseStringUTFChars
136088 ms |- JNIEnv* : 0xe385fdc0
136088 ms |- jstring : 0xc804d4b8
136088 ms |- char* : 0xc804d4b8
136088 ms |: 9b69f861-e054-4bc4-9daf-d36ae205ed3e
136088 ms ----------------------------Backtrace----------------------------
136088 ms |-> 0xc64e8f07: libmtguard.so!0x64f07 (libmtguard.so:0xc6484000)
/* TID 12169 */
136179 ms [+] JNIEnv->GetArrayLength
136179 ms |- JNIEnv* : 0xe385fdc0
136179 ms |- jarray : 0x21
136179 ms |= jsize : 754
136179 ms ----------------------------Backtrace----------------------------
136179 ms |-> 0xc64df6ef: libmtguard.so!0x5b6ef (libmtguard.so:0xc6484000)
/* TID 12169 */
136369 ms [+] JNIEnv->GetByteArrayRegion
136369 ms |- JNIEnv* : 0xe385fdc0
136369 ms |- jbyteArray : 0x21
136369 ms |- jsize : 0
136369 ms |- jsize : 754
136369 ms |- jbyte* : 0xc8037700
136369 ms |: 0000000: 47 45 54 20 2F 61 67 67 72 6F 75 70 2F 68 6F 6D GET /aggroup/hom
136369 ms |: 0000010: 65 70 61 67 65 2F 64 69 73 70 6C 61 79 20 5F 5F epage/display __
136369 ms |: 0000020: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
136369 ms ----------------------------Backtrace----------------------------
136369 ms |-> 0xc64df12b: libmtguard.so!0x5b12b (libmtguard.so:0xc6484000)
/* TID 12169 */
136479 ms [+] JNIEnv->ExceptionCheck
136479 ms |- JNIEnv* : 0xe385fdc0
136479 ms |= jboolean : 0 { false }
136479 ms ----------------------------Backtrace----------------------------
136479 ms |-> 0xc64efa1f: libmtguard.so!0x6ba1f (libmtguard.so:0xc6484000)
/* TID 12169 */
136584 ms [+] JNIEnv->FindClass
136584 ms |- JNIEnv* : 0xe385fdc0
136584 ms |- char* : 0xc656a2b0
136584 ms |: java/lang/String
136584 ms |= jclass : 0x55 { java/lang/String }
136584 ms ----------------------------Backtrace----------------------------
136584 ms |-> 0xc64eb013: libmtguard.so!0x67013 (libmtguard.so:0xc6484000)
/* TID 12169 */
136696 ms [+] JNIEnv->ExceptionCheck
136696 ms |- JNIEnv* : 0xe385fdc0
136696 ms |= jboolean : 0 { false }
136696 ms ----------------------------Backtrace----------------------------
136696 ms |-> 0xc64efa1f: libmtguard.so!0x6ba1f (libmtguard.so:0xc6484000)
/* TID 12169 */
136811 ms [+] JNIEnv->GetMethodID
136811 ms |- JNIEnv* : 0xe385fdc0
136811 ms |- jclass : 0x55 { java/lang/String }
136811 ms |- char* : 0xc656a2c1
136811 ms |: <init>
136811 ms |- char* : 0xc656a2c8
136811 ms |: ([BLjava/lang/String;)V
136811 ms |= jmethodID : 0x708504a4 { <init>([BLjava/lang/String;)V }
136811 ms ----------------------------Backtrace----------------------------
136811 ms |-> 0xc64eb097: libmtguard.so!0x67097 (libmtguard.so:0xc6484000)
/* TID 12169 */
137033 ms [+] JNIEnv->NewByteArray
137033 ms |- JNIEnv* : 0xe385fdc0
137033 ms |- jsize : 506
137033 ms |= jbyteArray : 0x65
137033 ms ----------------------------Backtrace----------------------------
137033 ms |-> 0xc64eb0c5: libmtguard.so!0x670c5 (libmtguard.so:0xc6484000)
/* TID 12169 */
137222 ms [+] JNIEnv->SetByteArrayRegion
137222 ms |- JNIEnv* : 0xe385fdc0
137222 ms |- jbyteArray : 0x65
137222 ms |- jsize : 0
137222 ms |- jsize : 506
137222 ms |- jbyte* : 0xc5a1620c
137222 ms |: 0000000: 7B 22 61 30 22 3A 22 31 2E 35 22 2C 22 61 31 22 {"a0":"1.5","a1"
137222 ms |: 0000010: 3A 22 39 62 36 39 66 38 36 31 2D 65 30 35 34 2D :"9b69f861-e054-
137222 ms |: 0000020: 34 62 63 34 2D 39 64 61 66 2D 64 33 36 61 65 32 4bc4-9daf-d36ae2
137222 ms |: 0000030: 30 35 65 64 33 65 22 2C 22 61 32 22 3A 22 34 36 05ed3e","a2":"46
137222 ms |: 0000040: 30 65 32 30 31 35 30 66 64 37 33 32 36 39 33 66 0e20150fd732693f
137222 ms |: 0000050: 37 31 31 39 66 39 31 64 61 37 36 35 66 39 35 39 7119f91da765f959
137222 ms |: 0000060: 65 31 36 66 30 66 22 2C 22 61 33 22 3A 32 2C 22 e16f0f","a3":2,"
137222 ms |: 0000070: 61 34 22 3A 31 36 31 36 36 35 31 39 34 34 2C 22 a4":1616651944,"
137222 ms |: 0000080: 61 35 22 3A 22 76 49 79 66 63 38 7A 54 58 51 41 a5":"vIyfc8zTXQA
137222 ms |: 0000090: 6F 53 58 6B 78 4E 6B 4E 38 74 4E 44 69 36 48 73 oSXkxNkN8tNDi6Hs
137222 ms |: 00000A0: 76 4C 70 37 5A 6C 6E 70 44 43 38 47 49 77 50 61 vLp7ZlnpDC8GIwPa
137222 ms |: 00000B0: 4C 72 55 4C 2B 79 71 76 54 74 2B 39 51 76 30 35 LrUL+yqvTt+9Qv05
137222 ms |: 00000C0: 36 54 33 6D 44 36 52 6B 36 6A 4F 42 42 66 42 37 6T3mD6Rk6jOBBfB7
137222 ms |: 00000D0: 65 5A 6E 6E 54 71 42 35 56 48 6F 4E 37 4F 33 36 eZnnTqB5VHoN7O36
137222 ms |: 00000E0: 41 62 73 6B 32 4C 48 42 30 43 4E 42 55 35 34 2B Absk2LHB0CNBU54+
137222 ms |: 00000F0: 63 32 63 51 33 6D 50 7A 56 59 70 59 4D 2B 61 5A c2cQ3mPzVYpYM+aZ
137222 ms |: 0000100: 42 69 64 4B 72 74 31 79 46 6C 66 2F 42 51 72 68 BidKrt1yFlf/BQrh
137222 ms |: 0000110: 74 79 4F 66 77 46 65 67 47 33 53 75 4A 4D 35 52 tyOfwFegG3SuJM5R
137222 ms |: 0000120: 56 4A 6C 71 52 43 4D 53 67 77 67 6D 4C 44 5A 4F VJlqRCMSgwgmLDZO
137222 ms |: 0000130: 66 32 7A 32 2F 38 6D 69 72 4B 56 4D 48 66 71 73 f2z2/8mirKVMHfqs
137222 ms |: 0000140: 68 6C 2B 35 74 52 50 79 7A 46 55 67 30 75 4B 76 hl+5tRPyzFUg0uKv
137222 ms |: 0000150: 36 50 55 2B 78 54 35 72 43 59 71 55 2B 62 4D 4D 6PU+xT5rCYqU+bMM
137222 ms |: 0000160: 48 73 70 47 52 55 43 71 44 64 65 79 41 74 6E 6F HspGRUCqDdeyAtno
137222 ms |: 0000170: 66 71 4F 4F 55 74 62 4B 31 73 72 4C 36 48 6C 46 fqOOUtbK1srL6HlF
137222 ms |: 0000180: 31 37 6D 55 67 2F 2F 44 7A 4F 2F 30 6F 51 49 4F 17mUg//DzO/0oQIO
137222 ms |: 0000190: 35 33 38 6A 49 6B 30 55 67 32 32 34 48 38 6B 37 538jIk0Ug224H8k7
137222 ms |: 00001A0: 39 57 6F 6A 39 43 75 62 4A 30 52 6B 65 36 54 6C 9Woj9CubJ0Rke6Tl
137222 ms |: 00001B0: 5A 2F 50 70 72 56 4D 66 71 37 72 4F 34 34 57 64 Z/PprVMfq7rO44Wd
137222 ms |: 00001C0: 30 22 2C 22 61 36 22 3A 30 2C 22 64 31 22 3A 22 0","a6":0,"d1":"
137222 ms |: 00001D0: 38 66 65 62 35 34 33 63 30 30 65 36 63 35 64 64 8feb543c00e6c5dd
137222 ms |: 00001E0: 32 32 33 33 37 39 65 33 31 61 39 63 30 61 34 39 223379e31a9c0a49
137222 ms |: 00001F0: 31 63 37 36 35 36 62 61 22 7D 1c7656ba"}
137222 ms ----------------------------Backtrace----------------------------
137222 ms |-> 0xc64eb107: libmtguard.so!0x67107 (libmtguard.so:0xc6484000)
可以发现,我们在参数还没完全转换完的情况下,unidbg就退出了
这种情况下,可能的原因有很多,但可能性较大的是两个
- 上下文环境缺失
- 样本使用某种手段检测或反制了unidbg
先看一下是否是上下文的问题,假设是上下文缺失,通俗的讲就是在SO加载后到我们的main函数调用前的这段时间里,样本需要调用一些函数对SO进行初始化,而我们没有注意也没做这个事,这导致了unidbg无法顺利运行
既然提出了假设,就需要去验证
使用Frida主动调用main,可以顺利得到结果,那如果换个时机呢?
我们在目标SO的JNIOnLoad刚执行完时再尝试一下call,如果存在初始化函数,这个时机点样本的初始化函数应该也还没来得及运行,call 应该是没有结果的
为了实现在这个时机点 hook 和 call,我们还需要借助一下android_dlopen_ext和Frida 的spawn模式
var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");
if (android_dlopen_ext != null) {
Interceptor.attach(android_dlopen_ext, {
onEnter: function (args) {
this.hook = false;
var soName = args[0].readCString();
if (soName.indexOf("libmtguard.so") !== -1) {
this.hook = true;
}
},
onLeave: function (retval) {
if (this.hook) {
var jniOnload = Module.findExportByName("libmtguard.so", "JNI_OnLoad");
Interceptor.attach(jniOnload, {
onEnter: function (args) {
console.log("Enter Mtguard JNI OnLoad");
},
onLeave: function (retval) {
console.log("After Mtguard JNI OnLoad");
call_mtgsig();
}
});
}
}
});
}
function call_mtgsig() {
Java.perform(function () {
var jinteger = Java.use("java.lang.Integer")
var jstring = Java.use("java.lang.String");
var NBridge = Java.use("com.meituan.android.common.mtguard.NBridge")
var objArr = [jstring.$new("9b69f861-e054-4bc4-9daf-d36ae205ed3e"), jstring.$new("GET /aggroup/homepage/display __reqTraceID=5ca01019-fafc-4f92-a80e-82ce1389aab7&abStrategy=d&allowToTrack=1&ci=1&cityId=1&clearTimeStamp=-1&clientName=android&clientType=android&firstPageAbtest=old&globalId=&limitForYouXuan=25&msid=96E3002678491E51616650388270&offsetForYouXuan=0&os_version=8.1.0&phone_model=Nexus%205X&scene=youxuanZhuanqu&showStyle=1&topic_session_id=4324505e-ccc6-4f7a-9943-d65223bbb9a7&userid=-1&utm_campaign=AgroupBgroupC0E0Ghomepage&utm_content=96E3002678491E5&utm_medium=android&utm_source=wandoujia&utm_term=1100090405&uuid=00000000000005FB514BD2BA040ABADC24C8D31AD4F18A162330366877733119&version_name=11.9.405&wifi-cur=0&wifi-mac=02%3A00%3A00%3A00%3A00%3A00%08&wifi-name=%3Cunknown%20ssid%3E%08&wifi-strength=-10%08&withRegion=0").getBytes(), jinteger.valueOf(2)]
var result = NBridge.main(203, objArr);
console.log("result:"+result);
})
}
[Nexus 5X::com.sankuai.meituan]-> %resume
[Nexus 5X::com.sankuai.meituan]->
Enter Mtguard JNI OnLoad
After Mtguard JNI OnLoad
result:null
返回确实是null,这一定程度上验证了我们的猜想——上下文环境缺失
但这并不是绝对的原因,也可能是别的原因导致的,逻辑链并没有完全导向我们的猜想
但是相对来说,上下文缺失有重大嫌疑
在分析和研究商用APP而不是demo时,我们需要意识到三点
- 构建完全严丝合缝的分析逻辑是很难的,需要猜测和试错
- 构建完全严丝合缝的分析逻辑是很难的,需要猜测和试错
- 构建完全严丝合缝的分析逻辑是很难的,需要猜测和试错
我们初步确认存在某种初始化操作,接下来尝试去找它
先试一下偷懒的办法,如果不成再用Jadx把相关的类仔细看一遍
能做初始化验证,并导致单独执行main函数无法返回结果的,大概率是native函数
以下是Unibdg日志中显示的Libmtguard中注册的native函数
JNIEnv->FindClass(com/meituan/android/common/mtguard/NBridge) was called from RX@0x400039f9[libmtguard.so]0x39f9
JNIEnv->RegisterNatives(com/meituan/android/common/mtguard/NBridge, RW@0x400e1004[libmtguard.so]0xe1004, 1) was called from RX@0x40003947[libmtguard.so]0x3947
RegisterNative(com/meituan/android/common/mtguard/NBridge, main(I[Ljava/lang/Object;)[Ljava/lang/Object;, RX@0x4005a38d[libmtguard.so]0x5a38d)
JNIEnv->FindClass(com/meituan/android/common/mtguard/NBridge$SIUACollector) was called from RX@0x40003a99[libmtguard.so]0x3a99
JNIEnv->RegisterNatives(com/meituan/android/common/mtguard/NBridge$SIUACollector, RW@0x4031305c, 10) was called from RX@0x40003acf[libmtguard.so]0x3acf
RegisterNative(com/meituan/android/common/mtguard/NBridge$SIUACollector, getHWProperty()Ljava/lang/String;, RX@0x40008ea9[libmtguard.so]0x8ea9)
RegisterNative(com/meituan/android/common/mtguard/NBridge$SIUACollector, getEnvironmentInfoExtra()Ljava/lang/String;, RX@0x4000567d[libmtguard.so]0x567d)
RegisterNative(com/meituan/android/common/mtguard/NBridge$SIUACollector, getEnvironmentInfo()Ljava/lang/String;, RX@0x40005379[libmtguard.so]0x5379)
RegisterNative(com/meituan/android/common/mtguard/NBridge$SIUACollector, getHWStatus()Ljava/lang/String;, RX@0x40018e05[libmtguard.so]0x18e05)
RegisterNative(com/meituan/android/common/mtguard/NBridge$SIUACollector, getHWEquipmentInfo()Ljava/lang/String;, RX@0x40026dcd[libmtguard.so]0x26dcd)
RegisterNative(com/meituan/android/common/mtguard/NBridge$SIUACollector, getExternalEquipmentInfo()Ljava/lang/String;, RX@0x40033971[libmtguard.so]0x33971)
RegisterNative(com/meituan/android/common/mtguard/NBridge$SIUACollector, getUserAction()Ljava/lang/String;, RX@0x4003e4a9[libmtguard.so]0x3e4a9)
RegisterNative(com/meituan/android/common/mtguard/NBridge$SIUACollector, getPlatformInfo()Ljava/lang/String;, RX@0x400447f1[libmtguard.so]0x447f1)
RegisterNative(com/meituan/android/common/mtguard/NBridge$SIUACollector, getLocationInfo()Ljava/lang/String;, RX@0x4004dc3d[libmtguard.so]0x4dc3d)
RegisterNative(com/meituan/android/common/mtguard/NBridge$SIUACollector, startCollection()Ljava/lang/String;, RX@0x40058e75[libmtguard.so]0x58e75)
JNIEnv->FindClass(com/meituan/android/common/dfingerprint/v3/DFPTest) was called from RX@0x4006b999[libmtguard.so]0x6b999
JNIEnv->RegisterNatives(com/meituan/android/common/dfingerprint/v3/DFPTest, RW@0x400e6650[libmtguard.so]0xe6650, 1) was called from RX@0x4006b9b7[libmtguard.so]0x6b9b7
RegisterNative(com/meituan/android/common/dfingerprint/v3/DFPTest, interface0(I[Ljava/lang/Object;)Ljava/lang/String;, RX@0x4006b82d[libmtguard.so]0x6b82d)
这里面所有函数都有嫌疑,甚至是main函数自己,甚至应该说,main函数的嫌疑最大
因为我们的参数1是203,可是参数1的潜在选择可不止这一种
或许其中某一个数作为参数1时,就充当着激活函数或者叫初始化函数
所以,我们需要在JNIOnLoad结束之后的这个时机做更多事
- 对所有动态注册的函数,在JAVA层进行HOOK,看一下到底是哪些函数,在SO刚加载进来就开始执行
- Call main203,在每一个Hook 触发的时候call main203
简而言之,我们想知道在谁之后,call main就可以顺利执行,在这之前的所有调用就是初始化函数
var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");
if (android_dlopen_ext != null) {
Interceptor.attach(android_dlopen_ext, {
onEnter: function (args) {
this.hook = false;
var soName = args[0].readCString();
if (soName.indexOf("libmtguard.so") !== -1) {
this.hook = true;
}
},
onLeave: function (retval) {
if (this.hook) {
var jniOnload = Module.findExportByName("libmtguard.so", "JNI_OnLoad");
Interceptor.attach(jniOnload, {
onEnter: function (args) {
console.log("Enter Mtguard JNI OnLoad");
},
onLeave: function (retval) {
console.log("After Mtguard JNI OnLoad");
hook_mtso();
}
});
}
}
});
}
function hook_mtso() {
Java.perform(function () {
Java.use("com.meituan.android.common.mtguard.NBridge").main.implementation = function(arg1, arg2) {
console.log("call com/meituan/android/common/mtguard/NBridge, main(I[Ljava/lang/Object;)[Ljava/lang/Object;");
call_mtgsig();
return this.main(arg1, arg2);
}
Java.use("com.meituan.android.common.mtguard.NBridge$SIUACollector").getHWProperty.implementation = function() {
console.log("com/meituan/android/common/mtguard/NBridge$SIUACollector, getHWProperty()Ljava/lang/String;");
call_mtgsig();
return this.getHWProperty();
}
Java.use("com.meituan.android.common.mtguard.NBridge$SIUACollector").getEnvironmentInfo.implementation = function() {
console.log("com/meituan/android/common/mtguard/NBridge$SIUACollector, getEnvironmentInfo()Ljava/lang/String;");
call_mtgsig();
return this.getEnvironmentInfo();
}
Java.use("com.meituan.android.common.mtguard.NBridge$SIUACollector").getEnvironmentInfoExtra.implementation = function() {
console.log("com/meituan/android/common/mtguard/NBridge$SIUACollector, getEnvironmentInfoExtra()Ljava/lang/String;");
call_mtgsig();
return this.getEnvironmentInfoExtra();
}
Java.use("com.meituan.android.common.mtguard.NBridge$SIUACollector").getHWStatus.implementation = function() {
console.log("com/meituan/android/common/mtguard/NBridge$SIUACollector, getHWStatus()Ljava/lang/String;");
call_mtgsig();
return this.getHWStatus();
}
Java.use("com.meituan.android.common.mtguard.NBridge$SIUACollector").getHWEquipmentInfo.implementation = function() {
console.log("com/meituan/android/common/mtguard/NBridge$SIUACollector, getHWEquipmentInfo()Ljava/lang/String;");
call_mtgsig();
return this.getHWEquipmentInfo();
}
Java.use("com.meituan.android.common.mtguard.NBridge$SIUACollector").getUserAction.implementation = function() {
console.log("com/meituan/android/common/mtguard/NBridge$SIUACollector, getUserAction()Ljava/lang/String;");
call_mtgsig();
return this.getUserAction();
}
Java.use("com.meituan.android.common.mtguard.NBridge$SIUACollector").getPlatformInfo.implementation = function() {
console.log("com/meituan/android/common/mtguard/NBridge$SIUACollector, getPlatformInfo()Ljava/lang/String;");
call_mtgsig();
return this.getPlatformInfo();
}
Java.use("com.meituan.android.common.mtguard.NBridge$SIUACollector").getLocationInfo.implementation = function() {
console.log("com/meituan/android/common/mtguard/NBridge$SIUACollector, getLocationInfo()Ljava/lang/String;");
call_mtgsig();
return this.getLocationInfo();
}
Java.use("com.meituan.android.common.mtguard.NBridge$SIUACollector").startCollection.implementation = function() {
console.log("com/meituan/android/common/mtguard/NBridge$SIUACollector, startCollection()Ljava/lang/String;");
call_mtgsig();
return this.startCollection();
}
Java.use("com.meituan.android.common.mtguard.NBridge$SIUACollector").getExternalEquipmentInfo.implementation = function() {
console.log("com/meituan/android/common/mtguard/NBridge$SIUACollector, getExternalEquipmentInfo()Ljava/lang/String;");
call_mtgsig();
return this.getExternalEquipmentInfo();
}
})
}
function call_mtgsig() {
Java.perform(function () {
var jinteger = Java.use("java.lang.Integer")
var jstring = Java.use("java.lang.String");
var NBridge = Java.use("com.meituan.android.common.mtguard.NBridge")
var objArr = [jstring.$new("9b69f861-e054-4bc4-9daf-d36ae205ed3e"), jstring.$new("GET /aggroup/homepage/display __reqTraceID=5ca01019-fafc-4f92-a80e-82ce1389aab7&abStrategy=d&allowToTrack=1&ci=1&cityId=1&clearTimeStamp=-1&clientName=android&clientType=android&firstPageAbtest=old&globalId=&limitForYouXuan=25&msid=96E3002678491E51616650388270&offsetForYouXuan=0&os_version=8.1.0&phone_model=Nexus%205X&scene=youxuanZhuanqu&showStyle=1&topic_session_id=4324505e-ccc6-4f7a-9943-d65223bbb9a7&userid=-1&utm_campaign=AgroupBgroupC0E0Ghomepage&utm_content=96E3002678491E5&utm_medium=android&utm_source=wandoujia&utm_term=1100090405&uuid=00000000000005FB514BD2BA040ABADC24C8D31AD4F18A162330366877733119&version_name=11.9.405&wifi-cur=0&wifi-mac=02%3A00%3A00%3A00%3A00%3A00%08&wifi-name=%3Cunknown%20ssid%3E%08&wifi-strength=-10%08&withRegion=0").getBytes(), jinteger.valueOf(2)]
var result = NBridge.main(203, objArr);
console.log("result:"+result);
})
}
// frida -U -f com.sankuai.meituan -l C:\Users\pr0214\Desktop\DTA\unidbg学习指南\unidbg进阶篇\unidbg的五个大实例\mtguard\new\mt10\lession10\testOnJniOnLoad.js
[Nexus 5X::com.sankuai.meituan]-> Enter Mtguard JNI OnLoad
After Mtguard JNI OnLoad
call com/meituan/android/common/mtguard/NBridge, main(I[Ljava/lang/Object;)[Ljava/lang/Object;
result:null
call com/meituan/android/common/mtguard/NBridge, main(I[Ljava/lang/Object;)[Ljava/lang/Object;
result:{"a0":"1.5","a1":"9b69f861-e054-4bc4-9daf-d36ae205ed3e","a2":"460e20150fd732693f7119f91da765f959e16f0f","a3":2,"a4":1617023178,"a5":"fSdm2rcaszWJ/GEN2Q2AjRHumgC7NCXqDt60WlBeuz9u9L+0DyV1Xew5wjn3DEvtF4rSTrOjHAh6ARJ8+DLhP41U3Ayxks9ZTaNMpzMzrkop/PDYkaOJOxpwc9oA9ebwEKzwtnjP0R0S3ZXqtKLrclykX7MLWs9bpWAqTxpPiTAnf1pUYKfH0rn2xzSXryofL1y9ostXEKzV+dveF5zZVpdsiJHiAY1yNp/MJLamFZTirM2/cj9uBC2JisxcGISPjqf+VYbdKgoQLOs6+GJtyA/h6S3xLkU0uoIKHplutHcInHpXViC/tYlzpnO+p+02DUb8qCtnh/sz7/sDHETYT6h4JF5da2I3EC7ypdNj3VP4UK0E9ekKBA==","a6":0,"d1":"0a33910a22d3002db06f8ad2b4977c8379dd3f0a"}
call com/meituan/android/common/mtguard/NBridge, main(I[Ljava/lang/Object;)[Ljava/lang/Object;
com/meituan/android/common/mtguard/NBridge$SIUACollector, startCollection()Ljava/lang/String;
call com/meituan/android/common/mtguard/NBridge, main(I[Ljava/lang/Object;)[Ljava/lang/Object;
result:{"a0":"1.5","a1":"9b69f861-e054-4bc4-9daf-d36ae205ed3e","a2":"460e20150fd732693f7119f91da765f959e16f0f","a3":2,"a4":1617023179,"a5":"FHyKQ0QJaH7NvwkVuDYTKIKJ+XlahtybcECQh4CTRgOlENk40NTsFHH/T429PgP2h6OgSL2lzNONPFbbALchHi3E7eGhWwgwt8PjECE/S5BkwbBk+/axHZdabfdOJhThxTLLeyJ8198buPO2HZ1ojU8RM/Wdh+TThMyahbgbk6jXH+h+tDFqEaecBQmjCN3wax1DcuEToPF2peHnmcyMQQT82m5OSVgzrMzKGol6OlJfnEVXufs789yqrRu5ZaXl+caF0Z8NYPOdgNgDitQ+uuRsFQnM6qGdQW8SdaenPn+6K4Pw9bApk5+obL4=","a6":0,"d1":"086c225497d75442f882e23634d6350e101c5f8d"}
可以发现,只有一次result:null,后面就可以顺利call出结果
这说明两点
- SO加载后首先执行main函数
- 第一个main函数是初始化函数
更详细的打印main函数,显示其参数和返回值
我们发现第一个main函数参数1是111,我们称之为main111
参数2就是一个对象数组,长度为1,没指定,也就是对象数组里一个空对象
package com.lession7;
import com.github.unidbg.linux.android.dvm.AbstractJni;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.api.PackageInfo;
import com.github.unidbg.linux.android.dvm.api.Signature;
import com.github.unidbg.linux.android.dvm.array.ArrayObject;
import com.github.unidbg.linux.android.dvm.array.ByteArray;
import com.github.unidbg.linux.android.dvm.wrapper.DvmInteger;
import com.github.unidbg.memory.Memory;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
public class NBridge extends AbstractJni {
private final AndroidEmulator emulator;
private final VM vm;
private final Module module;
NBridge(){
emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.meituan").build();
final Memory memory = emulator.getMemory(); // 模拟器的内存操作接口
memory.setLibraryResolver(new AndroidResolver(23)); // 设置系统类库解析
vm = emulator.createDalvikVM(new File("unidbg-android\\src\\test\\java\\com\\lession7\\mt.apk")); // 创建Android虚拟机
vm.setVerbose(true); // 设置是否打印Jni调用细节
DalvikModule dm = vm.loadLibrary(new File("unidbg-android\\src\\test\\java\\com\\lession7\\libmtguard.so"), true);
module = dm.getModule(); //
vm.setJni(this);
dm.callJNI_OnLoad(emulator);
}
public static void main(String[] args) {
NBridge test = new NBridge();
test.main111();
test.main203();
}
public String main203(){
List<Object> list = new ArrayList<>(10);
list.add(vm.getJNIEnv()); // 第一个参数是env
list.add(0); // 第二个参数,实例方法是jobject,静态方法是jclazz,直接填0,一般用不到
list.add(203);
StringObject input2_1 = new StringObject(vm, "9b69f861-e054-4bc4-9daf-d36ae205ed3e");
ByteArray input2_2 = new ByteArray(vm, "GET /aggroup/homepage/display __r0ysue".getBytes(StandardCharsets.UTF_8));
DvmInteger input2_3 = DvmInteger.valueOf(vm, 2);
vm.addLocalObject(input2_1);
vm.addLocalObject(input2_2);
vm.addLocalObject(input2_3);
// 完整的参数2
list.add(vm.addLocalObject(new ArrayObject(input2_1, input2_2, input2_3)));
Number number = module.callFunction(emulator, 0x5a38d, list.toArray())[0];
return vm.getObject(number.intValue()).getValue().toString();
};
public void main111(){
List<Object> list = new ArrayList<>(10);
list.add(vm.getJNIEnv()); // 第一个参数是env
list.add(0); // 第二个参数,实例方法是jobject,静态方法是jclazz,直接填0,一般用不到
list.add(111);
DvmObject<?> obj = vm.resolveClass("java/lang/object").newObject(null);
vm.addLocalObject(obj);
ArrayObject myobject = new ArrayObject(obj);
vm.addLocalObject(myobject);
list.add(vm.addLocalObject(myobject));
module.callFunction(emulator, 0x5a38d, list.toArray());
};
}
产生了一些报错,JAVA环境缺失,中规中矩补就是了
有人会比较困扰怎么得到正确的值,其实很简单,使用JNItrace trace三分钟,保存在本地,直接搜索,需要用的基本都在里面
不熟悉具体流程的可以来报课鸭,直播互动教学
通过这种方式补全JAVA环境的缺失
@Override
public DvmObject<?> callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
switch (signature) {
case "com/meituan/android/common/mtguard/NBridge->getPicName()Ljava/lang/String;":{
return new StringObject(vm, "ms_com.sankuai.meituan");
}
case "com/meituan/android/common/mtguard/NBridge->getSecName()Ljava/lang/String;":{
return new StringObject(vm, "ppd_com.sankuai.meituan.xbt");
}
case "com/meituan/android/common/mtguard/NBridge->getAppContext()Landroid/content/Context;":{
return vm.resolveClass("android/content/Context").newObject(null);
}
case "com/meituan/android/common/mtguard/NBridge->getMtgVN()Ljava/lang/String;": {
return new StringObject(vm, "4.4.7.3");
}
}
return super.callStaticObjectMethodV(vm, dvmClass, signature,vaList);
}
@Override
public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
switch (signature) {
case "android/content/Context->getPackageCodePath()Ljava/lang/String;":{
return new StringObject(vm, "/data/app/com.sankuai.meituan-TEfTAIBttUmUzuVbwRK1DQ==/base.apk");
}
}
return super.callObjectMethodV(vm, dvmObject, signature, vaList);
}
@Override
public int getIntField(BaseVM vm, DvmObject<?> dvmObject, String signature) {
switch (signature){
case "android/content/pm/PackageInfo->versionCode:I":{
return 1100090405;
}
};
return super.getIntField(vm, dvmObject, signature);
}
@Override
public DvmObject<?> newObjectV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
switch (signature) {
case "java/lang/Integer-><init>(I)V":
int input = vaList.getInt(0);
return new DvmInteger(vm, input);
}
return super.newObjectV(vm, dvmClass, signature, vaList);
}
运行效果
前面的实例中,我们主要补的是JAVA环境,这里补的是文件访问
文件访问的情况各种各样,比如从app的某个xml文件中读取key,读取某个资源文件的图片做运算,读取proc/self 目录下的文件反调试等等
可是我们哪来的手机文件目录,哪来的系统路径呢?我们只是一个虚拟的模拟器罢了所以unidbg对文件访问的相关API进行了重定向
当样本做文件访问时,unidbg重定向到本机的某个位置,进入 src/main/java/com/github/unidbg/file/BaseFileSystem.java
打印一下路径
接下来我们按照要求,在data目录下新建对应文件夹,并把我们的apk复制进去,改名成base.apk
这个文件访问就补好了,再运行代码时,就已经跨过这个坑,进行下一个流程了
除此之外,也可以通过代码的方式进行操作
我们的类实现文件重定向的接口即可,只需要三个步骤,如下:
// 1
public class NBridge extends AbstractJni implements IOResolver {
private final AndroidEmulator emulator;
private final VM vm;
private final Module module;
NBridge(){
emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.meituan").build();
final Memory memory = emulator.getMemory(); // 模拟器的内存操作接口
memory.setLibraryResolver(new AndroidResolver(23)); // 设置系统类库解析
vm = emulator.createDalvikVM(new File("C:\\Users\\pr0214\\Desktop\\DTA\\unidbg\\versions\\unidbg-2021-5-17\\unidbg-master\\unidbg-android\\src\\test\\java\\com\\lession7\\mt.apk")); // 创建Android虚拟机
// 2
emulator.getSyscallHandler().addIOResolver(this);
vm.setVerbose(true); // 设置是否打印Jni调用细节
DalvikModule dm = vm.loadLibrary(new File("C:\\Users\\pr0214\\Desktop\\DTA\\unidbg\\versions\\unidbg-2021-5-17\\unidbg-master\\unidbg-android\\src\\test\\java\\com\\lession7\\libmtguard.so"), true);
module = dm.getModule(); //
vm.setJni(this);
dm.callJNI_OnLoad(emulator);
}
// 3
@Override
public FileResult resolve(Emulator emulator, String pathname, int oflags) {
if (("/data/app/com.sankuai.meituan-TEfTAIBttUmUzuVbwRK1DQ==/base.apk").equals(pathname)) {
// 填入想要重定位的文件
return FileResult.success(new SimpleFileIO(oflags, new File("unidbg-android\\src\\test\\java\\com\\lession10\\mt.apk"), pathname));
}
return null;
}
}
两种方法各有其最佳的使用场景,如果两种都设置,代码方式设置的优先级更高
继续往下补JAVA环境
好像结果出来了,再修改一下输出函数,打印一下
public void main203(){
List<Object> list = new ArrayList<>(10);
list.add(vm.getJNIEnv()); // 第一个参数是env
list.add(0); // 第二个参数,实例方法是jobject,静态方法是jclazz,直接填0,一般用不到
list.add(203);
StringObject input2_1 = new StringObject(vm, "9b69f861-e054-4bc4-9daf-d36ae205ed3e");
ByteArray input2_2 = new ByteArray(vm, "GET /aggroup/homepage/display __r0ysue".getBytes(StandardCharsets.UTF_8));
DvmInteger input2_3 = DvmInteger.valueOf(vm, 2);
vm.addLocalObject(input2_1);
vm.addLocalObject(input2_2);
vm.addLocalObject(input2_3);
// 完整的参数2
list.add(vm.addLocalObject(new ArrayObject(input2_1, input2_2, input2_3)));
Number number = module.callFunction(emulator, 0x5a38d, list.toArray())[0];
StringObject result = (StringObject) ((DvmObject[])((ArrayObject)vm.getObject(number.intValue())).getValue())[0];
System.out.println(result.getValue());
};
大功告成
尾声
这是上下文缺失和补充的一个例子
资源链接:https://pan.baidu.com/s/1e-u8hbHqCFnS9P0vVM8GbQ
提取码:igcw
没有评论