Android/Androgoat

Emulator Detection

Analysis

LD player, Nox와 같은 애뮬레이터에서 앱을 실행 중인지 확인하는 기능이다.

애플리케이션을 실행하는 환경이 애뮬레이터인지 아닌지에 따라 출력되는 메시지가 다르다.

 

Code

EmulatorDetectionActivity$onCreate$1

final class EmulatorDetectionActivity$onCreate$1 implements View.OnClickListener {
    final /* synthetic */ EmulatorDetectionActivity this$0;

    EmulatorDetectionActivity$onCreate$1(EmulatorDetectionActivity emulatorDetectionActivity) {
        this.this$0 = emulatorDetectionActivity;
    }

    public final void onClick(View it) {
        if (this.this$0.isEmulator()) {
            Toast.makeText(this.this$0.getApplicationContext(), "This is Emulator", 1).show();
        } else {
            Toast.makeText(this.this$0.getApplicationContext(), "This is not Emulator", 1).show();
        }
    }
}

버튼을 눌렀을 때 호출되는 함수로 isEmulator() 함수 결괏값에 따라 애뮬레이터인지 판단하여 "This is Emulator" 혹은 "This is not Emulator" 문자열을 출력한다.

 

isEmulator

public final boolean isEmulator() {
        String str = Build.FINGERPRINT + Build.DEVICE + Build.MODEL + Build.BRAND + Build.PRODUCT + Build.MANUFACTURER + Build.HARDWARE;
        if (str != null) {
            String builddtls = str.toLowerCase();
            Intrinsics.checkExpressionValueIsNotNull(builddtls, "(this as java.lang.String).toLowerCase()");
            if (StringsKt.contains$default((CharSequence) builddtls, (CharSequence) "generic", false, 2, (Object) null) || StringsKt.contains$default((CharSequence) builddtls, (CharSequence) EnvironmentCompat.MEDIA_UNKNOWN, false, 2, (Object) null) || StringsKt.contains$default((CharSequence) builddtls, (CharSequence) "emulator", false, 2, (Object) null) || StringsKt.contains$default((CharSequence) builddtls, (CharSequence) "sdk", false, 2, (Object) null) || StringsKt.contains$default((CharSequence) builddtls, (CharSequence) "vbox", false, 2, (Object) null) || StringsKt.contains$default((CharSequence) builddtls, (CharSequence) "genymotion", false, 2, (Object) null) || StringsKt.contains$default((CharSequence) builddtls, (CharSequence) "x86", false, 2, (Object) null) || StringsKt.contains$default((CharSequence) builddtls, (CharSequence) "goldfish", false, 2, (Object) null) || StringsKt.contains$default((CharSequence) builddtls, (CharSequence) "test-keys", false, 2, (Object) null)) {
                return true;
            }
            return false;
        }
        throw new TypeCastException("null cannot be cast to non-null type java.lang.String");
    }

isEmulator() 함수에서는 Fingerprint, Device, Model 등과 같은 기기 고유 값을 이용하여 애뮬레이터인지 아닌지 판단한다. 만약 애뮬레이터가 맞다면 true를 반환하고 아니라면 false를 반환한다.

여기서 Fingerprint, Device, Model이 각각 어떤 의미인지 확인할 수 있다.

 

Exploit

smali

애뮬레이터 우회를 위한 첫 번째는 smali 코드 변조다. (이전 Root Detection과 거의 동일하다)

먼저 변조할 코드를 찾기 위해 APK Easy Tool을 이용하여 디컴파일을 진행한다.

 

이후 smali → owasp → sat → goat 경로를 따라가면 EmulatorDetectionActivity.smali 코드를 확인할 수 있다.

 

isEmulator() 함수 마지막 부분을 확인해보면 다음과 같은 부분을 확인할 수 있다.

이전 코드 중 애뮬레이터가 맞다면 cond_0으로 이동하여 v3 값을 0x1로 설정해준 후 이를 반환해주는 것을 확인할 수 있다.

 

그렇다면 우회는 간단하게 그냥 v3로 넣어주는 값을 0x1에서 0x0으로 변경해주면 끝이다.

 

이후 컴파일 및 설치를 진행한 다음 확인해보면 다음과 같이 "This is not Emulator" 메시지가 출력되는 것을 확인할 수 있다.

 

Frida

두 번째는 frida를 이용한 우회이다.

frida를 이용한 우회도 이전 Root Detection과 거의 유사하다.

코드는 다음과 같이 isEmulator() 함수를 후킹한 후 반환 값을 false로 주면 우회할 수 있다.

console.log("Script loaded successfully");
Java.perform(function x() {
    console.log("java perform function");
    var my_class = Java.use("owasp.sat.agoat.EmulatorDetectionActivity");
    my_class.isEmulator.implementation = function (args) {
        console.log("\n----0----");
        return false;
    };
});

 

스크립트 실행 후 CHECK EMULATOR 버튼을 눌러주면 다음과 같은 로그와 이전 smali변조와 같이 "This is not Emulator"라는 메시지를 애플리케이션에서 확인할 수 있다.

 

Reference

'Android > Androgoat' 카테고리의 다른 글

Network Intercepting  (0) 2021.09.20
Binary Patching  (0) 2021.09.20
Root Detection  (0) 2021.09.20