青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Linux平臺可以用gdb進行反匯編和調試。

Posted on 2008-06-17 21:16 T.S Liu 閱讀(17402) 評論(2)  編輯 收藏 引用 所屬分類: ubuntu
如果在Linux平臺可以用gdb進行反匯編和調試。(轉)

2. 最簡C代碼分析

    為簡化問題,來分析一下最簡的c代碼生成的匯編代碼:
    # vi test1.c
     
    int main()
    {
        return 0;
    }  
   
    編譯該程序,產生二進制文件:
    # gcc test1.c -o test1
    # file test1  
    test1: ELF 32-bit LSB executable 80386 Version 1, dynamically linked, not stripped

    test1是一個ELF格式32位小端(Little Endian)的可執行文件,動態鏈接并且符號表沒有去除。
    這正是Unix/Linux平臺典型的可執行文件格式。
    用mdb反匯編可以觀察生成的匯編代碼:

    # mdb test1
    Loading modules: [ libc.so.1 ]
    > main::dis                       ; 反匯編main函數,mdb的命令一般格式為  <地址>::dis
    main:          pushl   %ebp       ; ebp寄存器內容壓棧,即保存main函數的上級調用函數的棧基地址
    main+1:        movl    %esp,%ebp  ; esp值賦給ebp,設置main函數的棧基址
    main+3:          subl    $8,%esp
    main+6:          andl    $0xf0,%esp
    main+9:          movl    $0,%eax
    main+0xe:        subl    %eax,%esp
    main+0x10:     movl    $0,%eax    ; 設置函數返回值0
    main+0x15:     leave              ; 將ebp值賦給esp,pop先前棧內的上級函數棧的基地址給ebp,恢復原棧基址
    main+0x16:     ret                ; main函數返回,回到上級調用
    >

    注:這里得到的匯編語言語法格式與Intel的手冊有很大不同,Unix/Linux采用AT&T匯編格式作為匯編語言的語法格式
         如果想了解AT&T匯編可以參考文章:Linux AT&T 匯編語言開發指南

    問題:誰調用了 main函數?
    
     在C語言的層面來看,main函數是一個程序的起始入口點,而實際上,ELF可執行文件的入口點并不是main而是_start。
     mdb也可以反匯編_start:
      
    > _start::dis                       ;從_start 的地址開始反匯編
    _start:              pushl   $0
    _start+2:            pushl   $0
    _start+4:            movl    %esp,%ebp
    _start+6:            pushl   %edx
    _start+7:            movl    $0x80504b0,%eax
    _start+0xc:          testl   %eax,%eax
    _start+0xe:          je      +0xf            <_start+0x1d>
    _start+0x10:         pushl   $0x80504b0
    _start+0x15:         call    -0x75           <atexit>
    _start+0x1a:         addl    $4,%esp
    _start+0x1d:         movl    $0x8060710,%eax
    _start+0x22:         testl   %eax,%eax
    _start+0x24:         je      +7              <_start+0x2b>
    _start+0x26:         call    -0x86           <atexit>
    _start+0x2b:         pushl   $0x80506cd
    _start+0x30:         call    -0x90           <atexit>
    _start+0x35:         movl    +8(%ebp),%eax
    _start+0x38:         leal    +0x10(%ebp,%eax,4),%edx
    _start+0x3c:         movl    %edx,0x8060804
    _start+0x42:         andl    $0xf0,%esp
    _start+0x45:         subl    $4,%esp
    _start+0x48:         pushl   %edx
    _start+0x49:         leal    +0xc(%ebp),%edx
    _start+0x4c:         pushl   %edx
    _start+0x4d:         pushl   %eax
    _start+0x4e:         call    +0x152          <_init>
    _start+0x53:         call    -0xa3           <__fpstart>
    _start+0x58:        call    +0xfb        <main>              ;在這里調用了main函數
    _start+0x5d:         addl    $0xc,%esp
    _start+0x60:         pushl   %eax
    _start+0x61:         call    -0xa1           <exit>
    _start+0x66:         pushl   $0
    _start+0x68:         movl    $1,%eax
    _start+0x6d:         lcall   $7,$0
    _start+0x74:         hlt
    >

    問題:為什么用EAX寄存器保存函數返回值?
    實際上IA32并沒有規定用哪個寄存器來保存返回值。但如果反匯編Solaris/Linux的二進制文件,就會發現,都用EAX保存函數返回值。
    這不是偶然現象,是操作系統的ABI(Application Binary Interface)來決定的。
    Solaris/Linux操作系統的ABI就是Sytem V ABI。


    概念:SFP (Stack Frame Pointer) 棧框架指針 

    正確理解SFP必須了解:
        IA32 的棧的概念
        CPU 中32位寄存器ESP/EBP的作用
        PUSH/POP 指令是如何影響棧的
        CALL/RET/LEAVE 等指令是如何影響棧的

    如我們所知:
    1)IA32的棧是用來存放臨時數據,而且是LIFO,即后進先出的。棧的增長方向是從高地址向低地址增長,按字節為單位編址。
    2) EBP是棧基址的指針,永遠指向棧底(高地址),ESP是棧指針,永遠指向棧頂(低地址)。
    3) PUSH一個long型數據時,以字節為單位將數據壓入棧,從高到低按字節依次將數據存入ESP-1、ESP-2、ESP-3、ESP-4的地址單元。
    4) POP一個long型數據,過程與PUSH相反,依次將ESP-4、ESP-3、ESP-2、ESP-1從棧內彈出,放入一個32位寄存器。
    5) CALL指令用來調用一個函數或過程,此時,下一條指令地址會被壓入堆棧,以備返回時能恢復執行下條指令。
    6) RET指令用來從一個函數或過程返回,之前CALL保存的下條指令地址會從棧內彈出到EIP寄存器中,程序轉到CALL之前下條指令處執行
    7) ENTER是建立當前函數的棧框架,即相當于以下兩條指令:
        pushl   %ebp
        movl    %esp,%ebp
    8) LEAVE是釋放當前函數或者過程的棧框架,即相當于以下兩條指令:
        movl ebp esp
        popl  ebp

    如果反匯編一個函數,很多時候會在函數進入和返回處,發現有類似如下形式的匯編語句:
       
        pushl   %ebp            ; ebp寄存器內容壓棧,即保存main函數的上級調用函數的棧基地址
        movl    %esp,%ebp       ; esp值賦給ebp,設置 main函數的棧基址
        ...........             ; 以上兩條指令相當于 enter 0,0
        ...........
        leave                   ; 將ebp值賦給esp,pop先前棧內的上級函數棧的基地址給ebp,恢復原棧基址
        ret                     ; main函數返回,回到上級調用

    這些語句就是用來創建和釋放一個函數或者過程的棧框架的。
    原來編譯器會自動在函數入口和出口處插入創建和釋放棧框架的語句。
    函數被調用時:
    1) EIP/EBP成為新函數棧的邊界
    函數被調用時,返回時的EIP首先被壓入堆棧;創建棧框架時,上級函數棧的EBP被壓入堆棧,與EIP一道行成新函數棧框架的邊界
    2) EBP成為棧框架指針SFP,用來指示新函數棧的邊界
    棧框架建立后,EBP指向的棧的內容就是上一級函數棧的EBP,可以想象,通過EBP就可以把層層調用函數的棧都回朔遍歷一遍,調試器就是利用這個特性實現 backtrace功能的
    3) ESP總是作為棧指針指向棧頂,用來分配棧空間
    棧分配空間給函數局部變量時的語句通常就是給ESP減去一個常數值,例如,分配一個整型數據就是 ESP-4
    4) 函數的參數傳遞和局部變量訪問可以通過SFP即EBP來實現
    由于棧框架指針永遠指向當前函數的棧基地址,參數和局部變量訪問通常為如下形式:
        +8+xx(%ebp)         ; 函數入口參數的的訪問
        -xx(%ebp)           ; 函數局部變量訪問
           
    假如函數A調用函數B,函數B調用函數C ,則函數棧框架及調用關系如下圖所示:
   	+-------------------------+----> 高地址
| EIP (上級函數返回地址) |
+-------------------------+
+--> | EBP (上級函數的EBP) | --+ <------當前函數A的EBP (即SFP框架指針)
| +-------------------------+ +-->偏移量A
| | Local Variables | |
| | .......... | --+ <------ESP指向函數A新分配的局部變量,局部變量可以通過A的ebp-偏移量A訪問
| f +-------------------------+
| r | Arg n(函數B的第n個參數) |
| a +-------------------------+
| m | Arg .(函數B的第.個參數) |
| e +-------------------------+
| | Arg 1(函數B的第1個參數) |
| o +-------------------------+
| f | Arg 0(函數B的第0個參數) | --+ <------ B函數的參數可以由B的ebp+偏移量B訪問
| +-------------------------+ +--> 偏移量B
| A | EIP (A函數的返回地址) | |
| +-------------------------+ --+
+--- | EBP (A函數的EBP) |<--+ <------ 當前函數B的EBP (即SFP框架指針)
+-------------------------+ |
| Local Variables | |
| .......... | | <------ ESP指向函數B新分配的局部變量
+-------------------------+ |
| Arg n(函數C的第n個參數) | |
+-------------------------+ |
| Arg .(函數C的第.個參數) | |
+-------------------------+ +--> frame of B
| Arg 1(函數C的第1個參數) | |
+-------------------------+ |
| Arg 0(函數C的第0個參數) | |
+-------------------------+ |
| EIP (B函數的返回地址) | |
+-------------------------+ |
+--> | EBP (B函數的EBP) | --+ <------ 當前函數C的EBP (即SFP框架指針)
| +-------------------------+
| | Local Variables |
| | .......... | <------ ESP指向函數C新分配的局部變量
| +-------------------------+----> 低地址
frame of C

圖 1-1
      
    再分析test1反匯編結果中剩余部分語句的含義:
       
    # mdb test1
    Loading modules: [ libc.so.1 ]
    > main::dis                        ; 反匯編main函數
    main:          pushl   %ebp                           
    main+1:        movl    %esp,%ebp        ; 創建Stack Frame(棧框架)
    main+3:       subl    $8,%esp       ; 通過ESP-8來分配8字節堆棧空間
    main+6:       andl    $0xf0,%esp    ; 使棧地址16字節對齊
    main+9:       movl    $0,%eax       ; 無意義
    main+0xe:     subl    %eax,%esp     ; 無意義
    main+0x10:     movl    $0,%eax          ; 設置main函數返回值
    main+0x15:     leave                    ; 撤銷Stack Frame(棧框架)
    main+0x16:     ret                      ; main 函數返回
    >

    以下兩句似乎是沒有意義的,果真是這樣嗎?
        movl    $0,%eax
        subl     %eax,%esp
      
    用gcc的O2級優化來重新編譯test1.c:
    # gcc -O2 test1.c -o test1
    # mdb test1
    > main::dis
    main:         pushl   %ebp
    main+1:       movl    %esp,%ebp
    main+3:       subl    $8,%esp
    main+6:       andl    $0xf0,%esp
    main+9:       xorl    %eax,%eax      ; 設置main返回值,使用xorl異或指令來使eax為0
    main+0xb:     leave
    main+0xc:     ret
    >
    新的反匯編結果比最初的結果要簡潔一些,果然之前被認為無用的語句被優化掉了,進一步驗證了之前的猜測。
    提示:編譯器產生的某些語句可能在程序實際語義上沒有用處,可以用優化選項去掉這些語句。

    問題:為什么用xorl來設置eax的值?
    注意到優化后的代碼中,eax返回值的設置由 movl $0,%eax 變為 xorl %eax,%eax ,這是因為IA32指令中,xorl比movl有更高的運行速度。

    概念:Stack aligned 棧對齊
    那么,以下語句到底是和作用呢?
        subl    $8,%esp
       andl    $0xf0,%esp     ; 通過andl使低4位為0,保證棧地址16字節對齊
      
    表面來看,這條語句最直接的后果是使ESP的地址后4位為0,即16字節對齊,那么為什么這么做呢?
    原來,IA32 系列CPU的一些指令分別在4、8、16字節對齊時會有更快的運行速度,因此gcc編譯器為提高生成代碼在IA32上的運行速度,默認對產生的代碼進行16字節對齊

        andl $0xf0,%esp 的意義很明顯,那么 subl $8,%esp 呢,是必須的嗎?
    這里假設在進入main函數之前,棧是16字節對齊的話,那么,進入main函數后,EIP和EBP被壓入堆棧后,棧地址最末4位二進制位必定是1000,esp -8則恰好使后4位地址二進制位為0000。看來,這也是為保證棧16字節對齊的。

    如果查一下gcc的手冊,就會發現關于棧對齊的參數設置:
    -mpreferred-stack-boundary=n    ; 希望棧按照2的n次的字節邊界對齊, n的取值范圍是2-12

    默認情況下,n是等于4的,也就是說,默認情況下,gcc是16字節對齊,以適應IA32大多數指令的要求。

    讓我們利用-mpreferred-stack-boundary=2來去除棧對齊指令:
     
    # gcc -mpreferred-stack-boundary=2 test1.c -o test1
      
    > main::dis
    main:       pushl   %ebp
    main+1:     movl    %esp,%ebp
    main+3:     movl    $0,%eax
    main+8:     leave
    main+9:     ret
    >

    可以看到,棧對齊指令沒有了,因為,IA32的棧本身就是4字節對齊的,不需要用額外指令進行對齊。
    那么,棧框架指針SFP是不是必須的呢?
    # gcc -mpreferred-stack-boundary=2 -fomit-frame-pointer test1.c -o test
    > main::dis
    main:       movl    $0,%eax
    main+5:     ret
    >

    由此可知,-fomit-frame-pointer 可以去除SFP。
      
    問題:去除SFP后有什么缺點呢?
      
    1)增加調式難度
        由于SFP在調試器backtrace的指令中被使用到,因此沒有SFP該調試指令就無法使用。
    2)降低匯編代碼可讀性
        函數參數和局部變量的訪問,在沒有ebp的情況下,都只能通過+xx(esp)的方式訪問,而很難區分兩種方式,降低了程序的可讀性。
      
    問題:去除SFP有什么優點呢?
      
    1)節省棧空間
    2)減少建立和撤銷棧框架的指令后,簡化了代碼
    3)使ebp空閑出來,使之作為通用寄存器使用,增加通用寄存器的數量
    4)以上3點使得程序運行速度更快

    概念:Calling Convention  調用約定和 ABI (Application Binary Interface) 應用程序二進制接口
        
        函數如何找到它的參數?
        函數如何返回結果?
        函數在哪里存放局部變量?
        那一個硬件寄存器是起始空間?
        那一個硬件寄存器必須預先保留?

    Calling Convention  調用約定對以上問題作出了規定。Calling Convention也是ABI的一部分。
    因此,遵守相同ABI規范的操作系統,使其相互間實現二進制代碼的互操作成為了可能。
    例如:由于Solaris、Linux都遵守System V的ABI,Solaris 10就提供了直接運行Linux二進制程序的功能。
    詳見文章:
關注: Solaris 10的10大新變化
            
3. 小結
    本文通過最簡的C程序,引入以下概念:
        SFP 棧框架指針
        Stack aligned 棧對齊
        Calling Convention  調用約定 和 ABI (Application Binary Interface) 應用程序二進制接口
    今后,將通過進一步的實驗,來深入了解這些概念。通過掌握這些概念,使在匯編級調試程序產生的core dump、掌握C語言高級調試技巧成為了可能。

Feedback

# re: Linux平臺可以用gdb進行反匯編和調試。  回復  更多評論   

2012-04-02 20:57 by 胡海生
樓主牛B

# re: Linux平臺可以用gdb進行反匯編和調試。  回復  更多評論   

2016-05-18 09:08 by
找LINUX下的硬件解密和反匯編工程師
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲人成在线观看网站高清| 欧美**字幕| 日韩一级精品视频在线观看| 欧美午夜精品理论片a级按摩| 久久精品日韩欧美| 一区二区久久久久| 亚洲精品日产精品乱码不卡| 欧美1区免费| 蜜桃av噜噜一区| 久久噜噜亚洲综合| 久久天堂精品| 91久久香蕉国产日韩欧美9色| 国内一区二区三区| 国内揄拍国内精品久久| 国产在线精品一区二区中文| 国产日韩精品在线播放| 国产日韩成人精品| 国内外成人在线| 国产在线拍偷自揄拍精品| 欧美日韩国产综合网 | 这里只有精品丝袜| 91久久综合亚洲鲁鲁五月天| 欧美成熟视频| 在线精品国精品国产尤物884a| 久久亚洲美女| 国产日本欧美一区二区三区在线 | 国产精品亚洲精品| 欧美日韩系列| 国产精品美女主播| 国产一区二区日韩| 亚洲国产精品第一区二区| 亚洲日产国产精品| 午夜精品久久久久久久男人的天堂| 午夜在线精品偷拍| 亚洲男女毛片无遮挡| 久久精品国产77777蜜臀| 久久综合九色九九| 亚洲精品欧洲精品| 欧美一区二区黄色| 欧美电影在线观看| 国产精品va在线| 国产欧美日韩精品在线| 国内精品久久久久影院色| 亚洲日本理论电影| 久久本道综合色狠狠五月| 亚洲高清色综合| 亚洲视频在线看| 亚洲欧洲99久久| 欧美精品激情在线| 黑人巨大精品欧美一区二区| 亚洲少妇自拍| 老司机午夜免费精品视频| 夜夜夜精品看看| 麻豆视频一区二区| 国产精品成人一区二区三区夜夜夜 | av成人天堂| 久久九九国产精品怡红院| 亚洲精品你懂的| 亚洲欧美日韩综合aⅴ视频| 麻豆久久婷婷| 伊人精品在线| 久久性天堂网| 午夜精品久久99蜜桃的功能介绍| 欧美日韩国产成人精品| 国产精品欧美久久| 亚洲精品一区二区在线| 亚洲欧美日韩综合一区| 另类尿喷潮videofree| 欧美黄在线观看| 亚洲国产日韩欧美在线图片| 久久精品亚洲精品| 亚洲二区在线视频| 亚洲一级在线| 国产精品久久久久久久久久免费看 | 久久久国产午夜精品| 国产精品亚洲综合| 精品动漫3d一区二区三区免费版| 欧美亚洲在线| 亚洲在线免费视频| 国产精品入口尤物| 性欧美8khd高清极品| 亚洲午夜精品国产| 国产精品一二三四| 亚洲精品自在久久| 亚洲欧洲日本国产| 欧美日韩精品二区| 亚洲直播在线一区| 中文一区二区| 亚洲一区二区影院| 国产乱人伦精品一区二区| 亚洲免费在线电影| 午夜伦欧美伦电影理论片| 国产欧美日韩激情| 免费成人在线观看视频| 免费精品视频| 中文国产一区| 亚洲欧美日本在线| 国产原创一区二区| 欧美jizzhd精品欧美巨大免费| 久热re这里精品视频在线6| 亚洲国产成人精品久久久国产成人一区 | 99国产精品久久久久久久久久| 欧美日韩中文在线观看| 亚洲男女自偷自拍图片另类| 午夜国产一区| 亚洲激情校园春色| 男女av一区三区二区色多| 欧美韩日高清| 欧美一级片久久久久久久| 久久gogo国模裸体人体| 最新亚洲视频| 亚洲一区欧美| 亚洲精品美女在线观看播放| 亚洲午夜精品| 日韩亚洲欧美中文三级| 欧美网站在线观看| 亚洲国产成人久久综合一区| 黄色国产精品| 久久久五月婷婷| 午夜精品福利视频| 亚洲国产精品视频| 日韩西西人体444www| 欧美日韩一区二区在线观看| 久久久999国产| 亚洲欧美另类在线| 亚洲人成小说网站色在线| 欧美激情欧美狂野欧美精品| 99re6热在线精品视频播放速度 | 国产精品久久久久久久久搜平片| 欧美顶级少妇做爰| 蜜桃av噜噜一区| 欧美日韩国产成人| 国产视频精品免费播放| 一区精品在线| 一本色道久久综合亚洲精品高清| 一本色道久久88精品综合| 欧美一级二级三级蜜桃| 欧美福利视频在线观看| 夜夜嗨一区二区| 免费观看日韩| 国产午夜精品全部视频在线播放| 亚洲人午夜精品| 久久久亚洲欧洲日产国码αv| 欧美h视频在线| 国产精品自在在线| 在线国产欧美| 久久久亚洲人| 亚洲欧美日韩在线不卡| 欧美精品一区二区三区四区| 伊人男人综合视频网| 欧美一级视频精品观看| 一区二区三区视频观看| 欧美精品一区二区三区视频| 136国产福利精品导航| 久久一区二区三区四区| 国产精品视频| 久久av老司机精品网站导航| 夜夜嗨av一区二区三区| 免费人成网站在线观看欧美高清| 国产一区二区毛片| 久久尤物电影视频在线观看| 欧美在线观看网站| 国产亚洲一区在线| 久久嫩草精品久久久精品| 久久国产手机看片| 亚洲综合精品四区| 久久精品一区二区三区四区| 激情久久婷婷| 99热免费精品| 一二美女精品欧洲| 另类国产ts人妖高潮视频| 久热这里只精品99re8久| 国产精品日本| av成人激情| 在线不卡亚洲| 久久影视三级福利片| 免费91麻豆精品国产自产在线观看| 国产欧美日韩综合一区在线观看 | 欧美日韩国产高清| 欧美成人三级在线| 亚洲毛片网站| 香蕉久久国产| 久久精品视频在线看| 欧美国产综合一区二区| 猛干欧美女孩| 国语自产在线不卡| 一区二区三区不卡视频在线观看 | 欧美日韩成人在线观看| 久久久久综合网| 亚洲精品一二| 在线欧美小视频| 蜜桃av噜噜一区二区三区| 久久久久国产精品一区三寸| 国产精品国产三级国产普通话99| 亚洲精品乱码久久久久久日本蜜臀 | 99精品视频免费观看| 国产精品毛片在线| 亚洲国产日韩在线一区模特| 免费视频一区| 欧美一区二区三区视频免费播放 |