Frida를 이용한 iOS 메모리 분석

하게 된 계기

팀 내에서 암호 고도화 프로젝트를 진행하는데, 관련하여 어떤 부분까지 암호화되어야 안전한지에 대해 의문이 생겼다.

 

iOS 사용자의 입력 정보를 탈취하거나 하려면 어떻게 해야 할까? 탈취하면 어떤 정보가 나오는가? 간단히 시도해보았을 때 해커가 어느 부분까지 침투할 수 있는지를 살펴보고자 했다. (말은 거창하지만 실은 그닥 거창하지는 않다)

 

 

 

 

이용한 방법

두 가지 방법을 이용하였다.

  1. memory dump
  2. method hooking

먼저, Memory Dump는 정적인 방법으로, 특정 시점의 메모리 상태를 알 수 있다. fridump를 이용하여 현재 메모리 상태를 dump 뜨고 관찰하는 방법은 조금 노가다에 가까웠다. (물론 내가 느끼기에는….) 아무래도 요며칠 동안 잡고 있으면서 가볍게 공부를 한 입장에서 찾기에 그랬다는 것이지 해당 방법이 정말 그렇다는 것은 아니다.

dump와 함께 해당 앱의 ipa 파일을 생성해 보기도 했다. 내가 원하는 분석에는 별로 도움은 안 됐지만 정말 무궁무진하구나 ,, 싶었다. (이래서 보안 회사가 돈을 벌 수 있는 게 아닐까 싶었당. ㅋㅋ)

 

method hooking은 익숙한 방법이었다. 다른 점이 있다면 이전에는 앱 내에서 해당 메모리를 후킹하여 앞뒤로 시점을 확인했다면 이번에는 외부 기기에서 원하는 method를 후킹하고 변수를 확인하는 방식이다.

 

 

두 가지 방법 모두 탈옥 단말에서만 실행 가능하당. 사실 당연한 말이지만 Frida 자체가 Cydia(탈옥 앱스토어)에서 다운받을 수 있는 트윅(탈옥 앱)이다 보니 그렇다.

 

 

 

세팅 방법

아래 방법에서는 Frida 설치 방법 및 적용 방법을 다루지 않을 것이기 때문에 지금 간단히 이야기 해 보자면,

  1. Frida를 설치한 후, 탈옥한 iPhone 기기를 연결한다.
  2. memory dump 방법을 이용할 경우, fridump3를 다운받는다. (깃헙에 있음)
  3. frida-ps -Uai를 이용하여 target App의 PID를 확인한다.

이 정도면 기본적으로 아래 방법을 쓸 수 있다!

 

 

Memory dump

python fridump3 py -u -r -s [target App의 PID]

사진 출처: https://ch4njun.tistory.com/163

현재 탈옥 기기를 반납한 상태라 관련 사진을 퍼왔다 … ^__^

 

 

실행하면 fridump3 폴더 내에 dump 폴더가 생기고, 해당 폴더에서 메모리 구조를 확인할 수 있다.

위에서 쳤던 쉘 옵션 중 하나인 -s 는 문자열 로그를 모아 dump 폴더 내 string.txt 파일로 만들어준다.

원래는 쉘에서 grep 을 통해 찾았는데, 해당 옵션을 주어 찾으면 텍스트 에디터 안에서 찾을 수 있어서 편리하다. ^__^ b

 

 

 

 

 

 

 

method hooking

method hooking은 생각보다 간편하다.

frida -U -n [앱 이름 혹은 PID] -l [실행할 스크립트]

로 실행하면 된다.

 

 

 

해당 명령어를 정상적으로 실행하면 프리다 로고가 나오고, 내부에서 뭔가 더 실행할 수 있는 콘솔 창이 나오게 된다.

찾아보니 iOS 객체에 직접 접근하여 뭔가를 할 수도 있던데 그쪽은 해 보려고 했다가 도저히 모르겠어서 GPT의 도움을 받아 여러 스크립트를 만들었다.

 

 

해당 스크립트는 아래와 같다.

버튼을 클릭했을 때 tag를 print하는 스크립트

if (ObjC.available) {
    var UIControlEventTouchUpInside = 1 << 6;

    var UIButton = ObjC.classes.UIButton;
    
    Interceptor.attach(UIButton['- sendAction:to:forEvent:'].implementation, {
        onEnter: function (args) {
            var action = ObjC.selectorAsString(args[2]);
            var controlEvent = args[3].toInt32();
			
			var button = new ObjC.Object(args[0]);
			console.log("============= Button Clicked!!");
                //  UIButton의 tag를 출력
                var tag = button.tag();
                console.log("UIButton tag: " + tag.toString());
            }
        },
        onLeave: function (retval) {}
    });

    console.log("Hooking successful!");

} else {
    console.log("Objective-C runtime is not available!");
}

 

 

 

 

새로운 ViewController를 띄웠을 때, 버튼이 있다면 해당 버튼의 tag를 print하는 스크립트

// get_button_tags.js
if (ObjC.available) {
    try {
        // UIViewController의 viewDidAppear: 메서드를 후킹
        var UIViewController = ObjC.classes.UIViewController;
        var viewDidAppear = UIViewController['- viewDidAppear:'];

        Interceptor.attach(viewDidAppear.implementation, {
            onEnter: function (args) {
                // self는 첫 번째 인자
                this.self = new ObjC.Object(args[0]);
            },
            onLeave: function (retval) {
                console.log("View did appear for: " + this.self);
                function findButtons(view) {
                    var subviews = view.subviews();
                    for (var i = 0; i < subviews.count(); i++) {
                        var subview = subviews.objectAtIndex_(i);
                            // console.log("Subview: " + subview.toString());
                        if (subview.isKindOfClass_(ObjC.classes.UIButton)) {
                            console.log("UIButton tag: " + subview.tag());
                        }
                        if (subview.isKindOfClass_(ObjC.classes.NFilterCharButton)) {
                            console.log("NFilterCharButton KeyValue: " + subview.keyValue());
                        }
                        // 재귀적으로 서브뷰 탐색
                        findButtons(subview);
                    }
                }

                findButtons(this.self.view());
            }
        });

        console.log("Hooking successful!");
    } catch (e) {
        console.error("Error: " + e.message);
    }
} else {
    console.error("Objective-C runtime is not available!");
}

 

 

 

 

 

 

새삼 깨달은 점 … …

→ 탈옥을 한 사용자가 해당 앱에서 정보를 빼내고자 한다면 메모리에 올라가 있는 모든 것을 빼낼 수 있다.

버튼의 tag값, text값 같은 설정부터 깊게는 secureText로 숨겨놓은 텍스트까지 이 방식을 이용한다면 모두 볼 수 있었다.

 

ㄱ-

 

어떻게 하면 사용자도 편하고 나도 편하고 보안적으로 안전한 앱을 만들 수 있을지 고민을 마니 해바야겟다…

사실 사용자가 편하고 보안적으로 안전하려면 내가 편하는 건 거의 불가능이겠지만~~

 

그래도… … :3 계속 고민하는 개발자가 되어야겟슴

 

 

 

 

 

참고한 글

https://gflow-security.tistory.com/entry/iOS-App-Hooking-with-Frida1

 

iOS App Hooking with Frida(1)

이번 포스팅에서는 Frida를 사용한 iOS App hooking에 대하여 다루도록 하겠습니다.(도구 설치와 같은 기초 지식은 다루지 않습니다.) 문제파일은 아래 경로를 통하여 다운받을 수 있습니다.https://githu

gflow-security.tistory.com

 

https://aboutsc.tistory.com/60

 

[IOS] 프리다를 이용한 IOS 앱 후킹 (Level1)

[IOS] 프리다를 이용한 IOS 앱 후킹 (Level1)https://github.com/OWASP/owasp-mstg/tree/master/Crackmes(도구 설치 방법론은 다루지 않는다) 환경분석 : OS : Win7 TOOL : Frida, Frida-server, adb, Python2.7, 4S Rooting Phone 1) 앱을 단

aboutsc.tistory.com

 

https://velog.io/@silver35/iOSFrida-Bypass-Frida-Path-Checking

 

[iOS][Frida] Bypass Frida Path Checking

앱을 실행 후 Frida file check 버튼을 누르면 아래와 같이 Frida detected = true라는 알림창이 뜬다.xcode로 build된 파일을 열어서 swift_source/YDFridaVC.swift 소스코드를 보면, 버튼 클릭 시 YDFridaDe

velog.io

 

https://ch4njun.tistory.com/163

 

[FRIDUMP] Fridump를 이용한 Memory Dump 및 분석.

사용자가 Keypad를 통해서 어떤 민감한 데이터(ID나 Password와 같은)를 Application에 입력하고 이 Data가 이후에도 Memory상에 존재하는 경우 취약점이 발생할 수 있다. 입력후 환경에서 Memory Dump를 통해

ch4njun.tistory.com

https://www.postype.com/@cpuu/post/13054011

 

frida-ios-dump 를 이용한 애플리케이션 IPA 파일 추출: CPUU의 Daydreamin'

기본적으로 Apple iOS앱들은 암호화(Encrypted) 되어 있을 수 있다. 하지만 아무리 보안을 위해 암호화를 했다하더라도, 실행을 하기 위해서는 반드시 암호화를 해제한 상태로 구동을 하게 된다. 이

www.postype.com

https://www.postype.com/@cpuu/post/10415747

 

frida를 활용한 iOS process 메모리 분석: CPUU의 Daydreamin'

앞서 jailbreak 된 아이폰6을 준비했었다. 아이폰의 메모리를 덤프하고 프로세스 메모리를 분석하는 작업을 수행해보자. frida 설치(Jailbreaken iOS Device) 탈옥한 아이폰에는 기본적으로 시디아(cydia)가

www.postype.com