In this recipe, we will look at three additional checks that may indicate a tampered, compromised, or hostile environment. These are designed to be activated once you are ready for release.
These tamper checks can be located anywhere in your app, but it makes the most sense to allow them to be called from multiple places at a separate class or parent class.
public static boolean checkGooglePlayStore(Context context) { String installerPackageName = context.getPackageManager() .getInstallerPackageName(context.getPackageName()); return installerPackageName != null && installerPackageName.startsWith("com.google.android"); }
public static boolean isEmulator() { try { Class systemPropertyClazz = Class .forName("android.os.SystemProperties"); boolean kernelQemu = getProperty(systemPropertyClazz, "ro.kernel.qemu").length() > 0; boolean hardwareGoldfish = getProperty(systemPropertyClazz, "ro.hardware").equals("goldfish"); boolean modelSdk = getProperty(systemPropertyClazz, "ro.product.model").equals("sdk"); if (kernelQemu || hardwareGoldfish || modelSdk) { return true; } } catch (Exception e) { // error assumes emulator } return false; } private static String getProperty(Class clazz, String propertyName) throws Exception { return (String) clazz.getMethod("get", new Class[] { String.class }) .invoke(clazz, new Object[] { propertyName }); }
debuggable
flag enabled—something that should only be enabled during development:public static boolean isDebuggable(Context context){ return (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; }
Detecting if the installer was the Google Play store is a simple check that the package name of the installer app matches that of the Google Play store. Specifically, it checks if the installer's package starts with com.google.android
. It is a useful check if you are distributed solely through the Google store.
The Java Reflection API makes it possible to inspect classes, methods, and fields at runtime; and in this case, allows us to override the access modifiers that would prevent ordinary code from compiling. The emulator check uses reflection to access a hidden system class, android.os.SystemProperties
. A word of warning: using hidden APIs can be risky, as they can change between Android versions.
When debuggable
is enabled, it is possible to connect via the Android Debug Bridge and preform detailed dynamic analysis. The debuggable
variable is a simple property of the <application>
element in the AndroidManifest.xml
file. It is perhaps one of the easiest and most targeted properties to alter in order to perform dynamic analysis. In step 3, we saw how to check the value of the debuggable
flag on the application info object.
See the Application signature verification (anti-tamper) recipe for suggestions on what to do if you detect tampering. Once released to the Play store, on detecting that the app is running on an emulator or is being debugged, it is reasonable to assume that the app is under analysis and/or attack. Therefore, in these scenarios, it would be justified to take more aggressive actions to frustrate attackers, such as wiping app data or the shared preferences. Although, if you are going to wipe user data, ensure this is noted in your license agreement to avoid any potential legal issues.
SystemProperties.java
class from the Android source code at https://github.com/android/platform_frameworks_base/blob/master/core/java/android/os/SystemProperties.java PackageManager
class in the Android Developers Reference guide at https://developer.android.com/reference/android/content/pm/PackageManager.htmlApplicationInfo
class in the Android Developers Reference guide at https://developer.android.com/reference/android/content/pm/ApplicationInfo.html3.137.212.124