안드로이드 앱 분석

KIM DO YOON·2025년 6월 6일
0

정적, 동적 분석 방법과 APK 추출, Smali 코드 분석, ARM 아키텍처, Frida 를 이용한 동적 후킹에 대한 내용입니다.

정적 분석

정적 분석은 애플리케이션을 실행하지 않고 코드와 리소스를 분석하는 방법이다.

정적 분석의 목적

  1. 앱의 동작 로직 파악: 애플리케이션의 전체적인 구조와 실행 흐름을 이해
  2. 중요 정보 저장 여부 확인: 하드코딩된 API 키, 패스워드, 암호화 키 등의 민감한 정보 탐지
  3. 보안 취약점 식별: 부적절한 권한 설정, 안전하지 않은 데이터 저장 방식 등 확인
  4. 코드 품질 평가: 난독화 정도, 코드 복잡성, 보안 메커니즘 구현 상태 점검

APK 추출 방법

1. 백업 기능을 이용한 APK 추출

안드로이드 파일 관리자 앱을 활용한 추출 방법

2. ADB를 이용한 추출

개발자 옵션의 USB 디버깅을 활성화 한 후 ADB 명령어를 사용하는 방법

#설치된 패키지 목록 확인
adb shell pm list packages -f | grep 패키지명

#APK 파일 추출
adb pull /data/app/패키지경로/base.apk ./추출할파일명.apk

3. APK추출 전용 앱 활용

4. 온라인 APK 다운로다 활용

Smali 분석

Smali 는 Android DEX 바이트코드를 표현하는 어셈블리 언어로, APK 파일을 디컴파일하여 얻을 수 있다.

Smali 자료형 타입

V : void (반환값 없음)
Z : boolean (불린)
B : byte (바이트)
S : short (16비트 정수)
C : char (문자)
I : int (32비트 정수)
J : long (64비트 정수)
F : float (32비트 부동소수점)
D : double (64비트 부동소수점)
L : 클래스 타입 (예: Ljava/lang/String;)
[ : 배열 (예: [I는 int 배열)

메서드 종류

Static 메서드 : 'invoke-static'으로 호출되며, 'this' 인수가 암묵적으로 전달되지 않는 메서드

Direct 메서드 : 'invoke-direct'로 호출되며, 생성자나 private 메서드가 해당된다. vtable 개입 없이 직접 호출된다.

Virtual 메서드 : 'invoke-virtual'로 호출되며, 자식 클래스에서 override될 수 있는 메서드, 클래스와 관련된 vtable을 사용하여 호출된다.

주요 Smali 코드 스니펫

SO 라이브러리 로드:

const-string v0, "라이브러리명"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V

로그 출력:

const-string v8, "log-tag"
invoke-static {v1}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;
move-result-object v9
invoke-static {v8, v9}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

Toast 문자열 띄우기:

const/4 v0, 0x1
const-string v1, "메시지 내용"
invoke-static {p0, v1, v0}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
move-result-object v0
invoke-virtual {v0}, Landroid/widget/Toast;->show()V

ARM 아키텍처 이해

CISC (Complex Instruction Set Computer):

  • 명령어의 길이가 가변적으로 구성됨
  • 하나의 명령어로 복잡한 기능 수행 가능
  • 명령어의 길이가 달라서 동시에 여러 개 명령어 처리 불가

RISC (Reduced Instruction Set Computer):

  • 고정된 길이의 명령어 사용
  • 적은 수의 명령어를 가지고 복잡한 명령어 수행
  • 각 명령어가 한 클럭에 실행되도록 고정됨, 파이프라인 성능에 최적화
  • 컴파일러 최적화 과정이 복잡
  • 명령어 길이가 고정되어 있어 코드 효율이 낮음

ARM 모드

ARM 모드 : 32비트 명령어 세트를 사용하는 기본 모드
Thumb 모드 : 16비트 명령어 세트를 사용하는 모드, 메모리 효율성을 높이기 위해 도입됨.

AAPCS에 의한 레지스터

ARM 아키텍처에서는 AAPCS에 따라 레지스터가 정의된다.

r0-r3   : 함수 인자 및 반환값
r4-r11  : 지역 변수 저장
r12     : IP (Intra-Procedure-call scratch register)
r13(SP) : 스택 포인터
r14(LR) : 링크 레지스터 (복귀 주소)
r15(PC) : 프로그램 카운터 (다음 실행할 명령어 주소)

ARM 함수 호출 규약

  1. 프롤로그: 함수 호출 직전에 r4부터 r11까지 스택에 저장(push)하고 r14(복귀 주소)를 스택에 저장
  2. 인자 전달: r0~r3에 함수 인자 전달, 4개 이상이면 스택 사용
  3. 지역 변수: r4~r11 중 남아있는 곳에 할당
  4. 함수 수행: 실제 함수 로직 실행
  5. 반환값: r0에 반환값 저장
  6. 에필로그: 스택에서 r4~r11 값을 복구하고 r15(PC)에 복귀 주소를 덮어써서 원래 위치로 복귀

주요 ARM 명령어

데이터 이동:

MOV r1, r2          ; r2 값을 r1으로 이동

시프트 연산:

ASR r1, r2, #4      ; r2를 오른쪽으로 4비트 시프트 (부호 유지)
LSR r1, r2, #4      ; r2를 오른쪽으로 4비트 시프트 (0으로 채움)
LSL r1, r2, #4      ; r2를 왼쪽으로 4비트 시프트
ROR r1, r2, #4      ; r2를 오른쪽으로 4비트 회전

곱셈 연산:

UMULL r4, r5, r1, r2    ; 부호 없는 곱셈 (64비트 결과)
SMULL r4, r5, r1, r2    ; 부호 있는 곱셈 (64비트 결과)

분기 명령어:

B <address>         ; 무조건 분기
BL <address>        ; 서브루틴 호출 (LR에 복귀 주소 저장)
BNE <address>       ; 0이 아닌 경우 분기
BEQ <address>       ; 0이면 분기
CMP r1, r2          ; r1과 r2 비교

메모리 접근:

LDR r1, [r2]        ; 메모리에서 레지스터로 데이터 로드
STR r1, [r2]        ; 레지스터에서 메모리로 데이터 저장
LDMFD sp!, {r4-r6, pc}  ; 스택에서 여러 레지스터 복원
STMFD sp!, {r4-r6, lr}  ; 스택에 여러 레지스터 저장

동적 분석

동적 분석은 애플리케이션을 실제로 실행하면서 런타임 동작을 관찰하고 조작하는 분석 기법이다.

동적 분석 기법

  1. 디버깅: 애플리케이션 실행 중 중단점 설정하여 상태 확인
  2. 후킹: 함수 호출을 가로채어 동작 변경 또는 관찰
  3. 프록시: 네트워크 통신 가로채기 및 분석
  4. 코드패치: 런타임에 코드 수정

함수 후킹

함수 후킹은 소프트웨어 구성 요소 간에 발생하는 함수 호출, 메시지 이벤트 등을 중간에서 바꾸거나 가로채는 기법. 함수가 실행 직후와 반환 직전에 임의의 코드를 삽입하여 실행시키거나, 함수 실행 흐름을 임의의 코드로 변경할 수 있다.

Frida 프레임워크

Frida 타켓 프로세스에 프레임워크 agent을 인젝션하여 파이프를 만들어 놓고, 그 파이프를 통해 통신하며 인라인 후킹을 수행하는 도구. 프로세스의 메모리 영역에 Frida agent 가 삽입되는 방식으로 동작한다.

Frida 사용 방식

Spawn 방식

  • 앱 프로세스를 메모리에 적재한 뒤 실행 전에 스크립트를 로드하고 실행시키는 방식
  • 앱 프로세스 시작에 동작하는 탈옥, 루팅 무결성 검증 우회에서 유용
  • 메인 스레드 시작 전에 함수를 후킹할 수 있음
frida -U -l [script.js] -f [package name]

Attach 방식

  • 실행 중인 프로세스에 스크립트를 로드하는 방식
  • 앱 프로세스에서 동적으로 로딩되는 라이브러리 함수를 후킹할 때 유용
frida -U -l [script.js] -n [앱 이름]
frida -U -l [script.js] -p [PID]

Frida 스크립트 기본 구조

프로세스 제어 :

//Spawn 방식
var device = frida.get_usb_device();
var pid = device.spawn(["com.package.name"]);

//Attach 방식
var process_session = device.attach(pid);

통신 방법:

car op = recv('input', function(value){
  // 실행될 함수
});
op.wait();

//메시지 전송
send("전송할 데이터");

Java 후킹

기본 후킹 구조:

Java.perform(function() {
    var targetClass = Java.use("com.example.TargetClass");
    
    targetClass.targetMethod.implementation = function(param1, param2) {
        console.log("함수 호출됨: " + param1 + ", " + param2);
        
        // 원래 함수 호출
        var result = this.targetMethod(param1, param2);
        
        console.log("반환값: " + result);
        return result;
    };
});

메서드 오버로딩 처리:

Java.perform(function() {
    var targetClass = Java.use("com.example.TargetClass");
    
    // 특정 시그니처 지정
    targetClass.overloadedMethod.overload('java.lang.String', 'int').implementation = function(str, num) {
        console.log("String, int 버전 호출");
        return this.overloadedMethod(str, num);
    };
});

Interceptor를 이용한 네이티브 후킹

함수 주소 후킹:

Interceptor.attach(ptr("0x12345678"), {
    onEnter: function(args) {
        console.log("함수 진입");
        console.log("인자1: " + args[0]);
        console.log("인자2: " + args[1]);
    },
    onLeave: function(retval) {
        console.log("함수 종료");
        console.log("반환값: " + retval);
    }
});

SO 라이브러리 후킹:

Java.perform(function() {
    // System.loadLibrary 후킹
    var System = Java.use("java.lang.System");
    System.loadLibrary.implementation = function(libname) {
        console.log("라이브러리 로드: " + libname);
        
        var result = this.loadLibrary(libname);
        
        // 라이브러리 로드 후 네이티브 함수 후킹
        if (libname === "target_library") {
            var nativeFunc = Module.findExportByName("libtarget_library.so", "native_function");
            if (nativeFunc) {
                Interceptor.attach(nativeFunc, {
                    onEnter: function(args) {
                        console.log("네이티브 함수 호출");
                    }
                });
            }
        }
        
        return result;
    };
});

네트워크 분석

TCPdump

네트워크 패킷을 캡쳐하여 분석하는 도구

MITM Proxy

중간자 공격 방식으로 HTTP/HTTPS 통신을 가로채고 분석할 수 있는 도구

Burp Suite

앱 데이터 구조

앱이 설치된 후 생성되는 주요 디렉터리 구조는 다음과 같다.

/data/data/패키지명/
├── cache/          # 앱에서 사용하는 캐시 파일
├── databases/      # SQLite DB 파일 저장
├── lib/           # 앱에서 사용하는 .so 파일 저장
├── shared_prefs/  # SharedPreferences 파일
└── files/         # 앱에서 생성하는 일반 파일

정적 분석 보완 도구

MobSF (Mobile Security Framework): 모바일 앱 보안 분석을 위한 통합 플랫폼으로 정적/동적 분석을 모두 지원

QARK (Quick Android Review Kit): 안드로이드 앱의 보안 취약점을 자동으로 탐지하는 도구

AndroBugs: 안드로이드 앱의 잠재적 보안 문제를 식별하는 정적 분석 도구

동적 분석 보완 기법

Xposed Framework: 안드로이드 시스템을 수정하지 않고도 앱의 동작을 변경할 수 있는 프레임워크

Objection: Frida 기반의 런타임 모바일 익스플로잇 도구킷으로, SSL 핀닝 우회, 파일시스템 접근, 메모리 검색 등의 기능을 제공

보안 우회 기법

루팅 탐지 우회: 앱에서 루팅된 환경을 탐지하는 로직을 우회하는 기법

SSL 핀닝 우회: 앱에서 구현된 SSL 인증서 고정을 우회하여 HTTPS 통신을 분석할 수 있게 하는 기법

안티 디버깅 우회: 앱에서 디버깅을 탐지하고 차단하는 메커니즘을 우회하는 기법

profile
안녕하세요 김도윤 입니다.

0개의 댓글