# OSX Lazy Symbol Pointer Binding 주의 : 이 글은 의식에 흐름에 맡겨 작성되었습니다. ## Lazy Symbol Pointer OSX의 바이너리의 경우 Dynamic하게 함수를 호출할 수 있음. Linux의 got랑 비슷하며, OSX에서는 이를 Lazy Symbol Pointer라 부름. None-Lazy Symbol Pointer는 반대로 Static하게 박혀있는 값임. 이 값은 아래의 binding하는 과정에서 쓰기 위해 사용됨. ## Explorer malloc을 처음 실행할 때, `__stubs`위에 있는 malloc함수로 날라감. ``` jmp cs:_malloc_ptr ``` `_malloc_ptr`의 경우 실행한 적이 없다면 `__stub_helper`영역을 가르킨다. ``` __stub_helper:0000000100000F2C loc_100000F2C: ; CODE XREF: __stub_helper:0000000100000F41↓j __stub_helper:0000000100000F2C ; __stub_helper:0000000100000F4B↓j __stub_helper:0000000100000F2C lea r11, off_100001008 __stub_helper:0000000100000F33 push r11 __stub_helper:0000000100000F35 jmp cs:dyld_stub_binder_ptr __stub_helper:0000000100000F35 ; --------------------------------------------------------------------------- __stub_helper:0000000100000F3B align 4 __stub_helper:0000000100000F3C push 0 __stub_helper:0000000100000F41 jmp loc_100000F2C __stub_helper:0000000100000F46 ; --------------------------------------------------------------------------- __stub_helper:0000000100000F46 push 0Eh __stub_helper:0000000100000F4B jmp loc_100000F2C __stub_helper:0000000100000F4B __stub_helper ends ``` `dyld_stub_binder_ptr`로 {0, 0x100001008}의 값을 가지고 간다. ```assembly .align 2,0x90 .globl dyld_stub_binder dyld_stub_binder: pushq %rbp movq %rsp,%rbp subq $STACK_SIZE,%rsp # at this point stack is 16-byte aligned because two meta-parameters where pushed movq %rdi,RDI_SAVE(%rsp) # save registers that might be used as parameters movq %rsi,RSI_SAVE(%rsp) movq %rdx,RDX_SAVE(%rsp) movq %rcx,RCX_SAVE(%rsp) movq %r8,R8_SAVE(%rsp) movq %r9,R9_SAVE(%rsp) movq %rax,RAX_SAVE(%rsp) misaligned_stack_error_entering_dyld_stub_binder: movdqa %xmm0,XMMM0_SAVE(%rsp) movdqa %xmm1,XMMM1_SAVE(%rsp) movdqa %xmm2,XMMM2_SAVE(%rsp) movdqa %xmm3,XMMM3_SAVE(%rsp) movdqa %xmm4,XMMM4_SAVE(%rsp) movdqa %xmm5,XMMM5_SAVE(%rsp) movdqa %xmm6,XMMM6_SAVE(%rsp) movdqa %xmm7,XMMM7_SAVE(%rsp) dyld_stub_binder_: movq MH_PARAM_BP(%rbp),%rdi # call fastBindLazySymbol(loadercache, lazyinfo) movq LP_PARAM_BP(%rbp),%rsi call __Z21_dyld_fast_stub_entryPvl movq %rax,%r11 # save target movdqa XMMM0_SAVE(%rsp),%xmm0 # restore registers movdqa XMMM1_SAVE(%rsp),%xmm1 movdqa XMMM2_SAVE(%rsp),%xmm2 movdqa XMMM3_SAVE(%rsp),%xmm3 movdqa XMMM4_SAVE(%rsp),%xmm4 movdqa XMMM5_SAVE(%rsp),%xmm5 movdqa XMMM6_SAVE(%rsp),%xmm6 movdqa XMMM7_SAVE(%rsp),%xmm7 movq RDI_SAVE(%rsp),%rdi movq RSI_SAVE(%rsp),%rsi movq RDX_SAVE(%rsp),%rdx movq RCX_SAVE(%rsp),%rcx movq R8_SAVE(%rsp),%r8 movq R9_SAVE(%rsp),%r9 movq RAX_SAVE(%rsp),%rax addq $STACK_SIZE,%rsp popq %rbp addq $16,%rsp # remove meta-parameters jmp *%r11 # jmp to target ``` 안에서는 (loadercache, lazyinfo)를 인자로써, `_dyld_fast_stub_entry`를 호출. ```c #if __i386__ || __x86_64__ __attribute__((visibility("hidden"))) void* _dyld_fast_stub_entry(void* loadercache, long lazyinfo) { DYLD_NO_LOCK_THIS_BLOCK; static void* (*p)(void*, long) = NULL; if(p == NULL) _dyld_func_lookup("__dyld_fast_stub_entry", (void**)&p); return p(loadercache, lazyinfo); } #endif ``` p의 값이 존재하지 않는다면, "\_\_dyld\_fast\_stub\_entry"의 값을 인자로써 `_dyld_func_lookup`함수를 한번 수행하여 p에 값을 구해옴. 아래의 함수(dylib)에 들어가서 jmp rax를 통해 또 한번 이동을 하게됨. ```assembly _dyld_func_lookup @ libdyld.dylib: -> 0x7fff6b8bc508: 55 push rbp 0x7fff6b8bc509: 48 89 e5 mov rbp, rsp 0x7fff6b8bc50c: 48 8b 05 f5 9a 96 36 mov rax, qword ptr [rip + 0x36969af5] ; myDyldSection + 8 0x7fff6b8bc513: 5d pop rbp 0x7fff6b8bc514: ff e0 jmp rax ``` dyld의 영역에 있는 func_lookup으로 이동함 ```assembly _dyld_func_lookup @ dyld: -> 0x100011e80: 55 push rbp 0x100011e81: 48 89 e5 mov rbp, rsp 0x100011e84: 41 57 push r15 0x100011e86: 41 56 push r14 0x100011e88: 41 54 push r12 0x100011e8a: 53 push rbx 0x100011e8b: 49 89 f6 mov r14, rsi 0x100011e8e: 49 89 ff mov r15, rdi 0x100011e91: 4c 8d 25 b8 e1 04 00 lea r12, [rip + 0x4e1b8] ; "__dyld_register_func_for_add_image" 0x100011e98: 48 8d 1d 49 c5 05 00 lea rbx, [rip + 0x5c549] ; dyld_funcs + 8 0x100011e9f: 4c 89 e7 mov rdi, r12 0x100011ea2: 4c 89 fe mov rsi, r15 0x100011ea5: e8 56 22 03 00 call 0x100044100 ; strcmp 0x100011eaa: 85 c0 test eax, eax 0x100011eac: 74 18 je 0x100011ec6 ; <+70> 0x100011eae: 4c 8b 63 08 mov r12, qword ptr [rbx + 0x8] 0x100011eb2: 48 83 c3 10 add rbx, 0x10 0x100011eb6: 4d 85 e4 test r12, r12 0x100011eb9: 75 e4 jne 0x100011e9f ; <+31> 0x100011ebb: 49 c7 06 00 00 00 00 mov qword ptr [r14], 0x0 0x100011ec2: 31 c0 xor eax, eax 0x100011ec4: eb 2b jmp 0x100011ef1 ; <+113> 0x100011ec6: 48 8b 03 mov rax, qword ptr [rbx] 0x100011ec9: 48 8d 0d 2a 00 00 00 lea rcx, [rip + 0x2a] ; unimplemented() 0x100011ed0: 48 39 c8 cmp rax, rcx 0x100011ed3: 75 14 jne 0x100011ee9 ; <+105> 0x100011ed5: 48 8d 3d 37 dd 04 00 lea rdi, [rip + 0x4dd37] ; "unimplemented dyld function: %s\n" 0x100011edc: 31 c0 xor eax, eax 0x100011ede: 4c 89 e6 mov rsi, r12 0x100011ee1: e8 db 2c ff ff call 0x100004bc1 ; dyld::log(char const*, ...) 0x100011ee6: 48 8b 03 mov rax, qword ptr [rbx] 0x100011ee9: 49 89 06 mov qword ptr [r14], rax 0x100011eec: b8 01 00 00 00 mov eax, 0x1 0x100011ef1: 5b pop rbx 0x100011ef2: 41 5c pop r12 0x100011ef4: 41 5e pop r14 0x100011ef6: 41 5f pop r15 0x100011ef8: 5d pop rbp 0x100011ef9: c3 ret ``` 이 함수에서는 0x100060050의 주소에 있는 `__dyld_register_func_for_add_image`로 시작하여 우리는 binding하기 위한 스트링이랑 맞는 값을 찾는다.(strcmp로 비교) > strcmp를 하는 문자열 리스트는 아래에 첨부 [Click](#bonus1) > 위 스트링이 저장된 주소들의 경우 dyld_func라는 영역에서의 데이터를 가져오는 것이다. ``` (lldbinit) x/32gx 0x000000010006E3E0 0x10006e3e0: 0x0000000100060050 0x00000001000107d6 0x10006e3f0: 0x0000000100060073 0x0000000100010811 0x10006e400: 0x0000000100060099 0x0000000100012625 0x10006e410: 0x00000001000600a7 0x0000000100012564 0x10006e420: 0x00000001000600b6 0x0000000100012718 ``` 이는 대충 아래와 같이 생긴걸로 예측할 수 있다. ``` struct S{ *func_name *func } S_; S_ dyld_func[N]; ``` 여기서 `__dyld_fast_stub_entry`의 값을 발견하면 이에 맞는 func를 리턴하여 p의 값으로 저장이 될 것이다. 확인해보자. ``` (lldbinit) re r r14 r14 = 0x00007fffa2228f58 libdyld.dylib`_dyld_fast_stub_entry(void*, long)::p (lldbinit) x/gx 0x00007fffa2228f58 0x7fffa2228f58: 0x0000000100007b20 -> dyld::fastBindLazySymbol(ImageLoader**, unsigned long) ``` p의 값에 정상적으로 들어간 것을 알 수 있다. ```c p(loadercache, lazyinfo); ``` 이런식으로 원하는 값을 가져온 후 `jmp rax`의 코드로 인해 실행된다. 여기까지 함수 콜을 정리해보면 다음과 같다. ``` call malloc -> dyld_stub_binder @ libdyld.dylib -> _dyld_fast_stub_entry @ libdyld.dylib -> _dyld_func_lookup @ libdyld.dylib -> _dyld_func_lookup @ dyld -> dyld::fastBindLazySymbol @ dyld ``` ```c uintptr_t fastBindLazySymbol(ImageLoader** imageLoaderCache, uintptr_t lazyBindingInfoOffset) { uintptr_t result = 0; // get image if ( *imageLoaderCache == NULL ) { // save in cache *imageLoaderCache = dyld::findMappedRange((uintptr_t)imageLoaderCache); if ( *imageLoaderCache == NULL ) { const char* message = "fast lazy binding from unknown image"; dyld::log("dyld: %s\n", message); halt(message); } } // bind lazy pointer and return it try { result = (*imageLoaderCache)->doBindFastLazySymbol(lazyBindingInfoOffset, gLinkContext, (dyld::gLibSystemHelpers != NULL) ? dyld::gLibSystemHelpers->acquireGlobalDyldLock : NULL, (dyld::gLibSystemHelpers != NULL) ? dyld::gLibSystemHelpers->releaseGlobalDyldLock : NULL); } catch (const char* message) { dyld::log("dyld: lazy symbol binding failed: %s\n", message); halt(message); } // return target address to glue which jumps to it with real parameters restored return result; } ``` **fastBindLazySymbol** 함수를 호출할 때 들어간 인자에 어떤 값이 들어가는지 한번 확인해보자. ``` (lldbinit) re r rdi rsi rdi = 0x0000000100001008 (void *)0x0000000000000000 rsi = 0x0000000000000000 (lldbinit) x/gx $rdi 0x100001008: 0x0000000000000000 (lldbinit) x/32i 0000000100000F2C 0x100000f2c: 4c 8d 1d d5 00 00 00 lea r11, [rip + 0xd5] ; (void *)0x0000000000000000 0x100000f33: 41 53 push r11 0x100000f35: ff 25 c5 00 00 00 jmp qword ptr [rip + 0xc5] ; (void *)0x00007fff6b8bd214: dyld_stub_binder 0x100000f3b: 90 nop 0x100000f3c: 68 00 00 00 00 push 0x0 0x100000f41: e9 e6 ff ff ff jmp 0x100000f2c ``` `__stub_helper` 함수를 보면 0x100000f3c에서 push한 0x0이 **lazyBindingInfoOffset**, 0x100000f33에서 push한 0x0000000100001008이 **imageLoaderCache** 값이다. > 0x0000000100001008의 주소는 **__nl_symbol_ptr**영역에 있는 값이다. (none-lazy symbol pointer) > 현재 상황에서는 lazyBindingInfoOffset와 imageLoaderCache 둘다 0을 가지고 있다. imageLoaderCache가 0이기 때문에 findMappedRange함수를 수행함 ```c // // The MappedRanges structure is used for fast address->image lookups. // The table is only updated when the dyld lock is held, so we don't // need to worry about multiple writers. But readers may look at this // data without holding the lock. Therefore, all updates must be done // in an order that will never cause readers to see inconsistent data. // The general rule is that if the image field is non-NULL then // the other fields are valid. // struct MappedRanges { enum { count=400 }; struct { ImageLoader* image; uintptr_t start; uintptr_t end; } array[count]; MappedRanges* next; }; static MappedRanges sMappedRangesStart; ... ImageLoader* findMappedRange(uintptr_t target) { for (MappedRanges* p = &sMappedRangesStart; p != NULL; p = p->next) { for (int i=0; i < MappedRanges::count; ++i) { if ( p->array[i].image != NULL ) { if ( (p->array[i].start <= target) && (target < p->array[i].end) ) return p->array[i].image; } } } return NULL; } ``` 여기서는 0x100001008이 속하는 메모리 영역을 구한다. 영역을 구하면 fastBindLazySymbol로 돌아와서 아래의 코드를 수행함 ```c try { result = (*imageLoaderCache)->doBindFastLazySymbol(lazyBindingInfoOffset, gLinkContext, (dyld::gLibSystemHelpers != NULL) ? dyld::gLibSystemHelpers->acquireGlobalDyldLock : NULL, (dyld::gLibSystemHelpers != NULL) ? dyld::gLibSystemHelpers->releaseGlobalDyldLock : NULL); } ``` imageLoaderCache->doBindFastLazySymbol을 호출하고 결과를 return하는걸로 봐서 이제 디버깅이 끝나가는 것 같다. 여기서 리턴된 값을 `_lazy_symbol_ptr`에서 malloc으로 들어갈 것이다. 그럼 doBindFastLazySymbol소스를 볼까! ```c uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)()) { // <rdar://problem/8663923> race condition with flat-namespace lazy binding if ( this->usesTwoLevelNameSpace() ) { // two-level namespace lookup does not require lock because dependents can't be unloaded before this image } else { // acquire dyld global lock if ( lock != NULL ) lock(); } const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off; const uint8_t* const end = &start[fDyldInfo->lazy_bind_size]; uint8_t segIndex; uintptr_t segOffset; int libraryOrdinal; const char* symbolName; bool doneAfterBind; uintptr_t result; do { if ( ! getLazyBindingInfo(lazyBindingInfoOffset, start, end, &segIndex, &segOffset, &libraryOrdinal, &symbolName, &doneAfterBind) ) dyld::throwf("bad lazy bind info"); if ( segIndex >= fSegmentsCount ) dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", segIndex, fSegmentsCount-1); if ( segOffset > segSize(segIndex) ) dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(segIndex)); uintptr_t address = segActualLoadAddress(segIndex) + segOffset; result = this->bindAt(context, address, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL, true); // <rdar://problem/24140465> Some old apps had multiple lazy symbols bound at once } while (!doneAfterBind && !context.strictMachORequired); if ( !this->usesTwoLevelNameSpace() ) { // release dyld global lock if ( unlock != NULL ) unlock(); } return result; } ``` doBindFastLazySymbol에서는 getLazyBindingInfo를 호출하여 bind에 필요한 정보 (e.g. symbolName)등을 가지고 온다. ``` ImageLoaderMachOCompressed::doBindFastLazySymbol @ dyld: -> 0x100023312: e8 83 91 ff ff call 0x10001c49a ; ImageLoaderMachO::getLazyBindingInfo(unsigned int&, unsigned char const*, unsigned char const*, unsigned char*, unsigned long*, int*, char const**, bool*) (lldbinit) re r rdi rsi rdx rcx r8 r9 rdi = 0x00007ffeefbff250 rsi = 0x0000000100002020 rdx = 0x0000000100002040 rcx = 0x00007ffeefbff267 r8 = 0x00007ffeefbff258 r9 = 0x00007ffeefbff254 (lldbinit) x/gx $rsp 0x7ffeefbff210: 0x00007ffeefbff228 (lldbinit) x/gx $rsp+8 0x7ffeefbff218: 0x00007ffeefbff266 ``` getLazyBindingInfo호출 시의 인자 확인! ```c bool ImageLoaderMachO::getLazyBindingInfo(uint32_t& lazyBindingInfoOffset, const uint8_t* lazyInfoStart, const uint8_t* lazyInfoEnd, uint8_t* segIndex, uintptr_t* segOffset, int* ordinal, const char** symbolName, bool* doneAfterBind) { if ( lazyBindingInfoOffset > (lazyInfoEnd-lazyInfoStart) ) return false; uint8_t type = BIND_TYPE_POINTER; uint8_t symboFlags = 0; bool done = false; const uint8_t* p = &lazyInfoStart[lazyBindingInfoOffset]; while ( !done && (p < lazyInfoEnd) ) { uint8_t immediate = *p & BIND_IMMEDIATE_MASK; uint8_t opcode = *p & BIND_OPCODE_MASK; ++p; switch (opcode) { case BIND_OPCODE_DONE: *doneAfterBind = false; return true; break; case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: *ordinal = immediate; break; case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: *ordinal = (int)read_uleb128(p, lazyInfoEnd); break; case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: // the special ordinals are negative numbers if ( immediate == 0 ) *ordinal = 0; else { int8_t signExtended = BIND_OPCODE_MASK | immediate; *ordinal = signExtended; } break; case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: *symbolName = (char*)p; symboFlags = immediate; while (*p != '\0') ++p; ++p; break; case BIND_OPCODE_SET_TYPE_IMM: type = immediate; break; case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: *segIndex = immediate; *segOffset = read_uleb128(p, lazyInfoEnd); break; case BIND_OPCODE_DO_BIND: *doneAfterBind = ((*p & BIND_OPCODE_MASK) == BIND_OPCODE_DONE); lazyBindingInfoOffset += p - &lazyInfoStart[lazyBindingInfoOffset]; return true; break; case BIND_OPCODE_SET_ADDEND_SLEB: case BIND_OPCODE_ADD_ADDR_ULEB: case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: default: return false; } } return false; } ``` 자 우선 lazyInfoStart의 주소부터 시작함 ``` (lldbinit) x/2gx 0x0000000100002020 0x100002020: 0x6c616d5f40111072 0x1872009000636f6c ``` 맨 첫 바이트는 0x72다. ``` uint8_t immediate = *p & BIND_IMMEDIATE_MASK; uint8_t opcode = *p & BIND_OPCODE_MASK; ``` 위 코드에 의해 immediate는 0x2, opcode는 0x70이 된다. 0x70은 BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB이다.[Bind Macro](#bind_macro) 그렇기 때문에 아래의 case문에 들어간다. ```c case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: *segIndex = immediate; *segOffset = read_uleb128(p, lazyInfoEnd); break; ``` read_uleb128? 뭐지 보자. ```c static uint64_t read_uleb128 (const uint8_t ** offset, const uint8_t * end) { uint64_t result = 0; int bit = 0; do { uint64_t b; if (*offset == end) return (uint64_t) -1; b = **offset & 0x7f; if (bit >= 64 || b << bit >> bit != b) result = (uint64_t) -1; else result |= b << bit, bit += 7; } while (*(*offset)++ >= 0x80); return result; } ``` ㅇㅇ 그런가보다. 0x10을 uleb128에서 integer로 변환시키니까 0x10그대로인듯함 segIndex는 0x2, segOffset는 0x10 그 다음은 0x11일 경우니까.. original에 0x1들어감 ```c case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: *ordinal = immediate; break; ``` 그 다음은 0x40일 경우다. ```c case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: *symbolName = (char*)p; symboFlags = immediate; while (*p != '\0') ++p; ++p; break; ``` 오 이름 주소 가져온다. 0x0000000100002024를 symbolName에 넣을듯. symbolFlags에는 0이 들어가고, 포인터를 스트링의 끝으로 지정한다. 그러면 마지막은 \x00이므로 함수가 마무리된다. ```c case BIND_OPCODE_DONE: *doneAfterBind = false; return true; break; ``` 이제 리턴하면 대충 이런값들을 가지고 있을것이다. ``` segIndex = 0x02 segOffset = 0x10 libraryOrdinal = 0x1 symbolName = 0x0000000100002024 '_malloc' doneAfterBind = 0x0 ``` 자 이제 getLazyBindingInfo함수가 마무리되었으니, 다시 doBindFastLazySymbol로 돌아오면 아래의 코드를 실행할 것이다. ```c uintptr_t address = segActualLoadAddress(segIndex) + segOffset; result = this->bindAt(context, address, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL, true); ``` address에는 실제 segIndex에 맞는 주소 0x0000000100001000를 리턴해주고 segOffset만큼 더하여 0x0000000100001010의 값을 얻는다. 아래를 보면 알다시피 이 주소는 lazy\_symbol\_ptr에서 \_malloc\_ptr의 주소이다. ```c __la_symbol_ptr:0000000100001010 ; void *(__cdecl *malloc_ptr)(size_t) __la_symbol_ptr:0000000100001010 _malloc_ptr dq offset __imp__malloc ; DATA XREF: _malloc↑r ``` 이제 이를 바탕으로 bindAt을 함수를 실행한다. ```c uintptr_t ImageLoaderMachOCompressed::bindAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, uint8_t symboFlags, intptr_t addend, long libraryOrdinal, const char* msg, LastLookup* last, bool runResolver) { const ImageLoader* targetImage; uintptr_t symbolAddress; // resolve symbol symbolAddress = this->resolve(context, symbolName, symboFlags, libraryOrdinal, &targetImage, last, runResolver); // do actual update return this->bindLocation(context, addr, symbolAddress, targetImage, type, symbolName, addend, msg); } ``` resolve함수에서는 symbolName에 맞는 함수 주소를 반환하여 symbolAddress에 저장함 지금의 경우 symbolAddress는 0x00007FFF6BA7BD61이다. ```c (lldbinit) x/32i $rax 0x7fff6ba7bd61: 55 push rbp 0x7fff6ba7bd62: 48 89 e5 mov rbp, rsp 0x7fff6ba7bd65: 53 push rbx 0x7fff6ba7bd66: 50 push rax 0x7fff6ba7bd67: 48 89 f8 mov rax, rdi 0x7fff6ba7bd6a: 48 8d 3d 8f d2 7c 36 lea rdi, [rip + 0x367cd28f] ; virtual_default_zone 0x7fff6ba7bd71: 48 89 c6 mov rsi, rax 0x7fff6ba7bd74: e8 1d 00 00 00 call 0x7fff6ba7bd96 ; malloc_zone_malloc 0x7fff6ba7bd79: 48 89 c3 mov rbx, rax 0x7fff6ba7bd7c: 48 85 c0 test rax, rax 0x7fff6ba7bd7f: 75 0b jne 0x7fff6ba7bd8c ; <+43> 0x7fff6ba7bd81: e8 be 21 02 00 call 0x7fff6ba9df44 ; symbol stub for: __error 0x7fff6ba7bd86: c7 00 0c 00 00 00 mov dword ptr [rax], 0xc 0x7fff6ba7bd8c: 48 89 d8 mov rax, rbx 0x7fff6ba7bd8f: 48 83 c4 08 add rsp, 0x8 0x7fff6ba7bd93: 5b pop rbx 0x7fff6ba7bd94: 5d pop rbp 0x7fff6ba7bd95: c3 ret ``` 누가 봐도 malloc로직임. 그 후에 드디어 원하는 함수가 bind 된다!! 끝끝 최종 함수 콜 스택(큼직한것만) ``` call malloc -> dyld_stub_binder @ libdyld.dylib -> _dyld_fast_stub_entry @ libdyld.dylib -> _dyld_func_lookup @ libdyld.dylib -> _dyld_func_lookup @ dyld -> dyld::fastBindLazySymbol @ dyld -> ImageLoaderMachOCompressed::doBindFastLazySymbol -> ImageLoaderMachO::getLazyBindingInfo -> ImageLoaderMachOCompressed::bindAt -> ImageLoaderMachOCompressed::resolve -> ImageLoaderMachOCompressed::bindLocation ``` ## Bonus1 ``` 0x100060050: "__dyld_register_func_for_add_image" 0x100060073: "__dyld_register_func_for_remove_image" 0x100060099: "__dyld_dladdr" 0x1000600a7: "__dyld_dlclose" 0x1000600b6: "__dyld_dlerror" 0x1000600c5: "__dyld_dlopen_internal" 0x1000600dc: "__dyld_dlsym_internal" 0x1000600f2: "__dyld_dlopen_preflight_internal" 0x100060113: "__dyld_dlopen" 0x100060121: "__dyld_dlsym" 0x10006012e: "__dyld_dlopen_preflight" 0x100060146: "__dyld_image_count" 0x100060159: "__dyld_get_image_header" 0x100060171: "__dyld_get_image_vmaddr_slide" 0x10006018f: "__dyld_get_image_name" 0x1000601a5: "__dyld_get_image_slide" 0x1000601bc: "__dyld__NSGetExecutablePath" 0x1000601d8: "__dyld_register_thread_helpers" 0x1000601f7: "__dyld_fork_child" 0x100060209: "__dyld_make_delayed_module_initializer_calls" 0x100060236: "__dyld_set_variable" 0x10006024a: "__dyld_add_image_reexport" 0x100060264: "__dyld_get_all_image_infos" 0x10006027f: "__dyld_find_unwind_sections" 0x10006029b: "__dyld_fast_stub_entry" 0x1000602b2: "__dyld_image_path_containing_address" 0x1000602d7: "__dyld_shared_cache_some_image_overridden" 0x100060301: "__dyld_process_is_restricted" 0x10006031e: "__dyld_dynamic_interpose" 0x100060337: "__dyld_shared_cache_file_path" 0x100060355: "__dyld_get_image_header_containing_address" 0x100060380: "__dyld_is_memory_immutable" 0x10006039b: "__dyld_objc_notify_register" 0x1000603b7: "__dyld_get_shared_cache_uuid" 0x1000603d4: "__dyld_get_shared_cache_range" 0x1000603f2: "__dyld_images_for_addresses" 0x10006040e: "__dyld_register_for_image_loads" 0x10006042e: "__dyld_lookup_and_bind" 0x100060445: "__dyld_lookup_and_bind_with_hint" 0x100060466: "__dyld_lookup_and_bind_fully" 0x100060483: "__dyld_install_handlers" 0x10006049b: "__dyld_link_edit_error" 0x1000604b2: "__dyld_unlink_module" 0x1000604c7: "__dyld_bind_fully_image_containing_address" 0x1000604f2: "__dyld_image_containing_address" 0x100060512: "__dyld_register_binding_handler" 0x100060532: "__dyld_NSNameOfSymbol" 0x100060548: "__dyld_NSAddressOfSymbol" 0x100060561: "__dyld_NSModuleForSymbol" 0x10006057a: "__dyld_NSLookupAndBindSymbol" 0x100060597: "__dyld_NSLookupAndBindSymbolWithHint" 0x1000605bc: "__dyld_NSLookupSymbolInModule" 0x1000605da: "__dyld_NSLookupSymbolInImage" 0x1000605f7: "__dyld_NSMakePrivateModulePublic" 0x100060618: "__dyld_NSIsSymbolNameDefined" 0x100060635: "__dyld_NSIsSymbolNameDefinedWithHint" 0x10006065a: "__dyld_NSIsSymbolNameDefinedInImage" 0x10006067e: "__dyld_NSNameOfModule" 0x100060694: "__dyld_NSLibraryNameForModule" 0x1000606b2: "__dyld_NSAddLibrary" 0x1000606c6: "__dyld_NSAddLibraryWithSearching" 0x1000606e7: "__dyld_NSAddImage" 0x1000606f9: "__dyld_launched_prebound" 0x100060712: "__dyld_all_twolevel_modules_prebound" 0x100060737: "__dyld_call_module_initializers_for_dylib" 0x100060761: "__dyld_NSCreateObjectFileImageFromFile" 0x100060788: "__dyld_NSCreateObjectFileImageFromMemory" 0x1000607b1: "__dyld_NSDestroyObjectFileImage" 0x1000607d1: "__dyld_NSLinkModule" 0x1000607e5: "__dyld_NSSymbolDefinitionCountInObjectFileImage" 0x100060815: "__dyld_NSSymbolDefinitionNameInObjectFileImage" 0x100060844: "__dyld_NSIsSymbolDefinedInObjectFileImage" 0x10006086e: "__dyld_NSSymbolReferenceNameInObjectFileImage" 0x10006089c: "__dyld_NSSymbolReferenceCountInObjectFileImage" 0x1000608cb: "__dyld_NSGetSectionDataInObjectFileImage" ``` ## BIND_Macro ``` BIND_OPCODE_DO_BIND 0x90 BIND_OPCODE_DONE 0x00 BIND_OPCODE_SET_DYLIB_ORDINAL_IMM 0x10 BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB 0x70 BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM 0x40 BIND_OPCODE_SET_TYPE_IMM 0x50 BIND_TYPE_POINTER 0x01 BIND_OPCODE_MASK 0xF0 BIND_IMMEDIATE_MASK 0x0F ```