Security Tech

iOS에서 Frida 시작하기 (2탄)

작성자 - 보안왕 김보안

최근에 안드로이드 위주로 Frida를 다뤄봤는데, 이번에는 iOS 앱 후킹을 공부해봤다.
안드로이드랑 다른 점이 많아서 비교 위주로 정리한다.

Frida


1. 안드로이드 vs iOS 기본 뼈대

  • 안드로이드: 항상 Java.perform() 안에서 후킹해야 안정적
  • iOS: ObjC.schedule(ObjC.mainQueue, …) 안에서 후킹하는 게 일반적 (특히 UI 관련 메서드)
// 안드로이드 기본
Java.perform(function () {
    // ...
});

// iOS 기본
if (ObjC.available) {
    ObjC.schedule(ObjC.mainQueue, function () {
        // ...
    });
}

안드로이드는 Java.use("클래스명")으로 접근하지만, iOS는 ObjC.classes.클래스명으로 접근한다.


2. 메서드 지정 방식

  • 안드로이드: 메서드 오버로딩이 많아서 .overload('int','int') 같이 인자 타입 지정 필요
  • iOS: 메서드 이름이 selector로 구분됨
    • 인스턴스 메서드: "- 메서드명:"
    • 클래스 메서드: "+ 메서드명:"
// 안드로이드 예시
var addInt = Calc.add.overload('int','int');

// iOS 예시
var VC = ObjC.classes.UIViewController;
var method = VC["- viewDidAppear:"];

인스턴스 메서드 vs 클래스 메서드

  • 인스턴스 메서드(-): 객체(인스턴스)를 생성한 후 그 인스턴스에 대해 호출하는 메서드
    • 예: myViewController.viewDidAppear()
  • 클래스 메서드(+): 객체를 만들지 않고 클래스 자체에서 직접 호출하는 메서드
    • 예: [UIApplication sharedApplication] 은 클래스 메서드 호출

즉, selector 앞의 - / + 기호로 어떤 타입의 메서드인지 구분된다.


3. 함수/클래스 탐색

안드로이드와 마찬가지로 이름을 모르면 탐색해야 한다.

  • 안드로이드: Java.enumerateLoadedClasses
  • iOS: ObjC.enumerateLoadedClasses
// iOS 예시
ObjC.enumerateLoadedClasses({
    onMatch: function (name) {
        if (name.indexOf("Security") !== -1) {
            console.log(name);
        }
    },
    onComplete: function () {
        console.log("Done");
    }
});

또한 특정 클래스의 메서드 확인은 $ownMethods()를 쓰면 된다.

var Cls = ObjC.classes.UIApplication;
Cls.$ownMethods().forEach(function (m) { console.log(m); });

4. 후킹 예제 비교

안드로이드

var addInt = Calc.add.overload('int','int');
addInt.implementation = function (a,b) {
    console.log("add called");
    return addInt.call(this,a,b);
};

iOS

var VC = ObjC.classes.UIViewController;
var m = VC["- viewDidAppear:"];
Interceptor.attach(m.implementation, {
    onEnter: function (args) {
        var self = new ObjC.Object(args[0]);
        console.log(self.$className() + " viewDidAppear:");
    }
});

차이점:

  • 안드로이드는 .implementation을 직접 재정의
  • iOS는 Interceptor.attach로 진입/리턴 지점에 후킹

5. 예제: 탈옥 탐지 함수 관찰

ObjC.schedule(ObjC.mainQueue, function () {
    var Sec = ObjC.classes.SecurityChecker;
    var detect = Sec["- detectJailbreak"];
    Interceptor.attach(detect.implementation, {
        onEnter: function () {
            console.log("detectJailbreak() called");
        },
        onLeave: function (retval) {
            console.log("detectJailbreak() returned");
        }
    });
});

안드로이드에서 루팅 탐지 우회를 후킹하는 흐름과 비슷하지만, iOS는 selector 기반으로 집어야 한다는 차이가 있다.


6. 마무리

안드로이드 vs iOS 정리 포인트:

  • 실행 환경 진입점: Java.perform vs ObjC.schedule
  • 클래스 접근: Java.use vs ObjC.classes
  • 메서드 식별: 오버로딩 vs selector (- 인스턴스, + 클래스)
  • 후킹 방법: .implementation 덮어쓰기 vs Interceptor.attach

아직 네이티브 함수(open, ptrace, sysctl) 레벨 후킹은 더 공부가 필요하다.
일단은 ObjC 레벨 후킹에 익숙해지는 게 1순위일 듯.

Contents

이 글이 도움이 되었다면, 응원의 댓글 부탁드립니다.