Android hooking with Frida Framework(feat. Uncrackable1.apk)
오늘은 Frida 를 이용한 안드로이드 후킹을 해보려고 합니다. 후킹이라는 기술은 특정 분야에서만 사용하는게 아닌 다양한 분야에서 활용되므로 알아두는 것이 좋을 것 같습니다.
우선적으로 Hooking(후킹)이라 함은 컴퓨터공학 관점에서는 소프트웨어나 응용프로그램들 사이에서 발생하는 함수 호출, 메시지, 이벤트 등을 중간에서 가로채 해당 기능이나 내용을 변경하는 기술을 말합니다. 후킹이 정확하게 어떤 값을 조작하는지는 좀 더 깊게 공부해봐야 할 것 같습니다.
실습에 사용될 apk에는 보통 OWASP 에서 만들어둔 Uncrackable 시리즈를 씁니다.
https://github.com/OWASP/owasp-mstg/tree/master/Crackmes/Android
GitHub - OWASP/owasp-mstg: The Mobile Security Testing Guide (MSTG) is a comprehensive manual for mobile app security testing an
The Mobile Security Testing Guide (MSTG) is a comprehensive manual for mobile app security testing and reverse engineering. It describes the technical processes for verifying the controls listed in...
github.com
해당 앱을 다운 후 adb 를 통해 애뮬레이터에 설치 후 실행시켰더니.
으잉? 루팅이 감지되었다고 Ok 버튼을 누르면 무조건 앱이 종료되는군요..우선 어떤 호출들이 이런 결과를 만드는지 소스코드를 봐야 할 것 같습니다. jadx를 통해서 소스코드를 까보겠습니다.
c 클래스에 a,b,c 메소드를 통해 검증을 해서 뭔가 걸리면 루팅이 탐지되었다는 구문을 a 메소드로 보내고 있네요. 그럼 알람 창은 a 메소드가 아웃풋하는 것 같으니. a 메소드로 가보겠습니다.
우리가 알람창에서 봤던 구문을 보여주고, ok버튼을 클릭하면
System.exit(0) 함수를 call 해서 앱을 종료하는 거였군요.
이러면 구조는 전부 파악을 했습니다.
이제 frida 를 통해 후킹을 진행해보겠습니다. 아래 코드는 기본 후킹 코드 예제로 많이 쓰이고 있어서 가져왔습니다. 해당 jscode 함수에 내가 원하는 행동을 정의하고 후킹을 진행할 수 있습니다.
import frida, sys
#후킹할 앱 클래스
Hook_package = "owasp.mstg.uncrackable1"
def on_message(message, data):
print("{} -> {}".format(message, data))
#조작할 행위
jscode = """
Java.perform(function(){
var hookClass = Java.use("java.lang.System")
hookClass.exit.implementation = function() {
console.log("Hooking Success!!!");
}
});
"""
try:
device = frida.get_usb_device(timeout=10)
pid = device.spawn([Hook_package])
print("App is starting.. pid:{}".format(pid))
process = device.attach(pid)
device.resume(pid)
script = process.create_script(jscode)
script.on('message', on_message)
print("[-] Running FR1DA!")
script.load()
sys.stdin.read()
except Exception as e:
print(e)
frida 환경 구축 과정은 생략하였습니다!
위의 jscode 에 의하면 원래는 꺼져야할 앱이 꺼지지 않고, 콘솔에 hooking success 라는 문구가 출력될 것입니다.
해당 코드를 실행시키면, 이제 ok 버튼을 눌러도 꺼지지않고 콘솔에 공격자가 조작한 문구가 출력되는 것을 볼 수 있습니다.!!
VERIFY 버튼이 있는것을 보니 뭔가 검증과 일치하는 문자열을 넣어야하는 기분이 싸악 듭니다. 다시 코드로 넘어가보죠.
verify 라는 함수에서 저의 입력값을 a 클래스의 a함수로 넘겨 검증하고 있었습니다. a.a를 가보면 되겠죠?
저의 입력값(str)과 sg.vantagepoint.a.a.a() 에서 넘어온 값을 비교하고 있네요!
그렇다면 vantagepoint.a.a.a() 메소드로 가보겠습니다.
여기서 bArr = b("8d ~~~), bArr2 = Base64.decode("~~~") 입니다.
바이트 인자를 두개 받고있습니다. 첫번째 인자는 SecretKeySpec() 함수에 사용되고 있습니다.
해당 함수를 오라클 독스에 찾아보면, 첫 번째 인자는 암호화에 사용되는 key에 사용된다고 합니다. ->bArr의 정체는 키였습니다.
그리고 위의 bArr2(Base64)는 instance.doFinal() 함수에 사용되면서 리턴값으로 사용되고 있네요.
그렇다면 또 오라클 독스로 가봅시다.
한줄요약 - 해당 바이트 복호화해주는 함수입니다.
특이하게 암호화된 데이터를 base64로 디코딩한 데이터를 받아서 복호화하는 것 같습니다. 자바 특징인가..?
쨋든 우리가 원하는 문자열이 저기에 있는 것이죠 !!
후킹 코드입니다.
해당 기능을 하는 함수를 불러와 인자 값을 arg1(=키), arg2(=base64디코딩된 암호화데이터값) 으로 받아왔습니다.
그리고 다시 a 메소드로 보내서 복호화를 진행하여 복호화가 완료된 값(우리가 원하는 값) 을 result에 저장하여 console.log() 하려 했으나
byte로 리턴되기 때문에 for문을 사용해서 한글자씩 변수에 넣고 한번에 출력해주었습니다.
#불러올 메서드
var hookClass = Java.use("sg.vantagepoint.a.a");
#행동조작 - 인자값 가져옴
hookClass.a.implementation = function(arg1, arg2){
#함수에 넣고 리턴값 가져옴(복호화된 데이터)
var result = this.a(arg1,arg2);
var flag = "";
for(var i=0; i<result.length; i++){
flag += String.fromCharCode(result[i]);
}
#복호화된 데이터 출력
console.log("FLAG : "+flag);
#원래 함수에서 반환해야할 값 반환해주어야 함(그래야 에러가 안납니다)
return result
}
이제 frida를 실행하고 아무 값이나 넣으면 콘솔에 정답이 출력되겠죠?
짜잔!
후킹이라는 기술에 굉장히 감탄했고, 사용자에 따라 무궁무진하게 발전할 수 있는 것 같습니다. 앞으로 열공할 분야 !!!