In the past two years, Android virtualization technology and group control technology has developed rapidly, bringing many fun products and convenient tools. However, as App developers have a headache, malicious users (such as uncivilized users, such as brushing) take advantage of these technologies, the threshold of evil is too low to know where to go. We need to think about how to identify and defend. Here are some simple but effective schemes for malicious user identification (and subsequent blocking).

Anti simulator

This is easy to understand, the simulated machine, each time the simulation generated device ID, only exists in the life cycle of the simulator. Maybe the next simulation will be different. Solution: It is mainly to detect some characteristics of the running simulator, such as driver files, hardware information in the Build class, etc. For example, if the Build class contains a string containing an emulator, it is clearly an emulator:

 public static boolean isEmulatorAbsoluly(a) {
        if (Build.PRODUCT.contains("sdk") ||
                Build.PRODUCT.contains("sdk_x86") ||
                Build.PRODUCT.contains("sdk_google") ||
                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;
    }
Copy the code

There are other features that are only suspected, but uncertain. For these features, we can gather them to make a suspected degree score, and mark them as simulators when the score reaches a certain level:

     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_x86/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()
                        + File.separatorChar
                        + "windows"
                        + File.separatorChar
                        + "BstSharedFolder");

                if (sharedFolder.exists()) {
                    newRating += 10; }}catch (Exception e) {
                e.printStackTrace();
            }
            rating = newRating;
        }
        return rating > 3;// If a new attribute of suspicion is added or subtracted, the value should be reevaluated
Copy the code

Anti rolled

The trouble with multi-open is that if the real machine is multi-open, the detection of the simulator will be invalid because it is the real machine. Coping method: common soft open, generally around but UID, or with the host. Therefore, if there are two private directories under “/data/data” and the package names of two processes in the same UID, it violates the system’s setting of “creating only one private directory for an application”, and the application is opened more than once.

public static boolean isRunInVirtual(a) {

    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;
}
Copy the code

This method is learned in Jane’s book JZaratustra’s article: Android virtual machine more open detection. But there are some open, such as xiaomi’s own open, the process seems to be isolated independent UID, there is no good way to identify.

Anti Hook

No more talk, you Hook the way, you are the big man, you say what you say. Solution: detect whether the Xposed related application is installed, detect suspicious method to call the path, detect should not native native method, through /proc/[pid]/maps to detect suspicious shared object or JAR.

Detect whether the Xposed application is installed

PackageManager packageManager = context.getPackageManager();
List applicationInfoList  = packageManager.getInstalledApplications(PackageManager.GET_META_DATA);
 
for(ApplicationInfo applicationInfo : applicationInfoList) {
    if(applicationInfo.packageName.equals("de.robv.android.xposed.installer")) {
        Log.wtf("HookDetection"."Xposed found on the system.");
    }
    if(applicationInfo.packageName.equals("com.saurik.substrate")) {
        Log.wtf("HookDetection"."Substrate found on the system."); }}Copy the code

Detects suspicious methods calling the walkway

  try {
            throw new Exception("blah");
        } catch (Exception e) {
            int zygoteInitCallCount = 0;
            for (StackTraceElement stackTraceElement : e.getStackTrace()) {
                if (stackTraceElement.getClassName().equals("com.android.internal.os.ZygoteInit")) {
                    zygoteInitCallCount++;
                    if (zygoteInitCallCount == 2) {
                        Log.wtf("HookDetection"."Substrate is active on the device.");
                        isHook = true; }}if (stackTraceElement.getClassName().equals("com.saurik.substrate.MS$2") &&
                        stackTraceElement.getMethodName().equals("invoked")) {
                    Log.wtf("HookDetection"."A method on the stack trace has been hooked using Substrate.");
                    isHook = true;
                }
                if (stackTraceElement.getClassName().equals("de.robv.android.xposed.XposedBridge") &&
                        stackTraceElement.getMethodName().equals("main")) {
                    Log.wtf("HookDetection"."Xposed is active on the device.");
                    isHook = true;
                }
                if (stackTraceElement.getClassName().equals("de.robv.android.xposed.XposedBridge") &&
                        stackTraceElement.getMethodName().equals("handleHookedMethod")) {
                    Log.wtf("HookDetection"."A method on the stack trace has been hooked using Xposed.");
                    isHook = true; }}}Copy the code

/proc/[pid]/maps to detect suspicious shared objects or JARS:

 try {
            Set<String> libraries = new HashSet();
            String mapsFilename = "/proc/" + android.os.Process.myPid() + "/maps";
            BufferedReader reader = new BufferedReader(new FileReader(mapsFilename));
            String line;
            while((line = reader.readLine()) ! =null) {
                if (line.endsWith(".so") || line.endsWith(".jar")) {
                    int n = line.lastIndexOf("");
                    libraries.add(line.substring(n + 1)); }}for (String library : libraries) {
                if (library.contains("com.saurik.substrate")) {
                    Log.wtf("HookDetection"."Substrate shared object found: " + library);
                    isHook = true;
                }
                if (library.contains("XposedBridge.jar")) {
                    Log.wtf("HookDetection"."Xposed JAR found: " + library);
                    isHook = true;
                }
            }
            reader.close();
        } catch (Exception e) {
            Log.wtf("HookDetection", e.toString());
        }
Copy the code

Note, as long as the detection of several related functions Hook, anti-hook. It’s easy to get around.

The server analyzes data similarity

There are many identifiers that can be used to identify devices, including IMEI, MAC, PSEDuo_id, AAID, Gsf_id, etc. Since Google is opposed to only absolute tracking of users, these ids can be difficult or easy to change. For example, Android ids can be changed directly without root by using adb commands. However, it would be troublesome to change all of these signs. The client can report these ids to the server, and the server can make a similarity judgment based on other information, such as geographic location and IP address, and find out some accounts suspected of the same malicious user.

SD card stores homemade ID

If you have SD card write permissions, generate ids and encrypt them according to your own rules, and secretly write a hidden file (just add a dot before the file name or folder name) in a hidden place outside of your app’s private directory. As long as it is generated once, this will prevail. No matter how many vests the user modifies the device information and registers, the user can be identified as the same device user.

Mobile phone number SMS authentication

All login users must be bound with mobile phone numbers. From the product process to improve the vest cost, but also improve the user registration threshold.

Of course, the above methods can only be used to prevent the master, and these methods can be easily bypassed by the experienced contrarian. Write out, is the hope that can pool the wisdom, obtain more counter-ideas, improve the cost of counterfeit equipment malicious elements. (it is hope to encounter big guy to give directions actually, raise the knowledge level of the inferior rookie 😄) the classmate that has more thorough practice, beg comment, beg private letter.

Reference the Demo:

anti-counterfeit-android

Reference article:

Android anti-debugging AntiEmulator to detect Android emulators

Android emulator detection based on file characteristics

Anti-hooking tips for Android Java layer

Check Android VMS frequently