Android 模拟器检测

文章目录

    • 普遍检测代码如下:
    • 推荐模拟器检测方法:
        • 设备信息检测代码:
        • 蓝牙检测代码:
        • 光传感器检测代码:
        • CPU检测代码:

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

普遍检测代码如下:

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系统的更新,模拟器的增多,我们需要具体研究对应的一些变动来更新上述代码。

我们检测要注意一个问题,不一定能检测出所有的模拟器,但是一定不能误杀真机。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/286418.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python 自学(一) 之语言基础

目录 0. 前言 1. 声明python解释器 2. 变量的类型 type() id() P35 3. 类型转换 str() P37 4. 换行 r"" P40 5. 常用类型转换函数 P41 6. 特殊的运算符 // ** P42 7. 特殊的比较运算符 0 < a <100 P45 8. 函数输入 input() P51 9. print…

Windows使用IIS服务搭建WebDAV站点结合内网穿透公网访问

文章目录 1. 安装IIS必要WebDav组件2. 客户端测试3. cpolar内网穿透3.1 打开Web-UI管理界面3.2 创建隧道3.3 查看在线隧道列表3.4 浏览器访问测试 4. 安装Raidrive客户端4.1 连接WebDav服务器4.2 连接成功4.2 连接成功总结&#xff1a; 自己用Windows Server搭建了家用NAS主机&…

基于mediapipe的人体姿态估计模型——没有GPU依然速度飞起

关于人体姿态检测模型,我们前期也介绍过了很多相关的模型,比如基于Yolo-NAS的姿态检测以及基于YOLOv8的人体姿态检测,而人体姿态估计一直是计算机视觉任务中比较重要的一个模型。但是基于YOLO系列的人体姿态检测模型需要较大的算力,且很难在CPU模型上快速的运行。 基于medi…

【漏洞复现】OpenSSH ProxyCommand命令注入漏洞(CVE-2023-51385)

文章目录 前言一、漏洞背景二、漏洞详情三、影响范围四、漏洞验证 前言 OpenSSH存在命令注入漏洞&#xff08;CVE-2023-51385&#xff09;&#xff0c;攻击者可利用该漏洞注入恶意Shell字符导致命令注入。 一、漏洞背景 OpenSSH 是 SSH &#xff08;Secure SHell&#xff09…

经典目标检测YOLO系列(一)复现YOLOV1(3)正样本的匹配及损失函数的实现

经典目标检测YOLO系列(一)复现YOLOV1(3)正样本的匹配及损失函数的实现 之前&#xff0c;我们依据《YOLO目标检测》(ISBN:9787115627094)一书&#xff0c;提出了新的YOLOV1架构&#xff0c;并解决前向推理过程中的两个问题&#xff0c;继续按照此书进行YOLOV1的复现。 经典目标…

Qt(三):udp组播的发送与接收

1. 创建UDP套接字 使用QUdpSocket类创建一个UDP套接字。 udpSendnew QUdpSocket(this);udpRecenew QUdpSocket(this); 2. 绑定套接字 绑定套接字到一个本地地址和端口。可以使用bind()函数来完成。 如果要在组播中发送数据&#xff0c;可以将套接字绑定到一个通配符地址&#…

m1芯片电脑上的paragon15如何安装激活 m1芯片电脑上ntfs for mac如何安装

Paragon NTFS软件在M1芯片电脑上安装之后&#xff0c;最后一步会让我们“允许加载第三方内核扩展”&#xff0c;具体如下图所示。 图1&#xff1a;允许加载第三方内核扩展 按照图中提示“单击此处“&#xff0c;然后打开安全与隐私。接下来依次点击小锁标志进行解锁&#xff0c…

Abstract Factory抽象工厂模式(对象创建)

抽象工厂模式&#xff1a;Abstract Factory 链接&#xff1a;抽象工厂模式实例代码 解析 目的 在软件系统中&#xff0c;经常面临着“一系列相互依赖的对象工作”&#xff1b;同时&#xff0c;由于需求的变化&#xff0c;往往存在更多系列对象的创建工作。 如何应对这种变化…

EBDP:解锁大数据的奥秘✨

大数据时代已经来临&#xff0c;你是否也想掌握这门“显学”&#xff1f;&#x1f31f; EBDP&#xff0c;这个让众多专业人士趋之若鹜的认证&#xff0c;究竟有何魅力&#xff1f;今天就带你一探究竟&#xff01; &#x1f31f;EBDP&#xff1a;大数据的“敲门砖”&#x1faa…

无线路由器的五种工作模式:

1.Router&#xff0c;即无线路由模式&#xff0c;这也是我们最常用的一种工作方式&#xff0c;一般家里宽带连接&#xff0c;将宽带猫连接在无线路由的WAN口上&#xff0c;然后做拨号帐号设置&#xff0c;就用这个模式即可。 在Router&#xff08;无线路由&#xff09;模式下&a…

预售价80万元的ET9,能否拯救蔚来【不断恶化】的财务业绩?

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 发布新车型 12月23日&#xff0c;蔚来&#xff08;NIO&#xff09;的创始人李斌在西安NIO DAY上发布了蔚来自成立以来售价最高的车型--ET9。 据猛兽财经了解&#xff0c;ET9定位于行政旗舰&#xff0c;预售价为80万元&…

【C语言】Windows上用GTK写GUI程序

要使用GTK开发一个Windows图形用户界面程序&#xff0c;需要首先设置GTK开发环境。这通常包括安装GTK库和它的依赖&#xff0c;以及配置编译器和工具链。可以选择使用纯C语言和GTK库或者使用支持GTK绑定的其他语言&#xff0c;如Python、C或Rust。 1. 安装GTK开发库 在Window…

QT上位机开发(绘图软件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 稍微复杂一点的软件&#xff0c;一般都是带有绘图功能。绘图的意义&#xff0c;不仅仅是像CAD一样&#xff0c;可以进行模型的设计、比对和调试。它…

mysql之四大引擎、账号管理以及建库

一.数据库存储引擎 1.1存储引擎的查看 1.2InnoDB 1.3MyISAM 1.4 MEMORY 1.5 Archive 二.数据库管理 2.1元数据库简介 2.2元数据库分类 2.3 相关操作 2.4 MySQL库 三.数据表管理 3.1三大范式 3.2 基本数据类型 3.2.1优化原则 3.3 整形 3.4 实数 3.5 字符串 3.6 text&…

Java反射机制和动态代理

反射和动态代理 反射前言获取class对象的方式反射获取构造方法反射获取成员变量反射获取成员方法实例 动态代理 反射 前言 什么是反射&#xff1f; 反射允许对成员变量&#xff0c;成员方法和构造方法的信息进行编程访问。 为什么用反射 / 反射的作用&#xff1f; 可以轻易地获…

Scene Creator

场景创建器是一个方便、易于使用的编辑工具&#xff0c;旨在简化创建新场景的过程。使用场景创建器&#xff0c;您可以选择一个模板场景&#xff0c;定义一个目录来存储您的场景&#xff0c;并在需要时自动将新场景添加到构建中。 下载&#xff1a; ​​Unity资源商店链接 资…

微软好听的tts语音包下载,粤语,韩语,日语

微软的 tts 语音库&#xff0c;都是离线的&#xff0c;所以速度非常快 但资源比较少&#xff0c;比如粤语&#xff0c;韩语&#xff0c;日语 我发现一个老牌语音技术供应商。 资源丰富&#xff0c;可自行下载免费或收费语音包。 网站&#xff1a;正版用户专用配套播音员下载…

im6ull学习总结(三-2)文字显示中文字符

承接上篇文章 中文字符的点阵显示 使用点阵字库时&#xff0c;中文字符的显示原理跟 ASCII 字符是一样的。要注意的地方在于中文的编码&#xff1a;在 C 源文件中它的编码方式是 GB2312 还是 UTF-8&#xff1f;编译出的可执行程序&#xff0c;其中的汉字编码方式是 GB2312 还…

机器学习(二) -- 数据预处理(2)

系列文章目录 机器学习&#xff08;一&#xff09; -- 概述 机器学习&#xff08;二&#xff09; -- 数据预处理&#xff08;1-3&#xff09; 机器学习&#xff08;三&#xff09; -- 特征工程&#xff08;1-2&#xff09; 未完待续…… 目录 系列文章目录 前言 四、【数…

三、C语言中的分支与循环—while循环 (5)

本章分支结构的学习内容如下&#xff1a; 三、C语言中的分支与循环—if语句 (1) 三、C语言中的分支与循环—关系操作符 (2) 三、C语言中的分支与循环—条件操作符 与逻辑操作符(3) 三、C语言中的分支与循环—switch语句&#xff08;4&#xff09;分支结构 完 本章循环结…