[Rev] 리버싱 · 작성: 2026-07-19 19:34:18 · 수정: 2026-07-21 23:30:21 · 조회 13
iOS 앱 리버싱은 안드로이드와 목표는 같지만(로직 파악, 보호 우회), 애플의 코드 서명 강제라는 완전히 다른 전제 위에서 시작해야 한다는 게 가장 큰 차이다.
IPA도 ZIP 컨테이너다(Payload/App.app/ 아래에 실행 파일과 리소스). 핵심 실행 파일은 Mach-O 포맷 — PE 포맷/ELF와 마찬가지로 헤더·세그먼트·로드 커맨드로 구성되지만, iOS 바이너리 특유의 LC_CODE_SIGNATURE 로드 커맨드가 핵심이다 — 이게 바로 아래에서 다룰 코드 서명 강제의 근거다. Objective-C로 작성된 앱은 class-dump나 Hopper로 클래스/메서드 시그니처를 상당 부분 복원할 수 있다 — 안드로이드의 DEX 메타데이터만큼 완벽하진 않지만, Objective-C의 런타임(objc runtime)이 메서드 이름과 셀렉터(selector) 정보를 바이너리 안에 문자열 형태로 그대로 유지해야 동작하기 때문에(동적 메시지 디스패치 방식 때문), 이 정보를 그대로 추출할 수 있다.
iOS 커널(XNU)은 dyld(동적 링커)가 앱을 실행할 때 LC_CODE_SIGNATURE에 담긴 서명을 검증하고, 서명되지 않았거나 변조된 바이너리·라이브러리는 로드 자체를 거부한다. 이건 안드로이드의 서명 검증(설치 시점에만 확인)보다 훨씬 강력하다 — 실행 시점마다, 그리고 로드되는 모든 코드 페이지 단위로 서명을 검증하기 때문에, 바이너리를 조금이라도 패치하면 서명이 깨져서 실행 자체가 거부된다.
frida-server 설치)class-dump/Hopper로 정적 분석: Objective-C 메서드 시그니처를 복원해 SSL Pinning, 탈옥 탐지, 라이선스 체크 관련 함수 이름을 찾음 — Objective-C는 함수를 직접 호출하는 대신 objc_msgSend(receiver, selector, ...)라는 단일 함수를 거쳐 메시지를 전달하는 방식이라, 정적 분석에서 호출 대상을 바로 알기 어렵고 런타임에 셀렉터 이름으로 동적 분석하는 게 오히려 더 정확한 경우가 많다ObjC.classes.SecurityCheck["- isJailbroken"].implementation = ...처럼, Objective-C 런타임 API를 통해 특정 클래스의 메서드 구현 자체를 실행 중에 교체 — Frida 글에서 다룬 인라인 후킹 원리가 Objective-C 메서드 디스패치 계층에서도 동일하게 적용된다NSURLSession delegate의 인증서 검증 콜백 등)를 찾아, Frida로 항상 "검증 성공"을 반환하도록 만들면 프록시(Burp Suite 등)로 앱의 HTTPS 트래픽을 가로채 분석할 수 있다/Applications/Cydia.app 존재 여부), 샌드박스 탈출 가능 여부 확인, fork() 성공 여부(탈옥된 기기는 앱 샌드박스 밖에서 fork가 허용되는 경우가 있음) 등 플랫폼 고유의 여러 신호를 종합적으로 확인한다는 점이 다르다iOS 리버싱을 "안드로이드와 비슷한 작업"으로 접근하면 안 되는 이유가 여기 있다 — 커널이 실행 시점마다 코드 서명을 강제하는 구조 자체가, 리버싱의 첫 단계를 "코드를 읽는 것"이 아니라 "그 검증을 어떻게 우회할 것인가"로 바꿔놓는다.