Dylib Injection

R3zk0n ยท October 2, 2025

Contents

    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..

    1. Setup runtime envoriment in the kernel
    2. Load Shared Cache
    3. Instantiate the main program.
    4. Load the inserted dynamic library.
    5. Link the main program.
    6. Perform weak symbol binding.
    7. Execute the initialization method.
    8. 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)
    

    Untitled

    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;
    

    lol.svg

    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.

    Untitled

    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
    	}
    }
    

    Twitter, Facebook