← All posts
17 July 2026· Palak Bansal & Farhad Sajid (Honeywell)

macOS Dylib Injection: How DYLD_INSERT_LIBRARIES Works (and Why Apple Silicon Broke It)

#macos#dylib-injection#red-team#reverse-engineering#bsides-2024
macOS Dylib Injection: How DYLD_INSERT_LIBRARIES Works (and Why Apple Silicon Broke It)

This article is based on the talk "macOS Security: DYLD_INSERT_LIBRARIES Injection Challenges" by Palak Bansal and Farhad Sajid - product security engineers at Honeywell - at BSides Mumbai 2024. You can watch the full session on YouTube.

If you have done Windows red-teaming, you know DLL injection: drop a malicious library where a program will load it, and your code runs inside a trusted process. macOS has the exact same idea - it just wraps it in a stack of unfamiliar terminology and, on Apple Silicon, a wall of security controls. Palak Bansal and Farhad Sajid spent their BSides Mumbai 2024 talk translating that terminology into concepts DLL-injection folks already know, demonstrating a working dylib injection on Intel Macs, and then explaining why the very same attack quietly fails on an M-series machine.

Key Takeaways

  • Dylib injection is macOS's DLL injection. DYLD_INSERT_LIBRARIES forces the dynamic linker (dyld) to load an attacker's library into a target process - the direct counterpart to Windows DLL injection and Linux LD_PRELOAD.
  • It hinges on how dyld finds libraries. Understanding the Mach-O header, load commands, install names, and the @executable_path/@loader_path/@rpath search order is what tells you whether a binary is hijackable.
  • Apple Silicon deliberately broke the easy path. On M-series and T2 Macs, System Integrity Protection, AMFI, code-signing (Library Validation), the setuid bit, and restricted segments combine to stop the trivial injection that worked on Intel.

First, Apple's Security Foundations

Before the attack, the speakers grounded the audience in why macOS is hard to exploit in the first place. Apple builds security into the hardware: a boot ROM root of trust anchoring secure boot, a dedicated AES engine, and the Secure Enclave - a separate subsystem on the SoC that keeps user data safe even if the main kernel is compromised. On top of that sit a family of protections that reappear later as the reasons injection fails:

  • KIP (Kernel Integrity Protection) - the kernel code cannot be modified after init.
  • SCIP (System Coprocessor Integrity Protection) - the same guarantee for coprocessor firmware.
  • PAC (Pointer Authentication Codes) - protects function pointers and return addresses from memory-corruption attacks.
  • PPL (Page Protection Layer) - stops user-space code from being modified after signature verification.

How macOS Loads a Dynamic Library

Like Linux and Windows, macOS loads dynamic libraries - called dylibs - at runtime through a dynamic linker, dyld. When you launch an app, the Mach-O loader maps the app into memory, loads dyld, and hands control to it. dyld then reads the Mach-O header, whose load commands tell it what to link. The ones that matter for injection:

  • LC_LOAD_DYLIB - a library that must load, or the executable fails.
  • LC_LOAD_WEAK_DYLIB - a weakly linked library; the app still runs even if it is missing. (An attacker can grep for these, though disciplined developers rarely leave them.)
  • LC_REEXPORT_DYLIB - a library re-exported by the binary.

Each library has an install name that identifies it to dyld, plus a search path built from three prefixes: @executable_path (the main executable's directory), @loader_path (the directory of the binary doing the loading), and @rpath (a variable list of runtime search paths set via LC_RPATH). The order dyld searches those paths is exactly what makes a binary hijackable - the same way DLL search order does on Windows.

The Injection: Cache Poisoning and Search-Order Hijacking

dyld first checks the dyld shared cache - a prebuilt, per-application cache at /System/Library/dyld/. The speakers' first pen-test instinct: could you drop a poisoned (malicious) file named to match into that cache? Keep that thought - it is the macOS analogue of cache poisoning. If the library is not in the cache, dyld falls back to the LC_LOAD_DYLIB path and the configured @rpath, and finally to standard locations (/usr/local/lib, /usr/lib, /System/Library/Frameworks). Any writable, attacker-controllable step in that chain is a hijack opportunity - directly mirroring the Windows DLL search-order walk.

To find candidates, the speakers used otool -L (Apple's Mach-O inspector, comparable to using GDB/LLDB for this purpose) to list a binary's LC_LOAD_DYLIB entries and their paths, and checked for a declared LC_RPATH. When otool fell short on M1, they pointed to Patrick Wardle's dylib-hijack scanner as a reliable automation.

The Demo: Intel Just Works, Apple Silicon Doesn't

This was the heart of the talk. They wrote a tiny dylib whose constructor prints a message when loaded, and a trivial welcome binary:

  • On Intel: DYLD_INSERT_LIBRARIES=dylib.simple ./welcome ran the injected constructor before the program's own output. Injection worked, plainly and simply.
  • On Apple Silicon (M-series/T2): the same command against production binaries behaved inconsistently - it worked on some apps (Zenmap) but silently failed on others (Sublime Text). Chasing that difference is what the rest of the talk explained.

Why It Fails on M-Series: SIP, AMFI, Code Signing

Apple added layers that each independently block the easy injection:

  • SIP (System Integrity Protection). dyld runs a CSR check (csr_check / the CSR_ALLOW_TASK_FOR_PID and related bits from the CSR configuration). If SIP does not permit it, the dylib is simply not loaded - it is the first gate before any other check.
  • The setuid bit. The speakers demonstrated that setting the setuid bit on their welcome binary caused the injected dylib to stop loading; removing it let injection resume. Production apps from the App Store ship with these protections in place and cannot be trivially re-permissioned.
  • Restricted segments. Analogous to DEP/NX, a binary can declare a restricted segment; attempting injection against it crashes the process rather than loading foreign code. AMFI (Apple Mobile File Integrity) - the component that enforces SIP and code signing and sets a process's restrictions - checks for this.
  • Code signing / Library Validation. cs_require_lv and cs_require_lc_code signing flags enforce that a dylib is signed with the same code-signing identity as the main binary. A self-signed injected dylib is rejected by SIP's signature validation - so a Google-signed app will only load Google-signed libraries.

So How Do You Actually Defeat It?

The speakers were candid that on a hardened, production M-series Mac it is genuinely difficult. To succeed an attacker would need to either strip the setuid bit (not possible on a properly distributed app), reverse-engineer the binary to remove its restricted segment (high effort), or obtain the legitimate code-signing entitlement used to sign the target - only then could they sign and load a malicious dylib. That combination is exactly what Apple engineered to stop mass dylib injection on modern Macs.

Frequently Asked Questions

Is DYLD_INSERT_LIBRARIES the same as LD_PRELOAD?

Conceptually, yes. DYLD_INSERT_LIBRARIES is macOS's environment-variable mechanism for forcing a library into a process at load time, just as LD_PRELOAD is on Linux and DLL injection is on Windows. The difference is macOS's dyld shared cache, install-name search order, and - on Apple Silicon - the SIP/AMFI/code-signing controls layered on top.

Does dylib injection still work on modern Macs?

Not the trivial version. On Intel it was straightforward; on M-series and T2 Macs, SIP, AMFI, code signing (Library Validation), the setuid bit, and restricted segments block it unless the attacker can defeat all of those - for example by obtaining the app's real code-signing identity. It is a meaningful bar, not a formality.

How do I check whether a binary is vulnerable?

Start with otool -L <binary> to list its LC_LOAD_DYLIB entries and paths, and look for a declared LC_RPATH and any weakly linked (LC_LOAD_WEAK_DYLIB) libraries. For automation, Patrick Wardle's dylib-hijack scanner reports whether hijacking is possible and whether an rpath is declared.

Summary

Dylib injection is the macOS cousin of DLL injection, and the concepts map cleanly once you get past Apple's vocabulary: dyld loads dynamic libraries, the Mach-O header's load commands and @rpath/@executable_path/@loader_path search order decide where they come from, and DYLD_INSERT_LIBRARIES abuses that to run attacker code inside a trusted process. Palak and Farhad showed it working effortlessly on Intel - and then walled off by SIP, AMFI, code signing, the setuid bit, and restricted segments on Apple Silicon. For defenders the lesson is reassuring but conditional: modern macOS makes injection hard by default, but only if code signing and SIP are intact - which is exactly why they must stay that way.

About the Speakers

Palak Bansal is a product security engineer at Honeywell with close to five years of experience, focused on protecting digital systems. Farhad Sajid is also a product security engineer at Honeywell who has delivered over 2,000 hours of security training across educational institutions and government bodies, and has presented at multiple conferences. They delivered this talk at BSides Mumbai 2024.

Watch the Full Talk

Want every load command, code walkthrough, and live demo? Watch the complete BSides Mumbai 2024 session below.