在很多安全机构的检测中,关于模仿器的运行环境一般也会做监听处理,有的大概答应实行但是会提示用户,有的大概直接克制在模仿器上运行我方APP
怎样判定当前 app 是运行在Android真机,还是运行在模仿器? 大概做 Framework 的朋友头脑会更开阔一些,不过现在也可以跟我这门外汉一起来轻微相识下
攻略过程
其实我已经很久没有效过模仿器了,不过可以肯定的是模仿器与真机的本质区别大概率在于运行载体
Android 有非常多的模仿器,我已知的有官方自带的 Genymotion 模仿器,三方平台的夜神模仿器、天天模仿器等,以是想要完全鉴别出设备的运行环境,其实应该是存在肯定题目的,我们只能说尽大概保证肯定的容错率(我有想过很多应用平台提供的云机,但好像大多提供的都是真机,以是此项不在思量范围之内)
基础思考
以下的一些思考紧张结合了 怎样判定是否是模仿器还是真机、全面检测设备是否模仿器、一行代码帮你检测Android模仿器、安卓逆向环境检测–模仿器 等多篇新旧文章
- IMEI 设备辨认码:用于唯一标识移动设备(放弃)
模仿器的 IMEI 可以修改,早期平板大概没有IMEI,但是随着时代发展很多平板设备已拥有了属于本身的IMEI
- MAC地址:物理地址,硬件地址,也称局域网地址,由网络设备制造商生产时烧录在网卡(放弃)
模仿器的MAC地址是固定的几种,但是这些固定的地址随着模仿器类型递增,没有找到合适的,同时mac地址现在可以被模仿…
因为调用结果可以容易被修改,好比直接修改Android的源代码或者借助 Xposed Framework 举行修改(这种场景我虽未到场,但是应该可以参考Java的反射机制)
- 功能验证:初期模仿器功能并不完善,可以采用类似 打电话、发短信 等方式举行功能测试,但是后续随着模仿器升级已补全对应功能 (放弃)
- public boolean isSimulator1() {
- String url = "tel:" + "10086";
- Intent intent = new Intent();
- intent.setData(Uri.parse(url));
- intent.setAction(Intent.ACTION_DIAL);
- // 是否可以处理跳转到拨号的 Intent
- boolean canResolveIntent = intent.resolveActivity(mContext.getPackageManager()) != null;
- return !canResolveIntent;
- }
复制代码
涉及到敏感权限时需要申请权限,根据授权结果容易出现误判,同时影响用户体验
- private static String[]known_numbers = {"15555215554","15555215556",
- "15555215558","15555215560","15555215562","15555215564",
- "15555215566","15555215568","15555215570","15555215572",
- "15555215574","15555215576","15555215578","15555215580",
- "15555215582","15555215584",};
- public static Boolean CheckPhoneNumber(Context context){
- TelephonyManager telephonyManager =(TelephonyManager)context
- .getSystemService(Context.TELEPHONY_SERVICE);
- String phonenumber =telephonyManager.getLine1Number();
- for(String number :known_numbers){
- if(number.equalsIgnoreCase(phonenumber)){
- Log.v("Result:","Find PhoneNumber!");
- return true;
- }
- }
- Log.v("Result:","Not Find PhoneNumber!");
- return false;
- }
复制代码 特有文件检测 - 权限要求
设备IDS检测 - 权限要求
- CPU检测方法:现在的模仿器基本可以做到模仿手机号码,手机品牌,cpu信息等,好比清闲/夜神模仿器读取 ro.product.board 举行了处理,能得到预先设置的cpu信息(放弃)
- public static boolean checkIsNotRealPhone() {
- String cpuInfo = readCpuInfo();
- if ((cpuInfo.contains("intel") || cpuInfo.contains("amd"))) {
- return true;
- }
- return false;
- }
- public static String readCpuInfo() {
- String result = "";
- try {
- String[] args = {"/system/bin/cat", "/proc/cpuinfo"};
- ProcessBuilder cmd = new ProcessBuilder(args);
- Process process = cmd.start();
- StringBuffer sb = new StringBuffer();
- String readLine = "";
- BufferedReader responseReader = new BufferedReader(new InputStreamReader(process.getInputStream(), "utf-8"));
- while ((readLine = responseReader.readLine()) != null) {
- sb.append(readLine);
- }
- responseReader.close();
- result = sb.toString().toLowerCase();
- } catch (IOException ex) {
- }
- return result;
- }
复制代码 进阶思考
在一篇Blog内有看到这样一副图,值得借鉴,因为我末了使用的 easyprotector 框架就做了硬件信息检测
Tip:有爱好的可以参考以下方法扩展 easyprotector 框架内的模仿器检测部分
模仿器框架文件
-
- UNEXPORT void AntiEmulator::check_file() {
- char *(path[]) = {
- "/system/bin/androVM-prop", //检测androidVM
- "/system/bin/microvirt-prop", //检测逍遥模拟器--新版本找不到特征
- "/system/lib/libdroid4x.so", //检测海马模拟器
- "/system/bin/windroyed", //检测文卓爷模拟器
- "/system/bin/nox-prop", //检测夜神模拟器--某些版本找不到特征
- "/system/lib/libnoxspeedup.so",//检测夜神模拟器
- "/system/bin/ttVM-prop", //检测天天模拟器
- "/data/.bluestacks.prop", //检测bluestacks模拟器 51模拟器
- "/system/bin/duosconfig", //检测AMIDuOS模拟器
- "/system/etc/xxzs_prop.sh", //检测星星模拟器
- "/system/etc/mumu-configs/device-prop-configs/mumu.config", //网易MuMu模拟器
- "/system/priv-app/ldAppStore", //雷电模拟器
- "/system/bin/ldinit", //雷电模拟器
- "/system/bin/ldmountsf", //雷电模拟器
- "/system/app/AntStore", //小蚁模拟器
- "/system/app/AntLauncher", //小蚁模拟器
- "vmos.prop", //vmos虚拟机
- "fstab.titan", //光速虚拟机
- "init.titan.rc", //光速虚拟机
- "x8.prop", //x8沙箱和51虚拟机
- "/system/lib/libc_malloc_debug_qemu.so", //AVD QEMU
- "/system/bin/microvirtd",
- "/dev/socket/qemud",
- "/dev/qemu_pipe"};
- for (int i = 0; i < sizeof(path) / sizeof(char*); i++){
- if (Syscall::check_file_or_dir_exists(path[i])){
- LOGI("check_file %s file existing", path[i]);
- // TODO 风险
-
- }
- }
- }
复制代码 easyprotector 框架剖析
关于 easyprotector框架 文档可以参考 一行代码帮你检测Android模仿器(更新至1.1.0) 会更详细一些
我之以是在 github 选这个框架,紧张有几点原因
- 模仿器检测框架有限
- 该框架star高
- 检测方面思量全面(检测渠道、设备型号、硬件制造商、主板名称、基带信息、第三方应用数量、传感器数量、是否支持蓝牙、是否支持相机、是否支持闪光灯等)
- 本领之内可以适当扩展检测项
- 为了避免误判设备类型,内部参加条件判定,只有满意3项模仿器特性才会判定为模仿器
直接通过框架源码,检察下模仿器检测的实行过程
- 调用了 EasyProtectorLib.checkIsRunningInEmulator 方法
- 在 EasyProtectorLib 中找到了 checkIsRunningInEmulator 实际调用了 EmulatorCheckUtil
- 检察 EmulatorCheckUtil - readSysProperty 源码即可
源码中 ro.build、ro.product、gsm.version 含义,做Framework朋友大概比力相识,紧张用于检测一些体系级信息
之前有提到功能扩展和延伸,各人可自行在该类源码中举行扩展,不过最好另起方法,有自信的话改原方法也行
剥离 easyprotector框架 模仿器检测功能
因为 easyprotector框架 中涉及的功能比力多,我习惯性只抽出了我所需要的部分
CommandUtil (公共、基础)
简单来看紧张是通过反射机制获取一些体系的公共资源信息
EmulatorCheckUtil (核心)
- import android.content.Context;
- import android.content.pm.PackageManager;
- import android.hardware.Sensor;
- import android.hardware.SensorManager;
- import android.text.TextUtils;
- import static android.content.Context.SENSOR_SERVICE;
- import static cn.com.huaan.fund.acts.base.safe.CheckResult.RESULT_EMULATOR;
- import static cn.com.huaan.fund.acts.base.safe.CheckResult.RESULT_MAYBE_EMULATOR;
- import static cn.com.huaan.fund.acts.base.safe.CheckResult.RESULT_UNKNOWN;
- /**
- * Project Name:EasyProtector
- * Package Name:com.lahm.library
- * Created by lahm on 2018/6/8 15:01 .
- */
- public class EmulatorCheckUtil {
- private EmulatorCheckUtil() {
- }
- private static class SingletonHolder {
- private static final EmulatorCheckUtil INSTANCE = new EmulatorCheckUtil();
- }
- public static final EmulatorCheckUtil getSingleInstance() {
- return SingletonHolder.INSTANCE;
- }
- public boolean readSysProperty(Context context, EmulatorCheckCallback callback) {
- if (context == null)
- throw new IllegalArgumentException("context must not be null");
- int suspectCount = 0;
- //检测硬件名称
- CheckResult hardwareResult = checkFeaturesByHardware();
- switch (hardwareResult.result) {
- case RESULT_MAYBE_EMULATOR:
- ++suspectCount;
- break;
- case RESULT_EMULATOR:
- if (callback != null) callback.findEmulator("hardware = " + hardwareResult.value);
- return true;
- }
- //检测渠道
- CheckResult flavorResult = checkFeaturesByFlavor();
- switch (flavorResult.result) {
- case RESULT_MAYBE_EMULATOR:
- ++suspectCount;
- break;
- case RESULT_EMULATOR:
- if (callback != null) callback.findEmulator("flavor = " + flavorResult.value);
- return true;
- }
- //检测设备型号
- CheckResult modelResult = checkFeaturesByModel();
- switch (modelResult.result) {
- case RESULT_MAYBE_EMULATOR:
- ++suspectCount;
- break;
- case RESULT_EMULATOR:
- if (callback != null) callback.findEmulator("model = " + modelResult.value);
- return true;
- }
- //检测硬件制造商
- CheckResult manufacturerResult = checkFeaturesByManufacturer();
- switch (manufacturerResult.result) {
- case RESULT_MAYBE_EMULATOR:
- ++suspectCount;
- break;
- case RESULT_EMULATOR:
- if (callback != null)
- callback.findEmulator("manufacturer = " + manufacturerResult.value);
- return true;
- }
- //检测主板名称
- CheckResult boardResult = checkFeaturesByBoard();
- switch (boardResult.result) {
- case RESULT_MAYBE_EMULATOR:
- ++suspectCount;
- break;
- case RESULT_EMULATOR:
- if (callback != null) callback.findEmulator("board = " + boardResult.value);
- return true;
- }
- //检测主板平台
- CheckResult platformResult = checkFeaturesByPlatform();
- switch (platformResult.result) {
- case RESULT_MAYBE_EMULATOR:
- ++suspectCount;
- break;
- case RESULT_EMULATOR:
- if (callback != null) callback.findEmulator("platform = " + platformResult.value);
- return true;
- }
- //检测基带信息
- CheckResult baseBandResult = checkFeaturesByBaseBand();
- switch (baseBandResult.result) {
- case RESULT_MAYBE_EMULATOR:
- suspectCount += 2;//模拟器基带信息为null的情况概率相当大
- break;
- case RESULT_EMULATOR:
- if (callback != null) callback.findEmulator("baseBand = " + baseBandResult.value);
- return true;
- }
- //检测传感器数量
- int sensorNumber = getSensorNumber(context);
- if (sensorNumber <= 7) ++suspectCount;
- //检测已安装第三方应用数量
- int userAppNumber = getUserAppNumber();
- if (userAppNumber <= 5) ++suspectCount;
- //检测是否支持闪光灯
- boolean supportCameraFlash = supportCameraFlash(context);
- if (!supportCameraFlash) ++suspectCount;
- //检测是否支持相机
- boolean supportCamera = supportCamera(context);
- if (!supportCamera) ++suspectCount;
- //检测是否支持蓝牙
- boolean supportBluetooth = supportBluetooth(context);
- if (!supportBluetooth) ++suspectCount;
- //检测光线传感器
- boolean hasLightSensor = hasLightSensor(context);
- if (!hasLightSensor) ++suspectCount;
- //检测进程组信息
- CheckResult cgroupResult = checkFeaturesByCgroup();
- if (cgroupResult.result == RESULT_MAYBE_EMULATOR) ++suspectCount;
- if (callback != null) {
- StringBuffer stringBuffer = new StringBuffer("Test start")
- .append("\r\n").append("hardware = ").append(hardwareResult.value)
- .append("\r\n").append("flavor = ").append(flavorResult.value)
- .append("\r\n").append("model = ").append(modelResult.value)
- .append("\r\n").append("manufacturer = ").append(manufacturerResult.value)
- .append("\r\n").append("board = ").append(boardResult.value)
- .append("\r\n").append("platform = ").append(platformResult.value)
- .append("\r\n").append("baseBand = ").append(baseBandResult.value)
- .append("\r\n").append("sensorNumber = ").append(sensorNumber)
- .append("\r\n").append("userAppNumber = ").append(userAppNumber)
- .append("\r\n").append("supportCamera = ").append(supportCamera)
- .append("\r\n").append("supportCameraFlash = ").append(supportCameraFlash)
- .append("\r\n").append("supportBluetooth = ").append(supportBluetooth)
- .append("\r\n").append("hasLightSensor = ").append(hasLightSensor)
- .append("\r\n").append("cgroupResult = ").append(cgroupResult.value)
- .append("\r\n").append("suspectCount = ").append(suspectCount);
- callback.findEmulator(stringBuffer.toString());
- }
- //嫌疑值大于3,认为是模拟器
- return suspectCount > 3;
- }
- private int getUserAppNum(String userApps) {
- if (TextUtils.isEmpty(userApps)) return 0;
- String[] result = userApps.split("package:");
- return result.length;
- }
- private String getProperty(String propName) {
- String property = CommandUtil.getSingleInstance().getProperty(propName);
- return TextUtils.isEmpty(property) ? null : property;
- }
- /**
- * 特征参数-硬件名称
- *
- * @return 0表示可能是模拟器,1表示模拟器,2表示可能是真机
- */
- private CheckResult checkFeaturesByHardware() {
- String hardware = getProperty("ro.hardware");
- if (null == hardware) return new CheckResult(RESULT_MAYBE_EMULATOR, null);
- int result;
- String tempValue = hardware.toLowerCase();
- switch (tempValue) {
- case "ttvm"://天天模拟器
- case "nox"://夜神模拟器
- case "cancro"://网易MUMU模拟器
- case "intel"://逍遥模拟器
- case "vbox":
- case "vbox86"://腾讯手游助手
- case "android_x86"://雷电模拟器
- result = RESULT_EMULATOR;
- break;
- default:
- result = RESULT_UNKNOWN;
- break;
- }
- return new CheckResult(result, hardware);
- }
- /**
- * 特征参数-渠道
- *
- * @return 0表示可能是模拟器,1表示模拟器,2表示可能是真机
- */
- private CheckResult checkFeaturesByFlavor() {
- String flavor = getProperty("ro.build.flavor");
- if (null == flavor) return new CheckResult(RESULT_MAYBE_EMULATOR, null);
- int result;
- String tempValue = flavor.toLowerCase();
- if (tempValue.contains("vbox")) result = RESULT_EMULATOR;
- else if (tempValue.contains("sdk_gphone")) result = RESULT_EMULATOR;
- else result = RESULT_UNKNOWN;
- return new CheckResult(result, flavor);
- }
- /**
- * 特征参数-设备型号
- *
- * @return 0表示可能是模拟器,1表示模拟器,2表示可能是真机
- */
- private CheckResult checkFeaturesByModel() {
- String model = getProperty("ro.product.model");
- if (null == model) return new CheckResult(RESULT_MAYBE_EMULATOR, null);
- int result;
- String tempValue = model.toLowerCase();
- if (tempValue.contains("google_sdk")) result = RESULT_EMULATOR;
- else if (tempValue.contains("emulator")) result = RESULT_EMULATOR;
- else if (tempValue.contains("android sdk built for x86")) result = RESULT_EMULATOR;
- else result = RESULT_UNKNOWN;
- return new CheckResult(result, model);
- }
- /**
- * 特征参数-硬件制造商
- *
- * @return 0表示可能是模拟器,1表示模拟器,2表示可能是真机
- */
- private CheckResult checkFeaturesByManufacturer() {
- String manufacturer = getProperty("ro.product.manufacturer");
- if (null == manufacturer) return new CheckResult(RESULT_MAYBE_EMULATOR, null);
- int result;
- String tempValue = manufacturer.toLowerCase();
- if (tempValue.contains("genymotion")) result = RESULT_EMULATOR;
- else if (tempValue.contains("netease")) result = RESULT_EMULATOR;//网易MUMU模拟器
- else result = RESULT_UNKNOWN;
- return new CheckResult(result, manufacturer);
- }
- /**
- * 特征参数-主板名称
- *
- * @return 0表示可能是模拟器,1表示模拟器,2表示可能是真机
- */
- private CheckResult checkFeaturesByBoard() {
- String board = getProperty("ro.product.board");
- if (null == board) return new CheckResult(RESULT_MAYBE_EMULATOR, null);
- int result;
- String tempValue = board.toLowerCase();
- if (tempValue.contains("android")) result = RESULT_EMULATOR;
- else if (tempValue.contains("goldfish")) result = RESULT_EMULATOR;
- else result = RESULT_UNKNOWN;
- return new CheckResult(result, board);
- }
- /**
- * 特征参数-主板平台
- *
- * @return 0表示可能是模拟器,1表示模拟器,2表示可能是真机
- */
- private CheckResult checkFeaturesByPlatform() {
- String platform = getProperty("ro.board.platform");
- if (null == platform) return new CheckResult(RESULT_MAYBE_EMULATOR, null);
- int result;
- String tempValue = platform.toLowerCase();
- if (tempValue.contains("android")) result = RESULT_EMULATOR;
- else result = RESULT_UNKNOWN;
- return new CheckResult(result, platform);
- }
- /**
- * 特征参数-基带信息
- *
- * @return 0表示可能是模拟器,1表示模拟器,2表示可能是真机
- */
- private CheckResult checkFeaturesByBaseBand() {
- String baseBandVersion = getProperty("gsm.version.baseband");
- if (null == baseBandVersion) return new CheckResult(RESULT_MAYBE_EMULATOR, null);
- int result;
- if (baseBandVersion.contains("1.0.0.0")) result = RESULT_EMULATOR;
- else result = RESULT_UNKNOWN;
- return new CheckResult(result, baseBandVersion);
- }
- /**
- * 获取传感器数量
- */
- private int getSensorNumber(Context context) {
- SensorManager sm = (SensorManager) context.getSystemService(SENSOR_SERVICE);
- return sm.getSensorList(Sensor.TYPE_ALL).size();
- }
- /**
- * 获取已安装第三方应用数量
- */
- private int getUserAppNumber() {
- String userApps = CommandUtil.getSingleInstance().exec("pm list package -3");
- return getUserAppNum(userApps);
- }
- /**
- * 是否支持相机
- */
- private boolean supportCamera(Context context) {
- return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);
- }
- /**
- * 是否支持闪光灯
- */
- private boolean supportCameraFlash(Context context) {
- return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
- }
- /**
- * 是否支持蓝牙
- */
- private boolean supportBluetooth(Context context) {
- return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
- }
- /**
- * 判断是否存在光传感器来判断是否为模拟器
- * 部分真机也不存在温度和压力传感器。其余传感器模拟器也存在。
- *
- * @return false为模拟器
- */
- private boolean hasLightSensor(Context context) {
- SensorManager sensorManager = (SensorManager) context.getSystemService(SENSOR_SERVICE);
- Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); //光线传感器
- if (null == sensor) return false;
- else return true;
- }
- /**
- * 特征参数-进程组信息
- */
- private CheckResult checkFeaturesByCgroup() {
- String filter = CommandUtil.getSingleInstance().exec("cat /proc/self/cgroup");
- if (null == filter) return new CheckResult(RESULT_MAYBE_EMULATOR, null);
- return new CheckResult(RESULT_UNKNOWN, filter);
- }
- }
复制代码 EmulatorCheckCallback (配置)
回调监听,可以获取到具体检测结果
- public interface EmulatorCheckCallback {
- void findEmulator(String emulatorInfo);
- }
复制代码 CheckResult(配置)
对检测结果举行种别分别,方便管理
- public class CheckResult {
- public static final int RESULT_MAYBE_EMULATOR = 0;//可能是模拟器
- public static final int RESULT_EMULATOR = 1;//模拟器
- public static final int RESULT_UNKNOWN = 2;//可能是真机
- public int result;
- public String value;
- public CheckResult(int result, String value) {
- this.result = result;
- this.value = value;
- }
- }
复制代码 调用方式
- val readSysProperty = EmulatorCheckUtil.getSingleInstance().readSysProperty(context, null)
- if (readSysProperty) {
- //根据需要进行风险提示等相关业务
- ToastUtils.showToast("您当前可能运行在模拟器设备,请谨防安全风险!")
- }
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |