Advanced debugging skills

前言

iOS-Debug-Hacks involves the dynamic debugging, static analysis and decompile of third-party libraries.git

《Advanced Apple Debugging & Reverse Engineering》值得一看github

函數

談談對函數的理解bootstrap

一個函數調用包括將數據(以參數和返回值的形式)和控制從代碼的一部分轉移到另外一部分。在函數調用過程當中,數據傳遞、局部變量的分配和釋放是經過棧來實現的,而爲單個函數調用分配的那部分棧稱爲棧幀(Stack Frame)。函數

棧幀

  • 使用 bt 命令打印出當前線程的回溯信息
(lldb) bt
* thread #1, stop reason = trace
  * frame #0: 0x000000010e533fcf dyld`_dyld_debugger_notification + 1
    frame #1: 0x000000010e533795 dyld`gdb_image_notifier(dyld_image_mode, unsigned int, dyld_image_info const*) + 111
    frame #2: 0x000000010a36b269 dyld_sim`notifyGDB(dyld_image_states, unsigned int, dyld_image_info const*) + 40
    frame #3: 0x000000010a364142 dyld_sim`dyld::notifyBatchPartial(dyld_image_states, bool, char const* (*)(dyld_image_states, unsigned int, dyld_image_info const*), bool, bool) + 814
    frame #4: 0x000000010a36d107 dyld_sim`ImageLoader::link(ImageLoader::LinkContext const&, bool, bool, bool, ImageLoader::RPathChain const&, char const*) + 101
    frame #5: 0x000000010a364548 dyld_sim`dyld::link(ImageLoader*, bool, bool, ImageLoader::RPathChain const&, unsigned int) + 161
    frame #6: 0x000000010a365a5c dyld_sim`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 3840
    frame #7: 0x000000010a3613d4 dyld_sim`start_sim + 136
    frame #8: 0x000000010e52bded dyld`dyld::useSimulatorDyld(int, macho_header const*, char const*, int, char const**, char const**, char const**, unsigned long*, unsigned long*) + 2200
    frame #9: 0x000000010e5297a3 dyld`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 436
    frame #10: 0x000000010e5253d4 dyld`dyldbootstrap::start(macho_header const*, int, char const**, long, macho_header const*, unsigned long*) + 453
    frame #11: 0x000000010e5251d2 dyld`_dyld_start + 54

bt 命令是得益於棧幀才能實現的,棧幀能夠當作是函數執行的上下文,其中保存了函數的返回地址和局部變量,咱們知道堆是從低地址向高地址延伸的,而棧是從高地址向低地址延伸的。
每一個函數的每次調用,都會分配給它一個獨立的棧幀,rbp 寄存器指向當前棧幀的底部(高地址),被稱做幀指針,rsp 寄存器指向棧幀的頂部(低地址),被稱做棧指針spa

一次函數調用的過程

  • 調用函數將參數壓棧,若是沒有參數,或者能夠直接經過寄存器完成傳參,則這步能夠沒有。
  • 將執行完函數調用的下一條指令壓棧,其實就是返回地址。
  • 跳轉到被調函數的起始地址開始執行。
  • 被調函數將調用函數棧幀起始地址壓棧,棧幀起始地址存放在 %rbp 寄存器中。
  • 將 %rsp 寄存器賦值給 %rbp 寄存器,使得 %rbp 寄存器指向被調函數棧幀的起始地址。
  • 將被調用者保存寄存器壓棧,這步是可選的。

上述 2 和 3 步驟其實就是 call 指令的任務,而 4 和 5 經過彙編指令表示以下:線程

0x1054e09c0 <+0>:  pushq  %rbp //第四步
    0x1054e09c1 <+1>:  movq   %rsp, %rbp //第五步

Call 指令

call function

參數中的 function 是 TEXT 段的程序,
call 指令其實能夠拆解成兩步,debug

  • 第一步是將執行完 call 指令以後的地址壓棧,這個地址實際上是執行完調用函數體以後的返回地址;
  • 第二步是將指令執行跳轉到 function。

call 指令其實等價於下面的命令:3d

push next_instruction
jmp  function

傳參和返回值

在 OSX 中,最多能夠有 6 個整型(整數和指針)經過寄存器傳遞,這 6 個寄存器分別是 rdi, rsi, rdx, rcx, r8 和 r9(順序和參數的順序保持一致),指針

  • 若是一個函數超過 6 個參數該怎麼辦呢?

此時就須要藉助棧了,能夠將剩下的參數逆序壓入棧中。OSX 容許將 8 個浮點數經過浮點數寄存器 xmm0-xmm7 進行傳遞。code

  • 函數的返回值,

使用 rax 寄存器做爲整數返回值,浮點數返回值則使用 xmm0-xmm1 寄存器

輸出寄存器的值

當寄存器的值是字符串的時候,LLDB 能夠經過

po (char *) $rsi

命令輸出寄存器對應的字符串值,

不然直接使用 po $rsi,只會按照整數格式輸出 rsi 寄存器的值。

OC 的消息轉發

圖片描述
消息轉發流程越日後,處理消息所付出的代價也就越大。因此若非必要,應當儘早結束消息轉發流程。若是消息轉發的流程中都沒有處理未知消息,最終會調用 doesNotRecognizeSelector: 拋出異常,表示對象沒法正確識別此 SEL。

補充

@dynamic

使用 @dynamic 告訴編譯器不作處理,而後 Getter 和 Setter 方法是在運行時動態建立

打斷點

  • br s -n

br s 是 breakpoint set 的意思,-n 表示根據函數名稱來下斷點,做用和符號斷點同樣,

  • 使用 br s -F
  • 直接使用 b -[TCWebViewKit open]

只不過 b 命令是 _regexp-break 的縮寫,是用正則匹配的方式下斷點。

-給在指定的內存地址設置斷點,

下斷點的命令爲 br s -a 0x000000010940b24e,這種方式能夠用在知道 block 內存地址的時候,給 block 設置斷點。

相關文章
相關標籤/搜索