Android模拟器最新检测方法详解

 更新时间:2024年02月11日 11:46:09   作者:移动安全星球  
这篇文章主要介绍了Android模拟器的检测方法,在Android开发过程中,防作弊一直是老生常谈的问题,而模拟器的检测往往是防作弊中的重要一环,接下来我们来讲解有关于模拟器的检测方法,需要的朋友可以参考下
(福利推荐:【腾讯云】服务器最新限时优惠活动,云服务器1核2G仅99元/年、2核4G仅768元/3年,立即抢购>>>:9i0i.cn/qcloud

(福利推荐:你还在原价购买阿里云服务器?现在阿里云0.8折限时抢购活动来啦!4核8G企业云服务器仅2998元/3年,立即抢购>>>:9i0i.cn/aliyun

最近看到某客户端有一个检测模拟器的方法,我正常手机结果被判断是模拟器了,很好奇,于是找了一下原因。

普遍检测方法

public boolean isEmulator() {
    String url = "tel:" + "123456";
    Intent intent = new Intent();
    intent.setData(Uri.parse(url));
    intent.setAction(Intent.ACTION_DIAL);
    // 是否可以处理跳转到拨号的 Intent
    boolean canResolveIntent = intent.resolveActivity(mContext.getPackageManager()) != null;
    return Build.FINGERPRINT.startsWith("generic")
        || Build.FINGERPRINT.toLowerCase().contains("vbox")
        || Build.FINGERPRINT.toLowerCase().contains("test-keys")
        || Build.MODEL.contains("google_sdk")
        || Build.MODEL.contains("Emulator")
        || Build.SERIAL.equalsIgnoreCase("unknown")
        || Build.SERIAL.equalsIgnoreCase("android")
        || Build.MODEL.contains("Android SDK built for x86")
        || Build.MANUFACTURER.contains("Genymotion")
        || (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
        || "google_sdk".equals(Build.PRODUCT)
        || ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
            .getNetworkOperatorName().toLowerCase().equals("android")
        || !canResolverIntent;
}

这个代码检测模拟器有两个问题:

1、拨号检测,Android10.0及以上均为false

2、Build.SERIAL,Android8.0以上均为unknown

这导致8.0以上系统均会被误判

推荐模拟器检测方法

设备信息检测

private static final String[] known_numbers = {"15555215554", "15555215556", "15555215558", "15555215560", "15555215562", "15555215564", "15555215566", "15555215568", "15555215570", "15555215572", "15555215574", "15555215576", "15555215578", "15555215580", "15555215582", "15555215584",};
private boolean detectEmulator() {
        if (Build.FINGERPRINT.startsWith("generic") || Build.FINGERPRINT.startsWith("unknown")
                || Build.MODEL.contains("google_sdk") || Build.MODEL.contains("Emulator")
                || Build.MODEL.contains("Android SDK built for x86") || Build.MANUFACTURER.contains("Genymotion")
                || (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
                || "google_sdk".equals(Build.PRODUCT)) {
            return true;
        }
        if (Build.PRODUCT.equals("sdk") || Build.PRODUCT.equals("sdk_x86")
                || Build.PRODUCT.equals("vbox86p") || Build.PRODUCT.equals("emulator")) {
            return true;
        }
        if (Build.BOARD == null) {
            return true;
        }
        if (Build.BOARD.equals("unknown")
                || Build.BOARD.contains("android")
                || Build.BOARD.contains("droid")) {
            return true;
        }
        if (Build.DEVICE == null) {
            return true;
        }
        if (Build.DEVICE.equals("unknown")
                || Build.DEVICE.contains("android")
                || Build.DEVICE.contains("droid")) {
            return true;
        }
        if (Build.HARDWARE == null) {
            return true;
        }
        if (Build.HARDWARE.equals("goldfish")
                || Build.HARDWARE.equals("ranchu")
                || Build.HARDWARE.contains("ranchu")) {
            return true;
        }
        if (Build.BRAND == null) {
            return true;
        }
        if (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")) {
            return true;
        }
        if (Build.MANUFACTURER.equals("unknown")) {
            return true;
        }
        if (Build.MANUFACTURER.equals("Genymotion")) {
            return true;
        }
        if ((Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
                || "google_sdk".equals(Build.PRODUCT)) {
            return true;
        }
        if (Build.PRODUCT == null) {
            return true;
        }
        if (Build.PRODUCT.equals("sdk")
                || Build.PRODUCT.equals("sdk_x86")
                || Build.PRODUCT.equals("vbox86p")
                || Build.PRODUCT.equals("emulator")) {
            return true;
        }
        if (Build.HARDWARE.equals("goldfish")
                || Build.HARDWARE.equals("ranchu")) {
            return true;
        }
        if (Build.FINGERPRINT.startsWith("generic")
                || Build.FINGERPRINT.startsWith("unknown")
                || Build.MODEL.contains("google_sdk")
                || Build.MODEL.contains("Emulator")
                || Build.MODEL.contains("Android SDK built for x86")
                || Build.MANUFACTURER.contains("Genymotion")
                || (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
                || "google_sdk".equals(Build.PRODUCT)) {
            return true;
        }
        if (Build.PRODUCT == null) {
            return true;
        }
        if (Build.PRODUCT.equals("sdk")
                || Build.PRODUCT.equals("sdk_x86")
                || Build.PRODUCT.equals("vbox86p")
                || Build.PRODUCT.equals("emulator")) {
            return true;
        }
        if (Build.HARDWARE.equals("goldfish")
                || Build.HARDWARE.equals("ranchu")) {
            return true;
        }
        if (new File("/dev/socket/qemud").exists()
                || new File("/dev/qemu_pipe").exists()) {
            return true;
        }
        try {
            TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
            if (telephonyManager != null) {
                String deviceId = telephonyManager.getDeviceId();
                List<String> knownNumbers = Arrays.asList(known_numbers);
                if (knownNumbers.contains(deviceId)) {
                    return true;
                }
            }
        } catch (Exception e) {
        }
        return false;
    }

上述代码使用了多种方法来检测设备是否为模拟器,这些方法包括:

  • 检测 Build.FINGERPRINT 是否以 “generic” 或 “unknown” 开头
  • 检测 Build.MODEL 是否包含 “google_sdk”、“Emulator” 或 “Android SDK built for x86”
  • 检测 Build.MANUFACTURER 是否为 “Genymotion”
  • 检测 Build.PRODUCT 是否为 “sdk”、“sdk_x86”、“vbox86p” 或 “emulator”
  • 检测 Build.BOARD 是否为 “unknown” 或包含 “android” 或 “droid”
  • 检测 Build.DEVICE 是否为 “unknown” 或包含 “android” 或 “droid”
  • 检测 Build.HARDWARE 是否为 “goldfish”、“ranchu” 或包含 “ranchu”
  • 检测 Build.BRAND 是否以 “generic” 开头,且 Build.DEVICE 以 “generic” 开头
  • 检测 Build.PRODUCT 是否为 “google_sdk”
  • 检测是否存在文件 “/dev/socket/qemud” 或 “/dev/qemu_pipe”
  • 检测设备的电话号码是否为已知的模拟器电话号码

上述方法都是基于固件信息的判断,通过测试发现很多模拟器都失效,参考网上的教程,还有蓝牙、光线传感器、cpu检测,配合上面的固件信息,基本可以搞定大部分模拟器。

蓝牙检测方法

public boolean notHasBlueTooth() {
    BluetoothAdapter ba = BluetoothAdapter.getDefaultAdapter();
    if (ba == null) {
        return true;
    } else {
        // 如果有蓝牙不一定是有效的。获取蓝牙名称,若为null 则默认为模拟器
        String name = ba.getName();
        if (TextUtils.isEmpty(name)) {
            return true;
        } else {
            return false;
        }
    }
}

光传感器检测方法

public static Boolean notHasLightSensorManager(Context context) {
    SensorManager sensorManager = (SensorManager) context.getSystemService(SENSOR_SERVICE);
    Sensor sensor8 = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); //光
    if (null == sensor8) {
        return true;
    } else {
        return false;
    }
}

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;
}

以上检测方法也不是完全可行,随着Android系统的更新,模拟器的增多,我们需要具体研究对应的一些变动来更新上述代码。

我们检测要注意一个问题,不一定能检测出所有的模拟器,但是一定不能误杀真机。

总结

建议 首先使用传感器进行可疑性判断

蓝牙, wifi, 电池 可以作为 辅助数据进行监听。

附加传感器类型代码

public String getSensorTypeName(int type) {
        switch (type) {
            case Sensor.TYPE_ACCELEROMETER:
                return "加速度传感器";
            case Sensor.TYPE_GYROSCOPE:
                return "陀螺仪传感器";
            case Sensor.TYPE_LIGHT:
                return "环境光线传感器";
            case Sensor.TYPE_MAGNETIC_FIELD:
                return "电磁场传感器";
            case Sensor.TYPE_ORIENTATION:
                return "方向传感器";
            case Sensor.TYPE_PRESSURE:
                return "压力传感器";
            case Sensor.TYPE_PROXIMITY:
                return "距离传感器";
            case Sensor.TYPE_TEMPERATURE:
                return "温度传感器";
            case Sensor.TYPE_GRAVITY:
                return "重场传感器";
            case Sensor.TYPE_LINEAR_ACCELERATION:
                return "线性加速度传感器";
            case Sensor.TYPE_ROTATION_VECTOR:
                return "旋转矢量传感器";
            case Sensor.TYPE_RELATIVE_HUMIDITY:
                return "湿度传感器";
            case Sensor.TYPE_AMBIENT_TEMPERATURE:
                return "温度传感器";
            case Sensor.TYPE_GAME_ROTATION_VECTOR:
                return "游戏旋转矢量传感器";
            case Sensor.TYPE_STEP_COUNTER:
                return "计步器";
            case Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR:
                return "地磁旋转矢量传感器";
            case Sensor.TYPE_SIGNIFICANT_MOTION:
                return "特殊动作触发传感器";
            default:
                return "未知传感器";
        }
    }

到此这篇关于Android模拟器最新检测方法详解的文章就介绍到这了,更多相关Android模拟器检测内容请搜索程序员之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持程序员之家!

相关文章

  • Android自定义WheelView地区选择三级联动

    Android自定义WheelView地区选择三级联动

    这篇文章主要为大家详细介绍了Android自定义WheelView地区选择三级联动的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-02-02
  • Android三种网络通讯方式及Android的网络通讯机制

    Android三种网络通讯方式及Android的网络通讯机制

    在android平台目前提供了三种网络接口可以使用:分别是java.net.*(标准Java接口)、Org.apache接口和Android.net.*(Android网络接口),本文主要给大家介绍android三种网络通讯方式及android的网络通讯机制,小伙伴们一起学习吧
    2015-11-11
  • Android 照相机的实例应用

    Android 照相机的实例应用

    这篇文章主要介绍了Android 照相机的实例应用的相关资料,希望通过此文能掌握Android照相机的使用方法,需要的朋友可以参考下
    2017-08-08
  • Android Build类的详解及简单实例

    Android Build类的详解及简单实例

    这篇文章主要介绍了Android Build类的详解及简单实例的相关资料,希望通过本文大家能够理解掌握这部分内容,需要的朋友可以参考下
    2017-08-08
  • Kotlin自定义实现支付密码数字键盘的方法实例

    Kotlin自定义实现支付密码数字键盘的方法实例

    这篇文章主要给大家介绍了关于Kotlin如何自定义实现支付密码数字键盘的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-07-07
  • Android布局技巧之合并布局

    Android布局技巧之合并布局

    这篇文章主要为大家详细介绍了Android布局技巧之合并布局,感兴趣的小伙伴们可以参考一下
    2016-06-06
  • Android?Jetpack组件中LifeCycle作用详细介绍

    Android?Jetpack组件中LifeCycle作用详细介绍

    Jetpack是谷歌在Google?I/O?2017大会上发布一套帮助开发者解决Android架构设计的方案,而Lifecycle是Jetpack?architecture下的一部分,一起来看一下Lifecycle的使用及原理分析
    2022-09-09
  • Android文件选择器ExFilePicker的使用方法

    Android文件选择器ExFilePicker的使用方法

    这篇文章主要为大家详细介绍了Android文件选择器ExFilePicker的使用方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-07-07
  • Android10开发者常见问题(小结)

    Android10开发者常见问题(小结)

    这篇文章主要介绍了Android10开发者常见问题(小结),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-11-11
  • android播放gif格式图片示例

    android播放gif格式图片示例

    这篇文章主要介绍了android播放gif格式图片的方法,大家参考使用吧
    2014-01-01

最新评论

?


http://www.vxiaotou.com