본문 바로가기
mobile해킹/안드로이드

Uncrackable level 1 (정적분석 Part.1)

by HHack 2023. 5. 23.
반응형

Uncrackable level 1 정적분석으로 풀기

* 준비물 : jadx-gui, apk_easy_tool, Nox, UnCrackable-Level1.apk

0. UnCrackable-Level1.apk 설치

Step 1) UnCrackable-Level1.apk 깃허브에서 apk 파일을 다운 받을 수 있습니다.

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

Uncrackable level 1 다운 받는 깃허브 주소

Step 2) 다운 받은 apk 파일을 녹스에 드래그&드롭 하여 설치하고 실행시킨다.

루팅이 탐지 되었다고 알림창이 뜬다.

실행하면 루팅 탐지 알림이 뜨며 OK를 누르면 자동으로 앱이 종료된다.

먼저 루팅 탐지 우회부터 실습해 보도록 하겠습니다.

1. 루팅 탐지 우회

(1) jadx-gui로 디컴파일 하기

Step 1) jadx-gui를 실행하고 다운받은 UnCrackable-Level1.apk 파일을 드래그&드롭 한 뒤 메인 액티비티를 찾아간다. (sg.vantagepoint -> uncracakable1 -> MainActivity)

MainActivity 소스코드

이 중 onCreate 부분을 보면 루팅 탐지하는 알림 창을 띄우는 기능이 존재함을 알 수 있다.

import sg.vantagepoint.a.b;
import sg.vantagepoint.a.c;

@Override // android.app.Activity
protected void onCreate(Bundle bundle) {
	if (c.a() || c.b() || c.c()) {
		a("Root detected!");
	}
	if (b.a(getApplicationContext())) {
		a("App is debuggable!");
	}
	super.onCreate(bundle);
	setContentView(R.layout.activity_main);
}

코드를 해석해보면 sg.vantagepoint.a 패키지의  c클래스의 a, b, c 메서드 중 하나라도 true가 나오면 루팅 탐지 알림 창이 나오게 된다. 

 

Step 2) c클래스를 찾아가서 메소드를 확인해 보자. c.a에서 a에 마우스를 올리고 Ctrl과 같이 클릭하면 해당 메서드로 한 번에 이동한다.

c클래스에 나와있는 루팅 탐지 메소드 3가지

하나하나 코드를 해석해보자.

package sg.vantagepoint.a;

import android.os.Build;
import java.io.File;

/* loaded from: classes.dex */
public class c {
    public static boolean a() {
        for (String str : System.getenv("PATH").split(":")) {
            if (new File(str, "su").exists()) {
                return true;
            }
        }
        return false;
    }

    public static boolean b() {
        String str = Build.TAGS;
        return str != null && str.contains("test-keys");
    }

    public static boolean c() {
        for (String str : new String[]{"/system/app/Superuser.apk", "/system/xbin/daemonsu", "/system/etc/init.d/99SuperSUDaemon", "/system/bin/.ext/.su", "/system/etc/.has_su_daemon", "/system/etc/.installed_su_daemon", "/dev/com.koushikdutta.superuser.daemon/"}) {
            if (new File(str).exists()) {
                return true;
            }
        }
        return false;
    }
}

① c.a 메서드

java.lang.System.getenv 메소드는 현재 시스템의 환경변수 값을 얻어오는 메서드이다.즉, 녹스에 있는 환경변수 값을 읽어 File.exists() 메소드를 통해 su 명령어(바이너리)가 존재하는지 판단하여 루팅 탐지를 수행한다. 녹스에 설정되어 있는 환경 변수를 확인해 보자.

Step 1) cmd에 "nox_adb shell" 명령어로 녹스 shell에 진입

Step 2) printenv 명령어를 입력하면 설정되어 있는 PATH 값을 확인할 수 있다.

nox_adb shell 명령어로 nox에 진입 후 녹스에 설정되어 있는 환경변수 확인
sbin 디렉터리 확인

sbin 디렉터리 확인 결과 su명령어를 실행하는 환경변수는 존재하지 않는 것을 확인할 수 있다. 다른 디렉터리도 확인해 봐도 좋다. 현재 내 녹스에는 su명령어와 관련한 디렉터리는 존재하지 않는다.

 

c.b 메서드

App이 "test-keys"로 sign 되어있으면 root로 탐지한다. 즉, UnCrackable-Level1.apk 파일을 디컴파일하여 소스코드를 조작하고 "test-keys"라는 keystore로 signing을 수행하여 기기에 sideload 한 App을 감지하겠다는 뜻이다. 하지만 다른 keystore를 활용하여 signing 하는 경우에는 우회가 가능하다.

 

③ c.c 메서드

시스템 내에 String[]에 포함되는 경로의 파일이 하나라도 존재하면 root로 취급한다. a(), c()의 경우에는 superSu 프로그램을 통해 루팅 한 기기를 App단에서 탐지하는 함수들로 su, superuser.apk 등의 키워드를 이용하여 루팅 감지를 시도한다.

 

정석적인 풀이는 a, b, c 메서드 모두 true로 변조하는 게 정석이지만 나는 메인 액티비티에서 System.exit(0) 부분을 주석처리해서 OK를 눌러도 앱이 꺼지지 않도록 변조할 것이다. 

 

Step 3) APK Easy Tool을 활용하여 UnCrackable-Level1.apk를 디컴파일 한다.

apk 파일의 경로를 입력하여 디컴파일을 수행한다.

Step 4) 디컴파일한 파일에서 메인 액티비티 경로에 있는 smail 코드를 확인한다.

경로 : ~\APK Easy Tool v1.60 Portable\1-Decompiled APKs\UnCrackable-Level1\smali\sg\vantagepoint\uncrackable1\
MainActivity.smail

그 중에서 OK 메시지 창이 나오는 메인 액티비티의 a메소드를 확인해보자.

.method private a(Ljava/lang/String;)V
    .locals 3

    new-instance v0, Landroid/app/AlertDialog$Builder;

    invoke-direct {v0, p0}, Landroid/app/AlertDialog$Builder;-><init>(Landroid/content/Context;)V

    invoke-virtual {v0}, Landroid/app/AlertDialog$Builder;->create()Landroid/app/AlertDialog;

    move-result-object v0

    invoke-virtual {v0, p1}, Landroid/app/AlertDialog;->setTitle(Ljava/lang/CharSequence;)V

    const-string p1, "This is unacceptable. The app is now going to exit."

    invoke-virtual {v0, p1}, Landroid/app/AlertDialog;->setMessage(Ljava/lang/CharSequence;)V

    const-string p1, "OK"

    new-instance v1, Lsg/vantagepoint/uncrackable1/MainActivity$1;

    invoke-direct {v1, p0}, Lsg/vantagepoint/uncrackable1/MainActivity$1;-><init>(Lsg/vantagepoint/uncrackable1/MainActivity;)V

    const/4 v2, -0x3

    invoke-virtual {v0, v2, p1, v1}, Landroid/app/AlertDialog;->setButton(ILjava/lang/CharSequence;Landroid/content/DialogInterface$OnClickListener;)V

    const/4 p1, 0x0

    invoke-virtual {v0, p1}, Landroid/app/AlertDialog;->setCancelable(Z)V

    invoke-virtual {v0}, Landroid/app/AlertDialog;->show()V

    return-void
.end method

라인 20~22번 라인을 확인해보면 MainActivity$1을 불러오고 있다. 아마 MainActivity$1 안에 DialogInterface.OnClickListener 를 구현하는 클래스가 있을 것으로 보이며 onClick 메서드가 호출될 것이다. 즉, MainActivity$1를 수정해야 exit(0)을 우회할 수 있다.

 

Step 5) 디컴파일한 파일에서 메인 액티비티 경로에 있는 MainActivity$1의 smail 코드 중 invoke-static {p1}, Ljava/lang/System;->exit(I)V 를 주석 처리한다.

.class Lsg/vantagepoint/uncrackable1/MainActivity$1;
.super Ljava/lang/Object;

# interfaces
.implements Landroid/content/DialogInterface$OnClickListener;


# annotations
.annotation system Ldalvik/annotation/EnclosingMethod;
    value = Lsg/vantagepoint/uncrackable1/MainActivity;->a(Ljava/lang/String;)V
.end annotation

.annotation system Ldalvik/annotation/InnerClass;
    accessFlags = 0x0
    name = null
.end annotation


# instance fields
.field final synthetic a:Lsg/vantagepoint/uncrackable1/MainActivity;


# direct methods
.method constructor <init>(Lsg/vantagepoint/uncrackable1/MainActivity;)V
    .locals 0

    iput-object p1, p0, Lsg/vantagepoint/uncrackable1/MainActivity$1;->a:Lsg/vantagepoint/uncrackable1/MainActivity;

    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    return-void
.end method


# virtual methods
.method public onClick(Landroid/content/DialogInterface;I)V
    .locals 0

    const/4 p1, 0x0

    invoke-static {p1}, Ljava/lang/System;->exit(I)V

    return-void
.end method

MainActivity$1의 smail 코드를 확인해보니 invoke-static {p1}, Ljava/lang/System;->exit(I)V 부분이 확인되었다. 이 줄을 주석처리 하면 exit(0)을 우회하여 OK를 눌러도 앱이 꺼지지 않는다. 41번 라인을 주석처리하는 #을 붙여 # invoke-static {p1}, Ljava/lang/System;->exit(I)V 로 수정 해준다.

 

Step 6) 수정한 파일을 APK Easy Tool로 리컴파일 한다.

수정된 apk 파일 저장 경로 :  ~\APK Easy Tool v1.60 Portable\2-Recompiled APKs

smaill 수정 후 리컴파일 수행

 

Step 7) 새로운 APK 파일을 녹스로 실행시킨다.

루팅 탐지 우회에 성공화여 다음 화면으로 넘어간 화면이다.

하지만 이제 시작이다. 원하는 secret 값을 넣어야 success가 나오며 풀리게 된다. 암복호화 과정은 다음 포스팅에 설명하도록 하겠다.

반응형

'mobile해킹 > 안드로이드' 카테고리의 다른 글

Uncrackable level 2(Frida 활용)  (0) 2023.05.30
Uncrackable level 1 (정적분석 Part.2)  (0) 2023.05.24