[Rev] 리버싱 · 작성: 2026-07-19 19:34:18 · 수정: 2026-07-21 23:30:21 · 조회 10
안드로이드 앱 리버싱을 "APK 압축을 풀면 되는 것"이라고만 알면, 그 안의 코드가 x86 어셈블리가 아니라 **완전히 다른 바이트코드(Dalvik/DEX)**로 되어 있다는 것과, 그걸 왜 사람이 읽을 수 있는 Java 유사 코드로 거의 완벽하게 되돌릴 수 있는지를 놓치게 된다.
APK는 사실 ZIP 파일이다 — unzip app.apk만 해도 구조가 드러난다.
AndroidManifest.xml: 앱의 권한(퍼미션), 컴포넌트(액티비티/서비스), 진입점을 정의 — 바이너리 XML 형식이라 apktool로 디코딩해야 사람이 읽을 수 있다classes.dex: Dalvik 바이트코드 — 앱의 실제 로직이 여기 담겨있다resources.arsc, res/: 이미지, 문자열, 레이아웃 등 리소스lib/: 네이티브 코드(.so, C/C++로 작성된 부분)가 있다면 여기 — 이 부분은 PE 포맷 글에서 다룬 것과 달리 ELF 형식(안드로이드는 리눅스 기반)이라, 정적 분석 글에서 다룬 ELF 분석 기법을 그대로 적용할 수 있다C/C++를 네이티브 기계어로 컴파일하면 변수 이름·타입 정보 상당 부분이 소실되지만, 자바(Kotlin 포함)는 JVM/Dalvik 바이트코드로 컴파일될 때 타입 시스템과 클래스 구조 정보를 바이트코드 안에 그대로 유지해야 한다 — 리플렉션, 클래스 로딩 같은 자바 런타임 기능이 이 메타데이터에 의존하기 때문이다. jadx 같은 디컴파일러는 이 풍부한 메타데이터를 이용해서, DEX 바이트코드를 거의 원본과 다름없는 Java 소스코드로 되돌릴 수 있다 — 이게 안드로이드 리버싱이 네이티브 바이너리 리버싱보다 훨씬 "쉬운" 이유이자, 앱 개발자가 ProGuard/R8로 코드를 난독화(변수·클래스 이름을 의미 없는 문자로 치환)해야 하는 이유이기도 하다.
jadx-gui로 APK를 통째로 열어 Java 유사 코드로 디컴파일 — 클래스/메서드 이름이 난독화되지 않았다면 로직 파악이 매우 빠르다apktool d app.apk로 smali(Dalvik 바이트코드의 어셈블리 표현) 추출 — 디컴파일이 실패하거나 코드를 직접 패치해야 할 때는 이 smali 레벨에서 작업한다isDeviceRooted() 같은 함수로 루팅 여부를 체크해서 실행을 막는 경우, jadx로 그 함수를 찾은 뒤 (a) smali를 직접 패치해서 항상 false를 반환하게 만들거나, (b) Frida로 동적 계측 글에서 다룬 Interceptor.attach()로 그 함수의 반환값을 런타임에 조작하는 두 가지 방법 중 하나를 쓴다 — 코드를 영구 패치할지, 실행 중에만 바꿀지의 트레이드오프다lib/*.so)에 중요한 로직(라이선스 체크, 암호화 키)을 숨겨둔 앱이라면, DEX 디컴파일만으로는 부족하고 정적 분석·동적 분석 글에서 다룬 ELF 분석 기법을 그대로 적용해야 한다 — 자바 코드에서 JNI로 네이티브 함수를 호출하는 경계 지점을 먼저 찾는 게 시작점이다안드로이드 리버싱을 "APK 압축 해제"가 아니라 **"자바 런타임이 유지해야 하는 풍부한 타입 메타데이터를 역이용해 원본에 가까운 소스코드를 복원하는 작업"**으로 이해하면, 왜 난독화(ProGuard/R8)가 안드로이드 앱 보호의 첫 단계로 거의 항상 언급되는지가 같은 논리로 설명된다.