Translated ['src/generic-methodologies-and-resources/basic-forensic-meth

This commit is contained in:
Translator 2025-10-01 01:55:55 +00:00
parent e8d6897c44
commit cadcf88ec7
5 changed files with 393 additions and 162 deletions

View File

@ -61,6 +61,7 @@
- [Deofuscation vbs (cscript.exe)](generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/desofuscation-vbs-cscript.exe.md)
- [Discord Cache Forensics](generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/discord-cache-forensics.md)
- [Local Cloud Storage](generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/local-cloud-storage.md)
- [Mach O Entitlements And Ipsw Indexing](generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/mach-o-entitlements-and-ipsw-indexing.md)
- [Office file analysis](generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/office-file-analysis.md)
- [PDF File analysis](generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/pdf-file-analysis.md)
- [PNG tricks](generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/png-tricks.md)

View File

@ -1,8 +1,8 @@
# 특정 소프트웨어/파일 형식
# 특정 소프트웨어/파일 형식 요령
{{#include ../../../banners/hacktricks-training.md}}
여기에서 특정 파일 형식 및/또는 소프트웨어에 대한 흥미로운 팁을 제공합니다:
여기에서 특정 파일 형식 및/또는 소프트웨어에 대한 흥미로운 요령을 확인할 수 있습니다:
{{#ref}}
@ -54,4 +54,9 @@ video-and-audio-file-analysis.md
zips-tricks.md
{{#endref}}
{{#ref}}
mach-o-entitlements-and-ipsw-indexing.md
{{#endref}}
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -0,0 +1,213 @@
# Mach-O 권한(Entitlements) 추출 및 IPSW 인덱싱
{{#include ../../../banners/hacktricks-training.md}}
## 개요
이 페이지에서는 LC_CODE_SIGNATURE를 따라 코드 서명 SuperBlob을 파싱하여 Mach-O 바이너리에서 프로그래밍적으로 entitlements를 추출하는 방법과, Apple IPSW 펌웨어의 내용을 마운트하고 인덱싱하여 포렌식 검색/비교에 적용하는 방법을 다룹니다.
Mach-O 형식 및 코드 서명에 대한 복습이 필요하면 다음을 참조하세요:
- Check macOS code signing details (SuperBlob, Code Directory, special slots): [macOS Code Signing](../../../macos-hardening/macos-security-and-privilege-escalation/macos-security-protections/macos-code-signing.md)
- Check general Mach-O structures/load commands: [Universal binaries & Mach-O Format](../../../macos-hardening/macos-security-and-privilege-escalation/macos-files-folders-and-binaries/universal-binaries-and-mach-o-format.md)
## Mach-O 권한(Entitlements): 저장 위치
Entitlements는 LC_CODE_SIGNATURE load command가 참조하는 코드 서명 데이터 내부에 저장되며 __LINKEDIT 세그먼트에 배치됩니다. 서명은 여러 블롭(code directory, requirements, entitlements, CMS 등)을 포함하는 CS_SuperBlob입니다. entitlements 블롭은 데이터가 Apple Binary Property List(bplist00)인 CS_GenericBlob이며, 이 plist는 entitlement 키를 값에 매핑합니다.
주요 구조체 (xnu에서):
```c
/* mach-o/loader.h */
struct mach_header_64 {
uint32_t magic;
cpu_type_t cputype;
cpu_subtype_t cpusubtype;
uint32_t filetype;
uint32_t ncmds;
uint32_t sizeofcmds;
uint32_t flags;
uint32_t reserved;
};
struct load_command {
uint32_t cmd;
uint32_t cmdsize;
};
/* Entitlements live behind LC_CODE_SIGNATURE (cmd=0x1d) */
struct linkedit_data_command {
uint32_t cmd; /* LC_CODE_SIGNATURE */
uint32_t cmdsize; /* sizeof(struct linkedit_data_command) */
uint32_t dataoff; /* file offset of data in __LINKEDIT */
uint32_t datasize; /* file size of data in __LINKEDIT */
};
/* osfmk/kern/cs_blobs.h */
typedef struct __SC_SuperBlob {
uint32_t magic; /* CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0 */
uint32_t length;
uint32_t count;
CS_BlobIndex index[];
} CS_SuperBlob;
typedef struct __BlobIndex {
uint32_t type; /* e.g., CSMAGIC_EMBEDDED_ENTITLEMENTS = 0xfade7171 */
uint32_t offset; /* offset of entry */
} CS_BlobIndex;
typedef struct __SC_GenericBlob {
uint32_t magic; /* same as type when standalone */
uint32_t length;
char data[]; /* Apple Binary Plist containing entitlements */
} CS_GenericBlob;
```
중요 상수:
- LC_CODE_SIGNATURE cmd = 0x1d
- CS SuperBlob magic = 0xfade0cc0
- Entitlements blob type (CSMAGIC_EMBEDDED_ENTITLEMENTS) = 0xfade7171
- DER entitlements may be present via special slot (e.g., -7), see the macOS Code Signing page for special slots and DER entitlements notes
Note: Multi-arch (fat) binaries contain multiple Mach-O slices. You must pick the slice for the architecture you want to inspect and then walk its load commands.
## Extraction steps (generic, lossless-enough)
1) Parse Mach-O header; iterate ncmds worth of load_command records.
2) Locate LC_CODE_SIGNATURE; read linkedit_data_command.dataoff/datasize to map the Code Signing SuperBlob placed in __LINKEDIT.
3) Validate CS_SuperBlob.magic == 0xfade0cc0; iterate count entries of CS_BlobIndex.
4) Locate index.type == 0xfade7171 (embedded entitlements). Read the pointed CS_GenericBlob and parse its data as an Apple binary plist (bplist00) to key/value entitlements.
Implementation notes:
- Code signature structures use big-endian fields; swap byte order when parsing on little-endian hosts.
- The entitlements GenericBlob data itself is a binary plist (handled by standard plist libraries).
- Some iOS binaries may carry DER entitlements; also some stores/slots differ across platforms/versions. Cross-check both standard and DER entitlements as needed.
- For fat binaries, use the fat headers (FAT_MAGIC/FAT_MAGIC_64) to locate the correct slice and offset before walking Mach-O load commands.
## Minimal parsing outline (Python)
The following is a compact outline showing the control flow to find and decode entitlements. It intentionally omits robust bounds checks and full fat binary support for brevity.
```python
import plistlib, struct
LC_CODE_SIGNATURE = 0x1d
CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0
CSMAGIC_EMBEDDED_ENTITLEMENTS = 0xfade7171
# all code-signing integers are big-endian per cs_blobs.h
be32 = lambda b, off: struct.unpack_from(">I", b, off)[0]
def parse_entitlements(macho_bytes):
# assume already positioned at a single-arch Mach-O slice
magic, = struct.unpack_from("<I", macho_bytes, 0)
is64 = magic in (0xfeedfacf,)
if is64:
ncmds = struct.unpack_from("<I", macho_bytes, 0x10)[0]
sizeofcmds = struct.unpack_from("<I", macho_bytes, 0x14)[0]
off = 0x20
else:
# 32-bit not shown
return None
code_sig_off = code_sig_size = None
for _ in range(ncmds):
cmd, cmdsize = struct.unpack_from("<II", macho_bytes, off)
if cmd == LC_CODE_SIGNATURE:
# struct linkedit_data_command is little-endian in file
_, _, dataoff, datasize = struct.unpack_from("<IIII", macho_bytes, off)
code_sig_off, code_sig_size = dataoff, datasize
off += cmdsize
if code_sig_off is None:
return None
blob = macho_bytes[code_sig_off: code_sig_off + code_sig_size]
if be32(blob, 0x0) != CSMAGIC_EMBEDDED_SIGNATURE:
return None
count = be32(blob, 0x8)
# iterate BlobIndex entries (8 bytes each after 12-byte header)
for i in range(count):
idx_off = 12 + i*8
btype = be32(blob, idx_off)
boff = be32(blob, idx_off+4)
if btype == CSMAGIC_EMBEDDED_ENTITLEMENTS:
# GenericBlob is big-endian header followed by bplist
glen = be32(blob, boff+4)
data = blob[boff+8: boff+glen]
return plistlib.loads(data)
return None
```
Usage tips:
- fat binaries를 처리하려면 먼저 struct fat_header/fat_arch를 읽고, 원하는 architecture slice를 선택한 다음 subrange를 parse_entitlements에 전달하세요.
- macOS에서 다음 명령으로 결과를 검증할 수 있습니다: codesign -d --entitlements :- /path/to/binary
## Example findings
Privileged platform binaries는 종종 다음과 같은 민감한 entitlements를 요청합니다:
- com.apple.security.network.server = true
- com.apple.rootless.storage.early_boot_mount = true
- com.apple.private.kernel.system-override = true
- com.apple.private.pmap.load-trust-cache = ["cryptex1.boot.os", "cryptex1.boot.app", "cryptex1.safari-downlevel"]
이를 펌웨어 이미지 전반에 걸쳐 대규모로 검색하는 것은 attack surface mapping 및 releases/devices 간의 diffing에 매우 유용합니다.
## Scaling across IPSWs (mounting and indexing)
전체 이미지를 저장하지 않고 실행 파일을 열거하고 entitlements를 대규모로 추출하려면:
- @blacktop의 ipsw tool을 사용하여 펌웨어 파일시스템을 다운로드하고 마운트하세요. 마운트는 apfs-fuse를 활용하므로 전체 추출 없이 APFS 볼륨을 탐색할 수 있습니다.
```bash
# Download latest IPSW for iPhone11,2 (iPhone XS)
ipsw download ipsw -y --device iPhone11,2 --latest
# Mount IPSW filesystem (uses underlying apfs-fuse)
ipsw mount fs <IPSW_FILE>
```
- 마운트된 볼륨을 순회하여 Mach-O 파일을 찾고 (magic을 확인하거나 file/otool 사용), entitlements와 imported frameworks를 파싱한다.
- 수천 개의 IPSWs에 걸친 선형 성장을 피하기 위해 정규화된 뷰를 관계형 데이터베이스에 저장한다:
- executables, operating_system_versions, entitlements, frameworks
- many-to-many: executable↔OS version, executable↔entitlement, executable↔framework
Example query to list all OS versions containing a given executable name:
```sql
SELECT osv.version AS "Versions"
FROM device d
LEFT JOIN operating_system_version osv ON osv.device_id = d.id
LEFT JOIN executable_operating_system_version eosv ON eosv.operating_system_version_id = osv.id
LEFT JOIN executable e ON e.id = eosv.executable_id
WHERE e.name = "launchd";
```
DB 이식성에 대한 메모 (자체 인덱서를 구현하는 경우):
- ORM/추상화 계층을 사용하세요 (예: SeaORM) — 코드가 DB에 종속되지 않도록 유지 (SQLite/PostgreSQL).
- SQLite는 AUTOINCREMENT가 INTEGER PRIMARY KEY에만 필요합니다; Rust에서 i64 PK를 원한다면 엔티티를 i32로 생성하고 타입을 변환하세요. SQLite는 내부적으로 INTEGER를 8바이트 부호 있는 정수로 저장합니다.
## 오픈소스 도구 및 entitlement hunting 참고자료
- 펌웨어 마운트/다운로드: https://github.com/blacktop/ipsw
- Entitlement 데이터베이스 및 참조:
- Jonathan Levins entitlement DB: https://newosxbook.com/ent.php
- entdb: https://github.com/ChiChou/entdb
- 대규모 인덱서 (Rust, self-hosted Web UI + OpenAPI): https://github.com/synacktiv/appledb_rs
- 구조체 및 상수용 Apple 헤더:
- loader.h (Mach-O headers, load commands)
- cs_blobs.h (SuperBlob, GenericBlob, CodeDirectory)
코드 서명 내부 동작(Code Directory, special slots, DER entitlements)에 대한 자세한 내용은 다음을 참조하세요: [macOS Code Signing](../../../macos-hardening/macos-security-and-privilege-escalation/macos-security-protections/macos-code-signing.md)
## 참고자료
- [appledb_rs: a research support tool for Apple platforms](https://www.synacktiv.com/publications/appledbrs-un-outil-daide-a-la-recherche-sur-plateformes-apple.html)
- [synacktiv/appledb_rs](https://github.com/synacktiv/appledb_rs)
- [blacktop/ipsw](https://github.com/blacktop/ipsw)
- [Jonathan Levins entitlement DB](https://newosxbook.com/ent.php)
- [ChiChou/entdb](https://github.com/ChiChou/entdb)
- [XNU cs_blobs.h](https://github.com/apple-oss-distributions/xnu/blob/main/osfmk/kern/cs_blobs.h)
- [XNU mach-o/loader.h](https://github.com/apple-oss-distributions/xnu/blob/main/EXTERNAL_HEADERS/mach-o/loader.h)
- [SQLite Datatypes](https://sqlite.org/datatype3.html)
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -2,42 +2,42 @@
{{#include ../../../banners/hacktricks-training.md}}
## Basic Information
## 기본 정보
Mac OS 바이너리는 일반적으로 **유니버설 바이너리**로 컴파일됩니다. **유니버설 바이너리**는 **같은 파일에서 여러 아키텍처를 지원할 수 있습니다**.
Mac OS 바이너리는 보통 **universal binaries**로 컴파일됩니다. **universal binary**는 단일 파일에서 **여러 아키텍처를 지원할 수 있습니다**.
바이너리는 기본적으로 **Mach-O 구조**를 따르며, 이는 다음으로 구성됩니다:
러한 바이너리는 기본적으로 다음으로 구성된 **Mach-O 구조**를 따릅니다:
- 헤더
- 로드 명령
- 데이터
- Header
- Load Commands
- Data
![https://alexdremov.me/content/images/2022/10/6XLCD.gif](<../../../images/image (470).png>)
## Fat Header
다음 명령어로 파일을 검색합니다: `mdfind fat.h | grep -i mach-o | grep -E "fat.h$"`
다음으로 파일을 검색하세요: `mdfind fat.h | grep -i mach-o | grep -E "fat.h$"`
<pre class="language-c"><code class="lang-c"><strong>#define FAT_MAGIC 0xcafebabe
</strong><strong>#define FAT_CIGAM 0xbebafeca /* NXSwapLong(FAT_MAGIC) */
</strong>
struct fat_header {
<strong> uint32_t magic; /* FAT_MAGIC 또는 FAT_MAGIC_64 */
</strong><strong> uint32_t nfat_arch; /* 뒤따르는 구조체의 수 */
<strong> uint32_t magic; /* FAT_MAGIC or FAT_MAGIC_64 */
</strong><strong> uint32_t nfat_arch; /* number of structs that follow */
</strong>};
struct fat_arch {
cpu_type_t cputype; /* cpu 지정자 (int) */
cpu_subtype_t cpusubtype; /* 머신 지정자 (int) */
uint32_t offset; /* 이 객체 파일에 대한 파일 오프셋 */
uint32_t size; /* 이 객체 파일의 크기 */
uint32_t align; /* 2의 거듭제곱으로 정렬 */
cpu_type_t cputype; /* cpu specifier (int) */
cpu_subtype_t cpusubtype; /* machine specifier (int) */
uint32_t offset; /* file offset to this object file */
uint32_t size; /* size of this object file */
uint32_t align; /* alignment as a power of 2 */
};
</code></pre>
헤더에는 **매직** 바이트가 있으며, 그 뒤에 파일이 **포함하는** **아키텍처**의 **수**(`nfat_arch`)가 있습니다. 각 아키텍처`fat_arch` 구조체를 가집니다.
헤더**magic** 바이트와 뒤따르는 파일이 포함하는 **archs**의 **개수** (`nfat_arch`)를 포함하며, 각 arch`fat_arch` 구조체를 가집니다.
다음 명령어로 확인합니다:
다음으로 확인하세요:
<pre class="language-shell-session"><code class="lang-shell-session">% file /bin/ls
/bin/ls: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64e:Mach-O 64-bit executable arm64e]
@ -68,11 +68,11 @@ capabilities PTR_AUTH_VERSION USERSPACE 0
<figure><img src="../../../images/image (1094).png" alt=""><figcaption></figcaption></figure>
당신이 생각할 수 있듯이, 일반적으로 2개의 아키텍처를 위해 컴파일된 유니버설 바이너리는 1개의 아키텍처를 위해 컴파일된 것의 **크기를 두 배로 늘립니다**.
아마 생각하셨겠지만, 보통 2개 아키텍처용으로 컴파일된 universal binary는 단일 아키텍처용으로 컴파일된 것보다 크기가 **2배**가 됩니다.
## **Mach-O Header**
헤더는 파일에 대한 기본 정보를 포함하며, Mach-O 파일로 식별하기 위한 매직 바이트와 대상 아키텍처에 대한 정보를 포함합니다. 다음 명령어로 찾을 수 있습니다: `mdfind loader.h | grep -i mach-o | grep -E "loader.h$"`
헤더에는 Mach-O 파일로 식별하기 위한 magic 바이트와 대상 아키텍처에 대한 정보 등 파일의 기본 정보가 포함됩니다. 다음에서 찾을 수 있습니다: `mdfind loader.h | grep -i mach-o | grep -E "loader.h$"`
```c
#define MH_MAGIC 0xfeedface /* the mach magic number */
#define MH_CIGAM 0xcefaedfe /* NXSwapInt(MH_MAGIC) */
@ -99,19 +99,19 @@ uint32_t flags; /* flags */
uint32_t reserved; /* reserved */
};
```
### Mach-O 파일
### Mach-O 파일 형
다양한 파일 유형이 있으며, [**소스 코드에서 예를 찾아볼 수 있습니다**](https://opensource.apple.com/source/xnu/xnu-2050.18.24/EXTERNAL_HEADERS/mach-o/loader.h). 가장 중요한 유형은 다음과 같습니다:
여러 가지 파일 형식이 있으며, [**source code for example here**](https://opensource.apple.com/source/xnu/xnu-2050.18.24/EXTERNAL_HEADERS/mach-o/loader.h)에서 정의된 것을 확인할 수 있습니다. 가장 중요한 것들은 다음과 같습니다:
- `MH_OBJECT`: 재배치 가능한 오브젝트 파일 (컴파일의 중간 산출물, 아직 실행 파일이 아님).
- `MH_OBJECT`: 재배치 가능한 오브젝트 파일 (컴파일의 중간 산출물 아직 실행 파일이 아님).
- `MH_EXECUTE`: 실행 파일.
- `MH_FVMLIB`: 고정 VM 라이브러리 파일.
- `MH_CORE`: 코드 덤프
- `MH_PRELOAD`: 미리 로드된 실행 파일 (XNU에서 더 이상 지원되지 않음)
- `MH_PRELOAD`: 사전 로드된 실행 파일 (현재 XNU에서 더 이상 지원되지 않음)
- `MH_DYLIB`: 동적 라이브러리
- `MH_DYLINKER`: 동적 링커
- `MH_BUNDLE`: "플러그인 파일". gcc에서 -bundle을 사용하여 생성되며 `NSBundle` 또는 `dlopen`에 의해 명시적으로 로드됨.
- `MH_DYSM`: 동반 `.dSym` 파일 (디버깅을 위한 기호가 포함된 파일).
- `MH_BUNDLE`: "플러그인 파일". -bundle in gcc로 생성되며 명시적으로 `NSBundle` 또는 `dlopen`으로 로드됩니다.
- `MH_DYSM`: 동반 `.dSym` 파일 (디버깅을 위한 심볼이 포함된 파일).
- `MH_KEXT_BUNDLE`: 커널 확장.
```bash
# Checking the mac header of a binary
@ -120,54 +120,54 @@ Mach header
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
MH_MAGIC_64 ARM64 E USR00 EXECUTE 19 1728 NOUNDEFS DYLDLINK TWOLEVEL PIE
```
또는 [Mach-O View](https://sourceforge.net/projects/machoview/)를 사용하여:
또는 [Mach-O View](https://sourceforge.net/projects/machoview/):
<figure><img src="../../../images/image (1133).png" alt=""><figcaption></figcaption></figure>
## **Mach-O 플래그**
소스 코드는 라이브러리 로딩에 유용한 여러 플래그를 정의합니다:
소스 코드는 라이브러리 로딩에 유용한 여러 플래그도 정의되어 있다:
- `MH_NOUNDEFS`: 정의되지 않은 참조 없음 (완전 링크됨)
- `MH_NOUNDEFS`: 정의되지 않은 참조 없음 (완전 링크됨)
- `MH_DYLDLINK`: Dyld 링크
- `MH_PREBOUND`: 동적 참조가 미리 바인딩됨.
- `MH_SPLIT_SEGS`: 파일이 r/o 및 r/w 세그먼트로 분할됨.
- `MH_WEAK_DEFINES`: 바이너리에 약한 정의 기호가 있음
- `MH_BINDS_TO_WEAK`: 바이너리가 약한 기호를 사용함
- `MH_ALLOW_STACK_EXECUTION`: 스택을 실행 가능하게 만듦
- `MH_NO_REEXPORTED_DYLIBS`: 라이브러리에 LC_REEXPORT 명령 없음
- `MH_PREBOUND`: 동적 참조가 사전 바인딩됨
- `MH_SPLIT_SEGS`: 파일이 r/o 및 r/w 세그먼트로 분리됨
- `MH_WEAK_DEFINES`: 바이너리에 weak로 정의된 심볼이 있음
- `MH_BINDS_TO_WEAK`: 바이너리가 weak 심볼을 사용함
- `MH_ALLOW_STACK_EXECUTION`: 스택을 실행 가능하게
- `MH_NO_REEXPORTED_DYLIBS`: 라이브러리에 LC_REEXPORT 명령 없음
- `MH_PIE`: 위치 독립 실행 파일
- `MH_HAS_TLV_DESCRIPTORS`: 스레드 로컬 변수가 있는 섹션이 있음
- `MH_NO_HEAP_EXECUTION`: 힙/데이터 페이지에 대한 실행 없음
- `MH_HAS_OBJC`: 바이너리에 oBject-C 섹션이 있음
- `MH_HAS_TLV_DESCRIPTORS`: 스레드 로컬 변수 섹션이 있음
- `MH_NO_HEAP_EXECUTION`: 힙/데이터 페이지에서 실행 불가
- `MH_HAS_OBJC`: 바이너리에 Objective-C 섹션이 있음
- `MH_SIM_SUPPORT`: 시뮬레이터 지원
- `MH_DYLIB_IN_CACHE`: 공유 라이브러리 캐시의 dylibs/frameworks에서 사용됨.
- `MH_DYLIB_IN_CACHE`: shared library cache에 있는 dylib/framework에 사용됨.
## **Mach-O 로드 명령**
**메모리에서의 파일 레이아웃**은 여기에서 지정되며, **기호 테이블의 위치**, 실행 시작 시 메인 스레드의 컨텍스트, 그리고 필요한 **공유 라이브러리**가 자세히 설명됩니다. 동적 로더 **(dyld)**에 바이너리의 메모리 로딩 프로세스에 대한 지침이 제공됩니다.
파일의 메모리 레이아웃이 여기서 지정되며, 심볼 테이블의 위치, 실행 시작 시 메인 스레드의 컨텍스트, 그리고 필요한 공유 라이브러리들이 상세히 기술된다. 바이너리를 메모리에 로드하는 과정에 대해 동적 로더(dyld)에 지침을 제공한다.
**load_command** 구조체를 사용하며, 이는 언급된 **`loader.h`**에 정의되어 있습니다:
여기서는 앞서 언급한 **`loader.h`**에 정의된 **load_command** 구조체를 사용한다:
```objectivec
struct load_command {
uint32_t cmd; /* type of load command */
uint32_t cmdsize; /* total size of command in bytes */
};
```
약 **50가지의 다양한 유형의 로드 명령**이 시스템에 의해 다르게 처리됩니다. 가장 일반적인 것들은: `LC_SEGMENT_64`, `LC_LOAD_DYLINKER`, `LC_MAIN`, `LC_LOAD_DYLIB`, 및 `LC_CODE_SIGNATURE`입니다.
There are about **50 different types of load commands** that the system handles differently. The most common ones are: `LC_SEGMENT_64`, `LC_LOAD_DYLINKER`, `LC_MAIN`, `LC_LOAD_DYLIB`, and `LC_CODE_SIGNATURE`.
### **LC_SEGMENT/LC_SEGMENT_64**
> [!TIP]
> 기본적으로 이 유형의 로드 명령은 **이진 파일이 실행될 때 데이터 섹션에 표시된 오프셋에 따라 \_\_TEXT** (실행 코드) **및 \_\_DATA** (프로세스에 대한 데이터) **세그먼트를 로드하는 방법을 정의합니다.**
> 기본적으로, 이 유형의 Load Command는 바이너리가 실행될 때 데이터 섹션에 표시된 **오프셋에 따라 \_\_TEXT**(실행 코드) **및 \_\_DATA**(프로세스의 데이터) **세그먼트를 어떻게 로드하는지 정의합니다.**
이 명령은 **실행될 때 프로세스의 가상 메모리 공간에 매핑되는 세그먼트**를 **정의**합니다.
These commands **define segments** that are **mapped** into the **virtual memory space** of a process when it is executed.
**\_\_TEXT** 세그먼트와 같이 프로그램의 실행 코드를 포함하는 **다양한 유형**의 세그먼트가 있으며, **\_\_DATA** 세그먼트는 프로세스에서 사용하는 데이터를 포함합니다. 이러한 **세그먼트는 Mach-O 파일의 데이터 섹션에 위치합니다.**
There are **different types** of segments, such as the **\_\_TEXT** segment, which holds the executable code of a program, and the **\_\_DATA** segment, which contains data used by the process. These **segments are located in the data section** of the Mach-O file.
**각 세그먼트**는 여러 **섹션**으로 추가 **구분**될 수 있습니다. **로드 명령 구조**는 해당 세그먼트 내의 **이 섹션들에 대한 정보**를 포함합니다.
**Each segment** can be further **divided** into multiple **sections**. The **load command structure** contains **information** about **these sections** within the respective segment.
헤더에서는 먼저 **세그먼트 헤더**를 찾습니다:
In the header first you find the **segment header**:
<pre class="language-c"><code class="lang-c">struct segment_command_64 { /* for 64-bit architectures */
uint32_t cmd; /* LC_SEGMENT_64 */
@ -184,11 +184,11 @@ int32_t initprot; /* initial VM protection */
};
</code></pre>
세그먼트 헤더의 예:
Example of segment header:
<figure><img src="../../../images/image (1126).png" alt=""><figcaption></figcaption></figure>
이 헤더는 **그 뒤에 나타나는 헤더의 섹션 수를 정의합니다:**
This header defines the **number of sections whose headers appear after** it:
```c
struct section_64 { /* for 64-bit architectures */
char sectname[16]; /* name of this section */
@ -205,62 +205,62 @@ uint32_t reserved2; /* reserved (for count or sizeof) */
uint32_t reserved3; /* reserved */
};
```
**섹션 헤더**:
: **섹션 헤더**:
<figure><img src="../../../images/image (1108).png" alt=""><figcaption></figcaption></figure>
**섹션 오프셋** (0x37DC) + **아키텍처 시작 오프셋**을 추가하면, 이 경우 `0x18000` --> `0x37DC + 0x18000 = 0x1B7DC`
만약 **더하면** **섹션 오프셋** (0x37DC) + **arch가 시작하는 오프셋**, 이 경우 `0x18000` --> `0x37DC + 0x18000 = 0x1B7DC`
<figure><img src="../../../images/image (701).png" alt=""><figcaption></figcaption></figure>
**명령줄**에서 **헤더 정보**를 얻는 것도 가능합니다:
또한 **헤더 정보**를 **command line**에서 얻을 수도 있습니다:
```bash
otool -lv /bin/ls
```
Common segments loaded by this cmd:
- **`__PAGEZERO`:** 커널에 **주소 0**을 **매핑**하라고 지시하여 **읽거나, 쓸 수 없고, 실행할 수 없도록** 합니다. 구조체의 maxprot 및 minprot 변수는 이 페이지에 **읽기-쓰기-실행 권한이 없음**을 나타내기 위해 0으로 설정됩니다.
- 이 할당은 **NULL 포인터 역참조 취약점**을 완화하는 데 중요합니다. 이는 XNU가 첫 번째 페이지(오직 첫 번째) 메모리가 접근할 수 없도록 보장하는 하드 페이지 제로를 시행하기 때문입니다(단, i386 제외). 이 요구 사항을 충족하기 위해 바이너리는 첫 4k를 커버하는 작은 \_\_PAGEZERO를 제작하고 나머지 32비트 메모리를 사용자 및 커널 모드에서 접근 가능하게 할 수 있습니다.
- **`__TEXT`**: **읽기** 및 **실행** 권한이 있는 **실행 가능한** **코드**를 포함합니다(쓰기 불가)**.** 이 세그먼트의 일반적인 섹션:
- **`__PAGEZERO`:** 커널에**주소 0(address zero)**를 **매핑(map)**하도록 지시하여 **읽기/쓰기/실행이 불가능**하도록 합니다. 구조체의 maxprot 및 minprot 변수는 이 페이지에 대한 **읽기-쓰기-실행 권한이 없음을** 나타내기 위해 0으로 설정됩니다.
- 이 할당은 **NULL pointer dereference vulnerabilities**를 완화하는 데 중요합니다. 이는 XNU가 하드 페이지 제로를 적용하여 메모리의 첫 번째 페이지(첫 페이지만)를 접근 불가로 만들기 때문입니다(i386 제외). 바이너리는 작은 \_\_PAGEZERO( `-pagezero_size` 사용)를 만들어 처음 4K를 덮고 나머지 32비트 메모리는 유저 및 커널 모드에서 접근 가능하도록 하여 이 요구사항을 충족할 수 있습니다.
- **`__TEXT`**: **실행 가능한(executable)** **코드(code)**를 포함하며 **읽기(read)****실행(execute)** 권한을 가집니다(쓰기 권한 없음). 이 세그먼트의 일반 섹션:
- `__text`: 컴파일된 바이너리 코드
- `__const`: 상수 데이터(읽기 전용)
- `__[c/u/os_log]string`: C, 유니코드 또는 os 로그 문자열 상수
- `__stubs` `__stubs_helper`: 동적 라이브러리 로딩 과정에서 관련됨
- `__unwind_info`: 스택 언와인드 데이터.
- 이 모든 콘텐츠는 서명되지만 실행 가능하다고도 표시되어 있습니다(문자열 전용 섹션과 같이 이 권한이 반드시 필요하지 않은 섹션의 악용 가능성을 높임).
- **`__DATA`**: **읽기 가능**하고 **쓰기 가능**한 데이터를 포함합니다(실행 불가)**.**
- `__got:` 전역 오프셋 테이블
- `__nl_symbol_ptr`: 비게으른(로드 시 바인딩) 심볼 포인터
- `__la_symbol_ptr`: 게으른(사용 시 바인딩) 심볼 포인터
- `__const`: 읽기 전용 데이터여야 함(실제로는 아님)
- `__[c/u/os_log]string`: C, Unicode 또는 os 로그 문자열 상수
- `__stubs` and `__stubs_helper`: 동적 라이브러리 로딩 과정에 관여
- `__unwind_info`: 스택 언와인드 데이터
- 이 모든 내용은 서명되어 있되 실행 가능으로 표시되어 있다는 점에 유의하세요(문자열 전용 섹션처럼 반드시 실행 권한을 필요로 하지 않는 섹션을 악용할 수 있는 가능성이 증가합니다).
- **`__DATA`**: **읽기(readable)** 및 **쓰기(writable)** 가능한 데이터를 포함합니다(실행 불가).
- `__got`: 전역 오프셋 테이블(Global Offset Table)
- `__nl_symbol_ptr`: Non-lazy(로딩 시 바인딩) 심볼 포인터
- `__la_symbol_ptr`: Lazy(사용 시 바인딩) 심볼 포인터
- `__const`: 원래는 읽기 전용 데이터여야 함(실제론 그렇지 않음)
- `__cfstring`: CoreFoundation 문자열
- `__data`: 초기화된 전역 변수
- `__bss`: 초기화되지 않은 정적 변수
- `__objc_*` (\_\_objc_classlist, \_\_objc_protolist 등): Objective-C 런타임에서 사용되는 정보
- **`__DATA_CONST`**: \_\_DATA.\_\_const는 상수(쓰기 권한)가 보장되지 않으며, 다른 포인터와 GOT도 마찬가지입니다. 이 섹션은 `mprotect`를 사용하여 `__const`, 일부 초기화기 및 GOT 테이블(해결된 후)을 **읽기 전용**으로 만듭니다.
- **`__LINKEDIT`**: 심볼, 문자열 및 재배치 테이블 항목과 같은 링커(dyld)에 대한 정보를 포함합니다. `__TEXT` 또는 `__DATA`에 없는 콘텐츠를 위한 일반 컨테이너이며, 그 내용은 다른 로드 명령에서 설명됩니다.
- dyld 정보: 재배치, 비게으른/게으른/약한 바인딩 opcode 및 내보내기 정보
- 함수 시작: 함수의 시작 주소 테이블
- 코드 내 데이터: \_\_text의 데이터 섬
- 심볼 테이블: 바이너리의 심볼
- 간접 심볼 테이블: 포인터/스텁 심볼
- 문자열 테이블
- 코드 서명
- **`__OBJC`**: Objective-C 런타임에서 사용되는 정보를 포함합니다. 이 정보는 \_\_DATA 세그먼트의 다양한 \_\_objc\_\* 섹션에서도 발견될 수 있습니다.
- **`__RESTRICT`**: 내용이 없는 세그먼트로, **`__restrict`**라는 단일 섹션(비어 있음)을 포함하여 바이너리를 실행할 때 DYLD 환경 변수를 무시하도록 보장합니다.
- `__objc_*` (\_\_objc_classlist, \_\_objc_protolist, 등): Objective-C 런타임에서 사용하는 정보
- **`__DATA_CONST`**: \_\_DATA.\_\_const는 상수(쓰기 금지)라고 보장되지 않으며, 다른 포인터들과 GOT 또한 마찬가지입니다. 이 섹션은 `__const`, 일부 이니셜라이저 및 (해결된 후의) GOT 테이블을 `mprotect`를 사용해 **읽기 전용**으로 만듭니다.
- **`__LINKEDIT`**: 링커(dyld)를 위한 정보(심볼, 문자열, 재배치 테이블 엔트리 등)를 포함합니다. `__TEXT``__DATA`에 속하지 않는 내용을 담는 일반 컨테이너이며 그 내용은 다른 로드 커맨드에서 기술됩니다.
- dyld 정보: Rebase, Non-lazy/lazy/weak binding opcodes 및 export 정보
- Functions starts: 함수들의 시작 주소 표
- Data In Code: 데이터 섬들 in \_\_text
- Symbol Table: 바이너리 내의 심볼
- Indirect Symbol Table: 포인터/스텁 심볼
- String Table
- Code Signature
- **`__OBJC`**: Objective-C 런타임에서 사용하는 정보를 포함합니다. 이 정보는 \_\_DATA 세그먼트 내의 다양한 \_\_objc\_\* 섹션에서도 찾을 수 있습니다.
- **`__RESTRICT`**: 내용이 없는 세그먼트로, **`__restrict`**라는 단일 섹션(역시 비어 있음)을 가지며, 바이너리 실행 시 DYLD 환경 변수를 무시하도록 보장합니다.
코드에서 볼 수 있듯이, **세그먼트는 플래그도 지원합니다**(비록 많이 사용되지는 않지만):
As it was possible to see in the code, **segments also support flags** (although they aren't used very much):
- `SG_HIGHVM`: 코어 전용(사용되지 않음)
- `SG_HIGHVM`: Core 전용(사용 안 함)
- `SG_FVMLIB`: 사용되지 않음
- `SG_NORELOC`: 세그먼트에 재배치 없음
- `SG_PROTECTED_VERSION_1`: 암호화. 예를 들어 Finder가 `__TEXT` 세그먼트를 암호화하는 데 사용됩니다.
- `SG_PROTECTED_VERSION_1`: 암호화. 예를 들어 Finder가 텍스트 `__TEXT` 세그먼트를 암호화하는 데 사용됩니다.
### **`LC_UNIXTHREAD/LC_MAIN`**
**`LC_MAIN`**은 **entryoff 속성**에 있는 진입점을 포함합니다. 로드 시, **dyld**는 이 값을 (메모리 내) **바이너리의 기본 주소**에 **추가**한 다음, 이 명령어로 **점프**하여 바이너리 코드의 실행을 시작합니다.
**`LC_MAIN`**은 **entryoff attribute**에 엔트리포인트를 포함합니다. 로드 시, **dyld**는 단순히 이 값을 (메모리 상의) **바이너리의 베이스(base of the binary)**에 더한 후 해당 명령으로 **점프(jumps)**하여 바이너리 코드를 실행합니다.
**`LC_UNIXTHREAD`**는 메인 스레드를 시작할 때 레지스터가 가져야 할 값을 포함합니다. 이는 이미 사용 중단되었지만 **`dyld`**는 여전히 사용합니다. 이로 설정된 레지스터 값을 확인할 수 있습니다:
**`LC_UNIXTHREAD`**는 메인 스레드를 시작할 때 레지스터가 가져야 할 값들을 포함합니다. 이는 이미 deprecated 되었지만 **`dyld`**는 여전히 이를 사용합니다. 이로 인해 설정된 레지스터 값들은 다음 통해 확인할 수 있습니다:
```bash
otool -l /usr/lib/dyld
[...]
@ -286,34 +286,39 @@ cpsr 0x00000000
```
### **`LC_CODE_SIGNATURE`**
Mach-O 파일의 **코드 서명**에 대한 정보를 포함합니다. 이는 **서명 블롭**을 가리키는 **오프셋**만 포함합니다. 일반적으로 파일의 맨 끝에 위치합니다.\
그러나 이 섹션에 대한 일부 정보는 [**이 블로그 게시물**](https://davedelong.com/blog/2018/01/10/reading-your-own-entitlements/)과 이 [**gists**](https://gist.github.com/carlospolop/ef26f8eb9fafd4bc22e69e1a32b81da4)에서 찾을 수 있습니다.
{{#ref}}
../../../generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/mach-o-entitlements-and-ipsw-indexing.md
{{#endref}}
Contains information about the **code signature of the Macho-O file**. It only contains an **offset** that **points** to the **signature blob**. This is typically at the very end of the file.\
However, you can find some information about this section in [**this blog post**](https://davedelong.com/blog/2018/01/10/reading-your-own-entitlements/) and this [**gists**](https://gist.github.com/carlospolop/ef26f8eb9fafd4bc22e69e1a32b81da4).
### **`LC_ENCRYPTION_INFO[_64]`**
바이너리 암호화에 대한 지원. 그러나 물론, 공격자가 프로세스를 손상시키면 메모리를 암호화되지 않은 상태로 덤프할 수 있습니다.
바이너리 암호화를 지원합니다. 그러나 공격자가 프로세스를 침해하면 메모리를 암호화되지 않은 상태로 덤프할 수 있습니다.
### **`LC_LOAD_DYLINKER`**
프로세스 주소 공간에 공유 라이브러리를 매핑하는 **동적 링커 실행 파일**의 **경로**를 포함합니다. **값은 항상 `/usr/lib/dyld`로 설정됩니다**. macOS에서는 dylib 매핑이 **커널 모드**가 아닌 **사용자 모드**에서 발생한다는 점에 유의하는 것이 중요합니다.
공유 라이브러리를 프로세스 주소 공간에 맵핑하는 dynamic linker 실행 파일의 경로를 포함합니다. 값은 항상 `/usr/lib/dyld`로 설정됩니다. macOS에서는 dylib 매핑이 커널 모드가 아니라 사용자 모드에서 발생한다는 점을 유의하세요.
### **`LC_IDENT`**
구식이지만 패닉 시 덤프를 생성하도록 구성되면 Mach-O 코어 덤프가 생성되고 커널 버전이 `LC_IDENT` 명령에 설정됩니다.
구식이지만, panic 시 덤프를 생성하도록 구성하면 Mach-O 코어 덤프가 생성되고 커널 버전이 `LC_IDENT` 명령에 설정됩니다.
### **`LC_UUID`**
무작위 UUID. XNU가 나머지 프로세스 정보와 함께 캐시하므로 직접적으로 유용합니다. 충돌 보고서에서 사용할 수 있습니다.
무작위 UUID입니다. 자체적으로는 직접적인 용도는 제한적이지만 XNU가 이를 다른 프로세스 정보와 함께 캐시합니다. 크래시 리포트에 사용할 수 있습니다.
### **`LC_DYLD_ENVIRONMENT`**
프로세스가 실행되기 전에 dyld에 환경 변수를 지정할 수 있습니다. 이는 프로세스 내에서 임의의 코드를 실행할 수 있게 하므로 매우 위험할 수 있습니다. 따라서 이 로드 명령은 `#define SUPPORT_LC_DYLD_ENVIRONMENT`로 빌드된 dyld에서만 사용되며, 로드 경로를 지정하는 `DYLD_..._PATH`식의 변수로만 처리하도록 추가 제한됩니다.
프로세스가 실행되기 전에 dyld에 환경 변수를 지정할 수 있게 합니다. 이는 프로세스 내부에서 임의의 코드를 실행할 수 있게 하므로 매우 위험할 수 있습니다. 따라서 이 load command는 `#define SUPPORT_LC_DYLD_ENVIRONMENT`로 빌드된 dyld에서만 사용되며, 처리는 로드 경로를 지정하는 `DYLD_..._PATH`태의 변수로만 제한됩니다.
### **`LC_LOAD_DYLIB`**
로드 명령은 **동적** **라이브러리** 의존성을 설명하며, **로더**(dyld)에게 **해당 라이브러리를 로드하고 링크하라고 지시합니다**. Mach-O 바이너리가 요구하는 **각 라이브러리**에 대해 `LC_LOAD_DYLIB` 로드 명령이 있습니다.
load command는 loader(dyld)에게 라이브러리를 로드하고 링크하도록 지시하는 dynamic library 의존성을 설명합니다. Mach-O 바이너리가 필요로 하는 각 라이브러리마다 하나의 `LC_LOAD_DYLIB` load command가 있습니다.
- 이 로드 명령은 **`dylib_command`** 유형의 구조체입니다(실제 의존 동적 라이브러리를 설명하는 struct dylib 포함):
- This load command is a structure of type **`dylib_command`** (which contains a struct dylib, describing the actual dependent dynamic library):
```objectivec
struct dylib_command {
uint32_t cmd; /* LC_LOAD_{,WEAK_}DYLIB */
@ -330,7 +335,7 @@ uint32_t compatibility_version; /* library's compatibility vers number*/
```
![](<../../../images/image (486).png>)
이 정보를 cli를 통해서도 얻을 수 있습니다:
또는 cli에서 다음과 같이 이 정보를 얻을 수 있습니다:
```bash
otool -L /bin/ls
/bin/ls:
@ -338,30 +343,30 @@ otool -L /bin/ls
/usr/lib/libncurses.5.4.dylib (compatibility version 5.4.0, current version 5.4.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.0.0)
```
일부 잠재적인 맬웨어 관련 라이브러리는 다음과 같습니다:
일부 잠재적 악성코드 관련 라이브러리:
- **DiskArbitration**: USB 드라이브 모니터링
- **AVFoundation:** 오디오 및 비디오 캡처
- **CoreWLAN**: Wifi 스캔.
> [!NOTE]
> Mach-O 바이너리는 **LC_MAIN**에 지정된 주소 **이전**에 **실행**될 **하나 또는 여러 개의** **생성자**를 포함할 수 있습니다.\
> 모든 생성자의 오프셋은 **\_\_DATA_CONST** 세그먼트의 **\_\_mod_init_func** 섹션에 저장됩니다.
> [!TIP]
> A Mach-O binary can contain one or **more** **constructors**, that will be **executed** **before** the address specified in **LC_MAIN**.\
> The offsets of any constructors are held in the **\_\_mod_init_func** section of the **\_\_DATA_CONST** segment.
## **Mach-O 데이터**
파일의 핵심은 데이터 영역으로, 로드 명령 영역에 정의된 여러 세그먼트로 구성됩니다. **각 세그먼트 내에는 다양한 데이터 섹션이 포함될 수 있으며**, 각 섹션은 특정 유형에 대한 **코드 또는 데이터**를 보유합니다.
파일의 핵심에는 load-commands 영역에 정의된 여러 세그먼트로 구성된 데이터 영역이 있습니다. **각 세그먼트 안에는 다양한 데이터 섹션이 존재할 수 있으며**, 각 섹션은 특정 타입에 대한 **코드 또는 데이터를 보관**합니다.
> [!TIP]
> 데이터는 기본적으로 로드 명령 **LC_SEGMENTS_64**에 의해 로드되는 모든 **정보**를 포함하는 부분입니다.
> 데이터는 기본적으로 **LC_SEGMENTS_64** 로드 커맨드에 의해 로드되는 모든 **정보**를 포함하는 부분입니다.
![https://www.oreilly.com/api/v2/epubs/9781785883378/files/graphics/B05055_02_38.jpg](<../../../images/image (507) (3).png>)
여기에는 다음이 포함됩니다:
에는 다음이 포함됩니다:
- **함수 테이블:** 프로그램 함수에 대한 정보를 보합니다.
- **함수 테이블:** 프로그램 함수에 대한 정보를 보합니다.
- **심볼 테이블**: 바이너리에서 사용되는 외부 함수에 대한 정보를 포함합니다.
- 내부 함수, 변수 이름 등도 포함될 수 있습니다.
- 내부 함수 변수 이름 등도 포함될 수 있습니다.
확인하려면 [**Mach-O View**](https://sourceforge.net/projects/machoview/) 도구를 사용할 수 있습니다:
@ -373,18 +378,18 @@ size -m /bin/ls
```
## Objetive-C 공통 섹션
In `__TEXT` segment (r-x):
`__TEXT` 세그먼트 (r-x):
- `__objc_classname`: 클래스 이름 (문자열)
- `__objc_methname`: 메서드 이름 (문자열)
- `__objc_methtype`: 메서드 유형 (문자열)
- `__objc_classname`: 클래스 이름(문자열)
- `__objc_methname`: 메서드 이름(문자열)
- `__objc_methtype`: 메서드 타입(문자열)
In `__DATA` segment (rw-):
`__DATA` 세그먼트 (rw-):
- `__objc_classlist`: 모든 Objetive-C 클래스에 대한 포인터
- `__objc_nlclslist`: 비지연 Objective-C 클래스에 대한 포인터
- `__objc_nlclslist`: Non-Lazy Objective-C 클래스에 대한 포인터
- `__objc_catlist`: 카테고리에 대한 포인터
- `__objc_nlcatlist`: 비지연 카테고리에 대한 포인터
- `__objc_nlcatlist`: Non-Lazy 카테고리에 대한 포인터
- `__objc_protolist`: 프로토콜 목록
- `__objc_const`: 상수 데이터
- `__objc_imageinfo`, `__objc_selrefs`, `objc__protorefs`...

View File

@ -4,12 +4,17 @@
## 기본 정보
Mach-o 바이너리는 바이너리 내부의 서명의 **오프셋**과 **크기**를 나타내는 **`LC_CODE_SIGNATURE`**라는 로드 명령을 포함합니다. 실제로 GUI 도구인 MachOView를 사용하면 바이너리의 끝에서 이 정보를 포함하는 **코드 서명**이라는 섹션을 찾을 수 있습니다:
{{#ref}}
../../../generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/mach-o-entitlements-and-ipsw-indexing.md
{{#endref}}
Mach-o 바이너리에는 **`LC_CODE_SIGNATURE`**라는 load command가 포함되어 있으며, 이는 바이너리 내부 서명들의 **offset**과 **size**를 가리킵니다. 실제로 GUI 도구 MachOView를 사용하면 바이너리 끝에서 **Code Signature**라는 섹션에서 이 정보를 찾을 수 있습니다:
<figure><img src="../../../images/image (1) (1) (1) (1).png" alt="" width="431"><figcaption></figcaption></figure>
코드 서명의 매직 헤더는 **`0xFADE0CC0`**입니다. 그런 다음 이들을 포함하는 superBlob의 길이와 블롭 수와 같은 정보가 있습니다.\
이 정보는 [소스 코드 여기](https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/osfmk/kern/cs_blobs.h#L276)에서 찾을 수 있습니다:
Code Signature의 magic header는 **`0xFADE0CC0`**입니다. 그 다음에는 length와 superBlob가 포함하는 blobs의 개수 등 정보가 있습니다.\
이 정보는 [source code here](https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/osfmk/kern/cs_blobs.h#L276):
```c
/*
* Structure of an embedded-signature SuperBlob
@ -38,14 +43,14 @@ char data[];
} CS_GenericBlob
__attribute__ ((aligned(1)));
```
일반적으로 포함된 블롭은 Code Directory, Requirements 및 Entitlements와 Cryptographic Message Syntax (CMS)입니다.\
또한, 블롭에 인코딩된 데이터가 **Big Endian**으로 인코딩되어 있음을 주목하십시오.
일반적으로 포함된 blobs에는 Code Directory, Requirements and Entitlements, 그리고 Cryptographic Message Syntax (CMS)가 있습니다.\
또한, blobs에 인코딩된 데이터가 **Big Endian**으로 인코딩되어 있다는 점을 유의하세요.
또한, 서명은 이진 파일에서 분리되어 `/var/db/DetachedSignatures`에 저장될 수 있습니다 (iOS에서 사용).
또한, 서명은 binaries에서 분리되어 `/var/db/DetachedSignatures`에 저장될 수 있습니다 (iOS에서 사용).
## Code Directory Blob
[Code Directory Blob의 선언을 코드에서 찾는 것이 가능합니다](https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/osfmk/kern/cs_blobs.h#L104):
코드에서 [Code Directory Blob in the code](https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/osfmk/kern/cs_blobs.h#L104): 선언을 찾을 수 있습니다:
```c
typedef struct __CodeDirectory {
uint32_t magic; /* magic number (CSMAGIC_CODEDIRECTORY) */
@ -101,12 +106,12 @@ char end_withLinkage[0];
} CS_CodeDirectory
__attribute__ ((aligned(1)));
```
다양한 버전의 이 구조체가 있으며, 이전 버전은 정보가 적을 수 있습니다.
Note that there are different versions of this struct where old ones might contain less information.
## 서명 코드 페이지
## Signing Code Pages
전체 바이너리를 해싱하는 것은 비효율적이며, 메모리에 부분적으로만 로드될 경우에는 심지어 쓸모가 없습니다. 따라서 코드 서명은 실제로 각 바이너리 페이지가 개별적으로 해싱된 해시의 해시입니다.\
실제로 이전 **Code Directory** 코드에서 **페이지 크기가 지정되어** 있는 것을 볼 수 있습니다. 게다가, 바이너리의 크기가 페이지 크기의 배수가 아닐 경우, 필드 **CodeLimit**는 서명의 끝이 어디인지 지정합니다.
전체 binary를 해싱하는 것은 비효율적이며, 일부만 메모리에 로드되는 경우에는 무의미할 수 있습니다. 따라서 code signature는 각 binary page를 개별적으로 해싱한 hash들의 hash입니다.\
사실, 이전의 **Code Directory** 코드에서는 **page size is specified**가 필드 중 하나에 명시되어 있는 것을 볼 수 있습니다. 또한 binary의 크기가 page의 크기의 배수가 아닌 경우, 필드 **CodeLimit**가 서명의 끝 위치를 지정합니다.
```bash
# Get all hashes of /bin/ps
codesign -d -vvvvvv /bin/ps
@ -144,25 +149,25 @@ openssl sha256 /tmp/*.page.*
```
## Entitlements Blob
응용 프로그램에는 모든 권한이 정의된 **entitlement blob**이 포함될 수 있습니다. 또한 일부 iOS 바이너리는 특별 슬롯 -7에 권한이 특정되어 있을 수 있습니다(대신 -5 권한 특별 슬롯에).
애플리케이션은 모든 entitlements가 정의된 **entitlement blob**을 포함할 수 있다는 점을 유의하자. 또한 일부 iOS 바이너리는 entitlements가 특수 슬롯 -5 대신 -7에 위치할 수 있다.
## Special Slots
MacOS 응용 프로그램은 바이너리 내에서 실행하는 데 필요한 모든 것을 갖추고 있지 않지만 **외부 리소스**(일반적으로 응용 프로그램의 **bundle** 내)에 의존합니다. 따라서 바이너리 내에는 수정되지 않았는지 확인하기 위해 일부 흥미로운 외부 리소스의 해시를 포함하는 슬롯이 있습니다.
MacOS 애플리케이션은 실행에 필요한 모든 것을 바이너리 내부에 포함하지 않으며 보통 애플리케이션의 **bundle** 안에 있는 **external resources**를 사용한다. 따라서 바이너리 내부에는 일부 외부 리소스가 변경되지 않았는지 확인하기 위해 해당 리소스들의 해시를 담는 특수 슬롯들이 있다.
실제로, Code Directory 구조체에서 **`nSpecialSlots`**라는 매개변수를 볼 수 있으며, 이는 특별 슬롯의 수를 나타냅니다. 특별 슬롯 0은 없으며 가장 일반적인 슬롯( -1에서 -6까지)은 다음과 같습니다:
실제로 Code Directory structs에서 특수 슬롯의 개수를 나타내는 **`nSpecialSlots`**라는 파라미터를 볼 수 있다. 0번 특수 슬롯은 없으며, 가장 흔한 것들(1에서 6까지)은 다음과 같다:
- `info.plist`의 해시(또는 `__TEXT.__info__plist` 의 것).
- 요구 사항의 해시
- 리소스 디렉토리의 해시(번들 내의 `_CodeSignature/CodeResources` 파일의 해시).
- 응용 프로그램 특정(사용되지 않음)
- 권한의 해시
- DMG 코드 서명 전용
- DER 권한
- `info.plist`의 해시(또는 `__TEXT.__info__plist` 의 것).
- Requirements의 해시
- Resource Directory의 해시(`_CodeSignature/CodeResources` 파일(번들 내부)의 해시).
- 애플리케이션 전용(미사용)
- entitlements의 해시
- DMG code signatures 전용
- DER Entitlements
## Code Signing Flags
모든 프로세스에는 `status`로 알려진 비트마스크가 관련되어 있으며, 이는 커널에 의해 시작되며 일부는 **코드 서명**에 의해 재정의될 수 있습니다. 코드 서명에 포함될 수 있는 이러한 플래그는 [코드에서 정의되어 있습니다](https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/osfmk/kern/cs_blobs.h#L36):
모든 프로세스는 커널에 의해 설정되는 `status`라는 비트마스크와 관련되며, 이 중 일부는 **code signature**로 재정의될 수 있다. 코드 서명에 포함될 수 있는 이 플래그들은 [코드에 정의되어 있다](https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/osfmk/kern/cs_blobs.h#L36):
```c
/* code signing attributes of a process */
#define CS_VALID 0x00000001 /* dynamically valid */
@ -209,13 +214,13 @@ CS_RESTRICT | CS_ENFORCEMENT | CS_REQUIRE_LV | CS_RUNTIME | CS_LINKER_SIGNED)
```
Note that the function [**exec_mach_imgact**](https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/bsd/kern/kern_exec.c#L1420) can also add the `CS_EXEC_*` flags dynamically when starting the execution.
## 코드 서명 요구 사항
## 코드 서명 요구사항
각 애플리케이션은 실행될 수 있도록 **충족해야 하는** **요구 사항**을 저장합니다. **애플리케이션이 충족하지 않는 요구 사항을 포함하고 있다면**, 실행되지 않습니다(변경되었을 가능성이 높기 때문입니다).
각 애플리케이션은 실행되기 위해 충족해야 하는 몇 가지 **요구사항**을 저장합니다. 애플리케이션의 **요구사항**이 충족되지 않으면 해당 애플리케이션은 실행되지 않습니다(변조되었을 가능성이 높기 때문입니다).
바이너리의 요구 사항은 **특별한 문법**을 사용하며, 이는 **표현식**의 흐름으로 `0xfade0c00`을 매직으로 사용하여 블롭으로 인코딩됩니다. 이 **해시는 특별한 코드 슬롯에 저장됩니다**.
바이너리의 요구사항은 일련의 **표현식**으로 구성된 **특수 문법**을 사용하며, `0xfade0c00`를 매직으로 하는 blob으로 인코딩되고 그 **해시가 특수 코드 슬롯에 저장됩니다**.
바이너리의 요구 사항은 다음을 실행하여 확인할 수 있습니다:
바이너리의 요구사항은 다음 명령을 실행하면 확인할 수 있습니다:
```bash
codesign -d -r- /bin/ls
Executable=/bin/ls
@ -225,10 +230,10 @@ codesign -d -r- /Applications/Signal.app/
Executable=/Applications/Signal.app/Contents/MacOS/Signal
designated => identifier "org.whispersystems.signal-desktop" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = U68MSDN6DR
```
> [!NOTE]
> 이 서명이 인증 정보, TeamID, ID, 권한 및 기타 많은 데이터를 확인할 수 있는 방법에 유의하십시오.
> [!TIP]
> 이러한 서명은 인증서 정보, TeamID, IDs, entitlements 및 그 밖의 여러 데이터를 검사할 수 있다는 점에 주목하세요.
또한, `csreq` 도구를 사용하여 일부 컴파일된 요구 사항을 생성할 수 있습니다:
또한 `csreq` 도구를 사용하여 일부 컴파일된 요구사항을 생성할 수 있습니다:
```bash
# Generate compiled requirements
csreq -b /tmp/output.csreq -r='identifier "org.whispersystems.signal-desktop" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = U68MSDN6DR'
@ -240,55 +245,57 @@ od -A x -t x1 /tmp/output.csreq
0000020 00 00 00 21 6f 72 67 2e 77 68 69 73 70 65 72 73
[...]
```
It's possible to access this information and create or modify requirements with some APIs from the `Security.framework` like:
#### **유효성 검사**
- **`Sec[Static]CodeCheckValidity`**: 요구 사항에 따라 SecCodeRef의 유효성을 검사합니다.
- **`SecRequirementEvaluate`**: 인증서 컨텍스트에서 요구 사항을 검증합니다.
- **`SecTaskValidateForRequirement`**: `CFString` 요구 사항에 대해 실행 중인 SecTask를 검증합니다.
- **`Sec[Static]CodeCheckValidity`**: SecCodeRef의 Requirement별 유효성을 확인합니다.
- **`SecRequirementEvaluate`**: 인증서 컨텍스트에서 Requirement를 검증합니다.
- **`SecTaskValidateForRequirement`**: 실행 중인 SecTask를 `CFString` Requirement에 대해 검증합니다.
#### **코드 요구 사항 생성 및 관리**
#### **코드 Requirement 생성 및 관리**
- **`SecRequirementCreateWithData`:** 요구 사항을 나타내는 이진 데이터에서 `SecRequirementRef`를 생성합니다.
- **`SecRequirementCreateWithString`:** 요구 사항의 문자열 표현에서 `SecRequirementRef`를 생성합니다.
- **`SecRequirementCopy[Data/String]`**: `SecRequirementRef`의 이진 데이터 표현을 검색합니다.
- **`SecRequirementCreateGroup`**: 앱 그룹 멤버십에 대한 요구 사항을 생성합니다.
- **`SecRequirementCreateWithData`:** Requirement를 나타내는 이진 데이터로부터 `SecRequirementRef`를 생성합니다.
- **`SecRequirementCreateWithString`:** Requirement의 문자열 표현으로부터 `SecRequirementRef`를 생성합니다.
- **`SecRequirementCopy[Data/String]`**: `SecRequirementRef`의 이진 데이터 표현을 가져옵니다.
- **`SecRequirementCreateGroup`**: app-group 멤버십에 대한 Requirement를 생성합니다
#### **코드 서명 정보 접근**
- **`SecStaticCodeCreateWithPath`**: 코드 서명을 검사하기 위해 파일 시스템 경로에서 `SecStaticCodeRef` 객체를 초기화합니다.
- **`SecCodeCopySigningInformation`**: `SecCodeRef` 또는 `SecStaticCodeRef`에서 서명 정보를 얻습니다.
- **`SecStaticCodeCreateWithPath`**: 코드 서명을 검사하기 위해 파일 시스템 경로로부터 `SecStaticCodeRef` 객체를 초기화합니다.
- **`SecCodeCopySigningInformation`**: `SecCodeRef` 또는 `SecStaticCodeRef`로부터 서명 정보를 얻습니다.
#### **코드 요구 사항 수정**
#### **코드 Requirement 수정**
- **`SecCodeSignerCreate`**: 코드 서명 작업을 수행하기 위한 `SecCodeSignerRef` 객체를 생성합니다.
- **`SecCodeSignerSetRequirement`**: 서명 중에 적용할 새로운 요구 사항을 설정합니다.
- **`SecCodeSignerAddSignature`**: 지정된 서명자로 서명되는 코드에 서명을 추가합니다.
- **`SecCodeSignerSetRequirement`**: 서명 시 적용할 새로운 Requirement를 설정합니다.
- **`SecCodeSignerAddSignature`**: 지정된 서명자로 코드에 서명을 추가합니다.
#### **요구 사항으로 코드 검증**
#### **Requirement로 코드 검증**
- **`SecStaticCodeCheckValidity`**: 지정된 요구 사항에 대해 정적 코드 객체를 검증합니다.
- **`SecStaticCodeCheckValidity`**: 지정된 Requirement들에 대해 static code 객체의 유효성을 검증합니다.
#### **추가 유용한 API**
#### **추가 유용한 API**
- **`SecCodeCopy[Internal/Designated]Requirement`: SecCodeRef에서 SecRequirementRef를 가져옵니다.**
- **`SecCodeCopyGuestWithAttributes`**: 특정 속성을 기반으로 하는 코드 객체를 나타내는 `SecCodeRef`를 생성하며, 샌드박싱에 유용합니다.
- **`SecCodeCopyPath`**: `SecCodeRef`와 관된 파일 시스템 경로를 검색합니다.
- **`SecCodeCopySigningIdentifier`**: `SecCodeRef`에서 서명 식별자(예: 팀 ID)를 얻습니다.
- **`SecCodeGetTypeID`**: `SecCodeRef` 객체의 유형 식별자를 반환합니다.
- **`SecCodeCopy[Internal/Designated]Requirement`: Get SecRequirementRef from SecCodeRef**
- **`SecCodeCopyGuestWithAttributes`**: 특정 속성에 기반한 코드 객체를 나타내는 `SecCodeRef`를 생성합니다. sandboxing에 유용합니다.
- **`SecCodeCopyPath`**: `SecCodeRef`관된 파일 시스템 경로를 검색합니다.
- **`SecCodeCopySigningIdentifier`**: `SecCodeRef`로부터 서명 식별자(예: Team ID)를 얻습니다.
- **`SecCodeGetTypeID`**: `SecCodeRef` 객체들의 타입 식별자를 반환합니다.
- **`SecRequirementGetTypeID`**: `SecRequirementRef`의 CFTypeID를 가져옵니다.
#### **코드 서명 플래그 및 상수**
- **`kSecCSDefaultFlags`**: 코드 서명 작업을 위한 많은 Security.framework 함수에서 사용되는 기본 플래그입니다.
- **`kSecCSSigningInformation`**: 서명 정보를 검색해야 함을 지정하는 데 사용되는 플래그입니다.
- **`kSecCSSigningInformation`**: 서명 정보를 가져와야 함을 지정하는데 사용되는 플래그입니다.
## 코드 서명 강제 적용
## 코드 서명 강제
**커널**은 앱의 코드가 실행되기 전에 **코드 서명**을 **검사**합니다. 또한, 메모리에 새로운 코드를 작성하고 실행할 수 있는 한 가지 방법은 `mprotect``MAP_JIT` 플래그와 함께 호출될 때 JIT를 악용하는 것입니다. 이 작업을 수행하려면 애플리케이션에 특별한 권한이 필요합니다.
앱 코드가 실행되도록 허용하기 전에 **kernel**이 **코드 서명을 검사합니다**. 또한, 메모리에 새로운 코드를 쓰고 실행할 수 있게 하는 한 가지 방법은 `mprotect``MAP_JIT` 플래그로 호출될 때 JIT을 악용하는 것입니다. 이를 위해서는 애플리케이션에 특별한 entitlement가 필요하다는 점에 유의하세요.
## `cs_blobs` & `cs_blob`
[**cs_blob**](https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/bsd/sys/ubc_internal.h#L106) 구조체는 실행 중인 프로세스의 권한에 대한 정보를 포함합니다. `csb_platform_binary`는 애플리케이션이 플랫폼 이진 파일인지 여부도 알려줍니다(이는 보안 메커니즘을 적용하기 위해 OS에 의해 여러 순간에 확인됩니다. 예를 들어 이러한 프로세스의 작업 포트에 대한 SEND 권한을 보호하는 것입니다).
[**cs_blob**](https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/bsd/sys/ubc_internal.h#L106) struct는 해당 실행 중인 프로세스의 entitlement에 대한 정보를 포함합니다. `csb_platform_binary`는 또한 애플리케이션이 platform binary인지 여부를 알려줍니다(OS가 이러한 프로세스들의 task port에 대한 SEND rights를 보호하는 등 보안 메커니즘을 적용하기 위해 여러 시점에서 확인합니다).
```c
struct cs_blob {
struct cs_blob *csb_next;
@ -347,7 +354,7 @@ bool csb_csm_managed;
#endif
};
```
## References
## 참고자료
- [**\*OS Internals Volume III**](https://newosxbook.com/home.html)