본문 바로가기

← 블로그

iOS/바이너리

IPA 파일이란?

iOS 앱이 배포되고 설치되는 IPA 파일의 정체와 앱 번들 구조. Info.plist, 두 겹의 코드 서명, 프로비저닝 프로파일, App Thinning을 그림과 함께 정리합니다.

iOS 앱은 IPA 파일로 배포되고 설치됩니다. IPA가 무엇이고 그 안에 무엇이 어떤 규칙으로 들어 있는지, 그리고 코드 서명이 무결성을 어떻게 지키는지 정리합니다.

IPA는 zip이다


IPA는 iOS 앱을 담은 zip 압축 파일입니다. 확장자만 .ipa일 뿐 실제로는 zip이라 unzip으로 그대로 풀 수 있습니다.

unzip -q MyApp.ipa -d MyApp

풀면 최상위에 Payload/ 디렉토리가 있고 그 안에 앱 번들(.app)이 들어 있습니다. 앱 스토어에 올리는 IPA에는 SwiftSupport/(스위프트 런타임)나 Symbols/(크래시 심볼화용) 같은 폴더가 더 붙기도 합니다.

앱 번들 구조


앱의 실체는 Payload/MyApp.app/ 디렉토리 하나입니다. iOS에서 앱은 파일 하나가 아니라 번들이라는 폴더입니다.

MyApp.ipa (zip) Payload/ MyApp.app (앱 번들) MyApp 실행 바이너리 (Mach-O) Info.plist 앱 메타데이터 _CodeSignature/ 파일별 해시 embedded.mobileprovision 프로비저닝 프로파일 Frameworks/ 동적 라이브러리/프레임워크 Assets.car 컴파일된 이미지/에셋
구성요소하는 일
실행 바이너리앱의 코드. 확장자 없는 Mach-O 파일이고 번들 이름과 같음
Info.plist번들 식별자, 실행 파일 이름, 버전, 최소 iOS 버전 등
_CodeSignature/CodeResources번들 안 모든 파일의 해시 목록. 리소스 변조까지 잡는다
embedded.mobileprovision어떤 기기와 인증서로 실행을 허용하는지 담은 프로파일
Frameworks/앱이 쓰는 동적 라이브러리와 프레임워크(각각 내부에 dylib)
Assets.car이미지와 에셋 카탈로그를 하나로 컴파일한 파일
PlugIns/위젯, 공유 익스텐션 같은 앱 익스텐션(.appex)

Info.plist에는 무엇이 있나?


Info.plist는 시스템이 앱을 다루는 데 필요한 정보를 담은 딕셔너리입니다. 사람이 읽을 수 있게 plutil로 볼 수 있습니다.

plutil -p Payload/MyApp.app/Info.plist

자주 보는 키는 이렇습니다.

의미
CFBundleIdentifier앱을 유일하게 식별하는 번들 ID(com.example.MyApp)
CFBundleExecutable실행할 바이너리 파일 이름
CFBundleShortVersionString사용자에게 보이는 버전(1.2.0)
CFBundleVersion빌드 번호
MinimumOSVersion설치 가능한 최소 iOS 버전
UIRequiredDeviceCapabilities필요한 하드웨어 기능(예: arm64)

번들 ID와 버전이 여기 박혀 있어서, 시스템은 앱을 실행하기 전에 이 파일부터 읽습니다.

코드 서명: 두 겹의 봉인


IPA 구조에는 이 파일을 바꾸지 말라는 물리적 잠금이 없습니다. 무결성을 지키는 유일한 장치가 코드 서명이고, 서명은 두 겹으로 걸립니다.

코드 서명 = 두 겹 실행 바이너리 (Mach-O) LC_CODE_SIGNATURE 코드 페이지마다 해시 번들의 모든 파일 _CodeSignature/CodeResources 파일마다 해시 둘 중 하나라도 바뀌면 서명 무효 → iOS가 실행을 거부

한 겹은 실행 바이너리 자체입니다. Mach-O 안의 LC_CODE_SIGNATURE가 코드 페이지마다의 해시를 담아, 코드 한 바이트만 바뀌어도 검증이 실패합니다. 다른 한 겹은 번들 전체입니다. _CodeSignature/CodeResourcesInfo.plist, Assets.car 같은 리소스 파일의 해시가 들어 있어, 코드가 아니라 리소스를 바꿔도 걸립니다. 여기에 embedded.mobileprovision이 더해져, 이 서명이 어떤 인증서로 어떤 기기에서 유효한지를 못박습니다.

고치고 다시 묶기


바이너리나 리소스를 수정했다면 서명이 깨지므로 다시 서명해야 합니다. 안쪽 파일부터 서명하고 번들을 마지막에 봉인합니다.

# 1) 안쪽(프레임워크/바이너리)부터 재서명
codesign -f -s "<identity>" Payload/MyApp.app/Frameworks/*.framework
# 2) 번들 전체 재서명
codesign -f -s "<identity>" Payload/MyApp.app
# 3) 서명 검증
codesign -v --verbose=4 Payload/MyApp.app
# 4) 다시 zip 으로 묶어 .ipa 로
zip -qr MyApp.resigned.ipa Payload

서명에 쓴 인증서와 프로비저닝이 허용하는 기기에서만 설치됩니다. 이 재서명 단계를 이용해 완성된 앱에 코드를 심는 방법은 배포된 iOS 앱에 프레임워크 주입하기에서 다룹니다.

앱 스토어의 App Thinning


개발 중 만드는 IPA와 앱 스토어가 사용자에게 내려주는 IPA는 다릅니다. 앱 스토어는 업로드된 앱을 받아 기기별로 필요한 것만 골라 다시 포장합니다. 사용자의 기기 아키텍처에 맞는 슬라이스, 그 화면 배율에 맞는 이미지만 담아 내려주므로, 사용자가 실제로 받는 IPA는 업로드본보다 작습니다. 이것을 App Thinning이라고 부릅니다.