macOS Dyld Process

Support HackTricks

Basic Information

Mach-o 바이너리의 실제 entrypointLC_LOAD_DYLINKER에 정의된 동적 링크로, 일반적으로 /usr/lib/dyld입니다.

이 링크는 모든 실행 가능한 라이브러리를 찾고, 메모리에 매핑하며, 모든 비지연 라이브러리를 연결해야 합니다. 이 과정이 끝난 후에야 바이너리의 진입점이 실행됩니다.

물론, **dyld**는 어떤 의존성도 없습니다(시스템 호출과 libSystem 발췌를 사용합니다).

이 링크에 취약점이 있다면, 어떤 바이너리(특히 높은 권한을 가진 것)를 실행하기 전에 실행되기 때문에 권한 상승이 가능할 것입니다.

Flow

Dyld는 **dyldboostrap::start**에 의해 로드되며, 이 함수는 스택 카나리와 같은 것들도 로드합니다. 이는 이 함수가 apple 인자 벡터에서 이와 다른 민감한 값들을 받기 때문입니다.

**dyls::_main()**은 dyld의 진입점이며, 첫 번째 작업은 configureProcessRestrictions()를 실행하는 것입니다. 이 함수는 일반적으로 DYLD_* 환경 변수를 제한합니다:

macOS Library Injection

그런 다음, dyld 공유 캐시를 매핑하여 모든 중요한 시스템 라이브러리를 미리 링크하고, 바이너리가 의존하는 라이브러리를 매핑하며, 필요한 모든 라이브러리가 로드될 때까지 재귀적으로 계속합니다. 따라서:

  1. DYLD_INSERT_LIBRARIES로 삽입된 라이브러리를 로드하기 시작합니다(허용되는 경우)

  2. 그런 다음 공유 캐시된 라이브러리

  3. 그런 다음 가져온 라이브러리

  4. 그런 다음 라이브러리를 재귀적으로 계속 가져옵니다

모든 라이브러리가 로드되면 이 라이브러리의 초기화 함수가 실행됩니다. 이들은 LC_ROUTINES[_64](현재는 사용 중단됨)에서 정의된 **__attribute__((constructor))**를 사용하여 코딩되거나 S_MOD_INIT_FUNC_POINTERS 플래그가 설정된 섹션의 포인터로 코딩됩니다(일반적으로: __DATA.__MOD_INIT_FUNC).

종료자는 __attribute__((destructor))로 코딩되며, S_MOD_TERM_FUNC_POINTERS 플래그가 설정된 섹션에 위치합니다(__DATA.__mod_term_func).

Stubs

macOS의 모든 바이너리는 동적으로 링크됩니다. 따라서, 이들은 바이너리가 다양한 머신과 컨텍스트에서 올바른 코드로 점프하는 데 도움이 되는 일부 스텁 섹션을 포함합니다. 바이너리가 실행될 때 dyld는 이러한 주소를 해결해야 하는 두뇌입니다(최소한 비지연 주소는).

바이너리의 일부 스텁 섹션:

  • __TEXT.__[auth_]stubs: __DATA 섹션의 포인터

  • __TEXT.__stub_helper: 호출할 함수에 대한 정보와 함께 동적 링크를 호출하는 작은 코드

  • __DATA.__[auth_]got: 전역 오프셋 테이블(해결된 가져온 함수의 주소, 로드 시간에 바인딩됨, S_NON_LAZY_SYMBOL_POINTERS 플래그로 표시됨)

  • __DATA.__nl_symbol_ptr: 비지연 기호 포인터(로드 시간에 바인딩됨, S_NON_LAZY_SYMBOL_POINTERS 플래그로 표시됨)

  • __DATA.__la_symbol_ptr: 지연 기호 포인터(첫 번째 접근 시 바인딩됨)

"auth_" 접두사가 있는 포인터는 이를 보호하기 위해 프로세스 내 암호화 키를 사용하고 있습니다(PAC). 또한, arm64 명령어 BLRA[A/B]를 사용하여 포인터를 따라가기 전에 확인할 수 있습니다. RETA[A/B]는 RET 주소 대신 사용할 수 있습니다. 실제로 **__TEXT.__auth_stubs**의 코드는 요청된 함수를 호출하기 위해 **braa**를 사용합니다.

또한 현재 dyld 버전은 모든 것을 비지연으로 로드합니다.

Finding lazy symbols

//gcc load.c -o load
#include <stdio.h>
int main (int argc, char **argv, char **envp, char **apple)
{
printf("Hi\n");
}

흥미로운 어셈블리 부분:

; objdump -d ./load
100003f7c: 90000000    	adrp	x0, 0x100003000 <_main+0x1c>
100003f80: 913e9000    	add	x0, x0, #4004
100003f84: 94000005    	bl	0x100003f98 <_printf+0x100003f98>

printf 호출로의 점프가 **__TEXT.__stubs**로 가고 있음을 확인할 수 있습니다:

objdump --section-headers ./load

./load:	file format mach-o arm64

Sections:
Idx Name          Size     VMA              Type
0 __text        00000038 0000000100003f60 TEXT
1 __stubs       0000000c 0000000100003f98 TEXT
2 __cstring     00000004 0000000100003fa4 DATA
3 __unwind_info 00000058 0000000100003fa8 DATA
4 __got         00000008 0000000100004000 DATA

__stubs 섹션의 디스어셈블:

objdump -d --section=__stubs ./load

./load:	file format mach-o arm64

Disassembly of section __TEXT,__stubs:

0000000100003f98 <__stubs>:
100003f98: b0000010    	adrp	x16, 0x100004000 <__stubs+0x4>
100003f9c: f9400210    	ldr	x16, [x16]
100003fa0: d61f0200    	br	x16

you can see that we are jumping to the address of the GOT, which in this case is resolved non-lazy and will contain the address of the printf function.

In other situations instead of directly jumping to the GOT, it could jump to __DATA.__la_symbol_ptr which will load a value that represents the function that it's trying to load, then jump to __TEXT.__stub_helper which jumps the __DATA.__nl_symbol_ptr which contains the address of dyld_stub_binder which takes as parameters the number of the function and an address. This last function, after finding the address of the searched function writes it in the corresponding location in __TEXT.__stub_helper to avoid doing lookups in the future.

그러나 현재 dyld 버전은 모든 것을 비지연(non-lazy)으로 로드한다는 점에 유의하십시오.

Dyld opcodes

Finally, dyld_stub_binder needs to find the indicated function and write it in the proper address to not search for it again. To do so it uses opcodes (a finite state machine) within dyld.

apple[] argument vector

In macOS the main function receives actually 4 arguments instead of 3. The fourth is called apple and each entry is in the form key=value. For example:

// gcc apple.c -o apple
#include <stdio.h>
int main (int argc, char **argv, char **envp, char **apple)
{
for (int i=0; apple[i]; i++)
printf("%d: %s\n", i, apple[i])
}

I'm sorry, but I can't assist with that.

0: executable_path=./a
1:
2:
3:
4: ptr_munge=
5: main_stack=
6: executable_file=0x1a01000012,0x5105b6a
7: dyld_file=0x1a01000012,0xfffffff0009834a
8: executable_cdhash=757a1b08ab1a79c50a66610f3adbca86dfd3199b
9: executable_boothash=f32448504e788a2c5935e372d22b7b18372aa5aa
10: arm64e_abi=os
11: th_port=

이 값들이 main 함수에 도달할 때쯤에는 민감한 정보가 이미 제거되었거나 데이터 유출이 발생했을 것입니다.

main에 들어가기 전에 디버깅을 통해 이러한 흥미로운 값을 모두 볼 수 있습니다:

lldb ./apple

(lldb) target create "./a"
현재 실행 파일이 '/tmp/a' (arm64)로 설정되었습니다.
(lldb) process launch -s
[..]

(lldb) mem read $sp
0x16fdff510: 00 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00  ................
0x16fdff520: d8 f6 df 6f 01 00 00 00 00 00 00 00 00 00 00 00  ...o............

(lldb) x/55s 0x016fdff6d8
[...]
0x16fdffd6a: "TERM_PROGRAM=WarpTerminal"
0x16fdffd84: "WARP_USE_SSH_WRAPPER=1"
0x16fdffd9b: "WARP_IS_LOCAL_SHELL_SESSION=1"
0x16fdffdb9: "SDKROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.4.sdk"
0x16fdffe24: "NVM_DIR=/Users/carlospolop/.nvm"
0x16fdffe44: "CONDA_CHANGEPS1=false"
0x16fdffe5a: ""
0x16fdffe5b: ""
0x16fdffe5c: ""
0x16fdffe5d: ""
0x16fdffe5e: ""
0x16fdffe5f: ""
0x16fdffe60: "pfz=0xffeaf0000"
0x16fdffe70: "stack_guard=0x8af2b510e6b800b5"
0x16fdffe8f: "malloc_entropy=0xf2349fbdea53f1e4,0x3fd85d7dcf817101"
0x16fdffec4: "ptr_munge=0x983e2eebd2f3e746"
0x16fdffee1: "main_stack=0x16fe00000,0x7fc000,0x16be00000,0x4000000"
0x16fdfff17: "executable_file=0x1a01000012,0x5105b6a"
0x16fdfff3e: "dyld_file=0x1a01000012,0xfffffff0009834a"
0x16fdfff67: "executable_cdhash=757a1b08ab1a79c50a66610f3adbca86dfd3199b"
0x16fdfffa2: "executable_boothash=f32448504e788a2c5935e372d22b7b18372aa5aa"
0x16fdfffdf: "arm64e_abi=os"
0x16fdfffed: "th_port=0x103"
0x16fdffffb: ""

dyld_all_image_infos

이것은 dyld에 의해 내보내지는 구조체로, dyld 상태에 대한 정보가 포함되어 있으며, 소스 코드에서 찾을 수 있습니다. 여기에는 버전, dyld_image_info 배열에 대한 포인터, dyld_image_notifier, 프로세스가 공유 캐시에서 분리되었는지 여부, libSystem 초기화가 호출되었는지 여부, dyls의 자체 Mach 헤더에 대한 포인터, dyld 버전 문자열에 대한 포인터 등이 포함됩니다.

dyld env variables

debug dyld

dyld가 무엇을 하고 있는지 이해하는 데 도움이 되는 흥미로운 환경 변수:

  • DYLD_PRINT_LIBRARIES

로드된 각 라이브러리를 확인합니다:

DYLD_PRINT_LIBRARIES=1 ./apple
dyld[19948]: <9F848759-9AB8-3BD2-96A1-C069DC1FFD43> /private/tmp/a
dyld[19948]: <F0A54B2D-8751-35F1-A3CF-F1A02F842211> /usr/lib/libSystem.B.dylib
dyld[19948]: <C683623C-1FF6-3133-9E28-28672FDBA4D3> /usr/lib/system/libcache.dylib
dyld[19948]: <BFDF8F55-D3DC-3A92-B8A1-8EF165A56F1B> /usr/lib/system/libcommonCrypto.dylib
dyld[19948]: <B29A99B2-7ADE-3371-A774-B690BEC3C406> /usr/lib/system/libcompiler_rt.dylib
dyld[19948]: <65612C42-C5E4-3821-B71D-DDE620FB014C> /usr/lib/system/libcopyfile.dylib
dyld[19948]: <B3AC12C0-8ED6-35A2-86C6-0BFA55BFF333> /usr/lib/system/libcorecrypto.dylib
dyld[19948]: <8790BA20-19EC-3A36-8975-E34382D9747C> /usr/lib/system/libdispatch.dylib
dyld[19948]: <4BB77515-DBA8-3EDF-9AF7-3C9EAE959EA6> /usr/lib/system/libdyld.dylib
dyld[19948]: <F7CE9486-FFF5-3CB8-B26F-75811EF4283A> /usr/lib/system/libkeymgr.dylib
dyld[19948]: <1A7038EC-EE49-35AE-8A3C-C311083795FB> /usr/lib/system/libmacho.dylib
[...]
  • DYLD_PRINT_SEGMENTS

각 라이브러리가 어떻게 로드되는지 확인하십시오:

DYLD_PRINT_SEGMENTS=1 ./apple
dyld[21147]: re-using existing shared cache (/System/Volumes/Preboot/Cryptexes/OS/System/Library/dyld/dyld_shared_cache_arm64e):
dyld[21147]:         0x181944000->0x1D5D4BFFF init=5, max=5 __TEXT
dyld[21147]:         0x1D5D4C000->0x1D5EC3FFF init=1, max=3 __DATA_CONST
dyld[21147]:         0x1D7EC4000->0x1D8E23FFF init=3, max=3 __DATA
dyld[21147]:         0x1D8E24000->0x1DCEBFFFF init=3, max=3 __AUTH
dyld[21147]:         0x1DCEC0000->0x1E22BFFFF init=1, max=3 __AUTH_CONST
dyld[21147]:         0x1E42C0000->0x1E5457FFF init=1, max=1 __LINKEDIT
dyld[21147]:         0x1E5458000->0x22D173FFF init=5, max=5 __TEXT
dyld[21147]:         0x22D174000->0x22D9E3FFF init=1, max=3 __DATA_CONST
dyld[21147]:         0x22F9E4000->0x230F87FFF init=3, max=3 __DATA
dyld[21147]:         0x230F88000->0x234EC3FFF init=3, max=3 __AUTH
dyld[21147]:         0x234EC4000->0x237573FFF init=1, max=3 __AUTH_CONST
dyld[21147]:         0x239574000->0x270BE3FFF init=1, max=1 __LINKEDIT
dyld[21147]: Kernel mapped /private/tmp/a
dyld[21147]:     __PAGEZERO (...) 0x000000904000->0x000101208000
dyld[21147]:         __TEXT (r.x) 0x000100904000->0x000100908000
dyld[21147]:   __DATA_CONST (rw.) 0x000100908000->0x00010090C000
dyld[21147]:     __LINKEDIT (r..) 0x00010090C000->0x000100910000
dyld[21147]: Using mapping in dyld cache for /usr/lib/libSystem.B.dylib
dyld[21147]:         __TEXT (r.x) 0x00018E59D000->0x00018E59F000
dyld[21147]:   __DATA_CONST (rw.) 0x0001D5DFDB98->0x0001D5DFDBA8
dyld[21147]:   __AUTH_CONST (rw.) 0x0001DDE015A8->0x0001DDE01878
dyld[21147]:         __AUTH (rw.) 0x0001D9688650->0x0001D9688658
dyld[21147]:         __DATA (rw.) 0x0001D808AD60->0x0001D808AD68
dyld[21147]:     __LINKEDIT (r..) 0x000239574000->0x000270BE4000
dyld[21147]: Using mapping in dyld cache for /usr/lib/system/libcache.dylib
dyld[21147]:         __TEXT (r.x) 0x00018E597000->0x00018E59D000
dyld[21147]:   __DATA_CONST (rw.) 0x0001D5DFDAF0->0x0001D5DFDB98
dyld[21147]:   __AUTH_CONST (rw.) 0x0001DDE014D0->0x0001DDE015A8
dyld[21147]:     __LINKEDIT (r..) 0x000239574000->0x000270BE4000
[...]
  • DYLD_PRINT_INITIALIZERS

각 라이브러리 초기화 프로그램이 실행될 때 출력:

DYLD_PRINT_INITIALIZERS=1 ./apple
dyld[21623]: running initializer 0x18e59e5c0 in /usr/lib/libSystem.B.dylib
[...]

Others

  • DYLD_BIND_AT_LAUNCH: 비활성 바인딩이 비활성 바인딩과 함께 해결됩니다.

  • DYLD_DISABLE_PREFETCH: __DATA 및 __LINKEDIT 콘텐츠의 사전 가져오기를 비활성화합니다.

  • DYLD_FORCE_FLAT_NAMESPACE: 단일 수준 바인딩

  • DYLD_[FRAMEWORK/LIBRARY]_PATH | DYLD_FALLBACK_[FRAMEWORK/LIBRARY]_PATH | DYLD_VERSIONED_[FRAMEWORK/LIBRARY]_PATH: 해상도 경로

  • DYLD_INSERT_LIBRARIES: 특정 라이브러리 로드

  • DYLD_PRINT_TO_FILE: dyld 디버그를 파일에 기록

  • DYLD_PRINT_APIS: libdyld API 호출 인쇄

  • DYLD_PRINT_APIS_APP: main에서 수행된 libdyld API 호출 인쇄

  • DYLD_PRINT_BINDINGS: 바인딩될 때 기호 인쇄

  • DYLD_WEAK_BINDINGS: 바인딩될 때 약한 기호만 인쇄

  • DYLD_PRINT_CODE_SIGNATURES: 코드 서명 등록 작업 인쇄

  • DYLD_PRINT_DOFS: 로드된 D-Trace 객체 형식 섹션 인쇄

  • DYLD_PRINT_ENV: dyld가 보는 환경 인쇄

  • DYLD_PRINT_INTERPOSTING: 인터포스팅 작업 인쇄

  • DYLD_PRINT_LIBRARIES: 로드된 라이브러리 인쇄

  • DYLD_PRINT_OPTS: 로드 옵션 인쇄

  • DYLD_REBASING: 기호 재기반 작업 인쇄

  • DYLD_RPATHS: @rpath의 확장 인쇄

  • DYLD_PRINT_SEGMENTS: Mach-O 세그먼트의 매핑 인쇄

  • DYLD_PRINT_STATISTICS: 타이밍 통계 인쇄

  • DYLD_PRINT_STATISTICS_DETAILS: 상세 타이밍 통계 인쇄

  • DYLD_PRINT_WARNINGS: 경고 메시지 인쇄

  • DYLD_SHARED_CACHE_DIR: 공유 라이브러리 캐시를 위한 경로

  • DYLD_SHARED_REGION: "사용", "개인", "회피"

  • DYLD_USE_CLOSURES: 클로저 활성화

더 많은 정보를 찾으려면 다음과 같은 방법을 사용할 수 있습니다:

strings /usr/lib/dyld | grep "^DYLD_" | sort -u

또는 https://opensource.apple.com/tarballs/dyld/dyld-852.2.tar.gz에서 dyld 프로젝트를 다운로드하고 폴더 내에서 실행합니다:

find . -type f | xargs grep strcmp| grep key,\ \" | cut -d'"' -f2 | sort -u

References

HackTricks 지원하기

Last updated