본문 바로가기

카테고리 없음

Android UnCrackable L1

https://github.com/OWASP/owasp-mastg/raw/master/Crackmes/Android/Level_01/UnCrackable-Level1.apk

$ adb install UnCrackable-Level1.apk

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    android:versionCode="1"
    android:versionName="1.0"
    package="owasp.mstg.uncrackable1">
    <uses-sdk
        android:minSdkVersion="19"
        android:targetSdkVersion="28"/>
    <application
        android:theme="@style/AppTheme"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher"
        android:allowBackup="true">
        <activity
            android:label="@string/app_name"
            android:name="sg.vantagepoint.uncrackable1.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>
</manifest>

 

패키지명 : package="owasp.mstg.uncrackable1"

메인 엑티비티(분석 진입점) : <activity android:name="sg.vantagepoint.uncrackable1.MainActivity">

 

Jadx로 MainActivity 디컴파일 하여 확인

 

31번째 줄에서

if (c.a() || c.b() || c.c()) {

루팅을 검사하고있음을 확인

 

sg.vantagepoint.a.c.a(), sg.vantagepoint.a.c.b(), sg.vantagepoint.a.c.c()에서 확인할때 루팅이 감지되면 true으로 반환 하고 있는것을 확인 할 수 있음 따라 해당부분은 항상 false으로 반환하도록 하면 루팅이 우회될 것

 

34번째 줄

if (b.a(getApplicationContext())) {

에서는 디버거를 감지중인것을 확인 할 수 있다

// Java 계층에 접근 가능한 시점이 되었을 때 이 함수가 실행됨
Java.perform(function () {

    // 📌 1단계: 루팅 탐지 우회
    // 앱 내부 클래스 sg.vantagepoint.a.c 를 가져옴 (루팅 체크용)
    var RootCheck = Java.use("sg.vantagepoint.a.c");

    // static boolean a() 함수 후킹: 루팅 흔적 (su 바이너리) 존재 여부 판단
    RootCheck.a.implementation = function () {
        console.log("[+] Hooked c.a(): returning false (bypass)");  // 로그 출력
        return false;  // 항상 루팅 아님(false)으로 반환
    };

    // static boolean b() 함수 후킹: 빌드 태그가 test-keys인지 확인 (루팅 가능성)
    RootCheck.b.implementation = function () {
        console.log("[+] Hooked c.b(): returning false (bypass)");
        return false;
    };

    // static boolean c() 함수 후킹: 루팅에 관련된 파일 경로들 존재 여부 판단
    RootCheck.c.implementation = function () {
        console.log("[+] Hooked c.c(): returning false (bypass)");
        return false;
    };

    // 📌 2단계: 디버깅 여부 탐지 우회
    // sg.vantagepoint.a.b 클래스가 존재한다면 후킹 시도
    try {
        var DebugCheck = Java.use("sg.vantagepoint.a.b");

        // b.a(Context) 함수 후킹
        // 이 함수는 앱이 디버깅 가능한 상태인지 판단하는 용도
        DebugCheck.a.overload('android.content.Context').implementation = function (ctx) {
            console.log("[+] Hooked b.a(Context): returning false (bypass)");
            return false;  // 항상 디버깅 불가능하다고 반환
        };

    } catch (e) {
        // 클래스나 함수가 존재하지 않을 경우 에러 발생 → 로그 출력
        console.log("[-] b.a(Context) not hooked: " + e);
    }

});

 

✅ 개념 요약

Java.perform(...) Java 계층에 접근하기 위한 기본 구조
Java.use("...") 내부 Java 클래스를 조작할 수 있도록 Frida에 불러옴
.implementation = ... 해당 메서드를 런타임에서 재정의 (원래 코드 무시)
overload(...) 오버로딩된 메서드 중 정확한 시그니처 지정
결과 앱 내부 보안 로직을 런타임에 우회함

✅ Frida에서 overload(...)가 필요한 이유

Frida는 메서드 이름만으로는 어떤 오버로드된 함수인지 구분 못 해.
그래서 명확하게 어떤 함수 시그니처인지 알려줘야 해.

✅ 예: b.a(...)가 오버로드된 경우

sg.vantagepoint.a.b 클래스가 아래처럼 생겼다고 가정:

public class b {
    public static boolean a(Context ctx) {...}
    public static boolean a(String s) {...}
}

⛔ 잘못된 Frida 후킹 (충돌 발생 가능)

var b = Java.use("sg.vantagepoint.a.b");
b.a.implementation = function(x) { return false; };  // 어떤 a()? 모름

✅ 정확한 후킹

b.a.overload('android.content.Context').implementation = function(ctx) {
    return false;
};

→ 'android.content.Context' 인자를 받는 a() 함수만 정확히 후킹.

✅ 요약 정리

구분설명
a.implementation = ... 오버로딩이 없을 때만 가능
a.overload(...) 오버로딩된 경우 정확한 시그니처 지정 필요
왜 필요함? 이름만 같고 인자 다르면 다른 함수이기 때문
안 쓰면? 여러 오버로드가 있을 때 에러 or 잘못된 함수 후킹

✅ 언제 overload를 반드시 써야 하나?

  • 해당 함수 이름으로 여러 개의 시그니처가 정의된 경우
  • 앱이 a(String)과 a(Context) 같은 함수를 동시에 가지고 있을 때
  • Frida가 "ambiguous overload" 에러를 줄 때

 

MainActivity 루팅 다음단계로 텍스트를 입력받고 확인하는 과정이 존재함

 

받은 텍스트를  17번째 줄에서 bArrA 스트링 문자열을 비교하고있는 과정이 보인다 12번째 줄을 보면 특정 문자열을 base64 디코딩하는 과정을 거치고 sg.vantagepoint.a.a.a 으로 보내는것을 볼수 있다

 

9번 째줄을 확인하면 AES/ECB 모드로 복호화를 진행하는 것을 볼수 있다 (ECB 모드는 IV값 불필요, CBC모드는 IV값 필요)

 

이에따라 CyberChef로 문자열을 집어 넣은 후 base64 , AES Decrypt (ECB) 작업을 진행하여 암호화된 문자열을 복호화 할 수 있다.