Translated ['src/macos-hardening/macos-security-and-privilege-escalation

This commit is contained in:
Translator 2025-10-01 01:54:54 +00:00
parent b57dcf224b
commit d7c103d377
5 changed files with 401 additions and 172 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) - [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) - [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) - [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) - [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) - [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) - [PNG tricks](generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/png-tricks.md)

View File

@ -2,7 +2,7 @@
{{#include ../../../banners/hacktricks-training.md}} {{#include ../../../banners/hacktricks-training.md}}
这里你可以找到针对特定文件类型和/或软件的有趣技巧: 这里你可以找到针对特定文件类型和/或软件的有趣技巧:
{{#ref}} {{#ref}}
@ -54,4 +54,9 @@ video-and-audio-file-analysis.md
zips-tricks.md zips-tricks.md
{{#endref}} {{#endref}}
{{#ref}}
mach-o-entitlements-and-ipsw-indexing.md
{{#endref}}
{{#include ../../../banners/hacktricks-training.md}} {{#include ../../../banners/hacktricks-training.md}}

View File

@ -0,0 +1,213 @@
# Mach-O 权限提取与 IPSW 索引
{{#include ../../../banners/hacktricks-training.md}}
## 概述
本页介绍如何通过遍历 LC_CODE_SIGNATURE 并解析 code signing SuperBlob编程式地从 Mach-O 二进制中提取 entitlements以及如何通过挂载并索引 Apple IPSW 固件的内容,以便进行取证搜索/差异比较,从而实现规模化处理。
如果你需要复习 Mach-O 格式和 code signing可以参考macOS code signing 和 SuperBlob 内部结构。
- 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)
## Entitlements in Mach-O: where they live
Entitlements 存储在由 LC_CODE_SIGNATURE 加载命令引用的 code signature 数据中,并放置在 __LINKEDIT 段。该签名是一个 CS_SuperBlob包含多个 blobcode directory、requirements、entitlements、CMS 等。entitlements blob 是一个 CS_GenericBlob其数据是一个 Apple Binary Property List (bplist00),将 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
注意多架构fat二进制包含多个 Mach-O slices。你必须选择要检查的架构对应的 slice然后遍历其 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选择所需的架构切片然后将子范围传递给 parse_entitlements。
- 在 macOS 上可以使用以下命令验证结果codesign -d --entitlements :- /path/to/binary
## 示例发现
具有特权的平台二进制通常会请求如下敏感 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 和跨发布/设备的 diffing 非常有价值。
## Scaling across IPSWs (mounting and indexing)
要在不存储完整镜像的情况下,对可执行文件进行枚举并大规模提取 entitlements
- 使用 @blacktop 的 ipsw 工具下载并挂载固件文件系统。挂载使用 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 和导入的 frameworks。
- 将标准化视图持久化到关系型数据库,以避免在成千上万个 IPSWs 中线性增长:
- executables, operating_system_versions, entitlements, frameworks
- many-to-many: executable↔OS version, executable↔entitlement, executable↔framework
示例查询:列出包含给定 executable 名称的所有 OS versions
```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";
```
数据库可移植性说明(如果你实现自己的索引器):
- 使用 ORM/抽象层(例如 SeaORM以保持代码与数据库无关SQLite/PostgreSQL
- SQLite 仅在 INTEGER PRIMARY KEY 上需要 AUTOINCREMENT如果在 Rust 中希望使用 i64 作为主键,可生成实体为 i32 然后转换类型SQLite 在内部将 INTEGER 以 8 字节有符号整数存储。
## Open-source tooling and references for entitlement hunting
- 固件 挂载/下载: https://github.com/blacktop/ipsw
- Entitlement 数据库与参考:
- Jonathan Levins entitlement DB: https://newosxbook.com/ent.php
- entdb: https://github.com/ChiChou/entdb
- 大规模索引器Rustself-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)
## References
- [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

@ -1,43 +1,43 @@
# macOS Universal binaries & Mach-O Format # macOS 通用二进制 & Mach-O 格式
{{#include ../../../banners/hacktricks-training.md}} {{#include ../../../banners/hacktricks-training.md}}
## 基本信息 ## 基本信息
Mac OS 二进制文件通常被编译为 **universal binaries**。一个 **universal binary** 可以 **在同一文件中支持多个架构**。 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>) ![https://alexdremov.me/content/images/2022/10/6XLCD.gif](<../../../images/image (470).png>)
## Fat Header ## 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 <pre class="language-c"><code class="lang-c"><strong>#define FAT_MAGIC 0xcafebabe
</strong><strong>#define FAT_CIGAM 0xbebafeca /* NXSwapLong(FAT_MAGIC) */ </strong><strong>#define FAT_CIGAM 0xbebafeca /* NXSwapLong(FAT_MAGIC) */
</strong> </strong>
struct fat_header { struct fat_header {
<strong> uint32_t magic; /* FAT_MAGIC FAT_MAGIC_64 */ <strong> uint32_t magic; /* FAT_MAGIC or FAT_MAGIC_64 */
</strong><strong> uint32_t nfat_arch; /* 后续结构的数量 */ </strong><strong> uint32_t nfat_arch; /* number of structs that follow */
</strong>}; </strong>};
struct fat_arch { struct fat_arch {
cpu_type_t cputype; /* cpu 说明符 (int) */ cpu_type_t cputype; /* cpu specifier (int) */
cpu_subtype_t cpusubtype; /* 机器说明符 (int) */ cpu_subtype_t cpusubtype; /* machine specifier (int) */
uint32_t offset; /* 文件偏移到此目标文件 */ uint32_t offset; /* file offset to this object file */
uint32_t size; /* 此目标文件的大小 */ uint32_t size; /* size of this object file */
uint32_t align; /* 以 2 的幂为单位的对齐 */ uint32_t align; /* alignment as a power of 2 */
}; };
</code></pre> </code></pre>
头部包含 **magic** 字节,后面是文件 **包含****archs****数量** (`nfat_arch`),每个架构将有一个 `fat_arch` 结构。 该 header 包含 **magic** 字节,后跟文件包含的 **架构数量** (`nfat_arch`),每个架构都会有一个 `fat_arch` 结构。
使用以下命令检查: 检查示例
<pre class="language-shell-session"><code class="lang-shell-session">% file /bin/ls <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] /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> <figure><img src="../../../images/image (1094).png" alt=""><figcaption></figcaption></figure>
正如你所想,通常为 2 个架构编译的 universal binary **会使文件大小翻倍**,而为 1 个架构编译的文件则不会 正如你可能想到的,通常为 2 个架构编译的 universal binary 的大小会是只为 1 个架构编译的 **两倍**
## **Mach-O Header** ## **Mach-O Header**
头部包含有关文件的基本信息,例如用于识别它为 Mach-O 文件的 magic 字节和有关目标架构的信息。你可以在以下路径找到它: `mdfind loader.h | grep -i mach-o | grep -E "loader.h$"` 该 header 包含关于文件的基本信息,例如用于识别为 Mach-O 文件的 magic 字节以及目标架构的信息。你可以在以下位置找到它:`mdfind loader.h | grep -i mach-o | grep -E "loader.h$"`
```c ```c
#define MH_MAGIC 0xfeedface /* the mach magic number */ #define MH_MAGIC 0xfeedface /* the mach magic number */
#define MH_CIGAM 0xcefaedfe /* NXSwapInt(MH_MAGIC) */ #define MH_CIGAM 0xcefaedfe /* NXSwapInt(MH_MAGIC) */
@ -101,17 +101,17 @@ 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_EXECUTE`: 可执行文件。
- `MH_FVMLIB`: 固定虚拟机库文件。 - `MH_FVMLIB`: 固定 VM 库文件。
- `MH_CORE`: 代码转储 - `MH_CORE`: 代码转储
- `MH_PRELOAD`: 预加载的可执行文件(XNU 不再支持) - `MH_PRELOAD`: 预加载的可执行文件XNU 不再支持)
- `MH_DYLIB`: 动态库 - `MH_DYLIB`: 动态库
- `MH_DYLINKER`: 动态链接器 - `MH_DYLINKER`: 动态链接器
- `MH_BUNDLE`: “插件文件”。使用 gcc 的 -bundle 生成,并由 `NSBundle``dlopen` 显式加载。 - `MH_BUNDLE`: "插件文件"。使用 gcc 的 `-bundle` 生成,并由 `NSBundle``dlopen` 显式加载。
- `MH_DYSM`: 伴随的 `.dSym` 文件(用于调试的符号文件)。 - `MH_DYSM`: 配套的 `.dSym` 文件(包含调试符号)。
- `MH_KEXT_BUNDLE`: 内核扩展。 - `MH_KEXT_BUNDLE`: 内核扩展。
```bash ```bash
# Checking the mac header of a binary # Checking the mac header of a binary
@ -120,54 +120,54 @@ Mach header
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
MH_MAGIC_64 ARM64 E USR00 EXECUTE 19 1728 NOUNDEFS DYLDLINK TWOLEVEL PIE 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> <figure><img src="../../../images/image (1133).png" alt=""><figcaption></figcaption></figure>
## **Mach-O 标志** ## **Mach-O 标志**
源代码还定义了几个用于加载库的标志: 源代码还定义了若干对加载库有用的标志:
- `MH_NOUNDEFS`:没有未定义的引用(完全链接) - `MH_NOUNDEFS`: 没有未定义引用(完全链接)
- `MH_DYLDLINK`Dyld 链接 - `MH_DYLDLINK`: Dyld 链接
- `MH_PREBOUND`:动态引用预绑定。 - `MH_PREBOUND`: 动态引用预绑定
- `MH_SPLIT_SEGS`:文件分割 r/o 和 r/w 段。 - `MH_SPLIT_SEGS`: 文件将只读与读写段分离
- `MH_WEAK_DEFINES`:二进制文件具有弱定义符号 - `MH_WEAK_DEFINES`: 二进制包含弱定义符号
- `MH_BINDS_TO_WEAK`:二进制文件使用弱符号 - `MH_BINDS_TO_WEAK`: 二进制使用弱符号
- `MH_ALLOW_STACK_EXECUTION`:使堆栈可执行 - `MH_ALLOW_STACK_EXECUTION`: 使栈可执行
- `MH_NO_REEXPORTED_DYLIBS`:库没有 LC_REEXPORT 命令 - `MH_NO_REEXPORTED_DYLIBS`: 库不包含 LC_REEXPORT 命令
- `MH_PIE`:位置无关可执行文件 - `MH_PIE`: 位置无关可执行PIE
- `MH_HAS_TLV_DESCRIPTORS`:有一个包含线程局部变量的部分 - `MH_HAS_TLV_DESCRIPTORS`: 包含线程局部变量TLV
- `MH_NO_HEAP_EXECUTION`:堆/数据页面不执行 - `MH_NO_HEAP_EXECUTION`: 堆/数据页不可执行
- `MH_HAS_OBJC`:二进制文件具有 oBject-C 部分 - `MH_HAS_OBJC`: 二进制包含 Objective-C 段
- `MH_SIM_SUPPORT`模拟器支持 - `MH_SIM_SUPPORT`: 模拟器支持
- `MH_DYLIB_IN_CACHE`:用于共享库缓存中的 dylibs/frameworks。 - `MH_DYLIB_IN_CACHE`: 用于位于共享库缓存中的 dylibs/frameworks
## **Mach-O 加载命令** ## **Mach-O 加载命令**
**文件在内存中的布局**在这里指定,详细说明了 **符号表的位置**、执行开始时主线程的上下文以及所需的 **共享库**。向动态加载器 **(dyld)** 提供了有关二进制文件加载到内存中的过程的指令 这里指定了文件在内存中的布局说明了符号表的位置、执行开始时主线程的上下文以及所需的共享库。还向动态加载器dyld提供了如何将二进制加载到内存的指示
使用 **load_command** 结构,该结构在提到的 **`loader.h`** 中定义 该文件使用在提到的 `loader.h` 中定义的 **load_command** 结构:
```objectivec ```objectivec
struct load_command { struct load_command {
uint32_t cmd; /* type of load command */ uint32_t cmd; /* type of load command */
uint32_t cmdsize; /* total size of command in bytes */ uint32_t cmdsize; /* total size of command in bytes */
}; };
``` ```
有大约 **50 种不同类型的加载命令**,系统以不同方式处理它们。最常见的有: `LC_SEGMENT_64``LC_LOAD_DYLINKER``LC_MAIN``LC_LOAD_DYLIB``LC_CODE_SIGNATURE` 有大约 **50 种不同类型的 load commands**,系统会以不同方式处理。最常见的有:`LC_SEGMENT_64``LC_LOAD_DYLINKER``LC_MAIN``LC_LOAD_DYLIB``LC_CODE_SIGNATURE`
### **LC_SEGMENT/LC_SEGMENT_64** ### **LC_SEGMENT/LC_SEGMENT_64**
> [!TIP] > [!TIP]
> 基本上,这种类型的加载命令定义了 **如何加载 \_\_TEXT**(可执行代码)**和 \_\_DATA**(进程数据)**段**,根据二进制文件执行时在数据部分中指示的 **偏移量** > 基本上,这种类型的加载命令定义了在二进制被执行时**如何根据 Data 部分中指示的偏移量加载 \_\_TEXT**(可执行代码)**和 \_\_DATA**(进程数据)**段**。
这些命令 **定义了段**,在执行进程时被 **映射****虚拟内存空间** 中。 这些命令**定义了段segments**,在进程执行时会被**映射mapped**到进程的**虚拟内存空间**中。
**不同类型** 的段,例如 **\_\_TEXT** 段,它包含程序的可执行代码,以及 **\_\_DATA** 段,它包含进程使用的数据。这些 **段位于 Mach-O 文件的数据部分** 中。 存在不同类型的段,例如包含程序可执行代码的 **\_\_TEXT** 段,以及包含进程使用数据的 **\_\_DATA** 段。这些**段位于 Mach-O 文件的数据节**中。
**每个段** 可以进一步 **划分** 为多个 **节**。**加载命令结构** 包含关于 **这些节** 在各自段中的 **信息** **每个段**还可以进一步**划分为多个节sections**。load command 的结构包含关于该段中**这些节**的信息
在头部首先找到 **段头** 在头部首先可以找到**段头segment header**
<pre class="language-c"><code class="lang-c">struct segment_command_64 { /* for 64-bit architectures */ <pre class="language-c"><code class="lang-c">struct segment_command_64 { /* for 64-bit architectures */
uint32_t cmd; /* LC_SEGMENT_64 */ uint32_t cmd; /* LC_SEGMENT_64 */
@ -184,11 +184,11 @@ int32_t initprot; /* initial VM protection */
}; };
</code></pre> </code></pre>
段头的示例: Example of segment header:
<figure><img src="../../../images/image (1126).png" alt=""><figcaption></figcaption></figure> <figure><img src="../../../images/image (1126).png" alt=""><figcaption></figcaption></figure>
该头部定义了 **其后出现的节头数量** 该头部定义了其后出现的**节头数量**
```c ```c
struct section_64 { /* for 64-bit architectures */ struct section_64 { /* for 64-bit architectures */
char sectname[16]; /* name of this section */ char sectname[16]; /* name of this section */
@ -205,62 +205,62 @@ uint32_t reserved2; /* reserved (for count or sizeof) */
uint32_t reserved3; /* reserved */ uint32_t reserved3; /* reserved */
}; };
``` ```
示例 **节标题** 关于 **节标题** 的示例
<figure><img src="../../../images/image (1108).png" alt=""><figcaption></figcaption></figure> <figure><img src="../../../images/image (1108).png" alt=""><figcaption></figcaption></figure>
如果你 **添加** **节偏移** (0x37DC) + **架构开始的偏移**,在这种情况下 `0x18000` --> `0x37DC + 0x18000 = 0x1B7DC` 如果你**section 偏移量** (0x37DC) 与 **arch 起始处的偏移量** 相加,在本例中 `0x18000` --> `0x37DC + 0x18000 = 0x1B7DC`
<figure><img src="../../../images/image (701).png" alt=""><figcaption></figcaption></figure> <figure><img src="../../../images/image (701).png" alt=""><figcaption></figcaption></figure>
可以通过 **命令行** 获取 **头信息** 可以通过 **命令行** 获取 **头信息**
```bash ```bash
otool -lv /bin/ls otool -lv /bin/ls
``` ```
常见的由此命令加载的段: Common segments loaded by this cmd:
- **`__PAGEZERO`** 它指示内核**映射** **地址零**,以便**无法读取、写入或执行**。结构中的maxprot和minprot变量设置为零以指示此页面上**没有读写执行权限**。 - **`__PAGEZERO`:** 它指示内核 **映射** **地址零**,因此**无法被读取、写入或执行**。结构中的 maxprot 和 minprot 变量被设置为零,以表示该页**没有读写执行权限**。
- 此分配对于**缓解NULL指针解引用漏洞**非常重要。这是因为XNU强制实施一个硬页面零确保内存的第一页仅第一页不可访问在i386中除外。一个二进制文件可以通过制作一个小的\_\_PAGEZERO使用`-pagezero_size`来满足这些要求以覆盖前4k并使其余的32位内存在用户模式和内核模式下可访问 - 这种分配对于**缓解 NULL 指针解引用 漏洞**非常重要。因为 XNU 强制实施一个硬性的 page zero确保内存的第一页仅第一页不可访问i386 除外)。一个二进制可以通过构造一个小的 \_\_PAGEZERO使用 `-pagezero_size`)以覆盖前 4k并使剩余的 32 位内存在用户态和内核态都可访问,从而满足这一要求
- **`__TEXT`**:包含**可执行** **代码**,具有**读取**和**执行**权限(不可写)。此段的常见部分 - **`__TEXT`**: 包含具有**读取**和**执行**权限的**可执行****代码**(不可写)。此段的常见节
- `__text`编译的二进制代码 - `__text`: 已编译的二进制代码
- `__const`常量数据(只读) - `__const`: 常量数据(只读)
- `__ [c/u/os_log]string`C、Unicode或os日志字符串常量 - `__[c/u/os_log]string`: C、Unicode 或 os 日志字符串常量
- `__stubs``__stubs_helper`:在动态库加载过程中涉及 - `__stubs` and `__stubs_helper`: 在动态库加载过程中参与
- `__unwind_info`:堆栈展开数据。 - `__unwind_info`: 栈展开unwind数据
- 请注意,所有这些内容都是签名的,但也被标记为可执行(为不一定需要此权限的部分(如专用字符串部分)创建更多的利用选项)。 - 注意所有这些内容都已签名且被标记为可执行(这为某些并不一定需要此权限的节(例如专用于字符串的节)提供了更多利用途径)。
- **`__DATA`**:包含**可读**和**可写**的数据(不可执行)。 - **`__DATA`**: 包含**可读**且**可写**的数据(不可执行)。
- `__got:` 全局偏移表 - `__got:` 全局偏移表
- `__nl_symbol_ptr`:非惰性(加载时绑定)符号指针 - `__nl_symbol_ptr`: 非懒(在加载时绑定)符号指针
- `__la_symbol_ptr`:惰性(使用时绑定)符号指针 - `__la_symbol_ptr`: 懒(按需绑定)符号指针
- `__const`:应为只读数据(实际上不是 - `__const`: 应为只读数据(但实际上并非如此
- `__cfstring`CoreFoundation字符串 - `__cfstring`: CoreFoundation 字符串
- `__data`:全局变量(已初始化) - `__data`: 已初始化的全局变量
- `__bss`:静态变量(未初始化) - `__bss`: 未初始化的静态变量
- `__objc_*`\_\_objc_classlist\_\_objc_protolist等由Objective-C运行时使用的信息 - `__objc_*` (\_\_objc_classlist, \_\_objc_protolist, etc): 由 Objective-C 运行时使用的信息
- **`__DATA_CONST`**\_\_DATA.\_\_const不保证是常量写权限其他指针和GOT也是如此。此部分使用`mprotect`使`__const`、一些初始化程序和GOT表解析后**只读**。 - **`__DATA_CONST`**: \_\_DATA.\_\_const 并不保证是常量(存在写权限),其他指针和 GOT 也一样。此段使用 `mprotect``__const`、一些初始化器和 GOT 表(解析后)设置为**只读**。
- **`__LINKEDIT`**:包含链接dyld所需的信息例如符号、字符串和重定位表条目。它是一个通用容器包含不在`__TEXT``__DATA`中的内容,其内容在其他加载命令中描述。 - **`__LINKEDIT`**: 包含链路dyld所需的信息例如符号、字符串和重定位表条目。它是一个通用容器用于存放既不在 `__TEXT` 也不在 `__DATA` 中的内容,其内容在其它加载命令中有描述。
- dyld信息:重定位、非惰性/惰性/弱绑定操作码和导出信息 - dyld 信息Rebase、Non-lazy/lazy/weak binding opcodes 和导出信息
- 函数开始:函数的起始地址表 - Functions starts函数起始地址表
- 代码中的数据:\_\_text中的数据岛 - Data In Code\_\_text 中的数据岛
- 符号表:二进制中的符号 - SYmbol Table:二进制中的符号
- 间接符号表:指针/存根符号 - Indirect Symbol Table:指针/存根符号
- 字符串表 - String Table字符串表
- 代码签名 - Code Signature代码签名
- **`__OBJC`**包含由Objective-C运行时使用的信息。尽管这些信息也可能在\_\_DATA段中找到在各种\_\_objc\_\*部分中。 - **`__OBJC`**: 包含由 Objective-C 运行时使用的信息。尽管这些信息也可能出现在 \_\_DATA 段内的各种 \_\_objc\_\* 节中。
- **`__RESTRICT`**:一个没有内容的段,只有一个名为**`__restrict`**也为空的单一部分确保在运行二进制文件时它将忽略DYLD环境变量。 - **`__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`: 仅限内核(未使用)
- `SG_FVMLIB`未使用 - `SG_FVMLIB`: 未使用
- `SG_NORELOC`段没有重定位 - `SG_NORELOC`: 段没有重定位
- `SG_PROTECTED_VERSION_1`加密。例如Finder用于加密文本`__TEXT` - `SG_PROTECTED_VERSION_1`: 加密。例如 Finder 使用它来加密 `__TEXT` 段的文本
### **`LC_UNIXTHREAD/LC_MAIN`** ### **`LC_UNIXTHREAD/LC_MAIN`**
**`LC_MAIN`**包含**entryoff属性**中的入口点。在加载时,**dyld**简单地**将**此值添加到(内存中的)**二进制文件基址**,然后**跳转**到此指令以开始执行二进制代码。 **`LC_MAIN`** 包含入口点于 **entryoff 属性**。在加载时,**dyld** 简单地**将该值添加到**(二进制在内存中的)**基地址**,然后**跳转**到该指令以开始执行二进制的代码。
**`LC_UNIXTHREAD`**包含启动主线程时寄存器必须具有的值。这已经被弃用,但**`dyld`**仍在使用它。可以通过以下方式查看寄存器设置的值: **`LC_UNIXTHREAD`** 包含启动主线程时寄存器应具有的值。它已被弃用,但 **`dyld`** 仍在使用它。可以通过下面的方法查看由此设置的寄存器值:
```bash ```bash
otool -l /usr/lib/dyld otool -l /usr/lib/dyld
[...] [...]
@ -286,34 +286,39 @@ cpsr 0x00000000
``` ```
### **`LC_CODE_SIGNATURE`** ### **`LC_CODE_SIGNATURE`**
包含关于 **Macho-O 文件的代码签名** 的信息。它仅包含一个 **偏移量**,指向 **签名 blob**。这通常位于文件的最末尾。\ {{#ref}}
然而,您可以在 [**这篇博客文章**](https://davedelong.com/blog/2018/01/10/reading-your-own_entitlements/) 和这个 [**gists**](https://gist.github.com/carlospolop/ef26f8eb9fafd4bc22e69e1a32b81da4) 中找到一些关于此部分的信息。 ../../../generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/mach-o-entitlements-and-ipsw-indexing.md
{{#endref}}
包含有关 Mach-O 文件**代码签名**的信息。它仅包含一个**偏移量offset**,指向**签名 blob**。该偏移通常位于文件的末尾。\
你可以在[**this blog post**](https://davedelong.com/blog/2018/01/10/reading-your-own-entitlements/) 和 [**gists**](https://gist.github.com/carlospolop/ef26f8eb9fafd4bc22e69e1a32b81da4) 中找到关于该段的一些信息。
### **`LC_ENCRYPTION_INFO[_64]`** ### **`LC_ENCRYPTION_INFO[_64]`**
支持二进制加密。然而,当然,如果攻击者设法破坏了进程,他将能够以未加密的方式转储内存。 支持二进制加密。不过,如果攻击者成功攻陷进程,当然可以将内存以未加密形式 dump 出来
### **`LC_LOAD_DYLINKER`** ### **`LC_LOAD_DYLINKER`**
包含 **动态链接器可执行文件的路径**,该文件将共享库映射到进程地址空间。**值始终设置为 `/usr/lib/dyld`**。重要的是要注意,在 macOS 中dylib 映射发生在 **用户模式**,而不是内核模式 包含将共享库映射到进程地址空间的动态链接器可执行文件的路径。该值**始终设置为 `/usr/lib/dyld`**。需要注意的是,在 macOS 中dylib 的映射发生在**user mode**而不是内核态kernel mode
### **`LC_IDENT`** ### **`LC_IDENT`**
过时,但当配置为在崩溃时生成转储时,会创建一个 Mach-O 核心转储,并在 `LC_IDENT` 命令中设置内核版本 已废弃,但当配置为在 panic 时生成转储时,会创建 Mach-O core dump并且内核版本会在 `LC_IDENT` 命令中设置
### **`LC_UUID`** ### **`LC_UUID`**
随机 UUID。它对任何直接的事情都很有用,但 XNU 将其与其他进程信息一起缓存。它可以在崩溃报告中使用 随机 UUID。本身直接用途有限,但 XNU 会将其与其他进程信息一起缓存,可用于崩溃报告
### **`LC_DYLD_ENVIRONMENT`** ### **`LC_DYLD_ENVIRONMENT`**
允许在进程执行之前向 dyld 指示环境变量。这可能非常危险,因为它可能允许在进程内部执行任意代码,因此此加载命令仅在使用 `#define SUPPORT_LC_DYLD_ENVIRONMENT` 构建的 dyld 中使用,并进一步限制处理仅限于形式为 `DYLD_..._PATH` 的变量,指定加载路径 允许在进程执行前向 dyld 指定环境变量。这可能非常危险,因为它可能允许在进程内执行任意代码,因此该 load command 仅在 dyld 使用 `#define SUPPORT_LC_DYLD_ENVIRONMENT` 构建时使用,并且进一步将处理限制为形如 `DYLD_..._PATH` 的变量(用于指定加载路径)
### **`LC_LOAD_DYLIB`** ### **`LC_LOAD_DYLIB`**
此加载命令描述了一个 **动态** **库** 依赖关系,**指示** **加载器** (dyld) **加载和链接该库**。每个 Mach-O 二进制文件所需的库都有一个 `LC_LOAD_DYLIB` 加载命令 该 load command 描述了一个**动态库dynamic library**依赖,指示 loaderdyld去加载并链接该库。对于 Mach-O 二进制所需的每个库,都会有一个 `LC_LOAD_DYLIB` load command
- 此加载命令是 **`dylib_command`** 类型的结构(其中包含一个描述实际依赖动态库的 struct dylib - 该 load command 的结构类型为 **`dylib_command`**(其中包含一个 struct dylib描述实际的依赖动态库
```objectivec ```objectivec
struct dylib_command { struct dylib_command {
uint32_t cmd; /* LC_LOAD_{,WEAK_}DYLIB */ uint32_t cmd; /* LC_LOAD_{,WEAK_}DYLIB */
@ -330,7 +335,7 @@ uint32_t compatibility_version; /* library's compatibility vers number*/
``` ```
![](<../../../images/image (486).png>) ![](<../../../images/image (486).png>)
您还可以通过命令行获取此信息: 你也可以通过 cli 使用以下命令获取这些信息:
```bash ```bash
otool -L /bin/ls otool -L /bin/ls
/bin/ls: /bin/ls:
@ -338,53 +343,53 @@ otool -L /bin/ls
/usr/lib/libncurses.5.4.dylib (compatibility version 5.4.0, current version 5.4.0) /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) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.0.0)
``` ```
一些潜在的与恶意软件相关的库包括 一些可能与 malware 相关的库有
- **DiskArbitration**: 监控 USB 驱动器 - **DiskArbitration**监控 USB 驱动器
- **AVFoundation:** 捕获音频和视频 - **AVFoundation:** 捕获音频和视频
- **CoreWLAN**: Wifi 扫描。 - **CoreWLAN**WiFi 扫描。
> [!NOTE] > [!TIP]
> Mach-O 二进制文件可以包含一个或 **多个** **构造函数**,这些构造函数将在 **LC_MAIN** 指定的地址 **之前** **执行**。\ > 一个 Mach-O 二进制可以包含一个或多个 **构造函数**,这些函数会在 **LC_MAIN** 指定的地址之前**执行**。\
> 任何构造函数的偏移量保存在 **\_\_mod_init_func** 段的 **\_\_DATA_CONST** 部分中 > 任何构造函数的偏移量保存在 **__mod_init_func** section 的 **__DATA_CONST** segment
## **Mach-O 数据** ## **Mach-O 数据**
文件的核心是数据区域,由加载命令区域中定义的多个段组成。**每个段中可以包含多种数据部分**,每个部分 **保存特定类型的代码或数据** 在文件的核心是数据区域,该区域由 load-commands 区域定义的多个段组成。**每个段中可以包含多种数据节**,每个节都**包含特定类型的代码或数据**
> [!TIP] > [!TIP]
> 数据基本上是包含所有由加载命令 **LC_SEGMENTS_64** 加载的 **信息** 的部分。 > 数据基本上是由 load commands **LC_SEGMENTS_64** 加载的包含所有**信息**的部分
![https://www.oreilly.com/api/v2/epubs/9781785883378/files/graphics/B05055_02_38.jpg](<../../../images/image (507) (3).png>) ![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/) 工具: 要检查它,可以使用 [**Mach-O View**](https://sourceforge.net/projects/machoview/) 工具:
<figure><img src="../../../images/image (1120).png" alt=""><figcaption></figcaption></figure> <figure><img src="../../../images/image (1120).png" alt=""><figcaption></figcaption></figure>
者从命令行 在命令行中
```bash ```bash
size -m /bin/ls size -m /bin/ls
``` ```
## Objetive-C 常见部分 ## Objetive-C 常见
`__TEXT` 段 (r-x): `__TEXT` 段 (r-x):
- `__objc_classname`: 类名 (字符串) - `__objc_classname`: 类名(字符串)
- `__objc_methname`: 方法名 (字符串) - `__objc_methname`: 方法名(字符串)
- `__objc_methtype`: 方法类型 (字符串) - `__objc_methtype`: 方法类型(字符串)
`__DATA` 段 (rw-): `__DATA` 段 (rw-):
- `__objc_classlist`: 所有 Objective-C 类的指针 - `__objc_classlist`: 指向所有 Objetive-C 类的指针
- `__objc_nlclslist`: 非懒加载 Objective-C 类的指针 - `__objc_nlclslist`: 指向非延迟 Objective-C 类的指针
- `__objc_catlist`: 类别的指针 - `__objc_catlist`: 指向类别的指针
- `__objc_nlcatlist`: 非懒加载类别的指针 - `__objc_nlcatlist`: 指向非延迟类别的指针
- `__objc_protolist`: 协议列表 - `__objc_protolist`: 协议列表
- `__objc_const`: 常量数据 - `__objc_const`: 常量数据
- `__objc_imageinfo`, `__objc_selrefs`, `objc__protorefs`... - `__objc_imageinfo`, `__objc_selrefs`, `objc__protorefs`...

View File

@ -4,12 +4,17 @@
## 基本信息 ## 基本信息
Mach-o 二进制文件包含一个称为 **`LC_CODE_SIGNATURE`** 的加载命令,指示二进制文件内部签名的 **偏移量****大小**。实际上,使用 GUI 工具 MachOView可以在二进制文件的末尾找到一个名为 **Code Signature** 的部分,其中包含这些信息: {{#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> <figure><img src="../../../images/image (1) (1) (1) (1).png" alt="" width="431"><figcaption></figcaption></figure>
代码签名的魔术头是 **`0xFADE0CC0`**。然后你会得到一些信息,例如 superBlob 的长度和 blob 的数量。\ Code Signature 的 magic header 是 **`0xFADE0CC0`**。然后你会看到诸如 length 和 superBlob 中包含它们的 blobs 数量等信息。\
可以在 [源代码这里](https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/osfmk/kern/cs_blobs.h#L276) 找到这些信息: 可以在 [source code here](https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/osfmk/kern/cs_blobs.h#L276) 找到这些信息:
```c ```c
/* /*
* Structure of an embedded-signature SuperBlob * Structure of an embedded-signature SuperBlob
@ -38,14 +43,14 @@ char data[];
} CS_GenericBlob } CS_GenericBlob
__attribute__ ((aligned(1))); __attribute__ ((aligned(1)));
``` ```
常见的 blob 包含代码目录、要求和权限以及加密消息语法 (CMS)。\ 常见的 blobs 包括 Code Directory、Requirements 和 Entitlements以及 Cryptographic Message Syntax (CMS).\
此外,请注意 blob 中编码的数据是以 **大端字节序** 编码的。 此外,请注意 blobs 中的数据是以 **Big Endian.** 编码的。
此外,签名可以从二进制文件中分离并存储在 `/var/db/DetachedSignatures`iOS 使用)。 此外,签名可以从二进制文件中分离并存储在 `/var/db/DetachedSignatures`iOS 使用)。
## 代码目录 Blob ## Code Directory Blob
可以在代码中找到 [代码目录 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 ```c
typedef struct __CodeDirectory { typedef struct __CodeDirectory {
uint32_t magic; /* magic number (CSMAGIC_CODEDIRECTORY) */ uint32_t magic; /* magic number (CSMAGIC_CODEDIRECTORY) */
@ -101,12 +106,12 @@ char end_withLinkage[0];
} CS_CodeDirectory } CS_CodeDirectory
__attribute__ ((aligned(1))); __attribute__ ((aligned(1)));
``` ```
注意,这个结构有不同的版本,旧版本可能包含较少的信息。 注意该 struct 有不同版本,旧版本可能包含较少信息。
## 签名代码页 ## 签名代码页
对整个二进制文件进行哈希计算效率低下,如果它仅部分加载到内存中甚至是无用的。因此,代码签名实际上是一个哈希的哈希,其中每个二进制页面都是单独哈希的。\ 对整个二进制文件进行哈希既低效,在只部分加载到内存时甚至无用。因此,代码签名实际上是哈希的哈希,每个二进制页面分别进行哈希。\
实际上,在之前的 **Code Directory** 代码中,您可以看到 **页面大小在其字段中被指定**。此外,如果二进制文件的大小不是页面大小的倍数,字段 **CodeLimit** 指定了签名的结束位置。 实际上,在前面的 **Code Directory** 代码中,你可以看到 **页面大小在其字段之一中指定**。此外,如果二进制的大小不是页面大小的整数倍,字段 **CodeLimit** 指定了签名的结束位置。
```bash ```bash
# Get all hashes of /bin/ps # Get all hashes of /bin/ps
codesign -d -vvvvvv /bin/ps codesign -d -vvvvvv /bin/ps
@ -142,27 +147,27 @@ dd if=$BINARY of=/tmp/`basename $BINARY`.page.$i bs=$PAGESIZE skip=$i count=1
done done
openssl sha256 /tmp/*.page.* openssl sha256 /tmp/*.page.*
``` ```
## Entitlements Blob ## 权限 blob
请注意,应用程序可能还包含一个**entitlement blob**其中定义了所有的权限。此外一些iOS二进制文件可能在特殊槽-7中具体定义其权限而不是在-5权限特殊槽中)。 注意,应用程序可能还包含一个定义了所有权限的**权限 blob**。此外,一些 iOS 二进制文件可能会把它们的权限放在特殊槽位 -7而不是常见的 -5 entitlements 特殊槽位)。
## Special Slots ## 特殊槽位
MacOS应用程序并不具备执行所需的所有内容,而是还使用**外部资源**(通常在应用程序的**bundle**内)。因此,二进制文件中有一些槽将包含一些有趣的外部资源的哈希,以检查它们是否被修改。 MacOS 应用并非把执行所需的所有内容都包含在二进制内,它们也使用 **external resources**(通常位于应用的 **bundle** 内)。因此,二进制中存在一些槽位,用于存放某些重要外部资源的哈希,以便验证它们未被修改。
实际上,可以在代码目录结构中看到一个名为**`nSpecialSlots`**的参数指示特殊槽的数量。没有特殊槽0最常见的槽从-1到-6 事实上,可以在 Code Directory 结构体中看到一个名为 **`nSpecialSlots`** 的参数,用来指示特殊槽位的数量。注意没有特殊槽位 0最常见的槽位是从 -1 到 -6
- `info.plist`的哈希(或在`__TEXT.__info__plist`中的哈希)。 - `info.plist` 的哈希(或位于 `__TEXT.__info__plist` 中的那个)。
- 需求的哈希 - Requirements 的哈希
- 资源目录的哈希(在bundle内的`_CodeSignature/CodeResources`文件的哈希)。 - 资源目录的哈希(位于 bundle 内的 `_CodeSignature/CodeResources` 文件的哈希)。
- 应用程序特定(未使用) - 应用程序特定(未使用)
- 权限的哈希 - 权限的哈希
- 仅DMG代码签名 - 仅针对 DMG代码签名
- DER权限 - DER 权限
## Code Signing Flags ## 代码签名标志
每个进程都有一个相关的位掩码,称为`status`,由内核启动,其中一些可以被**代码签名**覆盖。这些可以包含在代码签名中的标志在[代码中定义](https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/osfmk/kern/cs_blobs.h#L36) 每个进程都有一个关联的位掩码,称为 `status`,由内核设置,其中一些位可以被**代码签名**覆盖。这些可以包含在代码签名中的标志在代码中定义 [defined in the code](https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/osfmk/kern/cs_blobs.h#L36):
```c ```c
/* code signing attributes of a process */ /* code signing attributes of a process */
#define CS_VALID 0x00000001 /* dynamically valid */ #define CS_VALID 0x00000001 /* dynamically valid */
@ -207,15 +212,15 @@ CS_RESTRICT | CS_ENFORCEMENT | CS_REQUIRE_LV | CS_RUNTIME | CS_LINKER_SIGNED)
#define CS_ENTITLEMENT_FLAGS (CS_GET_TASK_ALLOW | CS_INSTALLER | CS_DATAVAULT_CONTROLLER | CS_NVRAM_UNRESTRICTED) #define CS_ENTITLEMENT_FLAGS (CS_GET_TASK_ALLOW | CS_INSTALLER | CS_DATAVAULT_CONTROLLER | CS_NVRAM_UNRESTRICTED)
``` ```
注意函数 [**exec_mach_imgact**](https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/bsd/kern/kern_exec.c#L1420) 在启动执行时也可以动态添加 `CS_EXEC_*` 标志。 注意函数 [**exec_mach_imgact**](https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/bsd/kern/kern_exec.c#L1420) 在启动执行时也可以动态添加 `CS_EXEC_*` 标志。
## 代码签名要求 ## 代码签名要求
每个应用程序存储一些 **要求**,必须 **满足** 这些要求才能被执行。如果 **应用程序包含的要求未被应用程序满足**,则不会执行(因为它可能已被更改)。 每个应用会存储一些必须**满足**的**要求**,以便能够被执行。如果应用包含的这些**要求**没有被满足,应用将不会被执行(因为它可能已被篡改)。
二进制文件的要求使用 **特殊语法**,这是一个 **表达式** 的流,并使用 `0xfade0c00` 作为魔法编码为 blobs**哈希存储在特殊代码槽中** 二进制的这些要求使用一种**特殊语法**,是由一系列**表达式**组成,并以 blob 的形式编码,使用 `0xfade0c00` 作为魔数,其**哈希存储在一个特殊的 code slot 中**
可以通过运行来查看二进制文件的要求: 可以通过运行以下命令查看二进制的要求:
```bash ```bash
codesign -d -r- /bin/ls codesign -d -r- /bin/ls
Executable=/bin/ls Executable=/bin/ls
@ -225,10 +230,10 @@ codesign -d -r- /Applications/Signal.app/
Executable=/Applications/Signal.app/Contents/MacOS/Signal 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 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] > [!TIP]
> 注意这些签名可以检查诸如认证信息、TeamID、ID、权限以及许多其他数据。 > 注意这些签名如何检查诸如证书信息、TeamID、IDs、entitlements 等多种数据。
此外,可以使用 `csreq` 工具生成一些编译的要求 此外,可以使用 `csreq` 工具生成一些已编译的 requirements
```bash ```bash
# Generate compiled requirements # 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' 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,20 +245,20 @@ 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 0000020 00 00 00 21 6f 72 67 2e 77 68 69 73 70 65 72 73
[...] [...]
``` ```
可以通过 `Security.framework` 中的一些 API 访问此信息并创建或修改要求,例如: 可以通过 `Security.framework` 的一些 API 访问这些信息并创建或修改要求,如:
#### **检查有效性** #### **检查有效性**
- **`Sec[Static]CodeCheckValidity`**: 根据要求检查 SecCodeRef 的有效性。 - **`Sec[Static]CodeCheckValidity`**根据要求检查 SecCodeRef 的有效性。
- **`SecRequirementEvaluate`**: 在证书上下文中验证要求。 - **`SecRequirementEvaluate`**在证书上下文中验证要求。
- **`SecTaskValidateForRequirement`**: 验证正在运行的 SecTask 是否符合 `CFString` 要求 - **`SecTaskValidateForRequirement`**:根据 `CFString` 要求验证正在运行的 SecTask
#### **创建和管理代码要求** #### **创建和管理代码要求**
- **`SecRequirementCreateWithData`:** 从表示要求的二进制数据创建 `SecRequirementRef` - **`SecRequirementCreateWithData`:** 从表示要求的二进制数据创建 `SecRequirementRef`
- **`SecRequirementCreateWithString`:** 从要求的字符串表达式创建 `SecRequirementRef` - **`SecRequirementCreateWithString`:** 从要求的字符串表达式创建 `SecRequirementRef`
- **`SecRequirementCopy[Data/String]`**: 检索 `SecRequirementRef` 的二进制数据表示。 - **`SecRequirementCopy[Data/String]`**: 检索 `SecRequirementRef` 的二进制数据表示。
- **`SecRequirementCreateGroup`**: 为应用程序组成员资格创建要求。 - **`SecRequirementCreateGroup`**: 为 app-group 成员资格创建要求
#### **访问代码签名信息** #### **访问代码签名信息**
@ -262,35 +267,35 @@ od -A x -t x1 /tmp/output.csreq
#### **修改代码要求** #### **修改代码要求**
- **`SecCodeSignerCreate`**: 创建 `SecCodeSignerRef` 对象以执行代码签名操作 - **`SecCodeSignerCreate`**: 创建用于执行代码签名操作的 `SecCodeSignerRef` 对象。
- **`SecCodeSignerSetRequirement`**: 为代码签名者设置在签名期间应用的新要求。 - **`SecCodeSignerSetRequirement`**: 为签名器设置在签名期间应用的新要求。
- **`SecCodeSignerAddSignature`**: 将签名添加到使用指定签名者签名的代码中 - **`SecCodeSignerAddSignature`**: 使用指定签名器向正在签名的代码添加签名
#### **使用要求验证代码** #### **使用要求验证代码**
- **`SecStaticCodeCheckValidity`**: 根据指定要求验证静态代码对象。 - **`SecStaticCodeCheckValidity`**: 根据指定要求验证静态代码对象。
#### **其他有用的 API** #### **其他有用的 APIs**
- **`SecCodeCopy[Internal/Designated]Requirement`: 从 SecCodeRef 获取 SecRequirementRef** - **`SecCodeCopy[Internal/Designated]Requirement`: Get SecRequirementRef from SecCodeRef**
- **`SecCodeCopyGuestWithAttributes`**: 创建一个基于特定属性的代码对象的 `SecCodeRef`,适用于沙箱 - **`SecCodeCopyGuestWithAttributes`**: 基于特定属性创建表示代码对象的 `SecCodeRef`,可用于沙箱化
- **`SecCodeCopyPath`**: 检索与 `SecCodeRef` 关联的文件系统路径。 - **`SecCodeCopyPath`**: 检索与 `SecCodeRef` 关联的文件系统路径。
- **`SecCodeCopySigningIdentifier`**: 从 `SecCodeRef` 获取签名标识符(例如,团队 ID - **`SecCodeCopySigningIdentifier`**: 从 `SecCodeRef` 获取签名标识符(例如 Team ID
- **`SecCodeGetTypeID`**: 返回 `SecCodeRef` 对象的类型标识符。 - **`SecCodeGetTypeID`**: 返回 `SecCodeRef` 对象的类型标识符。
- **`SecRequirementGetTypeID`**: 获取 `SecRequirementRef` 的 CFTypeID - **`SecRequirementGetTypeID`**: 获取 `SecRequirementRef` 的 CFTypeID
#### **代码签名标志和常量** #### **代码签名标志和常量**
- **`kSecCSDefaultFlags`**: 在许多 Security.framework 函数中用于代码签名操作的默认标志。 - **`kSecCSDefaultFlags`**: 在许多 `Security.framework` 函数中用于代码签名操作的默认标志。
- **`kSecCSSigningInformation`**: 用于指定应检索签名信息的标志。 - **`kSecCSSigningInformation`**: 用于指定应检索签名信息的标志。
## 代码签名强制执行 ## 代码签名强制
**内核**是在允许应用程序代码执行之前**检查代码签名**的。此外,能够在内存中写入和执行新代码的一种方法是滥用 JIT如果 `mprotect` 被调用时带有 `MAP_JIT` 标志。请注意,应用程序需要特殊的权限才能做到这一点 内核负责在允许应用代码执行之前检查代码签名。此外,如果 `mprotect` 使用 `MAP_JIT` 标志被调用,滥用 JIT 是在内存中写入并执行新代码的一种方式。注意,应用需要特殊的 entitlement 才能这样做
## `cs_blobs` & `cs_blob` ## `cs_blobs` & `cs_blob`
[**cs_blob**](https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/bsd/sys/ubc_internal.h#L106) 结构包含有关正在运行的进程的权限信息。 `csb_platform_binary` 还指示应用程序是否为平台二进制文件(操作系统在不同时间检查这一点,以应用安全机制,例如保护这些进程的任务端口的 SEND 权限)。 [**cs_blob**](https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/bsd/sys/ubc_internal.h#L106) 结构包含关于运行进程 entitlement 的信息。`csb_platform_binary` 还表明应用是否为 platform binary操作系统在不同阶段检查该标志以应用安全机制,例如保护这些进程的任务端口的 SEND 权限)。
```c ```c
struct cs_blob { struct cs_blob {
struct cs_blob *csb_next; struct cs_blob *csb_next;
@ -349,7 +354,7 @@ bool csb_csm_managed;
#endif #endif
}; };
``` ```
## 参考文献 ## 参考资料
- [**\*OS Internals Volume III**](https://newosxbook.com/home.html) - [**\*OS Internals Volume III**](https://newosxbook.com/home.html)