Dylib Injection is important for downgrade attacks in MacOSX.
DYLD_INSERT_LIBRARIES is a old injection method, on macOSX.
Same concept was a LD_PRELOAD.
Standard Dylib Load Flow..
- Setup runtime envoriment in the kernel
- Load Shared Cache
- Instantiate the main program.
- Load the inserted dynamic library.
- Link the main program.
- Perform weak symbol binding.
- Execute the initialization method.
- Find the entry point and return.
OSX Internal Information.
The way OSX checks if SIP is enabled is by loading a function called csr_check.
This function is controlled via NVRAM variables which is mapped to csrActiveConfig. Its a unsigned 32 bit int which is a bitmask.
/* CSR configuration flags */
#define CSR_ALLOW_UNTRUSTED_KEXTS (1 << 0)
#define CSR_ALLOW_UNRESTRICTED_FS (1 << 1)
#define CSR_ALLOW_TASK_FOR_PID (1 << 2)
#define CSR_ALLOW_KERNEL_DEBUGGER (1 << 3)
#define CSR_ALLOW_APPLE_INTERNAL (1 << 4)
#define CSR_ALLOW_DESTRUCTIVE_DTRACE (1 << 5) /* name deprecated */
#define CSR_ALLOW_UNRESTRICTED_DTRACE (1 << 5)
#define CSR_ALLOW_UNRESTRICTED_NVRAM (1 << 6)
#define CSR_ALLOW_DEVICE_CONFIGURATION (1 << 7)
#define CSR_ALLOW_ANY_RECOVERY_OS (1 << 8)
#define CSR_ALLOW_UNAPPROVED_KEXTS (1 << 9)
#define CSR_ALLOW_EXECUTABLE_POLICY_OVERRIDE (1 << 10)
#define CSR_ALLOW_UNAUTHENTICATED_ROOT (1 << 11)
#define CSR_VALID_FLAGS (CSR_ALLOW_UNTRUSTED_KEXTS | \
CSR_ALLOW_UNRESTRICTED_FS | \
CSR_ALLOW_TASK_FOR_PID | \
CSR_ALLOW_KERNEL_DEBUGGER | \
CSR_ALLOW_APPLE_INTERNAL | \
CSR_ALLOW_UNRESTRICTED_DTRACE | \
CSR_ALLOW_UNRESTRICTED_NVRAM | \
CSR_ALLOW_DEVICE_CONFIGURATION | \
CSR_ALLOW_ANY_RECOVERY_OS | \
CSR_ALLOW_UNAPPROVED_KEXTS | \
CSR_ALLOW_EXECUTABLE_POLICY_OVERRIDE | \
CSR_ALLOW_UNAUTHENTICATED_ROOT)

The system has to locate csrActiveConfig variable which is defined in the boot_args structure?
Lets check the latest version of macosx!
Bootargs Structure
typedef struct boot_args {
uint16_t Revision; /* Revision of boot_args structure */
uint16_t Version; /* Version of boot_args structure */
uint8_t efiMode;/* 32 = 32-bit, 64 = 64-bit */
uint8_t debugMode;/* Bit field with behavior changes */
uint16_t flags;
char CommandLine[BOOT_LINE_LENGTH];/* Passed in command line */
uint32_t MemoryMap;/* Physical address of memory map */
uint32_t MemoryMapSize;
uint32_t MemoryMapDescriptorSize;
uint32_t MemoryMapDescriptorVersion;
Boot_VideoV1 VideoV1; /* Video Information */
uint32_t deviceTreeP; /* Physical address of flattened device tree */
uint32_t deviceTreeLength;/* Length of flattened tree */
uint32_t kaddr; /* Physical address of beginning of kernel text */
uint32_t ksize; /* Size of combined kernel text+data+efi */
uint32_t efiRuntimeServicesPageStart;/* physical address of defragmented runtime pages */
uint32_t efiRuntimeServicesPageCount;
uint64_t efiRuntimeServicesVirtualPageStart;/* virtual address of defragmented runtime pages */
uint32_t efiSystemTable;/* physical address of system table in runtime area */
uint32_t kslide;
uint32_t performanceDataStart;/* physical address of log */
uint32_t performanceDataSize;
uint32_t keyStoreDataStart;/* physical address of key store data */
uint32_t keyStoreDataSize;
uint64_t bootMemStart;
uint64_t bootMemSize;
uint64_t PhysicalMemorySize;
uint64_t FSBFrequency;
uint64_t pciConfigSpaceBaseAddress;
uint32_t pciConfigSpaceStartBusNumber;
uint32_t pciConfigSpaceEndBusNumber;
uint32_t csrActiveConfig; // CSR Active Config
uint32_t csrCapabilities; // csr Capabilites running./
uint32_t boot_SMC_plimit;
uint16_t bootProgressMeterStart;
uint16_t bootProgressMeterEnd;
Boot_Video Video; /* Video Information */
uint32_t apfsDataStart;/* Physical address of apfs volume key structure */
uint32_t apfsDataSize;
/* Version 2, Revision 1 */
uint64_t KC_hdrs_vaddr;
uint64_t arvRootHashStart; /* Physical address of root hash file */
uint64_t arvRootHashSize;
uint64_t arvManifestStart; /* Physical address of manifest file */
uint64_t arvManifestSize;
/* Reserved */
uint32_t __reserved4[700];
} boot_args;
the way this works is that the csr_get_config function will collect teh csrActiveConfig and return it in the config result,. then csrActiveConfig is retrived through the PE_STATE, which has a pointer reference to boot_args that stores the csrActiveConfig.

IDA can import the structure elements if provided, however! it appears that its having issues hitting the breakpoint.
Dylid Injection Concept for big sur.
static void configureProcessRestrictions(const macho_header* mainExecutableMH, const char* envp[])
{
uint64_t amfiInputFlags = 0;
#if TARGET_OS_SIMULATOR
amfiInputFlags |= AMFI_DYLD_INPUT_PROC_IN_SIMULATOR;
#elif TARGET_OS_OSX
if ( hasRestrictedSegment(mainExecutableMH) )
amfiInputFlags |= AMFI_DYLD_INPUT_PROC_HAS_RESTRICT_SEG;
#elif TARGET_OS_IPHONE
if ( isFairPlayEncrypted(mainExecutableMH) )
amfiInputFlags |= AMFI_DYLD_INPUT_PROC_IS_ENCRYPTED;
#endif
uint64_t amfiOutputFlags = 0;
const char* amfiFake = nullptr;
if constexpr(BUILD_FOR_TESTING == 1) {
amfiFake = _simple_getenv(envp, "DYLD_AMFI_FAKE");
} else if ( dyld3::internalInstall() && dyld3::BootArgs::enableDyldTestMode() ) {
amfiFake = _simple_getenv(envp, "DYLD_AMFI_FAKE");
}
if ( amfiFake != nullptr ) {
amfiOutputFlags = hexToUInt64(amfiFake, nullptr);
}
if ( (amfiFake != nullptr) || (amfi_check_dyld_policy_self(amfiInputFlags, &amfiOutputFlags) == 0) ) {
gLinkContext.allowAtPaths = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_AT_PATH);
gLinkContext.allowEnvVarsPrint = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_PRINT_VARS);
gLinkContext.allowEnvVarsPath = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_PATH_VARS);
gLinkContext.allowEnvVarsSharedCache = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_CUSTOM_SHARED_CACHE);
gLinkContext.allowClassicFallbackPaths = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_FALLBACK_PATHS);
gLinkContext.allowInsertFailures = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_FAILED_LIBRARY_INSERTION);
#ifdef AMFI_RETURNS_INTERPOSING_FLAG
gLinkContext.allowInterposing = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_LIBRARY_INTERPOSING);
#else
gLinkContext.allowInterposing = true;
#endif
}
else {
#if TARGET_OS_OSX
// support chrooting from old kernel
bool isRestricted = false;
bool libraryValidation = false;
// any processes with setuid or setgid bit set or with __RESTRICT segment is restricted
if ( issetugid() || hasRestrictedSegment(mainExecutableMH) ) {
isRestricted = true;
}
bool usingSIP = (csr_check(CSR_ALLOW_TASK_FOR_PID) != 0);
uint32_t flags;
if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) {
// On OS X CS_RESTRICT means the program was signed with entitlements
if ( ((flags & CS_RESTRICT) == CS_RESTRICT) && usingSIP ) {
isRestricted = true;
}
// Library Validation loosens searching but requires everything to be code signed
if ( flags & CS_REQUIRE_LV ) {
isRestricted = false;
libraryValidation = true;
}
}
gLinkContext.allowAtPaths = !isRestricted;
gLinkContext.allowEnvVarsPrint = !isRestricted;
gLinkContext.allowEnvVarsPath = !isRestricted;
gLinkContext.allowEnvVarsSharedCache = !libraryValidation || !usingSIP;
gLinkContext.allowClassicFallbackPaths = !isRestricted;
gLinkContext.allowInsertFailures = false;
gLinkContext.allowInterposing = true;
#else
halt("amfi_check_dyld_policy_self() failed\n");
#endif
}
}
