日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Mach-O的动态链接相关知识

發布時間:2025/3/15 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Mach-O的动态链接相关知识 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

0x00 摘要

通過分析Mach-O的動態鏈接過程,加深對Mach-O文件結構的理解。對Mach-O文件格式的簡單的分析看這里這里。

0x01 Mach-O Lazy Bind

Mach-O文件的通過dyld加載的時候并沒有確定每一個函數的具體地址在哪里,而是在真正調用該函數的時候通過過程連接表(procedure linkage table),后面簡稱PLT,來進行一次lazybind

結合Mach-O文件的分析與代碼的調試簡單的分析一下,只能算是管中窺豹了。

源碼很簡單。

1 2 3 4 5 6 7 8 #include <stdio.h> int main(int argc, const char * argv[]) { // insert code here... printf("Hello, World!\n"); printf("2Hello, World!\n"); return 0; }

分別在兩個printf函數處下斷點,啟動程序。

1.1 第一次調用prinf

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 lazy_bind`main: 0x100000f10 <+0>: pushq %rbp 0x100000f11 <+1>: movq %rsp, %rbp 0x100000f14 <+4>: subq $0x20, %rsp 0x100000f18 <+8>: leaq 0x57(%rip), %rax ; "Hello, World!\n" 0x100000f1f <+15>: movl $0x0, -0x4(%rbp) 0x100000f26 <+22>: movl %edi, -0x8(%rbp) 0x100000f29 <+25>: movq %rsi, -0x10(%rbp) 0x100000f2d <+29>: movq %rax, %rdi 0x100000f30 <+32>: movb $0x0, %al -> 0x100000f32 <+34>: callq 0x100000f56 ; symbol stub for: printf 0x100000f37 <+39>: leaq 0x47(%rip), %rdi ; "2Hello, World!\n" 0x100000f3e <+46>: movl %eax, -0x14(%rbp) 0x100000f41 <+49>: movb $0x0, %al 0x100000f43 <+51>: callq 0x100000f56 ; symbol stub for: printf 0x100000f48 <+56>: xorl %ecx, %ecx 0x100000f4a <+58>: movl %eax, -0x18(%rbp) 0x100000f4d <+61>: movl %ecx, %eax 0x100000f4f <+63>: addq $0x20, %rsp 0x100000f53 <+67>: popq %rbp 0x100000f54 <+68>: retq

在0x100000f52 <+34>行處通過callq 0x100000f64來調用printf。

執行callq指令之后代碼跳轉到這里:

1 2 lazy_bind`printf: -> 0x100000f56 <+0>: jmpq *0xb4(%rip) ; (void *)0x0000000100000f6c

1.2 __Data,__la_symbol_ptr 獲取函數地址

這里的jmpq要跳轉到0x0000000100000f6c這個地址是從__Data,__la_symbol_ptr中的Lazy Symbol Pointers中獲取到的。(怎么來的不是很清楚?)希望通過stub來調用printf函數。

通過命令行查看0x100001010處的地址獲得了同樣的值。

1 2 3 (lldb) x 0x100001010 0x100001010: 6c 0f 00 00 01 00 00 00 00 00 00 00 00 00 00 00 l............... 0x100001020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

1.3 通過__stub__helper進行lazybind

在Mach-O中每一個Symbol Stub可能有以下兩種行為其中之一:

  • 跳轉到函數的指令,執行函數體
  • 通過動態鏈接器查找函數的Symbol(符號),然后執行函數。

通過工具查看__stubs的Section數據,發現只有一個函數就是_printf。

這里的Data其實就是上面看到的jmpq代碼。執行之后代碼跳轉到了這樣的代碼片段。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 -> 0x100000f6c: pushq $0x0 0x100000f71: jmp 0x100000f5c 0x100000f76: gs ;這里往下都沒有!!!! 0x100000f78: insb %dx, %es:(%rdi) 0x100000f79: insb %dx, %es:(%rdi) 0x100000f7a: outsl (%rsi), %dx 0x100000f7b: subb $0x20, %al 0x100000f7d: pushq %rdi 0x100000f7e: outsl (%rsi), %dx 0x100000f7f: jb 0x100000fed 0x100000f81: andl %ecx, %fs:(%rdx) 0x100000f84: addb %dh, (%rdx) 0x100000f86: gs 0x100000f88: insb %dx, %es:(%rdi) 0x100000f89: insb %dx, %es:(%rdi) 0x100000f8a: outsl (%rsi), %dx

這里就是通過_stub_helper來調用dyld_stubbinder函數來計算printf函數的真實地址。通過下面的\_TEXT,__stub_helper具體信息可以看出,jmpq 0x100000f5c,就是在壓入參數0x0(函數的link的時候給的編號)之后跳轉到Section的起始處,調用了binder。

binder是一塊匯編代碼。這里就不做解釋了。作用就是計算具體的函數地址,并調用printf。

1.4 第二次調用printf函數

這個釋放斷點,程序將在調用第二個printf函數的地方停下來。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 lazy_bind`main: 0x100000f10 <+0>: pushq %rbp 0x100000f11 <+1>: movq %rsp, %rbp 0x100000f14 <+4>: subq $0x20, %rsp 0x100000f18 <+8>: leaq 0x57(%rip), %rax ; "Hello, World!\n" 0x100000f1f <+15>: movl $0x0, -0x4(%rbp) 0x100000f26 <+22>: movl %edi, -0x8(%rbp) 0x100000f29 <+25>: movq %rsi, -0x10(%rbp) 0x100000f2d <+29>: movq %rax, %rdi 0x100000f30 <+32>: movb $0x0, %al 0x100000f32 <+34>: callq 0x100000f56 ; symbol stub for: printf 0x100000f37 <+39>: leaq 0x47(%rip), %rdi ; "2Hello, World!\n" 0x100000f3e <+46>: movl %eax, -0x14(%rbp) 0x100000f41 <+49>: movb $0x0, %al -> 0x100000f43 <+51>: callq 0x100000f56 ; symbol stub for: printf 0x100000f48 <+56>: xorl %ecx, %ecx 0x100000f4a <+58>: movl %eax, -0x18(%rbp) 0x100000f4d <+61>: movl %ecx, %eax 0x100000f4f <+63>: addq $0x20, %rsp 0x100000f53 <+67>: popq %rbp 0x100000f54 <+68>: retq

執行指令之后發現和第一次調用printf已經不一樣了。

1 2 lazy_bind`printf: -> 0x100000f56 <+0>: jmpq *0xb4(%rip) ; (void *)0x00007fff96b1815c: printf

通過指令再一次查看0x100001010處的內存值。

1 2 3 (lldb) x 0x100001010 0x100001010: 5c 81 b1 96 ff 7f 00 00 00 00 00 00 00 00 00 00 \............... 0x100001020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

也就是說 __Data,__la_symbol_ptr中指向printf地址的值已經發生了變化,指向了printf的指令。

1.5 小結

證明了,延遲綁定只會在第一次調用的時候發生。整個流程與linux中的PLT與GOT在實現邏輯上基本是相同的,只是具體的代碼實現不一樣。

0x02 相關LoadCommand

上面只是通過調試簡單的了解了動態鏈接的表現,要理解動態鏈接還需對幾個數據結構有所了解。

2.1 LC_SYMTAB

LC_SYMTAB這個LoadCommand主要提供了兩個信息

  • Symbol Table的偏移量與Symbol Table中元素的個數
  • String Table的偏移量與String Table的長度

2.1.1 Symbol Table

在計算機科學中,符號表是一種用于語言翻譯器(例如編譯器和解釋器)中的數據結構。在符號表中,程序源代碼中的每個標識符都和它的聲明或使用信息綁定在一起,比如其數據類型、作用域以及內存地址。

–wiki

簡單的理解就是Symbol Table里面包含了所有會被調用的函數的信息,無論是已經bind的還是沒有bind的函數。

2.2.2 String Table

這個很好理解,在符號處理時所有會用到的字符串放在了這里。和__TEXT,__cstring不同。

2.2 LC_DYSYMTAB

LC_DYSYMTAB的數據結構,如圖所示。這一個LoadCommand與動態鏈接相關的就是紅框標出的兩個字段,標示了需要動態符號表的偏移量與符號個數。

動態符號表的數據結構非常的簡單,是一個32bit的索引的數組。通過索引可以在Symbol Table中尋找到對應的函數信息。

0x03 小結

通過分析兩次printf的調用流程,加深對Mach-O結構以及動態鏈接的流程理解,為進一步理解dyld的工作原理,源碼閱讀提供了知識的儲備。

通過和Linux的PTL與GOT比較可以更容易理解邏輯。

整個流程是如何通過代碼實現的還需要進一步的分析與研究。

0x04 參考

1.Dynamic Linking: ELF vs. Mach-O

http://timetobleed.com/dynamic-linking-elf-vs-mach-o/

2.Dynamic symbol table duel: ELF vs Mach-O, round 2

http://timetobleed.com/dynamic-symbol-table-duel-elf-vs-mach-o-round-2/


原文地址:http://turingh.github.io/2016/03/10/Mach-O%E7%9A%84%E5%8A%A8%E6%80%81%E9%93%BE%E6%8E%A5/

總結

以上是生活随笔為你收集整理的Mach-O的动态链接相关知识的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。