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 ); new AndroidModule (emulator, vm).register(memory);;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 ); new AndroidModule (emulator, vm).register(memory);; 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 |
发起调用
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 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
时,会创建tun0
或ppp0
节点,所以出现带有明显特征的网络接口名称,就可以认定是使用了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 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 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 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); }
输出:
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; ino_t st_ino; mode_t st_mode; nlink_t st_nlink; uid_t st_uid; gid_t st_gid; dev_t st_rdev; off_t st_size; blksize_t st_blksize; blkcnt_t st_blocks; time_t st_atime; time_t st_mtime; time_t st_ctime; };
这里是 st_blksize * st_blocks,返回 data 文件夹的实际大小,单位是 Bit。 在 Native 代码实现里也经常这个逻辑。最后看fileSize
函数的实现,它将很大的比特值转为更直观和合适的MB
和GB
。
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 (); } ); }
补
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 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 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 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 对应于加速度传感器,下面做常规补环境,
将 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;" :{ 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); }
是属性信息,用 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]
然后是 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);
返回 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 ; } 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 ; } case "android.hardware.usb.accessory" :{ return true ; } case "android.hardware.location.gps" :{ return true ; } 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); }
这是电池信息相关的文件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|-|-|-|-|
发起调用
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 ); }
构造函数报错
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(); 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); } ); }
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 kBInactive (anon) : 162744 kBActive (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 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) { case "com/meituan/android/common/mtguard/NBridge$SIUACollector->level:I" : return 3 ; case "com/meituan/android/common/mtguard/NBridge$SIUACollector->scale:I" : return 100 ; 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 |
元旦快乐!