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

Unidbg 的基本使用(六)跟着学习的第六天!

本篇样本dazhongdianping.7z

任务介绍

ADX反编译 APK,找到com.meituan.android.common.mtguard.NBridge类,其中有一个叫SIUACollector的内部类,里面有以下这些 Native 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private native String getEnvironmentInfo();

private native String getEnvironmentInfoExtra();

private native String getExternalEquipmentInfo();

private native String getHWEquipmentInfo();

private native String getHWProperty();

private native String getHWStatus();

private native String getLocationInfo();

private native String getPlatformInfo();

private native String getUserAction();

public native String startCollection();

在 Unidbg 中依次执行这十个函数,本篇的重点是巩固 Unidbg 补环境的形式,以及学习 Unidbg 补环境的内容。

初始化

首先搭一个基本架子

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
package com.dianping;

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.arm.backend.Unicorn2Factory;
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 NBridge extends AbstractJni {
private final AndroidEmulator emulator;
private final DvmClass SIUACollector;
private final VM vm;

public NBridge() {
emulator = AndroidEmulatorBuilder
.for32Bit()
.addBackendFactory(new Unicorn2Factory(true))
.setProcessName("")
.build();
Memory memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
vm = emulator.createDalvikVM(new File("unidbg-android/src/test/resources/dianping/dazhongdianping.apk"));
vm.setJni(this);
vm.setVerbose(true);
DalvikModule dm = vm.loadLibrary("mtguard", true);
SIUACollector = vm.resolveClass("com/meituan/android/common/mtguard/NBridge$SIUACollector");
dm.callJNI_OnLoad(emulator);
}

public static void main(String[] args) {
NBridge nBridge = new NBridge();
}
}

内部类和外部类之间通过$分割,这是 JAVA 中固定的表示法。

1
2
3
4
[09:30:26 975]  INFO [com.github.unidbg.linux.AndroidElfLoader] (AndroidElfLoader:481) - libmtguard.so load dependency libandroid.so failed
[09:30:26 979] INFO [com.github.unidbg.linux.AndroidElfLoader] (AndroidElfLoader:481) - libmtguard.so load dependency libjnigraphics.so failed
JNIEnv->FindClass(com/meituan/android/common/mtguard/NBridge) was called from RX@0x1200545d[libmtguard.so]0x545d
JNIEnv->RegisterNatives(com/meituan/android/common/mtguard/NBridge, RW@0x120d0004[libmtguard.so]0xd0004, 1) was called from RX@0x120054c7[libmtguard.so]0x54c7

加载 libandroid.so、libjnigraphics.so 失败,前面我们提过它俩的虚拟模块。只需早于目标 SO 加载的时机之前加载即可

1
2
3
4
5
6
vm.setVerbose(true);
// 使用 libandroid.so 的虚拟模块
new AndroidModule(emulator, vm).register(memory);;
// 使用 libjnigraphics.so 的虚拟模块
new JniGraphics(emulator, vm).register(memory);
DalvikModule dm = vm.loadLibrary("mtguard", true);

再次运行就不会报依赖库缺少的错误了

为了免于重复newObject,将SIUACollector处理为DvmObject。同时,添加对文件访问的拦截处理,样本可能有文件操作。

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
package com.dianping;

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.*;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.virtualmodule.android.AndroidModule;
import com.github.unidbg.virtualmodule.android.JniGraphics;

import java.io.File;

public class NBridge extends AbstractJni implements IOResolver {
private final AndroidEmulator emulator;
private final DvmObject<?> SIUACollector;
private final VM vm;

public NBridge() {
emulator = AndroidEmulatorBuilder
.for32Bit()
.addBackendFactory(new Unicorn2Factory(true))
.setProcessName("")
.build();
Memory memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
vm = emulator.createDalvikVM(new File("unidbg-android/src/test/resources/dianping/dazhongdianping.apk"));
vm.setJni(this);
vm.setVerbose(true);
emulator.getSyscallHandler().addIOResolver(this);
// 使用 libandroid.so 的虚拟模块
new AndroidModule(emulator, vm).register(memory);;
// 使用 libjnigraphics.so 的虚拟模块
new JniGraphics(emulator, vm).register(memory);
DalvikModule dm = vm.loadLibrary("mtguard", true);
SIUACollector = vm.resolveClass("com/meituan/android/common/mtguard/NBridge$SIUACollector").newObject(null);
dm.callJNI_OnLoad(emulator);
}

public static void main(String[] args) {
NBridge nBridge = new NBridge();
}

@Override
public FileResult resolve(Emulator emulator, String pathname, int oflags) {
System.out.println("lilac open:"+pathname);
return null;
}
}

getEnvironmentInfo

发起对 getEnvironmentInfo 的调用

1
2
3
4
5
6
7
8
9
public String getEnvironmentInfo(){
String result = SIUACollector.callJniMethodObject(emulator, "getEnvironmentInfo()Ljava/lang/String;").getValue().toString();
return result;
}

public static void main(String[] args) {
NBridge nBridge = new NBridge();
System.out.println("getEnvironmentInfo:"+nBridge.getEnvironmentInfo());
}

运行发现直接得出了结果,无需补环境。

1
2
3
4
Find native function Java_com_meituan_android_common_mtguard_NBridge_00024SIUACollector_getEnvironmentInfo => RX@0x12006df9[libmtguard.so]0x6df9
JNIEnv->NewStringUTF("0|0|0|-|0|") was called from RX@0x12006f5f[libmtguard.so]0x6f5f
JNIEnv->NewStringUTF("0|0|0|-|0|") was called from RX@0x12006ef7[libmtguard.so]0x6ef7
getEnvironmentInfo:0|0|0|-|0|

getEnvironmentInfoExtra

发起调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public String getEnvironmentInfo(){
String result = SIUACollector.callJniMethodObject(emulator, "getEnvironmentInfo()Ljava/lang/String;").getValue().toString();
return result;
}

public String getEnvironmentInfoExtra(){
String result = SIUACollector.callJniMethodObject(emulator, "getEnvironmentInfoExtra()Ljava/lang/String;").getValue().toString();
return result;
}

public static void main(String[] args) {
NBridge nBridge = new NBridge();
System.out.println("getEnvironmentInfo:"+nBridge.getEnvironmentInfo());
System.out.println("getEnvironmentInfoExtra:"+nBridge.getEnvironmentInfoExtra());
}

报错:

1
2
3
4
5
JNIEnv->NewGlobalRef(class java/lang/StringBuilder) was called from RX@0x12005f5d[libmtguard.so]0x5f5d
[10:07:00 233] WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x117, PC=unidbg@0xfffe0204, LR=RX@0x12007443[libmtguard.so]0x7443, syscall=null
java.lang.UnsupportedOperationException: java/lang/StringBuilder->allocObject
at com.github.unidbg.linux.android.dvm.AbstractJni.allocObject(AbstractJni.java:812)
at com.github.unidbg.linux.android.dvm.DvmClass.allocObject(DvmClass.java:74)

参考 AbstractJni 中的 allocObject 处理,分配一个 StringBuilder 对象

补环境:

(StringBuilder 是 JDK 中的类库,使用ProxyDvmObject.createObject更好)

1
2
3
4
5
6
7
8
9
10
@Override
public DvmObject<?> allocObject(BaseVM vm, DvmClass dvmClass, String signature) {
switch (signature){
case "java/lang/StringBuilder->allocObject":{
return dvmClass.newObject(new StringBuilder());
//return ProxyDvmObject.createObject(vm, new StringBuilder());
}
}
return super.allocObject(vm, dvmClass, signature);
}

继续报错:

1
2
3
[10:11:07 659]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x13b, PC=unidbg@0xfffe0444, LR=RX@0x120095f1[libmtguard.so]0x95f1, syscall=null
java.lang.UnsupportedOperationException: java/lang/StringBuilder-><init>()V
at com.github.unidbg.linux.android.dvm.AbstractJni.callVoidMethodV(AbstractJni.java:1007)

刚分配的StirngBuilder需要初始化,这里什么也不做,直接返回

1
2
3
4
5
6
7
8
9
@Override
public void callVoidMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
switch (signature){
case "java/lang/StringBuilder-><init>()V": {
return;
}
}
super.callVoidMethodV(vm, dvmObject, signature, vaList);
}

继续报错:

1
2
3
[10:14:02 528]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x12008403[libmtguard.so]0x8403, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->getEnvironmentInfo()Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)

这个方法就是我们上一个补的 getEnvironmentInfo,你可能希望像下面这样处理

1
2
3
4
5
6
7
8
9
@Override
public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
switch (signature){
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->getEnvironmentInfo()Ljava/lang/String;":{
return new StringObject(vm, getEnvironmentInfo());
}
}
return super.callObjectMethodV(vm, dvmObject, signature, vaList);
}

但这是行不通的,Native 方法里调用另一个 Native 方法,这属于嵌套调用,Unidbg 暂不支持这么做,所以这里手动填入先前得到的结果。

1
2
3
4
5
6
7
8
9
@Override
public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
switch (signature){
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->getEnvironmentInfo()Ljava/lang/String;":{
return new StringObject(vm, "0|0|0|-|0|");
}
}
return super.callObjectMethodV(vm, dvmObject, signature, vaList);
}

报错

1
2
3
4
[10:17:09 669]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x120085d9[libmtguard.so]0x85d9, syscall=null
java.lang.UnsupportedOperationException: java/lang/StringBuilder->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:69)

按照语义补 append 方法

1
2
3
4
case "java/lang/StringBuilder->append(Ljava/lang/String;)Ljava/lang/StringBuilder;":{
String str = vaList.getObjectArg(0).getValue().toString();
return ProxyDvmObject.createObject(vm, ((StringBuilder) dvmObject.getValue()).append(str));
}

继续报错:

1
2
3
4
[10:32:20 652]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x120079dd[libmtguard.so]0x79dd, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->isVPN()Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:77)

用于检测 VPN,JADX中看看它的实现,样本采用了自家的热修复技术Android热更新方案Robust,代码看起来不太舒服。

1
2
3
4
5
private String isVPN() {
Object[] objArr = new Object[0];
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
return PatchProxy.isSupport(objArr, this, changeQuickRedirect2, false, "b6362e66d9b10061bc6c2e73cafc0cc8", RobustBitConfig.DEFAULT_VALUE) ? (String) PatchProxy.accessDispatch(objArr, this, changeQuickRedirect2, false, "b6362e66d9b10061bc6c2e73cafc0cc8") : EnvInfoWorker.isVPN();
}

末尾的EnvInfoWorker.isVPN()是其实现函数。

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
public static synchronized String isVPN() {
synchronized (EnvInfoWorker.class) {
Object[] objArr = new Object[0];
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr, null, changeQuickRedirect2, true, "b5d91cd54a8036812d448963f1a976c9", RobustBitConfig.DEFAULT_VALUE)) {
return (String) PatchProxy.accessDispatch(objArr, null, changeQuickRedirect2, true, "b5d91cd54a8036812d448963f1a976c9");
}
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
if (networkInterfaces == null) {
return "0";
}
Iterator it = Collections.list(networkInterfaces).iterator();
while (it.hasNext()) {
NetworkInterface networkInterface = (NetworkInterface) it.next();
if (networkInterface.isUp()) {
if (networkInterface.getInterfaceAddresses().size() != 0 && "tun0".equals(networkInterface.getName())) {
return "1";
}
}
if ("ppp0".equals(networkInterface.getName())) {
return "1";
}
}
return "0";
}
}

当客户端运行VPN时,会创建tun0ppp0节点,所以出现带有明显特征的网络接口名称,就可以认定是使用了VPN协议进行通信。示例代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
private void isDeviceInVPN() {
try {
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
while (networkInterfaces.hasMoreElements()) {
String name = networkInterfaces.nextElement().getName();
if (name.equals("tun0") || name.equals("ppp0")) {
stop();
}
}
} catch (SocketException e) {
e.printStackTrace();
}
}

和样本此处进行对照,可以发现返回 0 是没检测到,返回 1 是检测到了。

可能会想图方便,使用 Frida 在真实设备上 Hook 或 Call 这个函数,直接获得结果。但这么做有弊端,如果你真机使用了 VPN,那么就会返回 1。

1
2
3
4
// 检测 VPN,通过 tun0/ppp0。
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->isVPN()Ljava/lang/String;":{
return new StringObject(vm, "0");
}

报错

1
2
3
[10:43:06 873]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x15b, PC=unidbg@0xfffe0644, LR=RX@0x120090c9[libmtguard.so]0x90c9, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->mContext:Landroid/content/Context;
at com.github.unidbg.linux.android.dvm.AbstractJni.getObjectField(AbstractJni.java:171)

Context 是一个相当特殊的对象,前面就遇到过它,这里我们先用最朴素的办法去处理它——resolveClass占位

1
2
3
4
5
6
7
8
9
@Override
public DvmObject<?> getObjectField(BaseVM vm, DvmObject<?> dvmObject, String signature) {
switch (signature){
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->mContext:Landroid/content/Context;":{
return vm.resolveClass("android/content/Context").newObject(null);
}
}
return super.getObjectField(vm, dvmObject, signature);
}

报错:

1
2
3
4
[10:45:33 075]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x12009cbb[libmtguard.so]0x9cbb, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->brightness(Landroid/content/Context;)Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:81)

回到JADX找到对应函数,似乎是获取屏幕亮度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private String brightness(Context context) {
Object[] objArr = {context};
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
return PatchProxy.isSupport(objArr, this, changeQuickRedirect2, false, "e3c10305f195ff2aac2fd606a6235fb2", RobustBitConfig.DEFAULT_VALUE) ? (String) PatchProxy.accessDispatch(objArr, this, changeQuickRedirect2, false, "e3c10305f195ff2aac2fd606a6235fb2") : DeviceInfoWorker.brightness(context);
}
public static String brightness(Context context) {
Object[] objArr = {context};
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr, null, changeQuickRedirect2, true, "af80a1c664fdf95866b7caabf24ab495", RobustBitConfig.DEFAULT_VALUE)) {
return (String) PatchProxy.accessDispatch(objArr, null, changeQuickRedirect2, true, "af80a1c664fdf95866b7caabf24ab495");
}
try {
return StringUtils.toString(Settings.System.getInt(context.getContentResolver(), "screen_brightness") / 255.0f);
} catch (Throwable th) {
c.a(th);
return StringUtils.toString(0.0f);
}
}

Google 搜索getContentResolver+screen_brightness,找到如下代码。

1
2
3
4
5
6
7
8
9
/**
* 获取系统默认屏幕亮度值 屏幕亮度值范围(0-255)
* **/
private int getScreenBrightness(Context context) {
ContentResolver contentResolver = context.getContentResolver();
int defVal = 0;
return Settings.System.getInt(contentResolver,
Settings.System.SCREEN_BRIGHTNESS, defVal);
}

它是 0-1 之间的一个数,然后转字符串,我这里选择 0.8。

1
2
3
4
// 获取亮度信息,字符串范围 0-1
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->brightness(Landroid/content/Context;)Ljava/lang/String;":{
return new StringObject(vm, "0.8");
}

继续报错:

1
2
3
4
[10:49:34 573]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x1200980b[libmtguard.so]0x980b, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->systemVolume(Landroid/content/Context;)Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:85)

这里获取音量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static String systemVolume(Context context) {
Object[] objArr = {context};
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr, null, changeQuickRedirect2, true, "94db8653238995da6df94fea33fbbf79", RobustBitConfig.DEFAULT_VALUE)) {
return (String) PatchProxy.accessDispatch(objArr, null, changeQuickRedirect2, true, "94db8653238995da6df94fea33fbbf79");
}
try {
AudioManager audioManager = (AudioManager) context.getSystemService("audio");
return StringUtils.toString((audioManager.getStreamVolume(1) * 100) / audioManager.getStreamMaxVolume(1));
} catch (Throwable th) {
c.a(th);
return "";
}
}

返回 0,也就是所谓的静音。

1
2
3
4
// 获取音量
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->systemVolume(Landroid/content/Context;)Ljava/lang/String;":{
return new StringObject(vm, "0");
}

报错

1
2
3
4
[10:51:26 777]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x123, PC=unidbg@0xfffe02c4, LR=RX@0x12007a8f[libmtguard.so]0x7a8f, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->isAccessibilityEnable()Z
at com.github.unidbg.linux.android.dvm.AbstractJni.callBooleanMethodV(AbstractJni.java:625)
at com.github.unidbg.linux.android.dvm.AbstractJni.callBooleanMethodV(AbstractJni.java:603)

这次是 isAccessibilityEnable,也就是所谓的无障碍服务。无障碍服务的本意是帮助残障人士、老人小孩等等,但长久以来被用于自动化控制,比如抢单,因此检测无障碍服务是风险检测、环境检测、反欺诈检测的重要一环。

1
2
3
4
5
6
7
8
9
@Override
public boolean callBooleanMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
switch (signature){
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->isAccessibilityEnable()Z":{
return false;
}
}
return super.callBooleanMethodV(vm, dvmObject, signature, vaList);
}

报错:

1
2
3
4
[10:53:18 354]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x171, PC=unidbg@0xfffe07a4, LR=RX@0x1200a2a9[libmtguard.so]0xa2a9, syscall=null
java.lang.UnsupportedOperationException: java/lang/String->valueOf(I)Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethodV(AbstractJni.java:504)
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethodV(AbstractJni.java:438)

补valueOf函数

1
2
3
4
5
6
7
8
9
@Override
public DvmObject<?> callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
switch (signature){
case "java/lang/String->valueOf(I)Ljava/lang/String;":{
return new StringObject(vm, String.valueOf(vaList.getIntArg(0)));
}
}
return super.callStaticObjectMethodV(vm, dvmClass, signature, vaList);
}
1
2
3
[10:54:11 080]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x12f, PC=unidbg@0xfffe0384, LR=RX@0x12007c73[libmtguard.so]0x7c73, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->uiAutomatorClickCount()I
at com.github.unidbg.linux.android.dvm.AbstractJni.callIntMethodV(AbstractJni.java:563)

uiAutomatorClickCount具体实现如下

1
2
3
4
5
6
7
8
9
10
11
public static int uiAutomatorClickCount() {
Object[] objArr = new Object[0];
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr, null, changeQuickRedirect2, true, "7e55945fa3fd311f57ffc508c34fa364", RobustBitConfig.DEFAULT_VALUE)) {
return ((Integer) PatchProxy.accessDispatch(objArr, null, changeQuickRedirect2, true, "7e55945fa3fd311f57ffc508c34fa364")).intValue();
}
if (!verify()) {
return 0;
}
return mAdapter.uiAutomatorCount();
}

Frida Hook 查看一下,在 JADX 1.4 及以上版本,在方法上右键可选择“复制为 Frida 代码”,减轻了不少负担。

1
2
3
4
5
6
7
8
9
Java.perform(function() {
let SIUACollector = Java.use("com.meituan.android.common.mtguard.NBridge$SIUACollector");
SIUACollector["uiAutomatorClickCount"].implementation = function () {
console.log('uiAutomatorClickCount is called');
let ret = this.uiAutomatorClickCount();
console.log('uiAutomatorClickCount ret value is ' + ret);
return ret;
};
})

返回:

1
2
[Nexus 5X::com.dianping.v1]-> uiAutomatorClickCount is called
uiAutomatorClickCount ret value is 0

1
2
3
4
5
6
7
8
9
@Override
public int callIntMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
switch (signature){
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->uiAutomatorClickCount()I":{
return 0;
}
}
return super.callIntMethodV(vm, dvmObject, signature, vaList);
}

报错

1
2
3
4
[11:02:31 397]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x120096f3[libmtguard.so]0x96f3, syscall=null
java.lang.UnsupportedOperationException: java/lang/StringBuilder->toString()Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:88)

补环境

1
2
3
case "java/lang/StringBuilder->toString()Ljava/lang/String;":{
return new StringObject(vm, dvmObject.getValue().toString());
}

运行后得出结果

1
2
JNIEnv->CallObjectMethodA(java.lang.StringBuilder@71be98f5, toString() => "0|0|0|-|0|0|0.8|0|0|0|") was called from RX@0x120096f3[libmtguard.so]0x96f3
getEnvironmentInfoExtra:0|0|0|-|0|0|0.8|0|0|0|

getExternalEquipmentInfo

新添对它的 Call

1
2
3
4
5
6
7
8
9
10
11
public String getExternalEquipmentInfo(){
String result = SIUACollector.callJniMethodObject(emulator, "getExternalEquipmentInfo()Ljava/lang/String;").getValue().toString();
return result;
}

public static void main(String[] args) {
NBridge nBridge = new NBridge();
System.out.println("getEnvironmentInfo:"+nBridge.getEnvironmentInfo());
System.out.println("getEnvironmentInfoExtra:"+nBridge.getEnvironmentInfoExtra());
System.out.println("getExternalEquipmentInfo:"+nBridge.getExternalEquipmentInfo());
}

运行并报错

1
2
3
4
[18:27:12 648]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x1203ddd7[libmtguard.so]0x3ddd7, syscall=null
java.lang.UnsupportedOperationException: android/content/Context->getApplicationContext()Landroid/content/Context;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:91)

简单占位

1
2
3
case "android/content/Context->getApplicationContext()Landroid/content/Context;":{
return vm.resolveClass("android/content/Context").newObject(null);
}

报错

1
2
3
4
[18:28:43 208]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x120396b5[libmtguard.so]0x396b5, syscall=null
java.lang.UnsupportedOperationException: android/content/Context->getSystemService(Ljava/lang/String;)Ljava/lang/Object;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:94)

系统服务是 Android 所提供的一项重要服务,在 JNI 补环境中出现频率相当高, Unidbg 对它做了专门处理,可以参考 AbstractJni,其中有如下代码片段。

补充:

1
2
3
4
5
case "android/content/Context->getSystemService(Ljava/lang/String;)Ljava/lang/Object;":{
StringObject serviceName = vaList.getObjectArg(0);
assert serviceName != null;
return new SystemService(vm, serviceName.getValue());
}

继续报错:

1
2
3
4
[18:36:37 627]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x123, PC=unidbg@0xfffe02c4, LR=RX@0x1203994d[libmtguard.so]0x3994d, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->isPermissionGranted(Ljava/lang/String;Landroid/content/Context;)Z
at com.github.unidbg.linux.android.dvm.AbstractJni.callBooleanMethodV(AbstractJni.java:625)
at com.dianping.NBridge.callBooleanMethodV(NBridge.java:109)

isPermissionGranted 根据函数名、函数实现可以看出,大致用来检查 App 是否有某项权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static boolean isPermissionGranted(String str, Context context) {
Object[] objArr = {str, context};
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr, null, changeQuickRedirect2, true, "44db3326d9c0bbe25993cff8771e9a68", RobustBitConfig.DEFAULT_VALUE)) {
return ((Boolean) PatchProxy.accessDispatch(objArr, null, changeQuickRedirect2, true, "44db3326d9c0bbe25993cff8771e9a68")).booleanValue();
}
try {
return Build.VERSION.SDK_INT >= 23 ? context.getApplicationContext().getApplicationInfo().targetSdkVersion >= 23 ? context.checkSelfPermission(str) == 0 : PermissionChecker.a(context, str) == 0 : PermissionChecker.a(context, str) == 0;
} catch (Throwable th) {
c.a(th);
DFPLog.error(th);
return false;
}
}

打印一下当前调用的参数

1
2
3
4
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->isPermissionGranted(Ljava/lang/String;Landroid/content/Context;)Z":{
String permissionName = vaList.getObjectArg(0).getValue().toString();
System.out.println("check permission:"+permissionName);
}

运行

1
2
3
check permission:android.permission.READ_PHONE_STATE
[18:38:42 815] WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x123, PC=unidbg@0xfffe02c4, LR=RX@0x1203994d[libmtguard.so]0x3994d, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->isPermissionGranted(Ljava/lang/String;Landroid/content/Context;)Z

Google 得知这个权限意味“允许访问电话状态权限”,我这里返回 True,更利用伪装成真实环境。

1
2
3
4
5
6
7
8
9
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->isPermissionGranted(Ljava/lang/String;Landroid/content/Context;)Z":{
String permissionName = vaList.getObjectArg(0).getValue().toString();
System.out.println("check permission:"+permissionName);
if(permissionName.equals("android.permission.READ_PHONE_STATE")){
return true;
}else {
throw new UnsupportedOperationException(signature);
}
}

报错

1
2
3
4
[18:40:01 392]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x192, PC=unidbg@0xfffe09b4, LR=RX@0x1203c723[libmtguard.so]0x3c723, syscall=null
java.lang.UnsupportedOperationException: android/os/Build$VERSION->SDK_INT:I
at com.github.unidbg.linux.android.dvm.AbstractJni.getStaticIntField(AbstractJni.java:136)
at com.github.unidbg.linux.android.dvm.AbstractJni.getStaticIntField(AbstractJni.java:128)

这里在获取 SDK 的版本,选择返回 29,也就是 Android10 版本。

1
2
3
4
5
6
7
8
9
@Override
public int getStaticIntField(BaseVM vm, DvmClass dvmClass, String signature) {
switch (signature) {
case "android/os/Build$VERSION->SDK_INT:I": {
return 29;
}
}
return super.getStaticIntField(vm, dvmClass, signature);
}

报错

1
2
3
4
[18:49:25 076]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x1203c617[libmtguard.so]0x3c617, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->checkBuildAttribute(Ljava/lang/String;)Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:100)

先打印一下参数

1
2
3
4
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->checkBuildAttribute(Ljava/lang/String;)Ljava/lang/String;":{
String arg = vaList.getObjectArg(0).getValue().toString();
System.out.println("checkBuildAttribute:"+arg);
}

输出:

1
checkBuildAttribute:-

JADX看看实现

1
2
3
4
5
private String checkBuildAttribute(String str) {
Object[] objArr = {str};
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
return PatchProxy.isSupport(objArr, this, changeQuickRedirect2, false, "3b097946787cf593049f918e4d2f8a4e", RobustBitConfig.DEFAULT_VALUE) ? (String) PatchProxy.accessDispatch(objArr, this, changeQuickRedirect2, false, "3b097946787cf593049f918e4d2f8a4e") : (TextUtils.isEmpty(str) || str.equalsIgnoreCase("unknown")) ? CommonConstant.Symbol.MINUS : str;
}

实际代码逻辑很简单,可以直接 copy。

1
(TextUtils.isEmpty(str) || str.equalsIgnoreCase("unknown")) ? CommonConstant.Symbol.MINUS : str

CommonConstant.Symbol.MINUS的值就是 -

补:

1
2
3
4
5
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->checkBuildAttribute(Ljava/lang/String;)Ljava/lang/String;":{
String arg = vaList.getObjectArg(0).getValue().toString();
System.out.println("checkBuildAttribute:"+arg);
return new StringObject(vm, arg.isEmpty() || arg.equalsIgnoreCase("unknown") ? "-" : arg);
}
1
2
3
4
[18:55:25 203]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x1203ce83[libmtguard.so]0x3ce83, syscall=null
java.lang.UnsupportedOperationException: android/view/WindowManager->getDefaultDisplay()Landroid/view/Display;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:105)

这是一个 Android 对象,补

1
2
3
case "android/view/WindowManager->getDefaultDisplay()Landroid/view/Display;":{
return vm.resolveClass("android/view/Display").newObject(null);
}

报错:

1
2
3
4
[18:57:06 805]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x12f, PC=unidbg@0xfffe0384, LR=RX@0x1203a291[libmtguard.so]0x3a291, syscall=null
java.lang.UnsupportedOperationException: android/view/Display->getHeight()I
at com.github.unidbg.linux.android.dvm.AbstractJni.callIntMethodV(AbstractJni.java:563)
at com.dianping.NBridge.callIntMethodV(NBridge.java:153)

又是 Android FrameWork 层的 API ,Google 搜索发现它和getWidth一起用于获取屏幕的宽高。对于这类基本设备信息,完全可以通过 ADB 获取。

1
2
adb shell wm size
Physical size: 1440x2560

补:

1
2
3
case "android/view/Display->getHeight()I":{
return 2560;
}

报错:

1
2
3
4
[19:01:12 229]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x1203f1e1[libmtguard.so]0x3f1e1, syscall=null
java.lang.UnsupportedOperationException: java/lang/StringBuilder->append(I)Ljava/lang/StringBuilder;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:108)

JDK 函数补

1
2
3
case "java/lang/StringBuilder->append(I)Ljava/lang/StringBuilder;":{
return ProxyDvmObject.createObject(vm, ((StringBuilder) dvmObject.getValue()).append(vaList.getIntArg(0)));
}

报错:

1
2
3
4
[19:03:03 854]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x1203bd31[libmtguard.so]0x3bd31, syscall=null
java.lang.UnsupportedOperationException: java/lang/StringBuilder->append(C)Ljava/lang/StringBuilder;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:111)
1
2
3
case "java/lang/StringBuilder->append(C)Ljava/lang/StringBuilder;":{
return ProxyDvmObject.createObject(vm, ((StringBuilder) dvmObject.getValue()).append((char)vaList.getIntArg(0)));
}

遇到新报错,刚补过高,现在是宽。

1
2
3
4
[19:05:34 347]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x12f, PC=unidbg@0xfffe0384, LR=RX@0x1203e717[libmtguard.so]0x3e717, syscall=null
java.lang.UnsupportedOperationException: android/view/Display->getWidth()I
at com.github.unidbg.linux.android.dvm.AbstractJni.callIntMethodV(AbstractJni.java:563)
at com.dianping.NBridge.callIntMethodV(NBridge.java:162)
1
2
3
case "android/view/Display->getWidth()I":{
return 1440;
}

继续运行:

1
2
3
4
[19:06:47 544]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x1203a117[libmtguard.so]0x3a117, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->getTotalInternalMemorySize()Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:114)

getTotalInternalMemorySize这个函数名就很清晰,再看看代码逻辑。

1
2
3
4
5
6
7
8
9
private String getTotalInternalMemorySize() {
Object[] objArr = new Object[0];
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr, this, changeQuickRedirect2, false, "838e88ccfd19d138e498f2d967b7d987", RobustBitConfig.DEFAULT_VALUE)) {
return (String) PatchProxy.accessDispatch(objArr, this, changeQuickRedirect2, false, "838e88ccfd19d138e498f2d967b7d987");
}
StatFs statFs = new StatFs(Environment.getDataDirectory().getPath());
return fileSize(statFs.getBlockCount() * statFs.getBlockSize());
}

Environment.getDataDirectory()就是 data 文件夹,它是存储的根目录。StatFs 底层对应于 stat 结构体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
mode_t st_mode; /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device ID (if special file) */
off_t st_size; /* total size, in bytes */
blksize_t st_blksize; /* blocksize for file system I/O */
blkcnt_t st_blocks; /* number of 512B blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last status change */
};

这里是 st_blksize * st_blocks,返回 data 文件夹的实际大小,单位是 Bit。 在 Native 代码实现里也经常这个逻辑。最后看fileSize函数的实现,它将很大的比特值转为更直观和合适的MBGB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private String fileSize(long j) {
Object[] objArr = {new Long(j)};
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr, this, changeQuickRedirect2, false, "6a69a740a4e2f5966b7d134666227a92", RobustBitConfig.DEFAULT_VALUE)) {
return (String) PatchProxy.accessDispatch(objArr, this, changeQuickRedirect2, false, "6a69a740a4e2f5966b7d134666227a92");
}
StringBuilder sb = new StringBuilder();
String str = "";
if (j >= 1024) {
str = "KB";
j /= 1024;
if (j >= 1024) {
str = "MB";
j /= 1024;
if (j >= 1024) {
str = "GB";
j /= 1024;
}
}
}
sb.append(j);
sb.append(str);
return sb.toString();
}

通过 Frida Call 获取这个值。测试机是 10GB。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function test(){
Java.perform(
function(){
let SIUACollector = Java.use("com.meituan.android.common.mtguard.NBridge$SIUACollector");
SIUACollector["getTotalInternalMemorySize"].implementation = function () {
console.log(`SIUACollector.getTotalInternalMemorySize is called`);
let result = this["getTotalInternalMemorySize"]();
console.log(`SIUACollector.getTotalInternalMemorySize result=${result}`);
return result;
};
// 获取系统上下文
var currentApplication = Java.use('android.app.ActivityThread').currentApplication();
var context = currentApplication.getApplicationContext();
//主动调用
SIUACollector.$new(context).getTotalInternalMemorySize();
}
);
}
//SIUACollector.getTotalInternalMemorySize result=10GB

1
2
3
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->getTotalInternalMemorySize()Ljava/lang/String;":{
return new StringObject(vm, "10GB");
}

报错:

1
2
3
4
[19:15:55 438]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x12036db3[libmtguard.so]0x36db3, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->getTotalExternalMemorySize()Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:117)

看一下getTotalExternalMemorySize具体实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private String getTotalExternalMemorySize() {
Object[] objArr = new Object[0];
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr, this, changeQuickRedirect2, false, "522b1633753f28d5eb161a6c3ff3faa1", RobustBitConfig.DEFAULT_VALUE)) {
return (String) PatchProxy.accessDispatch(objArr, this, changeQuickRedirect2, false, "522b1633753f28d5eb161a6c3ff3faa1");
}
try {
if (!Environment.getExternalStorageState().equals("mounted")) {
return "";
}
StatFs statFs = new StatFs(Environment.getExternalStorageDirectory().getPath());
return fileSize(statFs.getBlockCount() * statFs.getBlockSize());
} catch (Exception e) {
c.a(e);
MTGuardLog.error(e);
return "";
}
}

Environment.getExternalStorageDirectory()对应于 SD 卡存储的根目录,同理 Hook 处理。

1
2
3
4
5
6
7
8
9
Java.perform(function() {
let SIUACollector = Java.use("com.meituan.android.common.mtguard.NBridge$SIUACollector");
SIUACollector["getTotalExternalMemorySize"].implementation = function () {
console.log('getTotalExternalMemorySize is called');
let ret = this.getTotalExternalMemorySize();
console.log('getTotalExternalMemorySize ret value is ' + ret);
return ret;
};
})

1
2
3
4
//getTotalExternalMemorySize ret value is 10GB
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->getTotalExternalMemorySize()Ljava/lang/String;":{
return new StringObject(vm, "10GB");
}

报错

1
2
3
4
5
[19:21:20 590]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x123, PC=unidbg@0xfffe02c4, LR=RX@0x1203d9a3[libmtguard.so]0x3d9a3, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->isPermissionGranted(Ljava/lang/String;Landroid/content/Context;)Z
at com.dianping.NBridge.callBooleanMethodV(NBridge.java:135)
at com.github.unidbg.linux.android.dvm.AbstractJni.callBooleanMethodV(AbstractJni.java:603)
at com.github.unidbg.linux.android.dvm.DvmMethod.callBooleanMethodA(DvmMethod.java:124)

在确认是否有访问 WIFI 状态的权限,选择返回 True。

1
2
3
4
5
6
7
8
9
10
11
12
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->isPermissionGranted(Ljava/lang/String;Landroid/content/Context;)Z":{
String permissionName = vaList.getObjectArg(0).getValue().toString();
System.out.println("check permission:"+permissionName);
if(permissionName.equals("android.permission.READ_PHONE_STATE")){
return true;
}else if(permissionName.equals("android.permission.ACCESS_WIFI_STATE")){
return true;
}
else {
throw new UnsupportedOperationException(signature);
}
}

报错:

1
2
3
4
[19:24:26 483]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x16f, PC=unidbg@0xfffe0784, LR=RX@0x12036961[libmtguard.so]0x36961, syscall=null
java.lang.UnsupportedOperationException: android/text/TextUtils->isEmpty(Ljava/lang/CharSequence;)Z
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticBooleanMethodV(AbstractJni.java:191)
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticBooleanMethodV(AbstractJni.java:186)

这是 Android FrameWork 中处理字符串的工具类,这种工具函数往往不会很复杂,在 AOSP 中找一下它的实现。

1
2
3
public static boolean isEmpty(CharSequence str) {
return str == null || str.length() == 0;
}

1
2
3
4
5
6
7
8
9
10
@Override
public boolean callStaticBooleanMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
switch (signature) {
case "android/text/TextUtils->isEmpty(Ljava/lang/CharSequence;)Z": {
CharSequence str = (CharSequence) vaList.getObjectArg(0).getValue();
return str == null || str.length() == 0;
}
}
return super.callStaticBooleanMethodV(vm, dvmClass, signature, vaList);
}

报错:

1
2
3
4
[19:28:40 265]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x12039247[libmtguard.so]0x39247, syscall=null
java.lang.UnsupportedOperationException: android/telephony/TelephonyManager->getSimOperator()Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:121)

又是 Android FrameWork 里的函数,Google 找篇文章看一下,这篇](https://zhuanlan.zhihu.com/p/87602090),得知这是在获取运营商相关的信息。

同样可以使用 ADB 获取到相关信息。这里选择使用 Frida Hook 确认其值

1
2
3
4
5
6
7
8
9
Java.perform(function() {
let TelephonyManager = Java.use("android.telephony.TelephonyManager");
TelephonyManager["getSimOperator"].overload().implementation = function () {
console.log('getSimOperator is called');
let ret = this.getSimOperator();
console.log('getSimOperator ret value is ' + ret);
return ret;
};
})

没有插卡,返回为空

SimOperator 由 MCC + MNC 组成。
MCC由国际电信联盟 ITU 在全世界范围内统一分配和管理,唯一识别移动用户所属的国家,共3位,中国为460。
MNC用于识别移动用户所归属的移动通信网,2~3位。中国移动系统使用 00、02、04、07,中国联通系统使用 01、06、09,中国电信使用 03、05、11,中国铁通系统使用 20 。我的测试机手机卡是中国联通,具体值是46001。

1
2
3
case "android/telephony/TelephonyManager->getSimOperator()Ljava/lang/String;":{
return new StringObject(vm, "46001");
}

报错

1
2
3
4
[19:35:48 740]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x1203a91b[libmtguard.so]0x3a91b, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->getAccessSubType()Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:124)

在 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
25
26
27
28
29
30
31
@SuppressLint({"MissingPermission"})
private String getAccessSubType() {
Object[] objArr = new Object[0];
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr, this, changeQuickRedirect2, false, "4e473c1fae47c0bf65e6f819cd3eaf7c", RobustBitConfig.DEFAULT_VALUE)) {
return (String) PatchProxy.accessDispatch(objArr, this, changeQuickRedirect2, false, "4e473c1fae47c0bf65e6f819cd3eaf7c");
}
Context context = this.mContext;
if (context == null) {
return StringUtil.NULL;
}
if (!isPermissionGranted("android.permission.ACCESS_NETWORK_STATE", context)) {
return CommonConstant.Symbol.MINUS;
}
NetworkInfo networkInfo = null;
ConnectivityManager connectivityManager = (ConnectivityManager) this.mContext.getApplicationContext().getSystemService("connectivity");
if (connectivityManager != null) {
networkInfo = connectivityManager.getActiveNetworkInfo();
}
if (networkInfo == null) {
return StringUtil.NULL;
}
if (networkInfo.getType() == 1) {
return "wifi";
}
if (networkInfo.getType() != 0) {
return "";
}
int subtype = networkInfo.getSubtype();
return (subtype == 4 || subtype == 1 || subtype == 2) ? "2G" : (subtype == 3 || subtype == 8 || subtype == 6 || subtype == 5 || subtype == 12) ? "3G" : subtype == 13 ? "4G" : "";
}

是在获取网络类型

1
2
3
4
//检测当前的网络模式,可以是:wifi/2G/3G/4G
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->getAccessSubType()Ljava/lang/String;":{
return new StringObject(vm, "4G");
}

得到get!

1
getExternalEquipmentInfo:-|-|-|2560*1080|10GB|10GB|AQmac|46001|4G|

getHWEquipmentInfo

初始化

1
2
3
4
5
6
7
8
9
10
11
12
public String getHWEquipmentInfo(){
String result = SIUACollector.callJniMethodObject(emulator, "getHWEquipmentInfo()Ljava/lang/String;").getValue().toString();
return result;
}

public static void main(String[] args) {
NBridge nBridge = new NBridge();
System.out.println("getEnvironmentInfo:"+nBridge.getEnvironmentInfo());
System.out.println("getEnvironmentInfoExtra:"+nBridge.getEnvironmentInfoExtra());
System.out.println("getExternalEquipmentInfo:"+nBridge.getExternalEquipmentInfo());
System.out.println("getHWEquipmentInfo:"+nBridge.getHWEquipmentInfo());
}

运行报错

1
2
3
4
[19:40:25 928]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x12030eaf[libmtguard.so]0x30eaf, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->getCpuInfoType()Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:128)

JADX 中查看getCpuInfoType对应源代码

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
private String getCpuInfoType() {
BufferedReader bufferedReader;
Exception e;
Object[] objArr = new Object[0];
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr, this, changeQuickRedirect2, false, "e8c17f92755628eaeec9e7e53ee668d7", RobustBitConfig.DEFAULT_VALUE)) {
return (String) PatchProxy.accessDispatch(objArr, this, changeQuickRedirect2, false, "e8c17f92755628eaeec9e7e53ee668d7");
}
String str = "";
Closeable closeable = null;
try {
try {
bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream("/proc/cpuinfo")));
while (true) {
try {
try {
String readLine = bufferedReader.readLine();
if (readLine != null) {
if (!readLine.startsWith("Processor")) {
if (readLine.startsWith("model name")) {
str = "x86";
break;
}
} else {
str = "arm";
break;
}
} else {
break;
}
} catch (Throwable th) {
th = th;
closeable = bufferedReader;
c.a(th);
safeClose(closeable);
throw th;
}
} catch (Exception e2) {
e = e2;
c.a(e);
MTGuardLog.error(e);
safeClose(bufferedReader);
return str;
}
}
bufferedReader.close();
} catch (Exception e3) {
bufferedReader = null;
e = e3;
}
safeClose(bufferedReader);
return str;
} catch (Throwable th2) {
th = th2;
c.a(th);
safeClose(closeable);
throw th;
}
}

读取/proc/cpuinfo文件,判断是 ARM 还是 X86 架构,继续补环境

1
2
3
4
// CPU 架构
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->getCpuInfoType()Ljava/lang/String;":{
return new StringObject(vm, "arm");
}

报错:

1
2
3
4
[19:46:45 685]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x117, PC=unidbg@0xfffe0204, LR=RX@0x1202bcef[libmtguard.so]0x2bcef, syscall=null
java.lang.UnsupportedOperationException: java/io/BufferedReader->allocObject
at com.github.unidbg.linux.android.dvm.AbstractJni.allocObject(AbstractJni.java:812)
at com.dianping.NBridge.allocObject(NBridge.java:62)

前面遇到过一个 allocObject,可能想如法炮制

1
2
3
case "java/io/BufferedReader->allocObject":{
return ProxyDvmObject.createObject(vm, new BufferedReader());
}

但最好不要再这么做了,这是有隐患的处理办法。BufferedReader 没有无参构造函数,它所需要的初始化参数在allocObject阶段并没有展露出来,在后续真正初始化的时候才有值。

这里直接用空的dvmObject占位,这里的 dvmClass 等价于vm.resolveClass("java/io/BufferedReader")

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public DvmObject<?> allocObject(BaseVM vm, DvmClass dvmClass, String signature) {
switch (signature){
case "java/lang/StringBuilder->allocObject":{
return ProxyDvmObject.createObject(vm, new StringBuilder());
}
case "java/io/BufferedReader->allocObject":{
return dvmClass.newObject(null);
}
}
return super.allocObject(vm, dvmClass, signature);
}

报错是 InputStreamReader。

1
2
3
4
[19:51:11 744]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x117, PC=unidbg@0xfffe0204, LR=RX@0x1202a687[libmtguard.so]0x2a687, syscall=null
java.lang.UnsupportedOperationException: java/io/InputStreamReader->allocObject
at com.github.unidbg.linux.android.dvm.AbstractJni.allocObject(AbstractJni.java:812)
at com.dianping.NBridge.allocObject(NBridge.java:65)

同样占位处理

1
2
3
case "java/io/InputStreamReader->allocObject":{
return dvmClass.newObject(signature);
}

是 FileInputStream报错

1
2
3
4
19:52:22 987]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x117, PC=unidbg@0xfffe0204, LR=RX@0x1202c66f[libmtguard.so]0x2c66f, syscall=null
java.lang.UnsupportedOperationException: java/io/FileInputStream->allocObject
at com.github.unidbg.linux.android.dvm.AbstractJni.allocObject(AbstractJni.java:812)
at com.dianping.NBridge.allocObject(NBridge.java:68)

同理

1
2
3
case "java/io/FileInputStream->allocObject":{
return dvmClass.newObject(signature);
}

报错

1
2
3
[19:53:11 916]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x13b, PC=unidbg@0xfffe0444, LR=RX@0x12030391[libmtguard.so]0x30391, syscall=null
java.lang.UnsupportedOperationException: java/io/FileInputStream-><init>(Ljava/lang/String;)V
at com.github.unidbg.linux.android.dvm.AbstractJni.callVoidMethodV(AbstractJni.java:1007)

重点!!!到了调用其init函数做初始化的逻辑,因为callVoid系列 API 没有返回值,它的操作基于原先的占位dvmObject,因为我们没法对它重新赋值,所以我声明为一个全局变量处理它以及后面的逻辑

1
2
3
4
5
6
public class NBridge extends AbstractJni {
private final AndroidEmulator emulator;
private final VM vm;
private final Memory memory;

public FileInputStream fileInputStream;

然后补具体逻辑

1
2
3
4
5
6
7
8
9
10
case "java/io/FileInputStream-><init>(Ljava/lang/String;)V":{
String name = vaList.getObjectArg(0).getValue().toString();
System.out.println("FileInputStream:"+name);
try {
fileInputStream = new FileInputStream(name);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return;
}

运行发现有问题

1
2
3
4
5
6
7
8
9
10
[20:00:49 089]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x13b, PC=unidbg@0xfffe0444, LR=RX@0x12029ed7[libmtguard.so]0x29ed7, syscall=null
java.lang.UnsupportedOperationException: java/io/InputStreamReader-><init>(Ljava/io/InputStream;)V
at com.github.unidbg.linux.android.dvm.AbstractJni.callVoidMethodV(AbstractJni.java:1007)
at com.dianping.NBridge.callVoidMethodV(NBridge.java:238)
。。。
java.io.FileNotFoundException: \proc\cpuinfo (系统找不到指定的路径。)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at java.io.FileInputStream.<init>(FileInputStream.java:93)

程序在访问 /proc/cpuinfo 文件,首先将手机的 /proc/cpuinfo 文件拖到本地

1
2
C:\Users\25858>adb pull /proc/cpuinfo E:\Re\unidbg-master\unidbg-android\src\test\resources\dianping\files
/proc/cpuinfo: 1 file pulled, 0 skipped. 0.3 MB/s (1030 bytes in 0.004s)

然后做路径的重定位。

1
2
3
4
5
6
7
8
9
10
11
12
13
case "java/io/FileInputStream-><init>(Ljava/lang/String;)V":{
String name = vaList.getObjectArg(0).getValue().toString();
if(name.equals("/proc/cpuinfo")){
name = "unidbg-android/src/test/resources/dianping/files/cpuinfo";
}
System.out.println("FileInputStream:"+name);
try {
fileInputStream = new FileInputStream(name);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return;
}

报错

1
2
3
4
[20:05:53 326]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x13b, PC=unidbg@0xfffe0444, LR=RX@0x12029ed7[libmtguard.so]0x29ed7, syscall=null
java.lang.UnsupportedOperationException: java/io/InputStreamReader-><init>(Ljava/io/InputStream;)V
at com.github.unidbg.linux.android.dvm.AbstractJni.callVoidMethodV(AbstractJni.java:1007)
at com.dianping.NBridge.callVoidMethodV(NBridge.java:241)

同理处理为全局变量

1
2
3
4
5
6
public class NBridge extends AbstractJni implements IOResolver {
private final AndroidEmulator emulator;
private final DvmObject<?> SIUACollector;
private final VM vm;
public FileInputStream fileInputStream;
public InputStreamReader inputStreamReader;

1
2
3
4
case "java/io/InputStreamReader-><init>(Ljava/io/InputStream;)V":{
inputStreamReader = new InputStreamReader(fileInputStream);
return;
}

报错

1
2
3
4
[20:07:40 648]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x13b, PC=unidbg@0xfffe0444, LR=RX@0x1202afb7[libmtguard.so]0x2afb7, syscall=null
java.lang.UnsupportedOperationException: java/io/BufferedReader-><init>(Ljava/io/Reader;)V
at com.github.unidbg.linux.android.dvm.AbstractJni.callVoidMethodV(AbstractJni.java:1007)
at com.dianping.NBridge.callVoidMethodV(NBridge.java:246)

这其实是一个套娃逻辑。

1
2
3
4
5
6
7
public class NBridge extends AbstractJni implements IOResolver {
private final AndroidEmulator emulator;
private final DvmObject<?> SIUACollector;
private final VM vm;
public FileInputStream fileInputStream;
public InputStreamReader inputStreamReader;
public BufferedReader bufferedReader;

1
2
3
4
case "java/io/BufferedReader-><init>(Ljava/io/Reader;)V":{
bufferedReader = new BufferedReader(inputStreamReader);
return;
}

报错

1
2
3
4
[20:09:20 513]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x1202e89f[libmtguard.so]0x2e89f, syscall=null
java.lang.UnsupportedOperationException: java/io/BufferedReader->readLine()Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:144)

回归到了我们更熟悉的逻辑里了

1
2
3
4
5
6
7
8
9
10
11
12
13
case "java/io/BufferedReader->readLine()Ljava/lang/String;":{
String oneline;
try {
oneline = bufferedReader.readLine();
if(oneline != null){
return new StringObject(vm, oneline);
}else {
return null;
}
} catch (IOException e) {
e.printStackTrace();
}
}

报错

1
2
3
4
[20:10:29 565]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x12f, PC=unidbg@0xfffe0384, LR=RX@0x1202c179[libmtguard.so]0x2c179, syscall=null
java.lang.UnsupportedOperationException: java/lang/String->compareToIgnoreCase(Ljava/lang/String;)I
at com.github.unidbg.linux.android.dvm.AbstractJni.callIntMethodV(AbstractJni.java:563)
at com.dianping.NBridge.callIntMethodV(NBridge.java:222)

JDK 方法,正常补

1
2
3
4
5
6
7
8
case "java/lang/String->compareToIgnoreCase(Ljava/lang/String;)I": {
// 获取参数
String arg = (String) vaList.getObjectArg(0).getValue();
// 获取对象
String obj = (String) dvmObject.getValue();

return obj.compareToIgnoreCase(arg);
}

报错

1
2
3
4
5
[20:12:32 206]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x12f, PC=unidbg@0xfffe0384, LR=RX@0x1202dcc3[libmtguard.so]0x2dcc3, syscall=null
java.lang.UnsupportedOperationException: java/lang/String->lastIndexOf(I)I
at com.github.unidbg.linux.android.dvm.AbstractJni.callIntMethodV(AbstractJni.java:563)
at com.dianping.NBridge.callIntMethodV(NBridge.java:230)
at com.github.unidbg.linux.android.dvm.AbstractJni.callIntMethodV(AbstractJni.java:529)
1
2
3
case "java/lang/String->lastIndexOf(I)I":{
return dvmObject.getValue().toString().lastIndexOf(vaList.getIntArg(0));
}

报错

1
2
3
4
[20:13:33 823]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x12030025[libmtguard.so]0x30025, syscall=null
java.lang.UnsupportedOperationException: java/lang/String->substring(I)Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:157)

1
2
3
case "java/lang/String->substring(I)Ljava/lang/String;":{
return new StringObject(vm, dvmObject.getValue().toString().substring(vaList.getIntArg(0)));
}

报错

1
2
3
4
[20:14:47 879]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x13b, PC=unidbg@0xfffe0444, LR=RX@0x1202bcbf[libmtguard.so]0x2bcbf, syscall=null
java.lang.UnsupportedOperationException: java/io/BufferedReader->close()V
at com.github.unidbg.linux.android.dvm.AbstractJni.callVoidMethodV(AbstractJni.java:1007)
at com.dianping.NBridge.callVoidMethodV(NBridge.java:275)

1
2
3
4
5
6
7
8
case "java/io/BufferedReader->close()V":{
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
return;
}

报错

1
2
3
4
[20:16:09 012]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x120312c3[libmtguard.so]0x312c3, syscall=null
java.lang.UnsupportedOperationException: android/hardware/SensorManager->getDefaultSensor(I)Landroid/hardware/Sensor;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:160)

传感器一向是信息收集的重点,处理它需要更多的耐心。

首先是这个 API 的原型。

1
Sensor getDefaultSensor(int type)

它会从传感器服务获取指定类型的传感器,有相当多的类型,详见 Android 源码。在/frameworks/base/core/java/android/hardware/SensorManager.java

1
2
3
4
5
6
7
8
9
10
11
12
public static final int TYPE_ACCELEROMETER = 1;
public static final int TYPE_ORIENTATION = 3;
public static final int TYPE_POSE_6DOF = 28;
public static final int TYPE_PRESSURE = 6;
public static final int TYPE_PROXIMITY = 8;
public static final int TYPE_RELATIVE_HUMIDITY = 12;
public static final int TYPE_ROTATION_VECTOR = 11;
public static final int TYPE_SIGNIFICANT_MOTION = 17;
public static final int TYPE_STATIONARY_DETECT = 29;
public static final int TYPE_STEP_COUNTER = 19;
public static final int TYPE_STEP_DETECTOR = 18;
public static final int TYPE_GRAVITY = 9;

打印参数,看样本在获取哪类传感器

1
2
3
4
case "android/hardware/SensorManager->getDefaultSensor(I)Landroid/hardware/Sensor;":{
int type = vaList.getIntArg(0);
System.out.println("Sensor type:"+type);
}
1
Sensor type:1

1 对应于加速度传感器,下面做常规补环境,

将 type 设为 DvmObject 的值,因为担心样本会获取多种传感器,所以提前处理,便于区分。

1
2
3
4
case "android/hardware/SensorManager->getDefaultSensor(I)Landroid/hardware/Sensor;":{
int type = vaList.getIntArg(0);
return vm.resolveClass("android/hardware/Sensor").newObject(type);
}

继续

1
2
3
4
[20:20:11 181]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x12031b3b[libmtguard.so]0x31b3b, syscall=null
java.lang.UnsupportedOperationException: android/hardware/Sensor->getName()Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:164)

这里在获取传感器的名字

可以写一个小的 Android Java 层应用demo来获取加速度传感器的名字,来理解这里的逻辑,在 JNI 中看起来有点朦胧,但用 Java 复写其实相当简单。

1
2
3
4
5
public void getSensorName(){
SensorManager manager= (SensorManager)getSystemService(SENSOR_SERVICE);
Sensor accelSensor = manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
Log.e("lilac sensor Name",accelSensor.getName());
}

偷懒使用现成的ICM20690

收集传感器的信息,可以一定程度上阻止“改机”,国内主要品牌、主要型号 所使用的传感器信息都可以查到,如果你修改了 Android 设备信息,比如从 Pixel 改为小米 mix2s ,那么对应的,各类传感器信息也需要改。如果不协调同步的改,就会有破绽。

1
2
3
4
5
6
7
8
9
10
case "android/hardware/Sensor->getName()Ljava/lang/String;":{
//dvmObject.getValue() 的返回值是一个对象类型,不能直接赋值给基本类型 int。
int type = (Integer) dvmObject.getValue();
System.out.println("Sensor getName:"+type);
if(type == 1){
return new StringObject(vm, "ICM20690");
}else {
throw new UnsupportedOperationException(signature);
}
}

报错

1
2
3
4
[20:30:51 936]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x1202e717[libmtguard.so]0x2e717, syscall=null
java.lang.UnsupportedOperationException: android/hardware/Sensor->getVendor()Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:173)

传感器有一系列可供获取的属性。

  • getMaximumRange() 最大取值范围
  • getName() 设备名称
  • getPower() 功率
  • getResolution() 精度
  • getType() 传感器类型
  • getVentor() 设备供应商
  • getVersion() 设备版本号

这里是在获取设备供应商信息,在 Android demo 中如法炮制。

1
2
3
4
SensorManager manager= (SensorManager)getSystemService(SENSOR_SERVICE);
Sensor accelSensor = manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
Log.e("lilac sensor Name",accelSensor.getName());
Log.e("lilac sensor Vendor",accelSensor.getVendor());

qualcomm 也就是高通,继续补环境。

1
2
3
4
5
6
7
8
9
case "android/hardware/Sensor->getVendor()Ljava/lang/String;":{
int type = (Integer) dvmObject.getValue();
System.out.println("Sensor getVendor:"+type);
if(type == 1){
return new StringObject(vm, "qualcomm");
}else {
throw new UnsupportedOperationException(signature);
}
}

报错

1
2
3
4
[20:33:23 653]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x1202ad4f[libmtguard.so]0x2ad4f, syscall=null
java.lang.UnsupportedOperationException: android/hardware/Sensor->getName()Ljava/lang/String;
at com.dianping.NBridge.callObjectMethodV(NBridge.java:168)
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:262)

它又获取了另一种传感器。9 是 TYPE_GRAVITY 即重力传感器。将 Android demo 稍加修改。

1
2
3
4
SensorManager manager= (SensorManager)getSystemService(SENSOR_SERVICE);
Sensor accelSensor = manager.getDefaultSensor(Sensor.TYPE_GRAVITY);
Log.e("lilac sensor Name",accelSensor.getName());
Log.e("lilac sensor Vendor",accelSensor.getVendor());

设备名 gravity Non-wakeup,供应商依然是 qualcomm,继续完善补环境。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
case "android/hardware/Sensor->getName()Ljava/lang/String;":{
int type = (Integer) dvmObject.getValue();
System.out.println("Sensor getName:"+type);
if(type == 1){
return new StringObject(vm, "ICM20690");
}else if(type == 9){
return new StringObject(vm, "gravity Non-wakeup");
}
else {
throw new UnsupportedOperationException(signature);
}
}
case "android/hardware/Sensor->getVendor()Ljava/lang/String;":{
int type = (Integer) dvmObject.getValue();
System.out.println("Sensor getVendor:"+type);
if(type == 1){
return new StringObject(vm, "qualcomm");
}else if(type == 9){
return new StringObject(vm, "qualcomm");
}
else {
throw new UnsupportedOperationException(signature);
}
}

报错

1
2
3
4
[20:35:30 444]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x13b, PC=unidbg@0xfffe0444, LR=RX@0x1202c103[libmtguard.so]0x2c103, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->safeClose(Ljava/io/Closeable;)V
at com.github.unidbg.linux.android.dvm.AbstractJni.callVoidMethodV(AbstractJni.java:1007)
at com.dianping.NBridge.callVoidMethodV(NBridge.java:312)

JADX中对应的反编译代码

1
2
3
4
5
6
7
8
9
private void safeClose(Closeable closeable) {
Object[] objArr = {closeable};
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr, this, changeQuickRedirect2, false, "148f9159176b748328c0fe05c9308265", RobustBitConfig.DEFAULT_VALUE)) {
PatchProxy.accessDispatch(objArr, this, changeQuickRedirect2, false, "148f9159176b748328c0fe05c9308265");
} else {
MTGUtils.safeClose(closeable);
}
}

看起来是处理完之后的资源关闭操作,简单处理即可。

1
2
3
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->safeClose(Ljava/io/Closeable;)V":{
return;
}

结束

1
getHWEquipmentInfo:-|Qualcomm Technologies, Inc MSM8992|6|ICM20690|qualcomm|gravity  Non-wakeup|qualcomm|

getHWProperty

发起调用

1
2
3
4
public String getHWProperty(){
String result = SIUACollector.callJniMethodObject(emulator, "getHWProperty()Ljava/lang/String;").getValue().toString();
return result;
}

报错

1
2
3
4
5
[21:19:25 124]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x18d, PC=unidbg@0xfffe0964, LR=RX@0x1200fbfd[libmtguard.so]0xfbfd, syscall=null
java.lang.UnsupportedOperationException: android/os/Build->BOARD:Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.getStaticObjectField(AbstractJni.java:103)
at com.github.unidbg.linux.android.dvm.AbstractJni.getStaticObjectField(AbstractJni.java:53)
at com.github.unidbg.linux.android.dvm.DvmField.getStaticObjectField(DvmField.java:106)

需要获取设备的品牌方,对于 Build 下的信息,同样可以写 demo 看,这个办法相当灵活好用,或者使用 ADB 也行。

1
Log.e("lilac board:", Build.BOARD);

使用 ADB

1
2
3
4
bullhead:/ $  getprop | grep board
[ro.board.platform]: [msm8992]
[ro.product.board]: [bullhead]
bullhead:/ $

1
2
3
4
5
6
7
8
9
@Override
public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
switch (signature){
case "android/os/Build->BOARD:Ljava/lang/String;":{
return new StringObject(vm, "msm8992");
}
}
return super.getStaticObjectField(vm, dvmClass, signature);
}

报错

1
2
3
4
[21:23:29 418]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x18d, PC=unidbg@0xfffe0964, LR=RX@0x120160ab[libmtguard.so]0x160ab, syscall=null
java.lang.UnsupportedOperationException: android/os/Build->MANUFACTURER:Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.getStaticObjectField(AbstractJni.java:103)
at com.dianping.NBridge.getStaticObjectField(NBridge.java:334)

同理

1
Log.e("lilac board:", Build.MANUFACTURER);

同理adb

1
2
3
4
5
bullhead:/ $ getprop | grep manufacturer
[media.recorder.show_manufacturer_and_model]: [true]
[ro.product.manufacturer]: [LGE]
[ro.vendor.product.manufacturer]: [LGE]
bullhead:/ $

补环境

1
2
3
case "android/os/Build->MANUFACTURER:Ljava/lang/String;": {
return new StringObject(vm, "LGE");
}

还是获取Build类下的信息

1
2
3
4
[21:27:24 015]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x18d, PC=unidbg@0xfffe0964, LR=RX@0x12016b1b[libmtguard.so]0x16b1b, syscall=null
java.lang.UnsupportedOperationException: android/os/Build->BRAND:Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.getStaticObjectField(AbstractJni.java:103)
at com.dianping.NBridge.getStaticObjectField(NBridge.java:337)

adb

1
2
3
4
bullhead:/ $ getprop | grep brand
[ro.product.brand]: [google]
[ro.vendor.product.brand]: [google]
bullhead:/ $

1
2
3
case "android/os/Build->BRAND:Ljava/lang/String;":{
return new StringObject(vm, "google");
}

报错

1
2
3
[21:30:43 679]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x18d, PC=unidbg@0xfffe0964, LR=RX@0x12014c13[libmtguard.so]0x14c13, syscall=null
java.lang.UnsupportedOperationException: android/os/Build->MODEL:Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.getStaticObjectField(AbstractJni.java:103)
1
2
3
4
bullhead:/ $  getprop | grep model
[media.recorder.show_manufacturer_and_model]: [true]
[ro.product.model]: [Nexus 5X]
[ro.vendor.product.model]: [Nexus 5X]
1
2
3
case "android/os/Build->MODEL:Ljava/lang/String;":{
return new StringObject(vm, "Nexus 5X");
}

报错

1
2
3
4
[21:32:06 093]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x1200d2a7[libmtguard.so]0xd2a7, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->getSysProp(Ljava/lang/String;)Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:188)

getSysProp 是一个用于获取系统属性的工具函数,反编译代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static String getSysProp(String str) {
Object[] objArr = {str};
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr, null, changeQuickRedirect2, true, "31aaabc5cd1287cb6e2e62ee32d788e0", RobustBitConfig.DEFAULT_VALUE)) {
return (String) PatchProxy.accessDispatch(objArr, null, changeQuickRedirect2, true, "31aaabc5cd1287cb6e2e62ee32d788e0");
}
if (TextUtils.isEmpty(str)) {
return "";
}
try {
Class<?> cls = Class.forName("android.os.SystemProperties");
return (String) cls.getDeclaredMethod("get", String.class).invoke(cls, str);
} catch (Throwable th) {
c.a(th);
MTGuardLog.error(th);
return "";
}
}

打印一下参数

1
2
3
4
5
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->getSysProp(Ljava/lang/String;)Ljava/lang/String;":{
String name = vaList.getObjectArg(0).getValue().toString();
System.out.println("getSysProp:"+name);
}
//getSysProp:ro.product.cpu.abi

是属性信息,用 ADB 获取很方便,第一个就是。

1
2
3
4
5
bullhead:/ $ getprop | grep ro.product.cpu.abi
[ro.product.cpu.abi]: [arm64-v8a]
[ro.product.cpu.abilist]: [arm64-v8a,armeabi-v7a,armeabi]
[ro.product.cpu.abilist32]: [armeabi-v7a,armeabi]
[ro.product.cpu.abilist64]: [arm64-v8a]
1
2
3
4
5
6
7
8
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->getSysProp(Ljava/lang/String;)Ljava/lang/String;":{
String name = vaList.getObjectArg(0).getValue().toString();
System.out.println("getSysProp:"+name);
if(name.equals("ro.product.cpu.abi")){
return new StringObject(vm, "arm64-v8a");
}
throw new UnsupportedOperationException(signature);
}

报错

1
xxxxxxxxxx getSysProp:ro.product.cpu.abi2[21:35:05 199]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x12018613[libmtguard.so]0x18613, syscall=nulljava.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->getSysProp(Ljava/lang/String;)Ljava/lang/String;    at com.dianping.NBridge.callObjectMethodV(NBridge.java:192)javaadb getprop 找不到这个属性,所以返回空字符串 。

adb getprop 找不到这个属性,所以返回空字符串 。

1
2
3
if(name.equals("ro.product.cpu.abi2")){
return new StringObject(vm, "");
}

报错

1
2
3
[21:38:44 833]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x18d, PC=unidbg@0xfffe0964, LR=RX@0x1200d7e3[libmtguard.so]0xd7e3, syscall=null
java.lang.UnsupportedOperationException: android/os/Build->PRODUCT:Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.getStaticObjectField(AbstractJni.java:103)

接连是 PRODUCT、HARDWARE、DEVICE。

1
2
3
4
5
6
7
8
9
case "android/os/Build->PRODUCT:Ljava/lang/String;":{
return new StringObject(vm, "polaris");
}
case "android/os/Build->HARDWARE:Ljava/lang/String;":{
return new StringObject(vm, "qcom");
}
case "android/os/Build->DEVICE:Ljava/lang/String;":{
return new StringObject(vm, "polaris");
}

报错

1
2
3
4
5
getSysProp:ro.build.product
[21:50:47 118] WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x1201831f[libmtguard.so]0x1831f, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->getSysProp(Ljava/lang/String;)Ljava/lang/String;
at com.dianping.NBridge.callObjectMethodV(NBridge.java:195)
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:262)

又是所谓的多重校验,多个 API 获取相同信息。

1
2
3
bullhead:/ $ getprop | grep ro.build.product
[ro.build.product]: [bullhead]
//if(name.equals("ro.build.product")){return new StringObject(vm, "bullhead");}

然后是 HOST 和 ID

1
2
3
4
5
6
case "android/os/Build->HOST:Ljava/lang/String;":{
return new StringObject(vm, "c3-miui-ota-bd134.bj");
}
case "android/os/Build->ID:Ljava/lang/String;":{
return new StringObject(vm, "QKQ1.190828.002");
}

然后报错

1
2
3
4
[21:54:44 435]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x18d, PC=unidbg@0xfffe0964, LR=RX@0x12010777[libmtguard.so]0x10777, syscall=null
java.lang.UnsupportedOperationException: android/os/Build$VERSION->RELEASE:Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.getStaticObjectField(AbstractJni.java:103)
at com.dianping.NBridge.getStaticObjectField(NBridge.java:373)

adb

1
2
127|bullhead:/ $ getprop | grep ro.build.version.release
[ro.build.version.release]: [8.1.0]
1
2
3
case "android/os/Build$VERSION->RELEASE:Ljava/lang/String;": {
return new StringObject(vm,"8.1.0");
}

报错

1
2
3
[21:56:42 533]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x171, PC=unidbg@0xfffe07a4, LR=RX@0x1200c9c1[libmtguard.so]0xc9c1, syscall=null
java.lang.UnsupportedOperationException: java/lang/Integer->toString(I)Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethodV(AbstractJni.java:504)

jdk中函数,按照一般步骤走即可:获取对象、获取参数、调用方法、封装。由于这个是静态方法,不需要实例对象即可调用

1
2
3
case "java/lang/Integer->toString(I)Ljava/lang/String;":{
return new StringObject(vm, Integer.toString(vaList.getIntArg(0)));
}
1
2
3
4
[22:00:01 490]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x120102b3[libmtguard.so]0x102b3, syscall=null
java.lang.UnsupportedOperationException: android/content/Context->getResources()Landroid/content/res/Resources;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:202)

Resources 即资源,看来是新的一些逻辑。

1
2
3
case "android/content/Context->getResources()Landroid/content/res/Resources;":{
return vm.resolveClass("android/content/res/Resources").newObject(null);
}

报错,原来是想获取配置信息

1
2
3
4
[22:01:19 553]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x12012e8b[libmtguard.so]0x12e8b, syscall=null
java.lang.UnsupportedOperationException: android/content/res/Resources->getConfiguration()Landroid/content/res/Configuration;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:205)

补环境

1
2
3
case "android/content/res/Resources->getConfiguration()Landroid/content/res/Configuration;":{
return vm.resolveClass("android/content/res/Configuration").newObject(null);
}

报错

1
2
3
4
[22:02:16 165]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x15b, PC=unidbg@0xfffe0644, LR=RX@0x1201187f[libmtguard.so]0x1187f, syscall=null
java.lang.UnsupportedOperationException: android/content/res/Configuration->locale:Ljava/util/Locale;
at com.github.unidbg.linux.android.dvm.AbstractJni.getObjectField(AbstractJni.java:171)
at com.dianping.NBridge.getObjectField(NBridge.java:297)

是获取区域信息,locale 在 JDK 里也有,所以改用 createObject。

1
2
3
case "android/content/res/Configuration->locale:Ljava/util/Locale;":{
return ProxyDvmObject.createObject(vm, Locale.getDefault());
}
1
2
3
[22:03:18 914]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x18d, PC=unidbg@0xfffe0964, LR=RX@0x120123f7[libmtguard.so]0x123f7, syscall=null
java.lang.UnsupportedOperationException: android/os/Build->TAGS:Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.getStaticObjectField(AbstractJni.java:103)

ADB grep 一下, ro.build.tags的值是 release-keys。

1
2
bullhead:/ $ getprop | grep ro.build.tags
[ro.build.tags]: [release-keys]
1
2
3
case "android/os/Build->TAGS:Ljava/lang/String;":{
return new StringObject(vm, "release-keys");
}

继续运行,经过 FINGERPRINT、TYPE。

1
2
3
4
[22:04:39 106]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x18d, PC=unidbg@0xfffe0964, LR=RX@0x12011c97[libmtguard.so]0x11c97, syscall=null
java.lang.UnsupportedOperationException: android/os/Build->FINGERPRINT:Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.getStaticObjectField(AbstractJni.java:103)
at com.dianping.NBridge.getStaticObjectField(NBridge.java:392)
1
2
3
4
5
6
case "android/os/Build->FINGERPRINT:Ljava/lang/String;":{
return new StringObject(vm, "Xiaomi/polaris/polaris:10/QKQ1.190828.002/V12.0.2.0.QDGCNXM:user/release-keys");
}
case "android/os/Build->TYPE:Ljava/lang/String;":{
return new StringObject(vm, "user");
}

getSysProp报错

1
2
3
4
getSysProp:ro.build.description
[22:05:18 778] WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x1200ca17[libmtguard.so]0xca17, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->getSysProp(Ljava/lang/String;)Ljava/lang/String;
at com.dianping.NBridge.callObjectMethodV(NBridge.java:199)

ADB 获取信息并补环境

1
2
bullhead:/ $ getprop | grep ro.build.description
[ro.build.description]: [bullhead-user 8.1.0 OPM1.171019.011 4448085 release-keys]
1
2
3
if(name.equals("ro.build.description")){
return new StringObject(vm, "bullhead-user 8.1.0 OPM1.171019.011 4448085 release-keys");
}

报错

1
2
3
4
getSysProp:ro.secure
[22:06:48 855] WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x1201623b[libmtguard.so]0x1623b, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->getSysProp(Ljava/lang/String;)Ljava/lang/String;
at com.dianping.NBridge.callObjectMethodV(NBridge.java:202)
1
2
bullhead:/ $ getprop | grep ro.secure
[ro.secure]: [1]
1
2
3
if(name.equals("ro.secure")){
return new StringObject(vm, "1");
}

继续运行,ro.debuggable

1
2
3
if(name.equals("ro.debuggable")){
return new StringObject(vm, "0");
}

得!

1
2
JNIEnv->CallObjectMethodA(java.lang.StringBuilder@10bdf5e5, toString() => "msm8992|LGE|google|Nexus 5X|arm64-v8a|-|polaris|qcom|polaris|bullhead|c3-miui-ota-bd134.bj|QKQ1.190828.002|8.1.0|29|zh|CN|release-keys|Xiaomi/polaris/polaris:10/QKQ1.190828.002/V12.0.2.0.QDGCNXM:user/release-keys|user|bullhead-user 8.1.0 OPM1.171019.011 4448085 release-keys|1|0|") was called from RX@0x1201873b[libmtguard.so]0x1873b
getHWEquipmentInfo:msm8992|LGE|google|Nexus 5X|arm64-v8a|-|polaris|qcom|polaris|bullhead|c3-miui-ota-bd134.bj|QKQ1.190828.002|8.1.0|29|zh|CN|release-keys|Xiaomi/polaris/polaris:10/QKQ1.190828.002/V12.0.2.0.QDGCNXM:user/release-keys|user|bullhead-user 8.1.0 OPM1.171019.011 4448085 release-keys|1|0|

getHWStatus

1
2
3
4
public String getHWStatus(){
String result = SIUACollector.callJniMethodObject(emulator, "getHWStatus()Ljava/lang/String;").getValue().toString();
return result;
}
1
2
3
4
getSysProp:persist.sys.usb.config
[18:21:53 876] WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x12023a11[libmtguard.so]0x23a11, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->getSysProp(Ljava/lang/String;)Ljava/lang/String;
at com.dianping.NBridge.callObjectMethodV(NBridge.java:208)

在获取设备是否连接了 ADB,因此我返回空字符串,接连三个都是类似的属性。

1
2
3
4
5
6
7
8
9
if(name.equals("persist.sys.usb.config")){
return new StringObject(vm, "");
}
if(name.equals("sys.usb.config")){
return new StringObject(vm, "");
}
if(name.equals("sys.usb.state")){
return new StringObject(vm, "");
}

接着报错:

1
2
3
4
[18:23:28 657]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x123, PC=unidbg@0xfffe02c4, LR=RX@0x12026075[libmtguard.so]0x26075, syscall=null
java.lang.UnsupportedOperationException: android/content/pm/PackageManager->hasSystemFeature(Ljava/lang/String;)Z
at com.github.unidbg.linux.android.dvm.AbstractJni.callBooleanMethodV(AbstractJni.java:625)
at com.dianping.NBridge.callBooleanMethodV(NBridge.java:260)

hasSystemFeature用于确认设备是否有特定的功能模块,打印看看参数。

1
2
3
4
case "android/content/pm/PackageManager->hasSystemFeature(Ljava/lang/String;)Z":{
String name = vaList.getObjectArg(0).getValue().toString();
System.out.println("check hasSystemFeature:"+name);
//check hasSystemFeature:android.hardware.sensor.accelerometer

返回 True,然后又检测了不少传感器和硬件支持。

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
case "android/content/pm/PackageManager->hasSystemFeature(Ljava/lang/String;)Z":{
String name = vaList.getObjectArg(0).getValue().toString();
System.out.println("check hasSystemFeature:"+name);
switch (name){
// 检测加速传感器
case "android.hardware.sensor.accelerometer":{
return true;
}
// 检测陀螺仪传感器
case "android.hardware.sensor.gyroscope":{
return true;
}
// wifi
case "android.hardware.wifi":{
return true;
}
// 蓝牙
case "android.hardware.bluetooth":{
return true;
}
// 蓝牙低功耗
case "android.hardware.bluetooth_le":{
return true;
}
// 电话
case "android.hardware.telephony":{
return true;
}
// USB 配件API
case "android.hardware.usb.accessory":{
return true;
}
// gps
case "android.hardware.location.gps":{
return true;
}
// nfc
case "android.hardware.nfc":{
return true;
}
}
}

然后报错:

1
2
3
4
[18:29:26 187]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x12f, PC=unidbg@0xfffe0384, LR=RX@0x12021bb9[libmtguard.so]0x21bb9, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->boolean2Integer(Z)I
at com.github.unidbg.linux.android.dvm.AbstractJni.callIntMethodV(AbstractJni.java:563)
at com.dianping.NBridge.callIntMethodV(NBridge.java:350)

JADX 看一下 boolean2Integer 的实现。将true转成1,false转成0

1
2
3
private int boolean2Integer(boolean z) {
return z ? 1 : 0;
}

Unidbg 没法 getBoolean,连这步都节省了。

1
2
3
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->boolean2Integer(Z)I":{
return vaList.getIntArg(0);
}

继续报错

1
2
3
4
5
getSysProp:gsm.version.baseband
[18:32:44 275] WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x12024d7f[libmtguard.so]0x24d7f, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->getSysProp(Ljava/lang/String;)Ljava/lang/String;
at com.dianping.NBridge.callObjectMethodV(NBridge.java:217)

先adb获取信息,再补环境。这条分支上的补环境逻辑如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if(name.equals("gsm.version.baseband")){
return new StringObject(vm, "4.0.c2.6-00335-0914_2350_3c8fca6,4.0.c2.6-00335-0914_2350_3c8fca6");
}
if(name.equals("gsm.version.ril-impl")){
return new StringObject(vm, "Qualcomm RIL 1.0");
}
if(name.equals("gsm.sim.state")){
return new StringObject(vm, "ABSENT,ABSENT");
}
if(name.equals("gsm.sim.state.2")){
return new StringObject(vm, "");
}
if(name.equals("wifi.interface")){
return new StringObject(vm, "wlan0");
}

然后报错

1
2
3
4
[18:35:51 329]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x117, PC=unidbg@0xfffe0204, LR=RX@0x1202009d[libmtguard.so]0x2009d, syscall=null
java.lang.UnsupportedOperationException: java/io/File->allocObject
at com.github.unidbg.linux.android.dvm.AbstractJni.allocObject(AbstractJni.java:812)
at com.dianping.NBridge.allocObject(NBridge.java:74)

File 同样像之前处理

1
2
3
case "java/io/File->allocObject":{
return dvmClass.newObject(signature);
}

file的构造函数报错,

1
2
3
4
5
[18:40:55 762]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x13b, PC=unidbg@0xfffe0444, LR=RX@0x1201d887[libmtguard.so]0x1d887, syscall=null
java.lang.UnsupportedOperationException: java/io/File-><init>(Ljava/lang/String;)V
at com.github.unidbg.linux.android.dvm.AbstractJni.callVoidMethodV(AbstractJni.java:1007)
at com.dianping.NBridge.callVoidMethodV(NBridge.java:426)

用全局变量处理,因为担心会初始化多个文件,所以命名为 file1

1
2
3
public class NBridge extends AbstractJni implements IOResolver {
...
public File file1;

1
2
3
4
5
case "java/io/File-><init>(Ljava/lang/String;)V":{
String path = vaList.getObjectArg(0).getValue().toString();
System.out.println("filePath:"+path);
}
//filePath:/sys/class/power_supply/battery/voltage_now

这是电池信息相关的文件adb

1
2
bullhead:/ $ cat /sys/class/power_supply/battery/voltage_now
4031653

在 Unidbg 中建立对应的文件,补环境,做重定向。

1
2
3
4
5
6
7
8
case "java/io/File-><init>(Ljava/lang/String;)V":{
String path = vaList.getObjectArg(0).getValue().toString();
System.out.println("filePath:"+path);
if(path.equals("/sys/class/power_supply/battery/voltage_now")){
file1 = new File("unidbg-android/src/test/resources/dianping/files/voltage_now");
return;
}
}

报错

1
2
3
4
[18:45:41 424]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x123, PC=unidbg@0xfffe02c4, LR=RX@0x1201dc27[libmtguard.so]0x1dc27, syscall=null
java.lang.UnsupportedOperationException: java/io/File->exists()Z
at com.github.unidbg.linux.android.dvm.AbstractJni.callBooleanMethodV(AbstractJni.java:625)
at com.dianping.NBridge.callBooleanMethodV(NBridge.java:325)
1
2
3
case "java/io/File->exists()Z":{
return file1.exists();
}

直接返回 true 也行,因为这里判断的就是我们刚处理的 file1 是否存在。

样本开始处理另一个文件

1
2
3
4
filePath:/sys/class/power_supply/battery/temp
[18:46:47 771] WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x13b, PC=unidbg@0xfffe0444, LR=RX@0x12026601[libmtguard.so]0x26601, syscall=null
java.lang.UnsupportedOperationException: java/io/File-><init>(Ljava/lang/String;)V
at com.github.unidbg.linux.android.dvm.AbstractJni.callVoidMethodV(AbstractJni.java:1007)

补环境

1
2
3
public class NBridge extends AbstractJni implements IOResolver {
...
public File file2;

补环境

1
2
3
4
5
6
7
8
9
10
11
12
case "java/io/File-><init>(Ljava/lang/String;)V":{
String path = vaList.getObjectArg(0).getValue().toString();
System.out.println("filePath:"+path);
if(path.equals("/sys/class/power_supply/battery/voltage_now")){
file1 = new File("unidbg-android/src/test/resources/dianping/files/voltage_now");
return;
}
if(path.equals("/sys/class/power_supply/battery/temp")){
file2 = new File("unidbg-android/src/test/resources/dianping/files/voltage_now");
return;
}
}

get

1
getHWEquipmentInfo:-|-|-|1|1|4.0.c2.6-00335-0914_2350_3c8fca6,4.0.c2.6-00335-0914_2350_3c8fca6|Qualcomm RIL 1.0|ABSENT,ABSENT|-|0|0|1|wlan0|1|1|1|1|1|1|1|

getLocationInfo

发起调用

1
2
3
4
public String getLocationInfo(){
String result = SIUACollector.callJniMethodObject(emulator, "getLocationInfo()Ljava/lang/String;").getValue().toString();
return result;
}

报错

1
2
3
4
[19:37:55 228]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x12053cf7[libmtguard.so]0x53cf7, syscall=null
java.lang.UnsupportedOperationException: android/net/wifi/WifiManager->getConnectionInfo()Landroid/net/wifi/WifiInfo;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:249)

补环境,android的库函数,而且返回的是android库函数里的对象,那只能占位处理

1
2
3
case "android/net/wifi/WifiManager->getConnectionInfo()Landroid/net/wifi/WifiInfo;":{
return vm.resolveClass("android/net/wifi/WifiInfo").newObject(signature);
}

报错

1
2
3
4
[19:39:49 737]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x12058323[libmtguard.so]0x58323, syscall=null
java.lang.UnsupportedOperationException: android/net/wifi/WifiInfo->getSSID()Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:249)

搜索getSSID发现是获取wifi名称,查看手机所连接的 WIFI 名,做补环境

1
2
3
case "android/net/wifi/WifiInfo->getSSID()Ljava/lang/String;":{
return new StringObject(vm, "Redmi_7B25");
}

报错

1
2
3
4
[19:40:56 911]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x123, PC=unidbg@0xfffe02c4, LR=RX@0x1205980f[libmtguard.so]0x5980f, syscall=null
java.lang.UnsupportedOperationException: java/lang/String->equalsIgnoreCase(Ljava/lang/String;)Z
at com.github.unidbg.linux.android.dvm.AbstractJni.callBooleanMethodV(AbstractJni.java:625)
at com.dianping.NBridge.callBooleanMethodV(NBridge.java:330)

jdk函数,按照之前所提到的步骤走就行

1
2
3
4
5
6
7
8
9
10
case "java/lang/String->equalsIgnoreCase(Ljava/lang/String;)Z": {
//获取参数
String arg = vaList.getObjectArg(0).getValue().toString();
//获取实例对象
String obj = (String) dvmObject.getValue();
//调用方法
Boolean result = obj.equalsIgnoreCase(arg);
//封装
return result;
}

报错

1
2
3
4
[19:42:13 150]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x12058f2f[libmtguard.so]0x58f2f, syscall=null
java.lang.UnsupportedOperationException: android/telephony/TelephonyManager->getNetworkOperator()Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:253)

和前面处理的 getSimOperator 类似,可以通过 Hook 获取,这里返回空字符串。

1
2
case "android/telephony/TelephonyManager->getNetworkOperator()Ljava/lang/String;":
return new StringObject(vm, "");

结果

1
getLocationInfo:-|-|-|-|0|-|-|-|-|

getPlatformInfo

发起调用

1
2
3
4
public String getPlatformInfo(){
String result = SIUACollector.callJniMethodObject(emulator, "getPlatformInfo()Ljava/lang/String;").getValue().toString();
return result;
}

报错

1
2
3
4
5
JNIEnv->NewGlobalRef(class java/text/SimpleDateFormat) was called from RX@0x12005f5d[libmtguard.so]0x5f5d
[19:44:34 291] WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x117, PC=unidbg@0xfffe0204, LR=RX@0x120479cd[libmtguard.so]0x479cd, syscall=null
java.lang.UnsupportedOperationException: java/text/SimpleDateFormat->allocObject
at com.github.unidbg.linux.android.dvm.AbstractJni.allocObject(AbstractJni.java:812)
at com.dianping.NBridge.allocObject(NBridge.java:79)

同理处理

1
2
3
4
5
case "java/text/SimpleDateFormat->allocObject": {
return dvmClass.newObject(null);
//或者
// return dvmClass.newObject(new SimpleDateFormat());
}

构造函数报错

1
2
3
4
[19:47:06 926]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x13b, PC=unidbg@0xfffe0444, LR=RX@0x12047cf3[libmtguard.so]0x47cf3, syscall=null
java.lang.UnsupportedOperationException: java/text/SimpleDateFormat-><init>(Ljava/lang/String;Ljava/util/Locale;)V
at com.github.unidbg.linux.android.dvm.AbstractJni.callVoidMethodV(AbstractJni.java:1007)
at com.dianping.NBridge.callVoidMethodV(NBridge.java:463)

处理为全局变量,然后补环境。

1
2
3
4
5
6
case "java/text/SimpleDateFormat-><init>(Ljava/lang/String;Ljava/util/Locale;)V":{
String pattern = vaList.getObjectArg(0).getValue().toString();
Locale locale = (Locale) vaList.getObjectArg(1).getValue();
simpleDateFormat = new SimpleDateFormat(pattern, locale);
return;
}

报错

1
2
3
4
[19:49:48 613]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x18d, PC=unidbg@0xfffe0964, LR=RX@0x120476db[libmtguard.so]0x476db, syscall=null
java.lang.UnsupportedOperationException: android/os/Build$VERSION->SDK:Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.getStaticObjectField(AbstractJni.java:103)
at com.dianping.NBridge.getStaticObjectField(NBridge.java:529)

获取sdk版本,跟之前的对上就行,前面我们补过android/os/Build$VERSION->SDK_INT:I,它和此处作用一致,区别仅在于返回数字还是字符串。

1
2
3
case "android/os/Build$VERSION->SDK:Ljava/lang/String;": {
return new StringObject(vm,"29");
}

报错

1
2
3
4
5
[19:50:57 908]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x117, PC=unidbg@0xfffe0204, LR=RX@0x12047a29[libmtguard.so]0x47a29, syscall=null
java.lang.UnsupportedOperationException: java/util/Date->allocObject
at com.github.unidbg.linux.android.dvm.AbstractJni.allocObject(AbstractJni.java:812)
at com.dianping.NBridge.allocObject(NBridge.java:86)
at com.github.unidbg.linux.android.dvm.DvmClass.allocObject(DvmClass.java:74)

补环境

1
2
3
case "java/util/Date->allocObject": {
return dvmClass.newObject(null);
}

报错

1
2
3
4
[19:52:32 360]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x13b, PC=unidbg@0xfffe0444, LR=RX@0x1204aa6b[libmtguard.so]0x4aa6b, syscall=null
java.lang.UnsupportedOperationException: java/util/Date-><init>()V
at com.github.unidbg.linux.android.dvm.AbstractJni.callVoidMethodV(AbstractJni.java:1007)
at com.dianping.NBridge.callVoidMethodV(NBridge.java:477)

这是一个无参构造,那可以回头修改 allocObject 那儿的代码,使得思路更流畅。

1
2
3
case "java/util/Date->allocObject":{
return ProxyDvmObject.createObject(vm, new Date());
}

带参的init必须全局化变量处理,对应的allocObject仅占位即可,对于不带参的init,在allocObject中可以创建实例对象并返回,然后init处仅需return即可。然后下面初始化时就不用做什么了

1
2
3
case "java/util/Date-><init>()V":{
return;
}

报错

1
2
3
4
[19:55:52 884]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x12048993[libmtguard.so]0x48993, syscall=null
java.lang.UnsupportedOperationException: java/text/SimpleDateFormat->format(Ljava/util/Date;)Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:268)

这里处理的就是刚才的 SimpleDateFormat

1
2
3
4
5
6
7
8
9
10
case "java/text/SimpleDateFormat->format(Ljava/util/Date;)Ljava/lang/String;": {
//获取参数
Date date = (Date) vaList.getObjectArg(0).getValue();
//获取实例对象, 实例对象就是刚才全局化的dataFormat
// SimpleDateFormat dateFormat = (SimpleDateFormat) dvmObject.getValue();
//执行方法
String result = dataFormat.format(date);
//封装返回
return new StringObject(vm, result);
}

报错

1
2
3
4
[19:58:01 006]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x12f, PC=unidbg@0xfffe0384, LR=RX@0x1204845f[libmtguard.so]0x4845f, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->androidAppCnt(Landroid/content/Context;)I
at com.github.unidbg.linux.android.dvm.AbstractJni.callIntMethodV(AbstractJni.java:563)
at com.dianping.NBridge.callIntMethodV(NBridge.java:416)

jadx 中看 androidAppCnt 的实现

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
public static int androidAppCnt(Context context) {
int i;
Object[] objArr = {context};
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr, null, changeQuickRedirect2, true, "e2af58080aad5d311267bb7c71e6cce2", RobustBitConfig.DEFAULT_VALUE)) {
return ((Integer) PatchProxy.accessDispatch(objArr, null, changeQuickRedirect2, true, "e2af58080aad5d311267bb7c71e6cce2")).intValue();
}
int i2 = androidAppCntCache;
if (i2 != 0) {
return i2;
}
if (context == null) {
return 0;
}
if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
return androidAppCntCache;
}
try {
lock.lock();
} catch (Throwable th) {
c.a(th);
}
if (androidAppCntCache != 0) {
i = androidAppCntCache;
} else {
PackageManager pkgManager = getPkgManager(context);
if (pkgManager == null) {
i = androidAppCntCache;
} else {
androidAppCntCache = pkgManager.getInstalledApplications(128).size();
lock.unlock();
return androidAppCntCache;
}
}
lock.unlock();
return i;
}

getInstalledApplications用于获取设备上已安装的应用程序列表,整个代码逻辑就是在获取设备安装了多少 App,补环境时返回一个合适的数就行。

1
2
3
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->androidAppCnt(Landroid/content/Context;)I": {
return 12;
}

报错

1
2
3
4
[20:00:11 368]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x12048f23[libmtguard.so]0x48f23, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->appCache(Landroid/content/Context;)Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:279)

appCache函数如下,主要功能是获取当前 Android 应用的缓存大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static synchronized String appCache(Context context) {
synchronized (AppInfoWorker.class) {
Object[] objArr = {context};
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr, null, changeQuickRedirect2, true, "cf8fe1c6518d456cb61aad67de922620", RobustBitConfig.DEFAULT_VALUE)) {
return (String) PatchProxy.accessDispatch(objArr, null, changeQuickRedirect2, true, "cf8fe1c6518d456cb61aad67de922620");
} else if (appCacheSizeCache != null) {
return appCacheSizeCache;
} else if (context == null) {
return "unknown";
} else {
long fileSize = FileUtils.getFileSize(context.getCacheDir(), true) + FileUtils.getFileSize(context.getFilesDir(), true) + FileUtils.getFileSize(new File(context.getFilesDir().getPath() + File.separator + context.getPackageName() + "/shared_prefs"), true);
if (Environment.getExternalStorageState().equals("mounted")) {
fileSize += FileUtils.getFileSize(context.getCacheDir(), true);
}
String str = fileSize + "";
appCacheSizeCache = str;
return str;
}
}
}

Hook 获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function test(){
Java.perform(
function(){

let SIUACollector = Java.use("com.meituan.android.common.mtguard.NBridge$SIUACollector");
SIUACollector["appCache"].implementation = function (context) {
console.log(`SIUACollector.appCache is called: context=${context}`);
let result = this["appCache"](context);
console.log(`SIUACollector.appCache result=${result}`);
return result;
};

// 获取系统上下文
var currentApplication = Java.use('android.app.ActivityThread').currentApplication();
var context = currentApplication.getApplicationContext();
//主动调用
SIUACollector.$new(context).appCache(context);

}
);
}
//SIUACollector.appCache is called: context=com.dianping.v1.NovaHydraApplication@1fc5fc1
//SIUACollector.appCache result=96458538
1
2
3
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->appCache(Landroid/content/Context;)Ljava/lang/String;": {
return new StringObject(vm,"96458538");
}

报错

1
2
3
4
[20:02:37 937]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x1204adf7[libmtguard.so]0x4adf7, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->availableSystem()Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:282)

availableSystem代码逻辑如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static String availableSystem() {
Object[] objArr = new Object[0];
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr, null, changeQuickRedirect2, true, "234b4d16d3aab7d4062d61d2146219de", RobustBitConfig.DEFAULT_VALUE)) {
return (String) PatchProxy.accessDispatch(objArr, null, changeQuickRedirect2, true, "234b4d16d3aab7d4062d61d2146219de");
}
try {
File dataDirectory = Environment.getDataDirectory();
if (readable(dataDirectory)) {
StatFs statFs = new StatFs(dataDirectory.getPath());
return StringUtils.toString(statFs.getAvailableBlocks() * statFs.getBlockSize());
}
return "unknown";
} catch (Throwable th) {
c.a(th);
return "unknown";
}
}

返回值都是”unknown”,所以补环境时也这样返回或者frida

1
2
3
4
5
6
7
8
9
Java.perform(function() {
let DeviceInfoWorker = Java.use("com.meituan.android.common.dfingerprint.collection.workers.DeviceInfoWorker");
DeviceInfoWorker["availableSystem"].implementation = function () {
console.log('availableSystem is called');
let ret = this.availableSystem();
console.log('availableSystem ret value is ' + ret);
return ret;
};
})
1
2
3
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->availableSystem()Ljava/lang/String;": {
return new StringObject(vm,"unknown");
}

报错

1
2
3
4
[20:03:35 834]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x1204a037[libmtguard.so]0x4a037, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->totalMemory()Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:285)

JADX 中查看totalMemory代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static String totalMemory() {
Object[] objArr = new Object[0];
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr, null, changeQuickRedirect2, true, "a240e07aebe4d91708d281e29b9fc189", RobustBitConfig.DEFAULT_VALUE)) {
return (String) PatchProxy.accessDispatch(objArr, null, changeQuickRedirect2, true, "a240e07aebe4d91708d281e29b9fc189");
}
try {
FileReader fileReader = new FileReader("/proc/meminfo");
BufferedReader bufferedReader = new BufferedReader(fileReader);
bufferedReader.close();
fileReader.close();
return StringUtils.toString(Long.valueOf(bufferedReader.readLine().split("\\s+")[1]).intValue() * 1024);
} catch (Throwable th) {
c.a(th);
return "unknown";
}
}

这里是在访问 /proc/meminfo 的首行,我们也可以在 ADB 中复现这一过程。

1
2
3
4
5
6
7
8
9
10
11
bullhead:/ $  cat /proc/meminfo
MemTotal: 1851980 kB
MemFree: 66044 kB
Buffers: 63588 kB
Cached: 746360 kB
SwapCached: 3528 kB
Active: 699976 kB
Inactive: 514316 kB
Active(anon): 342028 kB
Inactive(anon): 162744 kB
Active(file): 357948 kB

乘 1024 是将 KB 转 B 1851980*1024=1896427520

Frida Hook 验证

1
2
3
4
5
6
7
8
9
Java.perform(function() {
let DeviceInfoWorker = Java.use("com.meituan.android.common.dfingerprint.collection.workers.DeviceInfoWorker");
DeviceInfoWorker["totalMemory"].implementation = function () {
console.log('totalMemory is called');
let ret = this.totalMemory();
console.log('totalMemory ret value is ' + ret);
return ret;
};
})

1
2
3
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->totalMemory()Ljava/lang/String;":{
return new StringObject(vm, "1896427520");
}

报错

1
2
3
4
[20:06:56 060]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x1204d603[libmtguard.so]0x4d603, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->getFirstLaunchTime(Landroid/content/Context;)Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:288)

getFirstLaunchTime 语义清晰,即 App 安装后的首次启动时间。

1
2
3
4
5
6
7
8
9
Java.perform(function() {
let SIUACollector = Java.use("com.meituan.android.common.mtguard.NBridge$SIUACollector");
SIUACollector["getFirstLaunchTime"].implementation = function (context) {
console.log('getFirstLaunchTime is called' + ', ' + 'context: ' + context);
let ret = this.getFirstLaunchTime(context);
console.log('getFirstLaunchTime ret value is ' + ret);
return ret;
};
})

结果偷懒直接用参考,毫秒级的时间戳

1
2
getFirstLaunchTime is called, context: com.dianping.v1.NovaHydraApplication@f86c4b2
getFirstLaunchTime ret value is 1665747253983

1
2
3
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->getFirstLaunchTime(Landroid/content/Context;)Ljava/lang/String;":{
return new StringObject(vm, "1665747253983");
}

1
getPlatformInfo:Android|com.dianping.v1|10.41.15|29|-|2024-12-30 20:08:32:032|12|96458538|-|1896427520|1665747253983|-|-|

getUserAction

调用

1
2
3
4
public String getUserAction(){
String result = SIUACollector.callJniMethodObject(emulator, "getUserAction()Ljava/lang/String;").getValue().toString();
return result;
}

报错

1
2
3
[20:09:40 912]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x15b, PC=unidbg@0xfffe0644, LR=RX@0x120424c5[libmtguard.so]0x424c5, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->batteryHelper:Lcom/meituan/android/common/dfingerprint/collection/utils/BatteryHelper;
at com.github.unidbg.linux.android.dvm.AbstractJni.getObjectField(AbstractJni.java:171)

获取batteryHelper实例对象报错,占位处理,这是电池相关的处理

1
2
3
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->batteryHelper:Lcom/meituan/android/common/dfingerprint/collection/utils/BatteryHelper;": {
return vm.resolveClass("com/meituan/android/common/dfingerprint/collection/utils/BatteryHelper").newObject(signature);
}

报错

1
2
3
4
[20:10:54 913]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x123, PC=unidbg@0xfffe02c4, LR=RX@0x120414af[libmtguard.so]0x414af, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->getBatteryInfo()Z
at com.github.unidbg.linux.android.dvm.AbstractJni.callBooleanMethodV(AbstractJni.java:625)
at com.dianping.NBridge.callBooleanMethodV(NBridge.java:378)

在确认是否可以获取电池相关的信息

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
private boolean getBatteryInfo() {
Object[] objArr = new Object[0];
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr, this, changeQuickRedirect2, false, "2d10aac5384d6818e2765f99df76bffe", RobustBitConfig.DEFAULT_VALUE)) {
return ((Boolean) PatchProxy.accessDispatch(objArr, this, changeQuickRedirect2, false, "2d10aac5384d6818e2765f99df76bffe")).booleanValue();
}
Context context = this.mContext;
if (context == null) {
return false;
}
BatteryStatus batteryStatus = BatteryHelper.getInstance(context).getBatteryStatus();
this.level = batteryStatus.batteryLevel;
this.scale = batteryStatus.batteryScale;
int i = batteryStatus.batteryStatus;
int i2 = batteryStatus.batteryPlugged;
if (i == 2 && i2 == 2) {
this.status = 1;
} else {
this.status = 0;
}
if (i2 == 2) {
this.plugged = true;
} else {
this.plugged = false;
}
return true;
}

选择返回true,这意味着获取到了电源状态信息,

1
2
3
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->getBatteryInfo()Z": {
return true;
}

运行报错

1
2
3
4
5
[20:12:26 739]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x160, PC=unidbg@0xfffe0694, LR=RX@0x120458d9[libmtguard.so]0x458d9, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->level:I
at com.github.unidbg.linux.android.dvm.AbstractJni.getIntField(AbstractJni.java:648)
at com.github.unidbg.linux.android.dvm.AbstractJni.getIntField(AbstractJni.java:640)
at com.github.unidbg.linux.android.dvm.DvmField.getIntField(DvmField.java:136)

this.level在上面的 getBatteryInfo 方法里进行了赋值(电池当前的电量级别)。如果追根溯源,逻辑则来自于下面这段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private void recordStatus(Intent intent) {
Object[] objArr = {intent};
ChangeQuickRedirect changeQuickRedirect2 = changeQuickRedirect;
if (PatchProxy.isSupport(objArr, this, changeQuickRedirect2, false, "ca80b285aa559be35b7dd31c0b707222", RobustBitConfig.DEFAULT_VALUE)) {
PatchProxy.accessDispatch(objArr, this, changeQuickRedirect2, false, "ca80b285aa559be35b7dd31c0b707222");
} else if (intent == null) {
} else {
try {
int intExtra = intent.getIntExtra("level", 0);
int intExtra2 = intent.getIntExtra("scale", 100);
int intExtra3 = intent.getIntExtra("status", -1);
int intExtra4 = intent.getIntExtra("plugged", 0);
this.mBatteryStatus.batteryTemperature = intent.getIntExtra("temperature", 0) / 10.0f;
this.mBatteryStatus.batteryLevel = intExtra;
this.mBatteryStatus.batteryScale = intExtra2;
this.mBatteryStatus.batteryStatus = intExtra3;
this.mBatteryStatus.batteryPlugged = intExtra4;
} catch (Exception e) {
c.a(e);
DFPLog.error(e);
}
}
}

level 就是当前电量,scale 是总电量,它并不一定是 100。status 表示是否处于充电状态,plugged 则表示充电模式,Android 有两种充电模式,1 是充电器充电,也叫 AC 充电。2 是 USB 充电,比如插电脑上充电。

样本接下来会依次获取这四个字段,读者可以 Hook SIUACollector 实例,获取这些字段。查看实例中某些字段的值,这个场景用 r0tracer 会很舒服。

1
2
//A. 简易trace单个函数
traceClass("com.meituan.android.common.mtguard.NBridge$SIUACollector")

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
public int getIntField(BaseVM vm, DvmObject<?> dvmObject, String signature) {
switch (signature) {
// level 电量剩余多少
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->level:I":
return 3;
// scale 电量精度
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->scale:I":
return 100;
// status 电池状态,未知、充电、放电、未充电、满电;
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->status:I":
return 0;
}
return super.getIntField(vm, dvmObject, signature);
}

报错

1
2
3
4
[20:15:40 116]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x15c, PC=unidbg@0xfffe0654, LR=RX@0x12043ca3[libmtguard.so]0x43ca3, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->plugged:Z
at com.github.unidbg.linux.android.dvm.AbstractJni.getBooleanField(AbstractJni.java:728)
at com.github.unidbg.linux.android.dvm.AbstractJni.getBooleanField(AbstractJni.java:723)

补环境

1
2
3
4
5
6
7
8
9
@Override
public boolean getBooleanField(BaseVM vm, DvmObject<?> dvmObject, String signature) {
switch (signature){
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->plugged:Z":{
return false;
}
}
return super.getBooleanField(vm, dvmObject, signature);
}

报错

1
2
3
4
[20:16:58 366]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:538) - handleInterrupt intno=2, NR=-452987096, svcNumber=0x120, PC=unidbg@0xfffe0294, LR=RX@0x12042d93[libmtguard.so]0x42d93, syscall=null
java.lang.UnsupportedOperationException: com/meituan/android/common/mtguard/NBridge$SIUACollector->getDataActivity(Landroid/content/Context;)Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:417)
at com.dianping.NBridge.callObjectMethodV(NBridge.java:291)

刚才的 r0tracer 还可以散发余热,日志中搜索 getDataActivity。

1
2
retval: 0 => "0"
*** exiting com.meituan.android.common.mtguard.NBridge$SIUACollector.getDataActivity
1
2
case "com/meituan/android/common/mtguard/NBridge$SIUACollector->getDataActivity(Landroid/content/Context;)Ljava/lang/String;":
return new StringObject(vm, "0");

得到

1
getUserAction:-|3|1|0|0|-|b722c34d-4593-4377-83d1-cfa7c27899f2|0|

元旦快乐!

评论