여러분, 혹시 내가 만든 앱이 조작될 가능성이 있지 않을까 하는 상상 한 번쯤은 해보시지 않았나요? 저는 안드로이드 개발자긴 하지만, 보안 지식이 그렇게 많진 않아 단순히 '세상에 완벽한 방어막은 없겠지' 정도로 순진하게 생각하고 살았는데요. 그랬던 제가 최근 업무를 하다가 알게 된 Frida라는 툴킷으로 새롭고 놀라운 경험을 하게 되었습니다. 그건 바로 앱 자체를 수정이나 조작하는 것이 아니라, 정상적으로 앱이 실행되는 상황임에도 동적 후킹으로 앱의 동작을 조작할 수 있다는 것이었어요.
🤔 도대체 어떻게 하는거야?
Frida는 DBI(Dynamic Binary Instrumentation) 프레임워크입니다. Android 환경뿐만 아니라 Windows, macOS, GNU/Linux, iOS 모든 곳에서 동작하죠. Frida API를 사용하면 공격자가 원하는 코드를 작성해 후킹이나 함수 추적 등의 행위를 할 수 있다고 해요.
저도 알게 된 지 얼마 안 되었기에 기술적인 내부 원리를 이 글에서 면밀히 다루진 않습니다. 다만 Frida를 사용해 본 경험을 바탕으로 조작 방법 및 결과를 소개하고 이를 방지할 수 있는 방법을 간략히 다뤄보려고 합니다.
Android 앱을 테스트하기 위해 PC 환경과 모바일 환경을 설정해야 합니다. PC에선 Frida가 조작 시도를 하고, 모바일에선 그 시도를 받아 처리할 Frida Server가 필요하기 때문입니다. 이 글에선 PC는 macOS, 모바일은 x86 기반의 Android API 30 AVD로 진행하겠습니다.
조작 시도를 하려면 분석을 먼저 해야겠죠? 안드로이드 APK를 분석할 수 있는 디컴파일러인 jadx-gui와 실제 조작을 담당하는 Frida를 설치합니다.
APK 파일 분석 결과를 기반으로 조작용 코드가 완성되었다면, 이를 전달받아 모바일에서 동작시켜줄 주체가 필요합니다. 이를 frida-server라고 하며, 실행에 루트 권한이 필요하므로 아래와 같이 환경을 만들어줍니다.
adb shell getprop ro.product.cpu.abi
x86
기준입니다.wget https://github.com/frida/frida/releases/download/16.1.4/frida-server-16.1.4-android-x86.xz
다운로드한 파일을 압축 해제 후 파일명을 간단하게 바꿉니다.
unxz frida-server-16.1.4-android-x86.xz
mv frida-server-16.1.4-android-x86 frida-server
관리자 권한으로 frida-server를 테스트폰에 옮겨 실행되도록 만듭니다.
adb root
adb push frida-server /data/local/tmp/
adb shell "chmod 755 /data/local/tmp/frida-server"
마지막으로 테스트폰에서 frida를 실행하면 됩니다. 터미널에서 계속 돌아가기 때문에 일시정지된 것처럼 보여도 정상이니 걱정마세요. [주의] 이때 테스트폰에 Frida가 사용하려고 하는 포트 번호가 이미 사용 중 일 수 있습니다. https://domdom.tistory.com/485 참고하여, 해당 프로세스를 종료하면 됩니다. (Frida가 구동되는 포트를 바꾸는 방법도 있는데, 그거 바꾸면 또 바꾸고 오류메시지 뜨는 것들이 딸려와서.. 그냥 기존 프로세스를 죽이거나 다른 AVD 사용하는 게 더 정신건강에 이롭습니다.)
adb shell "/data/local/tmp/frida-server &"
테스트용 앱 apk 파일: https://github.com/OWASP/owasp-mastg/tree/master/Crackmes/Android/Level_01
앱을 테스트폰에 설치하고 실행하면 아래와 같이 켜자마자 다이얼로그가 나오며, OK를 누르면 앱이 종료되어 버립니다.
우리의 목표는 저 다이얼로그가 표시되지 않도록 하는 것입니다. 다만 앱을 직접 수정하지 않고 앱의 코드를 후킹해야 하죠. 그러면 어떤 코드를 후킹 해야 할지 알아야겠죠? 그렇기에 위에서 APK 파일을 디컴파일해서 내부 소스를 확인하기 위해 jadx
를 설치했던 겁니다.
jadx-gui UnCrackable-Level1.apk
를 통해 아래와 같이 apk 파일을 디컴파일하고 내부 소스 트리 구조를 살펴보면, MainActivity
를 찾을 수 있습니다.
노란색으로 강조된 부분에서 앱 강제 종료가 수행되고 있습니다. MainActivity
내의 a
라는 이름으로 난독화된 메서드에서 발생하고 있네요. 또한 a
에서 다이얼로그를 띄워주고 있음도 알 수 있습니다. 여기서 Frida를 통해 a
라는 메서드를 후킹 해서, 내부 구현부가 아무 동작도 하지 않도록 조작하면 어떻게 될까요? 다이얼로그도 뜨지 않을 테고, 다이얼로그가 뜨지 않으니까 앱도 강제 종료되지 않겠죠. 이것을 위한 자바스크립트 코드를 아래와 같이 작성할 수 있습니다.
// frida_uncrackable.js
console.log("Starting..");
Java.perform(function () {
var MainActivity = Java.use("sg.vantagepoint.uncrackable1.MainActivity");
MainActivity.a.implementation = function (arg1) {
return;
}
});
이제 작성한 JS 코드를 아래와 같이 실행하면 됩니다. owasp.mstg.uncrackable1
는 앱의 패키지명입니다.
frida -U -l frida_uncrackable.js -f owasp.mstg.uncrackable1
그럼 아래처럼 다이얼로그가 안 뜹니다!!
Frida는 앱을 동적으로 조작할 수 있으므로 앱에서 Frida 프로세스가 동작중인지 탐지한 뒤에야 조작을 방지할 수 있습니다. 탐지 방법에 대한 상세 내용은 Android 환경 Frida 탐지에서 확인하실 수 있습니다. 다만 예시 코드가 C로 되어있습니다. 아직 저는 NDK/JNI 개발 경험이 없고, 상세 방지책 적용법까지 다루면 글이 길어질 듯 하네요. 추후 시간적 여유가 되면 방지하는 방법을 다른 글로 정리해 보겠습니다. 상세 내용에서 다루는 코드를 통해 탐지한 Frida 프로세스를 강제 종료하는 방향으로 구현하면 되리라 생각합니다.
https://youtu.be/iMNs8YAy6pk?si=RSRB1O9yTDRCXED5 를 참고하시면 Frida와 관련된 보다 상세 내용을 확인하실 수 있습니다. 2023.09.14 작성 시점 기준으로 삽질했던 경험을 글에 담았으니, 여기까지 다 읽으신 분은 덜 삽질하시기를 바라요 :)