Android App Hooking with Frida(1)





이번 포스팅에서는 Frida를 사용한 android app hooking에 대하여 다루도록 하겠습니다.

(도구 설치와 같은 기초 지식은 다루지 않습니다.)


오늘 시간에는 문제 풀이를 통해 Frida를 사용하여 안드로이드 앱을 분석하고 후킹하는 방법을 학습하도록 하겠습니다.


문제파일은 아래 경로를 통하여 다운받을 수 있습니다.

https://github.com/OWASP/owasp-mstg/tree/master/Crackmes


분석환경

OS: Windows10

Tools: Frida, Frida-server, adb, Python3.6, Genymotion


문제파일(UnCrackable-Level1.apk)을 다운로드하여 설치합니다.

[그림 1 apk설치]


앱 설치 후 실행 시 루팅 탐지가 동작하며 ok버튼을 클릭하면 앱이 강제로 종료됩니다.

[그림 2 루팅탐지]


그럼 문제파일(UnCrackable-Level1.apk)을 jadx를 이용하여 분석을 해보도록 하겠습니다.


먼저 MainActivity 살펴보겠습니다.

if (c.a() || c.b() || c.c()) 조건문을 통해 루팅을 탐지하고 있는것을 확인할 수 있습니다.

마찬가지로 if (b.a(getApplicationContext())) 조건문을 이용하여 디버깅 탐지를 수행하고 있습니다.

[그림 3 onCreate 함수 분석]


그러면 해당 조건문이 참이되어 넘어가는 a함수를 살펴보도록 하겠습니다.

Oncreate 함수에서 넘어온 문자열과 함께 "This in unacceptable. The app is now going to exit." 라는 문자열이 출력되며 OK버튼을 클릭 시 b함수로 이동합니다.

[그림 4 a함수 분석]


b함수는 앱을 종료하는 기능을 수행합니다.

[그림 5 b함수 분석]


그럼 루팅 탐지시 앱이 종료되지 않도록 우회를 시도해 보도록 하겠습니다. 

여러가지 방법이 있을 수 있겠지만 본 포스팅의 목적대로 Frida를 이용한 Hooking으로 우회를 시도하겠습니다.

앞서 설명드린대로 앱 설치 후 실행 시 루팅 탐지가 동작하며 ok버튼이 클릭되면 System.exit 함수가 호출되며 앱이 종료됩니다.


그럼 System.exit 함수가 호출될 때 해당 함수를 Hooking하여 앱이 종료되지 않도록 해보겠습니다.

아래의 코드는 System.exit 함수가 호출되면 단순히 로그만 출력되도록 작성된 frida 모듈을 사용한 파이썬 코드입니다.

Rooting_Bypass.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import frida, sys
 
def on_message(message, data):
    if message['type'== 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)
 
 
PACKAGE_NAME = "sg.vantagepoint.uncrackable1"
 
 
jscode= """
console.log("[*] Starting script");
    Java.perform(function() {
        console.log("[*] Hooking calls to System.exit");
        exitClass = Java.use("java.lang.System");
        exitClass.exit.implementation = function() {
            console.log("[*] System.exit called");
        }
      
    });
"""
process = frida.get_usb_device().attach(PACKAGE_NAME)
script = process.create_script(jscode)
script.on('message', on_message)
print('[*] Running Hook')
script.load()
sys.stdin.read()
 
cs


앱을 실행시킨 후 hooking코드를 실행하도록 하겠습니다.

[그림 6 Hooking]


ok버튼을 클릭해도 앱이 종료되지 않고 정상적으로 구동되는 것을 확인할 수 있습니다.

[그림 7 Hooking 성공]


루팅 탐지를 우회하면 뭔가 인증하는 절차를 가진 로직을 확인할 수 있는데 특정 문자열을 입력하여 인증을 하는것같습니다.

[그림 8 인증로직 확인]


verify 함수를 살펴보면 if (a.a(obj)) 조건문을 통해 인증을 수행하고 있는것을 확인할 수 있습니다.

[그림 9 verify함수 분석]


sg.vantagepoint.uncrackable1.a class를 살펴보면 

b("8d127684cbc37c17616d806cf50473cc")와 Base64.decode("5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc=", 0)를 sg.vantagepoint.a.a.a로 전달하여 입력된 문자열과 암호화된 데이터를 복호화한 값과 비교하여 결과를 리턴합니다.

[그림 10 sg.vantagepoint.uncrackable1.a class 분석]


sg.vantagepoint.a.a class의 a함수를 살펴보면 AES암호화를 사용하며 최종적으로 복호화된 문자열을 리턴하는데 b("8d127684cbc37c17616d806cf50473cc") 를 키값으로 사용하고 있습니다.

[그림 11 sg.vantagepoint.a.a class분석]


이제 구조를 알았으니 Hooking을 통해서 저장된 암호화 문자열이 복호화될 때 해당 문자열을 출력해보도록 하겠습니다.

Verify_Bypass.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import frida, sys
 
def on_message(message, data):
    if message['type'== 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)
 
 
PACKAGE_NAME = "sg.vantagepoint.uncrackable1"
 
 
jscode= """
console.log("[*] Starting script");
    Java.perform(function() {
    
        console.log("[*] Hooking System.exit");
        exitClass = Java.use("java.lang.System");
        exitClass.exit.implementation = function() {
            console.log("[*] System.exit called");
        }
        
        console.log("[*] Hooking a.class");
        aclass = Java.use("sg.vantagepoint.a.a");
        aclass.a.implementation = function(arg1,arg2){
            console.log("[*]Hooking a.Class");
            retval = this.a(arg1, arg2);
            Decripted_Code='';
            for(i=0;i<retval.length;i++){
                Decripted_Code+=String.fromCharCode(retval[i]);
            }
            console.log("Decripted Code :"+Decripted_Code);
            return retval;
        }
      
    });
"""
process = frida.get_usb_device().attach(PACKAGE_NAME)
script = process.create_script(jscode)
script.on('message', on_message)
print('[*] Running Hook')
script.load()
sys.stdin.read()
 
cs

앱을 실행시킨 후 Hooking코드를 실행시키면 다음과 같이 암호화되어 저장된 인증 문자열이 복호화되어 출력되는 것을 확인할 수 있습니다.

[그림 12 Hooking]


실제 해당 secret값이 맞는지 확인해볼까요?

[그림 13 Hooking 성공]


secret값이 일치한다는 메시지가 출력됩니다.


만일 어떤 secret값이 와도 Sucess 로직을 타도록 하고 싶다면 다음과 같이 코드를 작성하여 후킹하면 됩니다.

sg.vantagepoint.uncrackable1.a class의 a함수의 리턴값을 무조건 true로 리턴하도록하여 간단히 인증을 우회할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import frida, sys
 
def on_message(message, data):
    if message['type'== 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)
 
 
PACKAGE_NAME = "sg.vantagepoint.uncrackable1"
 
 
jscode= """
console.log("[*] Starting script");
    Java.perform(function() {
    
        console.log("[*] Hooking System.exit");
        exitClass = Java.use("java.lang.System");
        exitClass.exit.implementation = function() {
            console.log("[*System.exit called");
        }
        
        console.log("[*] Hooking a.class");
        aclass = Java.use("sg.vantagepoint.uncrackable1.a");
        aclass.a.implementation = function(arg1){
            return true;
            }
      
    });
"""
process = frida.get_usb_device().attach(PACKAGE_NAME)
script = process.create_script(jscode)
script.on('message', on_message)
print('[*] Running Hook')
script.load()
sys.stdin.read()
 
cs


실제 인증이 우회됐는지 확인해 보도록 하겠습니다.

[그림 14 Hooking 성공]


지금까지 Frida를 사용한 Android App Hooking에 대하여 알아봤습니다.


다음 포스팅에서는 두번째 Crackme 문제를 가지고 포스팅을 이어가도록 하겠습니다.


참고 : https://www.codemetrix.net/hacking-android-apps-with-frida-2/


'Mobile > Android' 카테고리의 다른 글

Android App Hooking with Frida(3)  (1) 2019.01.07
Android App Hooking with Frida(2)  (2) 2018.04.06

이 글을 공유하기

댓글