1. Frida

Inject JavaScript to explore native apps on Windows, macOS, Linux, iOS, Android, and QNX.

[출처] https://www.frida.re/


Frida는 자바스크립트를 이용하여 Windows, macOS, Linux, iOS, Android와 QNX와 같은 다양한 플랫폼에서 후킹을 할 수 있는 플랫폼이다. 파이썬 틀에 인젝션할 코드를 자바스크립트로 작성하여 파이썬으로 실행 할 수 있다. 



2. Frida Hooking

iOS Hooking#1 에서 분석한 동일한 앱을 대상으로 진행을 하였으므로 앱 분석은 생략한다.


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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# -*- conding: utf-8 -*-
import frida
import sys 
 
PACKAGE_NAME = "kr.history"
 
#send실행 시 출력할 format정의
def on_message(message, data):
    try:
        if message:
            print("[JSBACH] {0}".format(message["payload"]))
    except Exception as e:
        print(message)
        print(e)
 
 
def do_hook():
 
    hook = ""
    //Objective-C가 실행 가능한 환경인지 검사
    if(ObjC.available){
        //해당 attach된 프로세스의 메모리에 올라가있는 클래스들을 가져올 수있고,
        //아래 for문은 타겟 클래스가 존재하는지 검사했다.
        for(var className in ObjC.classes){
            if(className == "Game2ViewController"){
                send("Found our target class : " + className);
            }
        }
    
        //Hooking을 진행할 메서드 객체를 가져온다.
        var hook_method = ObjC.classes.Game2ViewController["- recognizeAnswer"];
        send("print hook_method : " + hook_method);
        
        Interceptor.attach(hook_method.implementation, {
        
            //onEnter는 후킹함수 진입 시 실행되며, args[0]에는 self객체가
            //args[1]에는 selector객체가 들어있어 접근 가능하며
            //args[2]에는 해당 함수의 매개변수들이 들어있다.
            //매개변수를 변경하고 싶다면 이곳에서 변경한다.
            onEnter: function(args){
                
                var receiver = new ObjC.Object(args[0]);
                send("Target class : " + receiver.$className);
                send("Target superclass : " + receiver.$superClass.$className);
                var sel = ObjC.selectorAsString(args[1]);
                send("typeof sel = " + typeof sel);
                send("Hooked the target method : " + sel);
            },
            //onLeave는 함수 종료전 처리를 할 수 있다.
            //retVal에는 원래의 return값이 들어있고, return값을 변경하고 싶다면
            //이곳에서 변경한다.
            onLeave: function(retVal){
                //오답 시 리턴 값
                var wrong   = -1;
                //정답 시 리턴 값
                var correct = 1; 
                //retVal은 Object객체이며, int값으로 사용하기 위해 toInt32()를 사용
                var orig_rtn = retVal.toInt32();
                if(-1 == orig_rtn){
                    send("answer is Wrong!! : " + orig_rtn);
                    send("answer is replaced!!");
                    //값을 변경하기위해 replace()를 사용하여 변경함
                    retVal.replace(correct);
                }
                else{
                    send("answer is Correct!! : " + orig_rtn);
                }
            }
        });
    }
    //Objective-C 실행 환경이 아닌 경우 로그 출력
    else{
        console.log("Objective-C Runtime is not available!");
    }
    """
 
    return hook
 
if __name__ == '__main__':
 
    try:
        #연결할 단말을 찾는다.
        device = frida.get_device_manager().enumerate_devices()[-1]
 
        #타겟으로 할 앱의 패키지명으로 단말에서 실행되고 있는 pid를 가져온다.
        pid = device.spawn([PACKAGE_NAME])
        print("[JSBACH] {} is starting. (pid : {})".format(PACKAGE_NAME, pid))
 
        #위에서 얻어온 pid에 attach한다.
        session = device.attach(pid)
        device.resume(pid)
 
        #후킹코드를 injection한다.
        script = session.create_script(do_hook())
        script.on('message', on_message)
        script.load()
        sys.stdin.read()
    except KeyboardInterrupt:
        sys.exit(0)



코드에 대한 설명은 주석으로 대체합니다.


3. 결과





4. 코드 작성하며 발생했던 사소한 문제점들

4-1) frida client / server간 버전 충돌 문제

frida --version / frida-server --version 으로 확인한 메이저버전이 다를 경우 frida가 제대로 동작하지 않는다.


4-2) 32bit 단말 문제

실습에 활용한 단말은 iPhone 5로 32bit 단말이였는데, Frida최신버전(9.1.27)버전에서 제대로 훜이 걸리지않음

-> 8.2.2버전으로 낮추어 설치하여 정상적으로 동작 (pip install frida==8.2.2)


4-3) retVal 값 format

onLeave에있는 retVal값을 toString()을 이용해 사용하면 헥사 값이 나온다. (위 예제에서는 오답인 경우 0xffffffff, 정답인 경우 0x1) 따라서 toInt32()함수를 이용해서 int로만들어 주었다.

0xffffffff를 parseInt()를 사용하여 출력하면 unsigned int형의 최대값인 4294967295가 출력되었다.


4-4) onEnter

onEnter에서 args에 접근하여 receiver와 receiver의 superclass를 출력할 때, 많은 예제코드에서 

$className이 생략되어있었는데, 타입 변환 에러가 나면서 제대로 출력되지 않았으며, frida API를 참조해서 

$className을 추가하여 해결함

[참조] https://www.frida.re/docs/javascript-api/#objc


4. 후기

Logos와 비교했을 때, 후킹 코드의 빌드나 설치가 별도로 필요 없어서 가볍다는 느낌이 들었다.

python과 JavaScript로 iOS후킹 코드를 짤 수 있다는 게 신기했고,

탈옥상태가 아닌 폰에서도 후킹이 가능하다는 점도 신기했다.

'Study > iOS' 카테고리의 다른 글

iOS Hooking#3(Cycript)  (3) 2017.05.06
iOS Hooking#1(Logos)  (1) 2017.04.18
2장 iOS 해킹 기초 (1)  (0) 2016.06.18
자주쓰는 데이터형 변환  (0) 2016.06.01
[iOS/GCC] __attribute__((constructor)) / __attribute__((desstructor))  (0) 2016.05.13

+ Recent posts