Build.PRODUCT.contains(“Andy”) ||
Build.PRODUCT.contains(“Droid4X”) ||
Build.PRODUCT.contains(“nox”) ||
Build.PRODUCT.contains(“vbox86p”)) {
return true;
}
if (Build.MANUFACTURER.equals(“Genymotion”) ||
Build.MANUFACTURER.contains(“Andy”) ||
Build.MANUFACTURER.contains(“nox”) ||
Build.MANUFACTURER.contains(“TiantianVM”)) {
return true;
}
if (Build.BRAND.contains(“Andy”)) {
return true;
}
if (Build.DEVICE.contains(“Andy”) ||
Build.DEVICE.contains(“Droid4X”) ||
Build.DEVICE.contains(“nox”) ||
Build.DEVICE.contains(“vbox86p”)) {
return true;
}
if (Build.MODEL.contains(“Emulator”) ||
Build.MODEL.equals(“google_sdk”) ||
Build.MODEL.contains(“Droid4X”) ||
Build.MODEL.contains(“TiantianVM”) ||
Build.MODEL.contains(“Andy”) ||
Build.MODEL.equals(“Android SDK built for x86_64”) ||
Build.MODEL.equals(“Android SDK built for x86”)) {
return true;
}
if (Build.HARDWARE.equals(“vbox86”) ||
Build.HARDWARE.contains(“nox”) ||
Build.HARDWARE.contains(“ttVM_x86”)) {
return true;
}
if (Build.FINGERPRINT.contains(“generic/sdk/generic”) ||
Build.FINGERPRINT.contains(“generic_x86/sdk_x86/generic_x86”) ||
Build.FINGERPRINT.contains(“Andy”) ||
Build.FINGERPRINT.contains(“ttVM_Hdragon”) ||
Build.FINGERPRINT.contains(“generic/google_sdk/generic”) ||
Build.FINGERPRINT.contains(“vbox86p”) ||
Build.FINGERPRINT.contains(“generic/vbox86p/vbox86p”)) {
return true;
}
return false;
}
还有的特征只是疑似,但不确定,对于这些特征,可以集合起来做一个疑似度评分,评分达到一定程度就标记为模拟器:
int newRating = 0;
if (rating < 0) {
if (Build.PRODUCT.contains(“sdk”) ||
Build.PRODUCT.contains(“Andy”) ||
Build.PRODUCT.contains(“ttVM_Hdragon”) ||
Build.PRODUCT.contains(“google_sdk”) ||
Build.PRODUCT.contains(“Droid4X”) ||
Build.PRODUCT.contains(“nox”) ||
Build.PRODUCT.contains(“sdk_x86”) ||
Build.PRODUCT.contains(“sdk_google”) ||
Build.PRODUCT.contains(“vbox86p”)) {
newRating++;
}
if (Build.MANUFACTURER.equals(“unknown”) ||
Build.MANUFACTURER.equals(“Genymotion”) ||
Build.MANUFACTURER.contains(“Andy”) ||
Build.MANUFACTURER.contains(“MIT”) ||
Build.MANUFACTURER.contains(“nox”) ||
Build.MANUFACTURER.contains(“TiantianVM”)) {
newRating++;
}
if (Build.BRAND.equals(“generic”) ||
Build.BRAND.equals(“generic_x86”) ||
Build.BRAND.equals(“TTVM”) ||
Build.BRAND.contains(“Andy”)) {
newRating++;
}
if (Build.DEVICE.contains(“generic”) ||
Build.DEVICE.contains(“generic_x86”) ||
Build.DEVICE.contains(“Andy”) ||
Build.DEVICE.contains(“ttVM_Hdragon”) ||
Build.DEVICE.contains(“Droid4X”) ||
Build.DEVICE.contains(“nox”) ||
Build.DEVICE.contains(“generic_x86_64”) ||
Build.DEVICE.contains(“vbox86p”)) {
newRating++;
}
if (Build.MODEL.equals(“sdk”) ||
Build.MODEL.contains(“Emulator”) ||
Build.MODEL.equals(“google_sdk”) ||
Build.MODEL.contains(“Droid4X”) ||
Build.MODEL.contains(“TiantianVM”) ||
Build.MODEL.contains(“Andy”) ||
Build.MODEL.equals(“Android SDK built for x86_64”) ||
Build.MODEL.equals(“Android SDK built for x86”)) {
newRating++;
}
if (Build.HARDWARE.equals(“goldfish”) ||
Build.HARDWARE.equals(“vbox86”) ||
Build.HARDWARE.contains(“nox”) ||
Build.HARDWARE.contains(“ttVM_x86”)) {
newRating++;
}
if (Build.FINGERPRINT.contains(“generic/sdk/generic”) ||
Build.FINGERPRINT.contains(“generic_x 《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】 86/sdk_x86/generic_x86”) ||
Build.FINGERPRINT.contains(“Andy”) ||
Build.FINGERPRINT.contains(“ttVM_Hdragon”) ||
Build.FINGERPRINT.contains(“generic_x86_64”) ||
Build.FINGERPRINT.contains(“generic/google_sdk/generic”) ||
Build.FINGERPRINT.contains(“vbox86p”) ||
Build.FINGERPRINT.contains(“generic/vbox86p/vbox86p”)) {
newRating++;
}
try {
String opengl = android.opengl.GLES20.glGetString(android.opengl.GLES20.GL_RENDERER);
if (opengl != null) {
if (opengl.contains(“Bluestacks”) ||
opengl.contains(“Translator”)
)
newRating += 10;
}
} catch (Exception e) {
e.printStackTrace();
}
try {
File sharedFolder = new File(Environment
.getExternalStorageDirectory().toString()
if (sharedFolder.exists()) {
newRating += 10;
}
} catch (Exception e) {
e.printStackTrace();
}
rating = newRating;
}
return rating > 3;//不能再少了,否则有可能误判,若增减了新的嫌疑度判定属性,要重新评估该值
多开麻烦在于真机多开,具备真机特征,模拟器的检测就失效了,因为它就是真机。 应对方法:普通的软多开,一般绕不过uid,还是用宿主的。因此,如果满足同一uid下的两个进程对应的包名,在"/data/data"下有两个私有目录,则违背了系统 "只为一个应用创建唯一一个私有目录"的设定,则该应用被多开了。
public static boolean isRunInVirtual() {
String filter = getUidStrFormat();
String result = exec(“ps”);
if (result == null || result.isEmpty()) {
return false;
}
String[] lines = result.split(“\n”);
if (lines == null || lines.length <= 0) {
return false;
}
int exitDirCount = 0;
for (int i = 0; i < lines.length; i++) {
if (lines[i].contains(filter)) {
int pkgStartIndex = lines[i].lastIndexOf(" “);
String processName = lines[i].substring(pkgStartIndex <= 0
? 0 : pkgStartIndex + 1, lines[i].length());
File dataFile = new File(String.format(”/data/data/%s",
processName, Locale.CHINA));
if (dataFile.exists()) {
exitDirCount++;
}
}
}
return exitDirCount > 1;
}
这个方法是在简书 JZaratustra 大佬的文章里学到的:[Android虚拟机多开检测](()。 但是有一些多开,比如小米自带的多开这种,进程好像都是隔离的独立uid的,暂时没有好办法识别。
Anti Hook