실습에 사용한 앱은 OWASP Mobile Security Testing Guide(UnCrackable Level 1) APK이다.

1. Jadx로 클래스/메서드 확인
Frida로 후킹을 하기 위해서는 정확한 클래스명과 메서드명을 알아야 한다.
APK 디컴파일 및 MainActivity 확인

암호화 관련 클래스 확인

a(String str) → 문자열 검증
b(String str) → 문자열 → 바이트 배열 변환
내부 AES 처리: sg.vantagepoint.a.a.a(byte[], byte[])
2. Frida attach 및 환경 준비
앱 분석을 위해 Frida를 사용하기 전, 먼저 환경을 준비했다.
A. 프로세스 목록 확인
앱이 실행 중인지, Frida에서 접근 가능한지 확인하기 위해 프로세스 목록을 가져왔다.
frida-ps -Uai
-U : USB 또는 에뮬레이터 연결 디바이스 사용
-a : 모든 앱 표시
-i : 정보 표시 강화

이 명령으로 owasp.mstg.uncrackable1 앱 프로세스를 확인했다.
B. 직접 attach 하기
처음에는 PID를 통해 앱에 attach 하고 한 줄씩 코드를 입력하며 테스트했다.
frida -U -p <PID>
- PID를 이용해 attach
- 실시간으로 Frida 콘솔에서 Java 환경에 접근 가능
- 하지만 한 줄씩 코드를 입력해야 했기 때문에, 효율이 떨어짐
3. 스크립트 적용 및 Java 클래스 후킹
효율적인 분석을 위해 Frida 스크립트를 만들어 적용하는 방법으로 변경했다.
frida -U -f owasp.mstg.uncrackable1 -l myscript.js
-f : 앱을 spawn 후 attach
-l : Frida 스크립트 로드
myscript.js : 후킹 및 로그 추적 코드
myscripts.js
Java.perform(function(){
Java.scheduleOnMainThread(function(){
try {
var MainActivity = Java.use("sg.vantagepoint.uncrackable1.MainActivity");
console.log("[*] MainActivity loaded: " + MainActivity.$className);
} catch (e) {
console.log("[!] Error: " + e);
}
});
});

MainActivity가 로드되자 정상적으로 로그가 출력됐다!
이제 앱 내부에 버튼을 눌렀을 때 호출되는 코드를 확인해보자.

예상대로 문자열을 검증하는 a 메서드를 호출한다.
여기서 조건문만 바꿔준다면 Success가 출력되겠지만,
"어떤 문자를 입력해야 되는지"를 파악하고자 했다.
그에 맞게 스크립트를 추가했다.
Java.perform(function() {
Java.scheduleOnMainThread(function() {
try {
var aClass = Java.use("sg.vantagepoint.uncrackable1.a");
var aHelperClass = Java.use("sg.vantagepoint.a.a"); // AES 클래스
// 원래 a(String) 저장
var original_a = aClass.a.overload('java.lang.String');
// a(String) 후킹
aClass.a.overload('java.lang.String').implementation = function(str) {
console.log("[*] a(String) called with arg: " + str);
// Base64 decode 후킹 (임시 하드코딩)
var Base64 = Java.use("android.util.Base64");
var decoded = Base64.decode("5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc=", 0);
console.log(" [*] Base64 decoded bytes: " + bytesToHex(decoded));
// AES 후킹
var original_AES = aHelperClass.a.overload('[B','[B');
aHelperClass.a.overload('[B','[B').implementation = function(keyBytes, dataBytes) {
console.log(" [*] AES key: " + bytesToHex(keyBytes));
console.log(" [*] AES data: " + bytesToHex(dataBytes));
var ret = original_AES.call(this, keyBytes, dataBytes);
console.log(" [*] AES returned: " + bytesToHex(ret));
return ret;
};
// 원래 a(String) 호출
var result = original_a.call(this, str);
console.log("[*] a(String) returned: " + result);
return result;
};
} catch (e) {
console.log("[!] Error: " + e);
}
});
});
// 바이트 배열 → 16진수 문자열
function bytesToHex(byteArray) {
if (!byteArray) return "";
var hex = [];
for (var i = 0; i < byteArray.length; i++) {
hex.push((byteArray[i] & 0xff).toString(16).padStart(2, "0"));
}
return hex.join("");
}
4. 로그 분석

로그의 내용은 다음과 같았다.
[*] Base64 decoded bytes: e5426215cb5b9a06c3a0b5e6a4bd769a49e8f074f82eff1d95ab7c17147618e7
[*] AES key: 8d127684cbc37c17616d806cf50473cc
[*] AES data: e5426215cb5b9a06c3a0b5e6a4bd769a49e8f074f82eff1d95ab7c17147618e7
[*] AES returned: 492077616e7420746f2062656c69657665
[*] a(String) returned: true
Base64 decode → 앱 내부 초기 암호문
AES key / data → 암호화 연산에 사용되는 값
AES returned → 최종 암호문 비교용 값
a(String) returned → 입력값과 내부 암호문의 비교 결과
AES 결과를 hex -> ASCII로 변환하면 "I want to believe"이다.
즉, 사용자가 입력한 값과 비교되는 실제 문자열은 "I want to believe"인 것이다.
트러블슈팅?
- 루트 권한 문제: frida-ps에서 앱 프로세스가 보이지 않음 -> 루트 환경 필요
- 클래스 경로 불일치: Jadx로 확인한 패키지명(sg.vantagepoint.uncrackable1)과 Frida 후킹 시점에 맞추지 않으면 ClassNotFoundException 발생
- 메서드 호출 순서 문제: Java.perform과 scheduleOnMainThread를 적절히 사용해야 후킹 성공
후킹 스크립트 작성 시, 클래스명과 메서드 오버로드 타입을 정확히 확인해야 한다!!
Frida를 통해 단순 디컴파일 이상의 실시간 메서드 추적과 암호화 로직 분석이 가능하다.
Windows 환경에서 OllyDbg를 쓰던 경험이 모바일 앱 분석에도 큰 도움이 되었다.
앞으로는 다양한 암호화/인증 로직을 실시간 후킹으로 분석하며, 앱 취약점 탐지나 보안 연구에 활용할 계획이다.
'Security > Application' 카테고리의 다른 글
| Android SSL Pinning - 런타임 우회 (0) | 2025.09.29 |
|---|---|
| Frida JS/Hooking 문법 규칙 (0) | 2025.09.21 |
| Android 앱 Frida 분석을 위한 개발환경 구축 (0) | 2025.09.04 |
| reversing.kr Music Player 풀이 (1) | 2025.02.16 |
| reversing.kr Unpack 풀이 (0) | 2025.02.16 |