Hacktricks-skills macos-dyld-analysis
Analyze macOS dynamic linker (dyld) internals, debug library loading, examine Mach-O binary structure, and identify potential security implications. Use this skill whenever the user needs to understand how macOS loads executables, debug dyld behavior, analyze library injection vectors, examine stub sections, or investigate privilege escalation through the dynamic linker. Trigger for any questions about dyld environment variables, Mach-O segments, lazy vs non-lazy binding, or macOS binary internals.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-library-injection/macos-dyld-process/SKILL.MDmacOS Dyld Analysis
A skill for analyzing macOS dynamic linker internals, debugging library loading, and understanding Mach-O binary structure.
What this skill does
This skill helps you:
- Understand how macOS loads and links executables via dyld
- Debug library loading with dyld environment variables
- Analyze Mach-O binary structure (stubs, GOT, lazy/non-lazy symbols)
- Identify potential security implications of dyld behavior
- Examine the
argument vector and dyld internalsapple[]
When to use this skill
Use this skill when you need to:
- Debug why a macOS binary isn't loading correctly
- Understand library injection mechanisms (
)DYLD_INSERT_LIBRARIES - Analyze Mach-O binary structure for security research
- Investigate privilege escalation vectors through dyld
- Examine how symbols are resolved (lazy vs non-lazy binding)
- Debug dyld behavior with environment variables
- Understand the
argument vector in macOSapple[]
Core Concepts
Dyld Loading Flow
- dyldbootstrap::start loads dyld and sets up stack canary
- dyld::_main() runs
(restrictsconfigureProcessRestrictions()
vars)DYLD_* - Maps dyld shared cache (prelinked system libraries)
- Loads libraries in order:
(if allowed)DYLD_INSERT_LIBRARIES- Shared cached libraries
- Imported libraries (recursively)
- Runs library initializers (
)__attribute__((constructor)) - Executes binary entry point
Mach-O Stub Sections
| Section | Purpose |
|---|---|
| Pointers from sections |
| Code invoking dynamic linking |
| Global Offset Table (resolved at load time) |
| Non-lazy symbol pointers |
| Lazy symbol pointers (bound on first access) |
Note: Modern dyld loads everything as non-lazy.
prefixed sections use pointer authentication (PAC).auth_
Key Environment Variables
Debug Variables
| Variable | Purpose |
|---|---|
| Print each library as it loads |
| Show segment mappings |
| Show when initializers run |
| Print symbol bindings |
| Print environment seen by dyld |
| Write debug output to file |
Behavior Variables
| Variable | Purpose |
|---|---|
| Inject library (restricted on signed binaries) |
| Library search path |
| Framework search path |
| Resolve lazy bindings at launch |
| Single-level symbol namespace |
| Disable data prefetching |
The apple[] Argument Vector
macOS main() receives 4 arguments instead of 3:
int main(int argc, char **argv, char **envp, char **apple)
The
apple[] vector contains sensitive values like:
- Path to the executableexecutable_path
- Stack canary valuestack_guard
- Memory allocator entropymalloc_entropy
- Code directory hashexecutable_cdhash
- Boot hashexecutable_boothash
- Pointer munging valueptr_munge
Warning: These values are sanitized before reaching main() to prevent data leaks.
Common Tasks
1. List Loaded Libraries
DYLD_PRINT_LIBRARIES=1 ./your_binary
2. Examine Segment Mappings
DYLD_PRINT_SEGMENTS=1 ./your_binary
3. Analyze Stub Sections
# List sections objdump --section-headers ./binary # Disassemble stubs objdump -d --section=__stubs ./binary
4. Find All DYLD Environment Variables
strings /usr/lib/dyld | grep "^DYLD_" | sort -u
5. Debug Before main() Entry
lldb ./binary (lldb) process launch -s (lldb) mem read $sp (lldb) x/55s <stack_address>
6. Examine dyld_all_image_infos
The
dyld_all_image_infos structure (exported by dyld) contains:
- Version information
- Pointer to
arraydyld_image_info - Image notifier callback
- Shared cache detachment status
- libSystem initializer status
- dyld's own Mach header pointer
Security Considerations
Privilege Escalation Risk
If dyld contains vulnerabilities, they can be exploited before any binary executes, including privileged ones. This is because:
- dyld runs before the target binary
- dyld has no dependencies (uses syscalls directly)
- dyld handles all library loading
Library Injection Restrictions
DYLD_INSERT_LIBRARIES is restricted on:
- System binaries
- Signed executables
- Processes with hardened runtime
Pointer Authentication (PAC)
On arm64e:
prefixed sections use in-process encryption keysauth_
verifies pointers before followingBLRA[A/B]
replacesRETA[A/B]
for authenticated returnsRET
uses__TEXT.__auth_stubs
instead ofbraabl
References
- OS Internals, Volume I: User Mode by Jonathan Levin
- dyld Source Code
- Mach-O Format Documentation
Scripts
See the
scripts/ directory for helper tools:
- Extract all DYLD_* environment variables from dyldlist_dyld_env_vars.sh
- List and analyze Mach-O sectionsanalyze_macho_sections.sh
- Quick debug script for dyld behaviordebug_dyld_loading.sh