一行代码帮你检测Android模拟器优劣
码个蛋(codeegg)第 797 次推文
作者:普通的程序员
博客:https://www.jianshu.com/p/434b3075b5dd
文章目录
简介
初代常规手段
进阶手段
改良手段和新思路
最终方案
测试结果&demo地址
1
简介
最近有业务上的要求,要求app在本地进行诸如软件多开、hook框架、模拟器等安全检测,防止作弊行为。
防作弊一直是老生常谈的问题,而模拟器的检测往往是防作弊中的重要一环,但在查找资料的过程中发现,网上的模拟器检测方案已经有些过时了,只能自己再跟进学习,本文对这次学习内容进行总结。
2
初代常规手段
早期模拟器没那么多套路,特征值非常明显,某些值甚至是一长串的0,检测起来很方便,常规的方案如
检查手机IMEI等一系列编号
TelephonyManager tm = (TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE); String deviceid = tm.getDeviceId();//获取IMEI号 String te1 = tm.getLine1Number();//获取本机号码 String imei = tm.getSimSerialNumber();//获得SIM卡的序号 String imsi = tm.getSubscriberId();//得到用户Id
读取手机品牌信息
android.os.Build.BRAND,
android.os.Build.MANUFACTURER,
android.os.Build.MODEL...
检查cpu信息
String value = null;Object roSecureObj;try {roSecureObj = Class.forName("android.os.SystemProperties").getMethod("get", String.class).invoke(null, "ro.product.board");if (roSecureObj != null) value = (String) roSecureObj;} catch (Exception e) {value = null;} finally {return value;}
优点:通过检查真机上最直白的几个特征,就可以完成模拟器的检测
缺点:
现在的模拟器基本可以做到模拟手机号码,手机品牌,cpu信息等,比如逍遥/夜神模拟器读取ro.product.board进行了处理,能得到预先设置的cpu信息;
真机的手机号码也不一定就能拿到(比如电信卡);
拿手机号码这个需要权限,用户不一定喜欢。
所以决定弃用以上方案。
3
进阶手段
再思考真机上的特征,进一步我们有通过检查硬件信息的思路,形如蓝牙,语音输入设备,wlan,相机等
检查mac地址
Enumeration networkInterfaces;String str = null;networkInterfaces = NetworkInterface.getNetworkInterfaces();while (networkInterfaces.hasMoreElements()) {NetworkInterface networkInterface = (NetworkInterface) networkInterfaces.nextElement();if (networkInterface != null) {byte[] hardwareAddress;byte[] bArr = new byte[0];hardwareAddress = networkInterface.getHardwareAddress();if (!(hardwareAddress == null || hardwareAddress.length == 0)) {String str2;StringBuilder stringBuilder = new StringBuilder();...//方法太长}}}
}
检查电池温度,轮询检查电量,充电状态
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);Intent batteryStatus = context.registerReceiver(null, filter);if (batteryStatus == null) return false;int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);return chargePlug == BatteryManager.BATTERY_PLUGGED_USB;//检测usb充电
优点:比初代方案有深入;
缺点:
1.mac地址现在可以被模拟,且获取mac地址的代码有点长(M以下版本还要传context)写起来不不优雅;
2.通过电池信息来准确检测,需要一定的时间间隔,属于非实时方案;
3.蓝牙和相机需要添加相应权限。
所以不推荐集成。
4
改进方案和新的研究
在研究各个模拟器的过程中,尤其是在研究build.prop文件时,发现以下(但不限于)问题
1.基带信息几乎没有;
2.处理器信息ro.product.board和ro.board.platform有冲突或者不一致;
3.部分模拟器在读控制组信息时读取不到;
4.连上wifi但会出现 Link encap:UNSPEC未指定网卡类型的情况。
借着问题依次进行解析。
基带信息
基带是手机上的一块电路板,刷基带实际上就是刷这个电路的控制软件。
我是这样去理解模拟器没有基带信息的情况"因为模拟器没有真实的电路板(基带电路),所以没法刷基带软件进去,所以没办法得到基带信息",不知道这样理解对不对,欢迎拍砖。
当然了,部分真机在刷机失败的时候也会出现丢失基带的情况,这部分机器我们不多讨论。
try {roSecureObj = Class.forName("android.os.SystemProperties").getMethod("get", String.class).invoke(null, "gsm.version.baseband");if (roSecureObj != null) value = (String) roSecureObj;} catch (Exception e) {value = null;}
处理器信息
最简单的方法就是直接拿android.os.Build.BOARD,实际上也是去读取ro.product.board值,
这个值代表cpu型号,比如msm8998是骁龙835,hi3650是麒麟950。
这个值真机几乎不为空,AS模拟器会有如gphone的特征值,部分模拟器上是可以随时变更的(因为拿模拟器来玩高帧率模式的手游)。
可是还有一个ro.board.platform值,这个值代表主板平台,极少的模拟器会去更改这个值,甚至有的模拟器没有这个值,一般来说真机的两值相等。
当然真机也有例外,测试机一加5T两者都是msm8998,而华为P9 board值EVA-AL10,platform值hi3650。
两者不一致
根据处理器信息做一个检测指标。
String productBoard = CommandUtil.getSingleInstance().getProperty("ro.product.board");
if (productBoard == null | "".equals(productBoard)) ++suspectCount;String boardPlatform = CommandUtil.getSingleInstance().getProperty("ro.board.platform");
if (boardPlatform == null | "".equals(boardPlatform)) ++suspectCount;if (productBoard != null && boardPlatform != null && !productBoard.equals(boardPlatform))++suspectCount;
渠道信息
渠道信息是ro.build.flavor值,在有限的真机和模拟机器的测试情况下,有以下推测
『真机基本上都有这个值,部分模拟器没有这个值,基于vbox的模拟器上有特征值:vbox』
vbox特征值,平台信息空
根据渠道信息做一个检测指标
String buildFlavor = CommandUtil.getSingleInstance().getProperty("ro.build.flavor");
if (buildFlavor == null | "".equals(buildFlavor) | (buildFlavor != null && buildFlavor.contains("vbox")))++suspectCount;
进程组信息
利用读取maps文件检测软件多开的时候,在部分模拟器上却遇到了runtimeException异常。
原因是读取/proc/self/cgroup进程组信息的时候,部分模拟器没有这个值,因为个人水平有限,暂时不知道原因是什么,不过却刚好拿这个做检测方案。
关键代码
process = Runtime.getRuntime().exec("sh");bufferedOutputStream = new BufferedOutputStream(process.getOutputStream());bufferedInputStream = new BufferedInputStream(process.getInputStream());bufferedOutputStream.write("cat /proc/self/cgroup");bufferedOutputStream.write('\n');bufferedOutputStream.flush();
wlan驱动未指定异常
Android离不开unix,所以尝试了adb shell 运行指令。运行ifconfig时,发现在连接wifi的情况下,AS模拟器显示 『wlan0 Link encap:UNSPEC』 未指定网卡类型,而真机情况下是『wlan0 Link encap:Ethernet』以太网。
AS模拟器的wlan情况
不过接着测试非wifi情况下,该值都拿不到,所以不推荐使用。
5
最终方案
结合以上研究,得出一个嫌疑指数,综合判断是否运行在模拟器中。
EasyProtectorLib.checkIsRunningInEmulator()的代码实现如下
public boolean readSysProperty() {int suspectCount = 0;//读基带信息String basebandVersion = CommandUtil.getSingleInstance().getProperty("gsm.version.baseband");if (basebandVersion == null | "".equals(basebandVersion)) ++suspectCount;//读渠道信息,针对一些基于vbox的模拟器String buildFlavor = CommandUtil.getSingleInstance().getProperty("ro.build.flavor");if (buildFlavor == null | "".equals(buildFlavor) | (buildFlavor != null && buildFlavor.contains("vbox")))++suspectCount;//读处理器信息,这里经常会被处理String productBoard = CommandUtil.getSingleInstance().getProperty("ro.product.board");if (productBoard == null | "".equals(productBoard)) ++suspectCount;//读处理器平台,这里不常会处理String boardPlatform = CommandUtil.getSingleInstance().getProperty("ro.board.platform");if (boardPlatform == null | "".equals(boardPlatform)) ++suspectCount;//高通的cpu两者信息一般是一致的if (productBoard != null && boardPlatform != null && !productBoard.equals(boardPlatform))++suspectCount;//一些模拟器读取不到进程租信息String filter = CommandUtil.getSingleInstance().exec("cat /proc/self/cgroup");if (filter == null || filter.length() == 0) ++suspectCount;return suspectCount > 2;}
以下是测试情况*
*O代表该方案检测为模拟器,X代表检测不到
*Xamarin/Manymo因为网络原因暂未进行测试
6
demo地址
本文方案已经集成到EasyProtectorLib
https://github.com/lamster2018/EasyProtector
文档:https://www.jianshu.com/p/c37b1bdb4757
模拟器的检测秉持一句话:抓取特征值与真机比较。
相关文章:
为什么别人的成长叫蓝图,你的成长始终是流浪!
全网最详细的Activity启动流程(Android10)
Activity 传数据有什么难点?
今日问题:
双十一大家剁了多少手呢?
专属升级社区:《这件事情,我终于想明白了》
一行代码帮你检测Android模拟器优劣相关推荐
- 检测Android模拟器的方法和代码实现
专自:https://bbs.pediy.com/thread-225717.htm 刚刚看了一些关于Detect Android Emulator的开源项目/文章/论文, 我看的这些其实都是13年1 ...
- 利用任务调度特性检测Android模拟器
Author:leonnewton 0x00 前言 DEXLabs发表过题为<Detecting Android Sandboxes>的博客,文章提出了一个检测Android沙箱的方法,并 ...
- 利用cache特性检测Android模拟器
Author:leonnewton 0x00 序 目前对Android模拟器的检测,主要是从特定的系统值来进行区分的.例如,getDeviceId().getLine1Number()这类函数,还有a ...
- 一行代码帮你彻底解决pip下载速度慢的问题,更改pip源至国内镜像(无须新建文件夹), 享受飞一般的速度
目录 1.pip安装慢的原因 2.一行代码更改pip源至国内镜像 3.一些主流的镜像网站 1.pip安装慢的原因 使用Python的人必然会用到一个工具就是pip, 它帮助我们安装各种第三方库, 用起 ...
- app检测android模拟器代码
注:isTrulyDevice()方法返回 false则设备为模拟器. /** * 真实设备检测 * * @return true:真机,false:模拟器 */ public final stati ...
- Python 代码转 Latex 公式,这个开源库用一行代码帮你搞定
转自 | 机器之心 数学是数据科学和机器学习的重要基础,数学运算的结果对于机器学习项目而言是至关重要的.在编写代码时,我们常常需要定义数学公式的计算形式.像 S=r^2 这样简单的数学公式,大概不会出 ...
- Python代码转Latex公式,这个开源库用一行代码帮你搞定
视学算法报道 编辑:小舟 转载自公众号:机器之心 你的代码中有数学公式吗? 数学是数据科学和机器学习的重要基础,数学运算的结果对于机器学习项目而言是至关重要的.在编写代码时,我们常常需要定义数学公式的 ...
- python识别latex公式_Python代码转Latex公式,这个开源库用一行代码帮你搞定
来源:机器之心 数学是数据科学和机器学习的重要基础,数学运算的结果对于机器学习项目而言是至关重要的.在编写代码时,我们常常需要定义数学公式的计算形式.像 S=r^2 这样简单的数学公式,大概不会出现拼 ...
- python一行代码实现白噪声检测
print(u'The result of white noise detection:', acorr_ljungbox(diff1, lags=1)) 会打印出来两个值,如果第二个值小于0.05, ...
最新文章
- NIO中那些奇怪的Buffer
- 从单体式架构迁移到微服务架构,看这篇文章就行了!
- 凸多边形面积_C++计算任意多边形的面积
- 闲鱼直播三周内实现点击率翻倍,我们是这么做到的...
- 解决cv2.error: OpenCV(xxx) C:\projects\opencv-python\opencv_contrib\modules\xfeatures2d\src\sift.cpp问题
- java+editor类_GLIPS Graffiti editor
- 批量添加手机联系人 | csv/excel转vcf
- windows11安装MAVEN
- 仿CAD画椭圆弧步骤思路(附加代码)
- 网易面试题,小易沉迷游戏
- 《2021政府工作报告》词云图一览
- 数据结构 基于字符串模式匹配算法的病毒感染检测问题
- c语言编程 BMI判断健康,BMI指数真的可以反映人的健康状态吗?看完你就懂了
- 神州信息与北京市地方金融监督管理局、房山区人民政府签署战略合作
- Java基础强化训练——开发工具及输出语句训练
- gentoo的USE参数详细说明
- 《操作系统》学习笔记|6.6外存空间管理
- MySQL数据库基本管理
- r语言拟合MA模型,及时序图,自相关图,偏自相关图
- 欸,自娱自乐的学习必然是缓慢的
热门文章
- zabbix监控交换机设备
- CDH6.3.2 端口使用整理
- 达人评测 i9 12900H和i5 12500h选哪个
- 80后、90后扎心图鉴
- python画圆形螺旋线-Python turtle 绘制彩色螺旋线
- Java之~ 等额本息,等额本金,组合贷
- 深度体验学习国产API工具Eolink
- 【MAPBOX基础功能】32、实现mapbox的测距功能
- 字节跳动Android实习面试凉凉经,震撼来袭免费下载!
- 远程桌面提示无法连接远程计算机,win7系统远程连接提示“此计算机无法连接到远程计算机”的解决方法...