来看看这个应用怎么做模拟器检测的

某次系统发版后,有用户反馈说一个自己下载安装的三方应用打不开,点了以后没有反应。应用是一个并不多见的视频类应用,像这种小应用出现问题是很常见的。但是测试报告说我们旗下几个机型,只有一个比较古早的机型会有这种问题,一开始接手这个问题的同事也没有看出来问题原因,因此抽出时间瞅瞅。

首先看看现象,应用安装后,在桌面点击打开,点击打开的时候连启动动画、Splash页面在内的任何画面、窗口、Activity都没有出现,说明"崩溃"的很早。但诡异的是,应用的进程还在,没有崩溃日志,并且pid没有变化,能确认不是崩溃后重启,可以断定没有发生崩溃。既然应用没有崩溃,又没有显示出画面,那先看看应用启动到哪一步了:

通过events日志可以确认应用是到了onCreate()的,旧Activity正常退出,新Activity(即问题应用的Activity)正常启动。但是在问题Activity resume完成之前,它就退出了(wm_finish_activity),并且原因是应用自己请求的退出(app-request)。

这就有意思了,看来这个问题要分析一下应用的逻辑。挂上调试器,定位一下应用Activity退出时的堆栈:

看,SplashActivity的onCreate()里面调用了initData(),这个initData()里面调用了finish()。由于该Activity是Splash Activity,退出后没有跳转到Main Activity,这种情况下应用不会显示出任何画面,因此让用户感受到就是点了之后没有反应,点不动,点不开。

initData()是问题的关键,它调用finish()的逻辑决定了应用是否正常启动。逆向一下这个app,定位关键逻辑:

可见前两个if case都可能导致finish(),其中第二个case EmulatorDetector,一看就没问题,毕竟咱是真机,不可能让你检测为模拟器吧?

第一个if case SecurityUtil非常简单:

但是也很诡异,因为它通过查找su来检测设备是否root,我们的发行版本是不会有这个东西的,必然不会让if case判断为true,也就不可能是导致finish()的原因。那只能硬着头皮来看第二个if case:

EmulatorDetector通过检测了模拟器特征,包括模拟器特有的文件、系统属性、包名、sdk等。这部分检测比较常规,在我们的发行版本不可能检测为模拟器。逐一排查后发现一个非主流的、也是导致问题根因的检测:

这个检测的逻辑非常简单,就是查看一下设备有没有光线传感器(type为5)。App的逻辑很简单,模拟器是没有光线传感器的,App的思路是"无光线传感器那就是模拟器"。这里设备如果没有光线传感器(我们这台古早的设备还真的没有)就会return true->if case里面会进入finish()。

实锤一下是这个东西导致的问题。我们的设备因年代古早加上场景特殊,确实没有光线传感器,返回为null理所应当。由于App在这里获取的传感器并没有实际使用,仅仅用于判空,因此我们构造一个空的Sensor返回给应用,是足够安全且有效的(当然,仅用作实锤这个问题)。在getDefaultSensor()这个方法这里返回一个空的Sensor实例:

放入系统验证一下,很快啊,应用就成功启动了。真相终于大白,应用想出来这一招非常得劲,确实能检测到一些模拟器,但是说不定也会错杀呢?

相关推荐
温辉_xh11 分钟前
uiautomator案例
android
工业甲酰苯胺1 小时前
MySQL 主从复制之多线程复制
android·mysql·adb
少说多做3432 小时前
Android 不同情况下使用 runOnUiThread
android·java
Estar.Lee3 小时前
时间操作[计算时间差]免费API接口教程
android·网络·后端·网络协议·tcp/ip
找藉口是失败者的习惯4 小时前
从传统到未来:Android XML布局 与 Jetpack Compose的全面对比
android·xml
Jinkey5 小时前
FlutterBasic - GetBuilder、Obx、GetX<Controller>、GetxController 有啥区别
android·flutter·ios
大白要努力!6 小时前
Android opencv使用Core.hconcat 进行图像拼接
android·opencv
天空中的野鸟7 小时前
Android音频采集
android·音视频
饮长安千年月8 小时前
浅谈就如何解出Reverse-迷宫题之老鼠走迷宫的一些思考
python·网络安全·逆向·ctf
小白也想学C8 小时前
Android 功耗分析(底层篇)
android·功耗