Bypassing Emulator Detection with Code Tampering.

Himash
4 min readNov 13, 2019

--

Recently when i was doing penetration testing i got a very interesting android app project. the app is implemented with a separate class with over 520 lines of code for emulator detection. the emulator detection class implemented all possible ways to detect emulator with complicated logical flow. after some common experimentation with the app i decided to tamper the emulator detection class.

Very hard part of the reverse engineering is we have to ‘play’ with the app without disturbing the logical flow of the application. otherwise the app won’t function at all.

Emulator detection class contains two type of emulator detection mechanisms.

1.Basic Check

2. Advanced Check

private boolean detect(){log(getDeviceInfo());boolean bool2 = checkBasic();StringBuilder localStringBuilder = new StringBuilder();localStringBuilder.append("Check basic ");localStringBuilder.append(bool2);log(localStringBuilder.toString());boolean bool1 = bool2;
if (!bool2){
bool1 = checkAdvanced();
localStringBuilder = new StringBuilder();localStringBuilder.append("Check Advanced ");localStringBuilder.append(bool1);log(localStringBuilder.toString());}bool2 = bool1;
if (!bool1){
bool2 = checkPackageName();
localStringBuilder = new StringBuilder();localStringBuilder.append("Check Package Name ");localStringBuilder.append(bool2);log(localStringBuilder.toString());}return bool2;}

The above piece of code is the function that test basic and advanced emulator check. to tamper code for an android application we have to do it on the smali code. there is huge differences in the logical flow of the code between the smali and decompiled java code.

The way to find the exact code part in the smali we can use string search, if it’s not there then we can search for functions or just count conditions to get the exact same place of code as in java. by analyzing detect () function i figured it out that, to bypass all the emulator checking mechanism i have to tamper the code by doing the following.

return false only in checkBasic function

return true only in checkAdvanced function

to do above tampering first i need to dig in to the checkBasic () function

private boolean checkBasic(){
boolean bool = Build.FINGERPRINT.startsWith("generic");
int k = 0;
int i;
if ((!bool) && (!Build.MODEL.contains("google_sdk")) && (!Build.MODEL.toLowerCase().contains("droid4x")) && (!Build.MODEL.contains("Emulator")) && (!Build.MODEL.contains("Android SDK built for x86")) && (!Build.MANUFACTURER.contains("Genymotion")) && (!Build.HARDWARE.equals("goldfish")) && (!Build.HARDWARE.equals("vbox86")) && (!Build.PRODUCT.equals("sdk")) && (!Build.PRODUCT.equals("google_sdk")) && (!Build.PRODUCT.equals("sdk_x86")) && (!Build.PRODUCT.equals("vbox86p")) && (!Build.BOARD.toLowerCase().contains("nox")) && (!Build.BOOTLOADER.toLowerCase().contains("nox")) && (!Build.HARDWARE.toLowerCase().contains("nox")) && (!Build.PRODUCT.toLowerCase().contains("nox")) && (!Build.SERIAL.toLowerCase().contains("nox"))) {
i = 0;
} else {
i = 1;
}
if (i != 0) {
return true;
}
int j = k;
if (Build.BRAND.startsWith("generic"))
{
j = k;
if (Build.DEVICE.startsWith("generic")) {
j = 1;
}
}
i |= j;
if (i != 0) {
return true;
}
return i | "google_sdk".equals(Build.PRODUCT);
}

By analyzing the code it is possible to go the exact same piece of code in the smali. i just landed on the same function in the smali and tempered the true as false.

private boolean checkAdvanced()
{
return (checkTelephony()) || (checkFiles(GENY_FILES, "Geny")) || (checkFiles(ANDY_FILES, "Andy")) || (checkFiles(NOX_FILES, "Nox")) || (checkQEmuDrivers()) || (checkFiles(PIPES, "Pipes")) || (checkIp()) || ((checkQEmuProps()) && (checkFiles(X86_FILES, "X86")));
}
private boolean checkFiles(String[] paramArrayOfString, String paramString)
{
int j = paramArrayOfString.length;
int i = 0;
while (i < j)
{
if (new File(paramArrayOfString[i]).exists())
{
paramArrayOfString = new StringBuilder();paramArrayOfString.append("Check ");paramArrayOfString.append(paramString);paramArrayOfString.append(" is detected");log(paramArrayOfString.toString());return true;
}
i += 1;
}
return false;
}
private boolean checkQEmuDrivers(){
File[] arrayOfFile = new File[2];
arrayOfFile[0] = new File("/proc/tty/drivers");
arrayOfFile[1] = new File("/proc/cpuinfo");
int k = arrayOfFile.length;
int i = 0;
while (i < k){
Object localObject2 = arrayOfFile[i];
if ((((File)localObject2).exists()) && (((File)localObject2).canRead())){
Object localObject1 = new byte['?'];
try
{
localObject2 = new FileInputStream((File)localObject2);
((FileInputStream)localObject2).read((byte[])localObject1);((FileInputStream)localObject2).close();
}catch (Exception localException){
localException.printStackTrace();}localObject1 = new String((byte[])localObject1);
String[] arrayOfString = QEMU_DRIVERS;
int m = arrayOfString.length;
int j = 0;
while (j < m)
{
if (((String)localObject1).contains(arrayOfString[j]))
{
log("Check QEmuDrivers is detected");
return true;
}
j += 1;
}}
i += 1;
}
return false;
}
private boolean checkQEmuProps(){
Property[] arrayOfProperty = PROPERTIES;
int m = arrayOfProperty.length;
int k = 0;
int i = 0;
while (k < m)
{
Object localObject = arrayOfProperty[k];
String str = getProp(this.mContext, ((Property)localObject).name);
int j = i;
if (((Property)localObject).seek_value == null)
{
j = i;
if (str != null) {
j = i + 1;
}}
localObject = ((Property)localObject).seek_value;i = j;
if (localObject != null)
{
i = j;
if (str.contains((CharSequence)localObject)) {
i = j + 1;
}}
k += 1;
}
if (i >= 5)
{
log("Check QEmuProps is detected");
return true;
}
return false;
}

above piece of code is the checkAdvanced() function. by analyzing the advanced function i decided to return true. by exploring the smali code i just landed in the exact function in the smali and i just tampered the code to return true. and i just compiled and signed the binary and installed it on the emulator.

Photo by Persnickety Prints on Unsplash

This is very interesting because the application loaded with all possible way of emulator detection and some bit of hard logical flow.

--

--

No responses yet