亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > ASM > 正文

反匯編分析objc函數樞紐objc_msgSend

2019-11-09 14:20:09
字體:
來源:轉載
供稿:網友

轉自 點擊打開鏈接 作者bbqz007

反匯編分析objc函數樞紐objc_msgSend

在分析objc_msgSend之前,先來搞清楚另一個問題。

函數是什么?可能會答 void foo(void) {} 像這樣就是一個函數?;蛘吆瘮蛋ê瘮翟秃秃瘮刀x,是一段執行某樣功能的機器代碼。

調用函數時必須要準備兩個要素,函數原型和函數入口地址。

函數原型的作用是什么?答聲明了函數調用的方式。不夠具體。函數原型是函數調用方和函數定義之間的關于參數傳遞和結果返回的協議約定。這個協議分別作用在函數入口兩邊的代碼,一邊是調用方在調用處協議的構建,另一邊是函數定義對協議的訪問解釋。傳統地就是調用棧。原型是一個協議,協議是可以傳遞的。所以在函數被調用處,到函數的入口地址之間,是可以做任何處理,只要協議不被破壞。

而objc在函數調用和函數入口之間加入了動態綁定的處理,這個處理就是msgSend。

大家都知道這個原型id(*IMP)(id, char*, …),而這個卻只是用于傳遞的協議,并非函數的真正的原型。對于后面的省略號,傳統地是va_list訪問,但是實際上省略號即第三個參數開始可以是任何其實傳參方式。至于從第三個參數開始之后的協議是怎么約定的,在objc函數調用處和函數定義是必須明確清楚的。然而在之兩者之間的中繼路由過程中,只需要知道前兩個參數的約定,就是這個原型id()(id, char*,…),所以msgSend也是這個原型。

舉例-(id)foo:(int)i;

id foo(id, char*, int) —> id msgSend(id, char*, …) —> (id()(id, char*, …))foo

作為objc中函數調用的樞紐,我們現在就來看一下它的反匯編樣貌:

復制代碼
libobjc.A.dylib`objc_msgSend:->  0x107b68800 <+0>:   testq  %rdi, %rdi    0x107b68803 <+3>:   jle    0x107b68850               ; <+80>    // except the bad pointer to obj    0x107b68805 <+5>:   movq   (%rdi), %r11    0x107b68808 <+8>:   movq   %rsi, %r10    0x107b6880b <+11>:  andl   0x18(%r11), %r10d         ;            // (int32)(%rsi) &= (int32)0x18(%rdi) , (%rsi) <<= 4, (int64)(%rsi) += (int64)0x10(%rdi).    0x107b6880f <+15>:  shlq   $0x4, %r10    0x107b68813 <+19>:  addq   0x10(%r11), %r10    0x107b68817 <+23>:  cmpq   (%r10), %rsi    0x107b6881a <+26>:  jne    0x107b68820               ; <+32>    0x107b6881c <+28>:  jmpq   *0x8(%r10)                 ;            // jmp to imp    0x107b68820 <+32>:  cmpq   $0x1, (%r10)    0x107b68824 <+36>:  jbe    0x107b68833               ; <+51>    0x107b68826 <+38>:  addq   $0x10, %r10    0x107b6882a <+42>:  cmpq   (%r10), %rsi    0x107b6882d <+45>:  jne    0x107b68820               ; <+32>    0x107b6882f <+47>:  jmpq   *0x8(%r10)                                0x107b68833 <+51>:  jb     0x107b68871               ; <+113>    0x107b68835 <+53>:  movq   0x8(%r10), %r10    0x107b68839 <+57>:  jmp    0x107b68845               ; <+69>    0x107b6883b <+59>:  cmpq   $0x1, (%r10)    0x107b6883f <+63>:  jbe    0x107b6884e               ; <+78>    0x107b68841 <+65>:  addq   $0x10, %r10    0x107b68845 <+69>:  cmpq   (%r10), %rsi    0x107b68848 <+72>:  jne    0x107b6883b               ; <+59>    0x107b6884a <+74>:  jmpq   *0x8(%r10)                                0x107b6884e <+78>:  jmp    0x107b68871               ; <+113>    0x107b68850 <+80>:  je     0x107b68866               ; <+102>    // a neg pointer is a objc debug tagged pointer classes.    0x107b68852 <+82>:  leaq   0x348df7(%rip), %r11      ; objc_debug_taggedpointer_classes    0x107b68859 <+89>:  movq   %rdi, %r10    0x107b6885c <+92>:  shrq   $0x3c, %r10    0x107b68860 <+96>:  movq   (%r11,%r10,8), %r11    0x107b68864 <+100>: jmp    0x107b68808               ; <+8>        // jump back and deal with this debug obj.    0x107b68866 <+102>: xorl   %eax, %eax                 ;            // deal with a nil obj.    0x107b68868 <+104>: xorl   %edx, %edx    0x107b6886a <+106>: xorps  %xmm0, %xmm0    0x107b6886d <+109>: xorps  %xmm1, %xmm1    0x107b68870 <+112>: retq                                ;            // bad return clause.    0x107b68871 <+113>: pushq  %rbp    0x107b68872 <+114>: movq   %rsp, %rbp    0x107b68875 <+117>: subq   $0x88, %rsp                 ;            // and total push size 0x38, %rsp is 0xc0 bytes far away from %rbp when next call in soon    0x107b6887c <+124>: movdqa %xmm0, -0x80(%rbp)    0x107b68881 <+129>: pushq  %rax    0x107b68882 <+130>: movdqa %xmm1, -0x70(%rbp)    0x107b68887 <+135>: pushq  %rdi    0x107b68888 <+136>: movdqa %xmm2, -0x60(%rbp)    0x107b6888d <+141>: pushq  %rsi    0x107b6888e <+142>: movdqa %xmm3, -0x50(%rbp)    0x107b68893 <+147>: pushq  %rdx    0x107b68894 <+148>: movdqa %xmm4, -0x40(%rbp)    0x107b68899 <+153>: pushq  %rcx    0x107b6889a <+154>: movdqa %xmm5, -0x30(%rbp)    0x107b6889f <+159>: pushq  %r8    0x107b688a1 <+161>: movdqa %xmm6, -0x20(%rbp)    0x107b688a6 <+166>: pushq  %r9    0x107b688a8 <+168>: movdqa %xmm7, -0x10(%rbp)    0x107b688ad <+173>: movq   %rdi, %rdi    0x107b688b0 <+176>: movq   %rsi, %rsi    0x107b688b3 <+179>: movq   %r11, %rdx                 ;            // isa member of obj of %rdi    0x107b688b6 <+182>: callq  0x107b59c57               ; _class_lookupMethodAndLoadCache3    0x107b688bb <+187>: movq   %rax, %r11    0x107b688be <+190>: movdqa -0x80(%rbp), %xmm0    0x107b688c3 <+195>: popq   %r9    0x107b688c5 <+197>: movdqa -0x70(%rbp), %xmm1    0x107b688ca <+202>: popq   %r8    0x107b688cc <+204>: movdqa -0x60(%rbp), %xmm2    0x107b688d1 <+209>: popq   %rcx    0x107b688d2 <+210>: movdqa -0x50(%rbp), %xmm3    0x107b688d7 <+215>: popq   %rdx    0x107b688d8 <+216>: movdqa -0x40(%rbp), %xmm4    0x107b688dd <+221>: popq   %rsi    0x107b688de <+222>: movdqa -0x30(%rbp), %xmm5    0x107b688e3 <+227>: popq   %rdi    0x107b688e4 <+228>: movdqa -0x20(%rbp), %xmm6    0x107b688e9 <+233>: popq   %rax    0x107b688ea <+234>: movdqa -0x10(%rbp), %xmm7    0x107b688ef <+239>: leave      0x107b688f0 <+240>: cmpq   %r11, %r11    0x107b688f3 <+243>: jmpq   *%r11                     ;            // the real imp address related to set    0x107b688f6 <+246>: nopw   %cs:(%rax,%rax)復制代碼

 

代碼中分兩部分,第一部分是取出正確的receiver,請看我的反c偽代碼:

 

從代碼中可以看到,0指針被過濾直接返回,負數指針被轉換成正確的指針。負數指針?第一眼你可能會和我一樣認為這是一個訪問到了內核空間的指針,因為在32位體系系統中,一般地高2G地址是內核地址,最高位為1。但是在64位下并非就代表訪問到了內核地址,現在的x64處理器有效尋址不是64位有效尋址,而是48位,而且高16位必須與第48位一致,其余的看作無效地址。這個負數地址,其實是一類被定義為tagged的指針,作用類似于erlang的原子量atom。

接下來是另一部分,找到正確的地址入口,然后跳過去,調用協議原封不動。請看我的反c偽代碼:

從代碼中可以看到SEL自始至終也只不過是一個調用名稱,SEL和IMP以key-value方式存放在各種查找表中。不用多說,先從常用cache中查找,沒有就從類描述中找出真實入口地址。在cache查找中有這么3點邏輯,

1.不命中,而且有效地址,下一個key-value

2.不命中,并且無效地址,中止在cache的查找

3.不命中,并且為1,必須還是首次遇到1,然后cache forward,繼續在cache中查找。

 

最后是我手工對msgSend原代碼中各處調用宏后的代碼

復制代碼
/******************************************************************** * * id objc_msgSend(id self, SEL    _cmd,...); * ********************************************************************/        .data    .align 3    .globl _objc_debug_taggedpointer_classes_objc_debug_taggedpointer_classes:    .fill 16, 8, 0    ENTRY    _objc_msgSend    MESSENGER_START    GetIsaCheckNil    NORMAL        // r11 = self->isa, or return zero    CacheLookup NORMAL        // calls IMP on success    GetIsaSupport    NORMAL// cache miss: go search the method listsLCacheMiss:    // isa still in r11    MethodTableLookup %a1, %a2    // r11 = IMP    cmp    %r11, %r11        // set eq (nonstret) for forwarding    jmp    *%r11            // goto *imp    END_ENTRY    _objc_msgSend/******************************************************************** * * id objc_msgSend(id self, SEL    _cmd,...); Expand * ********************************************************************/    .data    .align 3    .globl _objc_debug_taggedpointer_classes_objc_debug_taggedpointer_classes:    .fill 16, 8, 0    // ENTRY    _objc_msgSend.text    .globl    _objc_msgSend    .align    6, 0x90_objc_msgSend:    .cfi_startPRoc    // MESSENGER_START4:    .section __DATA,__objc_msg_break    .quad 4b    .quad ENTER    .text    testq    %a1, %a1    jle    LNilOrTagged_f    // MSB tagged pointer looks negative    movq    (%a1), %r11    // r11 = isaLGetIsaDone:        movq    %a2, %r10        // r10 = _cmd    andl    24(%r11), %r10d        // r10 = _cmd & class->cache.mask    shlq    $$4, %r10        // r10 = offset = (_cmd & mask)<<4    addq    16(%r11), %r10        // r10 = class->cache.buckets + offset    cmpq    (%r10), %a2        // if (bucket->sel != _cmd)    jne     1f            //     scan more    // CacheHit must always be preceded by a not-taken `jne` instruction    CacheHit $0            // call or return imp1:    // loop    cmpq    $$1, (%r10)    jbe    3f            // if (bucket->sel <= 1) wrap or miss    addq    $$16, %r10        // bucket++2:        cmpq    (%r10), %a2        // if (bucket->sel != _cmd)    jne     1b            //     scan more    // CacheHit must always be preceded by a not-taken `jne` instruction    CacheHit $0            // call or return imp3:    // wrap or miss    jb    LCacheMiss_f        // if (bucket->sel < 1) cache miss    // wrap    movq    8(%r10), %r10        // bucket->imp is really first bucket    jmp     2f    // Clone scanning loop to miss instead of hang when cache is corrupt.    // The slow path may detect any corruption and halt later.1:    // loop    cmpq    $$1, (%r10)    jbe    3f            // if (bucket->sel <= 1) wrap or miss    addq    $$16, %r10        // bucket++2:        cmpq    (%r10), %a2        // if (bucket->sel != _cmd)    jne     1b            //     scan more    // CacheHit must always be preceded by a not-taken `jne` instruction    CacheHit $0            // call or return imp3:    // double wrap or miss    jmp    LCacheMiss_f    .align 3LNilOrTagged:    jz    LNil_f        // flags set by NilOrTaggedTest    // tagged        leaq    _objc_debug_taggedpointer_classes(%rip), %r11    movq    %a1, %r10    shrq    $$60, %r10    movq    (%r11, %r10, 8), %r11    // read isa from table    jmp    LGetIsaDone_bLNil:    // nil    xorl    %eax, %eax    xorl    %edx, %edx    xorps    %xmm0, %xmm0    xorps    %xmm1, %xmm14:    .section __DATA,__objc_msg_break    .quad 4b    .quad NIL_EXIT    .text    ret    // cache miss: go search the method listsLCacheMiss:    // isa still in r114:    .section __DATA,__objc_msg_break    .quad 4b    .quad SLOW_EXIT    .text        SaveRegisters    // _class_lookupMethodAndLoadCache3(receiver, selector, class)    movq    %a1, %a1    movq    %a2, %a2    movq    %r11, %a3    call    __class_lookupMethodAndLoadCache3    // IMP is now in %rax    movq    %rax, %r11    RestoreRegisters    cmp    %r11, %r11        // set eq (nonstret) for forwarding    jmp    *%r11            // goto *imp        // END_ENTRY    _objc_msgSend    .cfi_endprocLExit_objc_msgSend:復制代碼


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品自产拍在线观看中文| 久久噜噜噜精品国产亚洲综合| 粗暴蹂躏中文一区二区三区| 91a在线视频| 久久精品亚洲一区| 91精品久久久久久久久久另类| 日韩精品视频观看| 久久久精品免费视频| 日韩精品电影网| 成人黄色片网站| 久久6免费高清热精品| 日韩av在线影视| 国产精品一区专区欧美日韩| 欧美激情欧美激情在线五月| 亚洲精品www久久久| 波霸ol色综合久久| 5566日本婷婷色中文字幕97| 成人在线视频福利| 综合激情国产一区| 国产精品久久婷婷六月丁香| 欧美性videos高清精品| 亚洲深夜福利在线| 欧美高清自拍一区| 欧美亚洲伦理www| 九九综合九九综合| 久久九九精品99国产精品| 精品国产一区av| 日韩毛片中文字幕| 国产激情视频一区| 国产一区二区香蕉| 精品国产电影一区| 92版电视剧仙鹤神针在线观看| 亚洲图片欧洲图片av| 6080yy精品一区二区三区| 国产69精品99久久久久久宅男| 亚洲片在线观看| 日本精品免费一区二区三区| 亚洲精品99久久久久中文字幕| 91精品久久久久久久久中文字幕| 91网在线免费观看| 欧美国产激情18| 国产精品福利网站| 这里只有精品视频| 国产精品久久久久久久久久| 国产精品xxxxx| 亚洲日韩欧美视频一区| 91精品久久久久久综合乱菊| 亚洲最新在线视频| 亚洲精品videossex少妇| 国产精品综合久久久| 国产精品久久综合av爱欲tv| 欧美激情成人在线视频| 亚洲综合av影视| 57pao成人国产永久免费| 国内免费久久久久久久久久久| 日韩视频精品在线| 欧美性xxxxhd| xvideos成人免费中文版| 九九久久久久99精品| 久久成年人免费电影| 久久久久www| 韩国欧美亚洲国产| 亚洲成人精品视频| 成人午夜激情网| 欧美电影免费观看高清完整| 国产精品视频大全| 亚洲欧美在线播放| 日韩专区在线播放| 久久精品视频中文字幕| 国产91色在线| 亚洲人成电影在线观看天堂色| 一区二区三区黄色| 国产在线播放91| 国产日韩亚洲欧美| 91精品国产综合久久香蕉的用户体验| 久久电影一区二区| 精品久久久久久亚洲精品| 日韩欧美在线观看视频| 最近中文字幕2019免费| 久久精品国产91精品亚洲| 日韩电影免费在线观看中文字幕| 久久综合免费视频| 亚洲欧洲高清在线| 欧美激情欧美激情| 一区二区av在线| 国产91精品久久久久| 国产精品aaa| 日本三级久久久| 中文国产亚洲喷潮| 欧美日韩免费观看中文| 亚洲综合色激情五月| 欧美激情一区二区久久久| 亚洲综合自拍一区| 欧美一级bbbbb性bbbb喷潮片| 国产精品电影一区| 欧美高清视频一区二区| 成人免费网视频| 亚洲国产成人精品久久久国产成人一区| 97在线视频精品| 不卡中文字幕av| 亚洲精品久久久久久久久久久久| 992tv成人免费影院| 久久久999成人| 国产专区精品视频| 91精品国产91久久久| 欧美专区国产专区| 国产精品老女人视频| 亚洲欧美激情精品一区二区| 亚洲欧美中文日韩在线| 在线观看日韩av| 丝袜一区二区三区| 日韩一区二区三区国产| 久久精品国产清自在天天线| 久久99久久久久久久噜噜| 亚洲精品自拍视频| 成人做爽爽免费视频| 中文字幕在线看视频国产欧美| 欧美一级淫片aaaaaaa视频| 日日噜噜噜夜夜爽亚洲精品| 九九热99久久久国产盗摄| 成人综合国产精品| 欧美一级大片在线免费观看| 日韩美女主播视频| 亚洲激情在线观看| 懂色av一区二区三区| 亚洲精品成a人在线观看| 91在线免费观看网站| 中文字幕久热精品在线视频| 欧美亚洲日本网站| 97久久精品国产| 成人动漫网站在线观看| 久久久久北条麻妃免费看| 亚洲免费高清视频| 国产欧洲精品视频| 久久精品国产99国产精品澳门| 久久久视频精品| 国产亚洲成精品久久| 日本精品一区二区三区在线| 国产精品精品视频一区二区三区| 欧美日本亚洲视频| 96pao国产成视频永久免费| 日韩精品在线视频观看| 国产亚洲欧美日韩一区二区| 欧美精品成人在线| 97在线看免费观看视频在线观看| 亚洲美女av黄| 亚洲精品资源美女情侣酒店| 国产成人精品亚洲精品| 欧美亚洲国产成人精品| 亚洲性夜色噜噜噜7777| 亚洲天堂网在线观看| 精品久久国产精品| 日韩三级成人av网| www.日韩.com| 久久久精品国产| 日韩av在线导航| 欧美亚洲在线观看| 欧美激情精品在线| 国产日产久久高清欧美一区| 亚洲国内精品视频| 黑人欧美xxxx| 亚洲国产精品女人久久久| 国产69精品久久久久99| 97国产精品免费视频|