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

loop_in_codes

低調(diào)做技術(shù)__歡迎移步我的獨(dú)立博客 codemaro.com 微博 kevinlynx

C/C++中手動(dòng)獲取調(diào)用堆棧

當(dāng)我們的程序core掉之后,如果能獲取到core時(shí)的函數(shù)調(diào)用堆棧將非常有利于定位問(wèn)題。在Windows下可以使用SEH機(jī)制;在Linux下通過(guò)gdb使用coredump文件即可。

但有時(shí)候由于某些錯(cuò)誤導(dǎo)致堆棧被破壞,發(fā)生拿不到調(diào)用堆棧的情況。

一些基礎(chǔ)預(yù)備知識(shí)本文不再詳述,可以參考以下文章:

需要知道的信息:

  • 函數(shù)調(diào)用對(duì)應(yīng)的call指令本質(zhì)上是先壓入下一條指令的地址到堆棧,然后跳轉(zhuǎn)到目標(biāo)函數(shù)地址
  • 函數(shù)返回指令ret則是從堆棧取出一個(gè)地址,然后跳轉(zhuǎn)到該地址
  • EBP寄存器始終指向當(dāng)前執(zhí)行函數(shù)相關(guān)信息(局部變量)所在棧中的位置,ESP則始終指向棧頂
  • 每一個(gè)函數(shù)入口都會(huì)保存調(diào)用者的EBP值,在出口處都會(huì)重設(shè)EBP值,從而實(shí)現(xiàn)函數(shù)調(diào)用的現(xiàn)場(chǎng)保存及現(xiàn)場(chǎng)恢復(fù)
  • 64位機(jī)器增加了不少寄存器,從而使得函數(shù)調(diào)用的參數(shù)大部分時(shí)候可以通過(guò)寄存器傳遞;同時(shí)寄存器名字發(fā)生改變,例如EBP變?yōu)镽BP

在函數(shù)調(diào)用中堆棧的情況可用下圖說(shuō)明:

將代碼對(duì)應(yīng)起來(lái):

    void g() {
        int *p = 0;
        long a = 0x1234;
        printf("%p %x\n", &a, a);
        printf("%p %x\n", &p, p);
        f();
        *p = 1;
    }
    void b(int argc, char **argv) {
        printf("%p %p\n", &argc, &argv);
        g();
    }
    int main(int argc, char **argv) {
        b(argc, argv);
        return 0;
    }

在函數(shù)g()中斷點(diǎn),看看堆棧中的內(nèi)容(64位機(jī)器):

(gdb) p $rbp
$2 = (void *) 0x7fffffffe370
(gdb) p &p
$3 = (int **) 0x7fffffffe368
(gdb) p $rsp
$4 = (void *) 0x7fffffffe360
(gdb) x/8ag $rbp-16
0x7fffffffe360: 0x1234  0x0
0x7fffffffe370: 0x7fffffffe390  0x400631 <b(int, char**)+43>
0x7fffffffe380: 0x7fffffffe498  0x1a561cbc0
0x7fffffffe390: 0x7fffffffe3b0  0x40064f <main(int, char**)+27>

對(duì)應(yīng)的堆棧圖:

可以看看例子中0x400631 <b(int, char**)+43>0x40064f <main(int, char**)+27>中的代碼:

(gdb) disassemble 0x400631
...
0x0000000000400627 <b(int, char**)+33>: callq  0x400468 <printf@plt>
0x000000000040062c <b(int, char**)+38>: callq  0x4005ae <g()>
0x0000000000400631 <b(int, char**)+43>: leaveq                           # call的下一條指令
...
(gdb) disassemble 0x40064f
... 
0x000000000040063f <main(int, char**)+11>:      mov    %rsi,-0x10(%rbp)
0x0000000000400643 <main(int, char**)+15>:      mov    -0x10(%rbp),%rsi
0x0000000000400647 <main(int, char**)+19>:      mov    -0x4(%rbp),%edi
0x000000000040064a <main(int, char**)+22>:      callq  0x400606 <b(int, char**)>
0x000000000040064f <main(int, char**)+27>:      mov    $0x0,%eax         # call的下一條指令
...

順帶一提,每個(gè)函數(shù)入口和出口,對(duì)應(yīng)的設(shè)置RBP代碼為:

(gdb) disassemble g
...
0x00000000004005ae <g()+0>:     push   %rbp               # 保存調(diào)用者的RBP到堆棧
0x00000000004005af <g()+1>:     mov    %rsp,%rbp          # 設(shè)置自己的RBP
...
0x0000000000400603 <g()+85>:    leaveq                    # 等同于:movq %rbp, %rsp
                                                          #         popq %rbp
0x0000000000400604 <g()+86>:    retq                      

由以上可見(jiàn),通過(guò)當(dāng)前的RSP或RBP就可以找到調(diào)用堆棧中所有函數(shù)的RBP;找到了RBP就可以找到函數(shù)地址。因?yàn)椋魏螘r(shí)候的RBP指向的堆棧位置就是上一個(gè)函數(shù)的RBP;而任何時(shí)候RBP所在堆棧中的前一個(gè)位置就是函數(shù)返回地址。

由此我們可以自己構(gòu)建一個(gè)導(dǎo)致gdb無(wú)法取得調(diào)用堆棧的例子:

    void f() {
        long *p = 0;
        p = (long*) (&p + 1); // 取得g()的RBP
        *p = 0;  // 破壞g()的RBP
    }
    void g() {
        int *p = 0;
        long a = 0x1234;
        printf("%p %x\n", &a, a);
        printf("%p %x\n", &p, p);
        f();
        *p = 1; // 寫(xiě)0地址導(dǎo)致一次core
    }
    void b(int argc, char **argv) {
        printf("%p %p\n", &argc, &argv);
        g();
    }
    int main(int argc, char **argv) {
        b(argc, argv);
        return 0;
    }

使用gdb運(yùn)行該程序:

Program received signal SIGSEGV, Segmentation fault.
g () at ebp.c:37
37          *p = 1;
(gdb) bt
Cannot access memory at address 0x8
(gdb) p $rbp
$1 = (void *) 0x0

bt無(wú)法獲取堆棧,在函數(shù)g()中RBP被改寫(xiě)為0,gdb從0偏移一個(gè)地址長(zhǎng)度即0x8,嘗試從0x8內(nèi)存位置獲取函數(shù)地址,然后提示Cannot access memory at address 0x8

RBP出現(xiàn)了問(wèn)題,我們就可以通過(guò)RSP來(lái)手動(dòng)獲取調(diào)用堆棧。因?yàn)镽SP是不會(huì)被破壞的,要通過(guò)RSP獲取調(diào)用堆棧則需要偏移一些局部變量所占的空間:

(gdb) p $rsp
$2 = (void *) 0x7fffffffe360
(gdb) x/8ag $rsp+16             # g()中局部變量占16字節(jié)
0x7fffffffe370: 0x7fffffffe390  0x400631 <b(int, char**)+43>
0x7fffffffe380: 0x7fffffffe498  0x1a561cbc0
0x7fffffffe390: 0x7fffffffe3b0  0x40064f <main(int, char**)+27>
0x7fffffffe3a0: 0x7fffffffe498  0x100000000

基于以上就可以手工找到調(diào)用堆棧:

g()
0x400631 <b(int, char**)+43>
0x40064f <main(int, char**)+27>

上面的例子本質(zhì)上也是破壞堆棧,并且僅僅破壞了保存了的RBP。在實(shí)際情況中,堆棧可能會(huì)被破壞得更多,則可能導(dǎo)致手動(dòng)定位也較困難。

堆棧被破壞還可能導(dǎo)致更多的問(wèn)題,例如覆蓋了函數(shù)返回地址,則會(huì)導(dǎo)致RIP錯(cuò)誤;例如堆棧的不平衡。導(dǎo)致堆棧被破壞的原因也有很多,例如局部數(shù)組越界;delete/free棧上對(duì)象等

omit-frame-pointer

使用RBP獲取調(diào)用堆棧相對(duì)比較容易。但現(xiàn)在編譯器都可以設(shè)置不使用RBP(gcc使用-fomit-frame-pointer,msvc使用/Oy),對(duì)于函數(shù)而言不設(shè)置其RBP意味著可以節(jié)省若干條指令。在函數(shù)內(nèi)部則完全使用RSP的偏移來(lái)定位局部變量,包括嵌套作用域里的局部變量,即使程序?qū)嶋H運(yùn)行時(shí)不會(huì)進(jìn)入這個(gè)作用域。

例如:

    void f2() {
        int a = 0x1234;
        if (a > 0) {
            int b = 0xff;
            b = a;
        }
    }

gcc中使用-fomit-frame-pointer生成的代碼為:

(gdb) disassemble f2
Dump of assembler code for function f2:
0x00000000004004a5 <f2+0>:      movl   $0x1234,-0x8(%rsp)    # int a = 0x1234
0x00000000004004ad <f2+8>:      cmpl   $0x0,-0x8(%rsp)       
0x00000000004004b2 <f2+13>:     jle    0x4004c4 <f2+31>      
0x00000000004004b4 <f2+15>:     movl   $0xff,-0x4(%rsp)      # int b = 0xff
0x00000000004004bc <f2+23>:     mov    -0x8(%rsp),%eax
0x00000000004004c0 <f2+27>:     mov    %eax,-0x4(%rsp)
0x00000000004004c4 <f2+31>:     retq

可以發(fā)現(xiàn)f2()沒(méi)有操作RBP之類(lèi)的指令了。

posted on 2014-09-02 22:14 Kevin Lynx 閱讀(5975) 評(píng)論(3)  編輯 收藏 引用 所屬分類(lèi): c/c++

評(píng)論

# re: C/C++中手動(dòng)獲取調(diào)用堆棧[未登錄](méi) 2014-09-06 13:42 春秋十二月

寫(xiě)的不錯(cuò),使用幀指針確有一定的風(fēng)險(xiǎn)。由于64位擴(kuò)展了通用寄存器的個(gè)數(shù),參數(shù)和局部變量可以用寄存器存儲(chǔ)傳遞,因此許多實(shí)現(xiàn)短小的函數(shù)就沒(méi)有棧幀或不用幀指針了。  回復(fù)  更多評(píng)論   

# re: C/C++中手動(dòng)獲取調(diào)用堆棧 2014-09-29 14:26 liyou

64位機(jī)器的寄存器知識(shí)去哪獲取。  回復(fù)  更多評(píng)論   

# re: C/C++中手動(dòng)獲取調(diào)用堆棧 2014-10-02 20:13 Kevin Lynx

@liyou
google 'x86_64 registers' 例如http://hackeradam17.com/2014/03/18/an-introduction-to-x86_64-assembly-language/  回復(fù)  更多評(píng)論   

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            欧美特黄一级| 亚洲精品一区中文| 亚洲精品久久久一区二区三区| 国产欧美一区二区精品秋霞影院| 欧美日韩一区在线观看| 欧美日韩另类视频| 国产精品一区免费在线观看| 国产日产欧美一区| 亚洲盗摄视频| 99精品国产高清一区二区| 亚洲午夜久久久久久久久电影院| 亚洲一区国产视频| 久久久青草婷婷精品综合日韩| 麻豆精品在线视频| 亚洲精品久久嫩草网站秘色| 欧美激情第8页| 一区二区高清在线| 久久都是精品| 欧美精品自拍| 国产日韩在线不卡| 99成人在线| 久久国产精品网站| 亚洲国产欧美久久| 亚洲影院色在线观看免费| 久久综合久久综合久久综合| 欧美午夜精品理论片a级大开眼界 欧美午夜精品理论片a级按摩 | 亚洲高清视频的网址| 夜夜嗨一区二区| 久久久久久久久岛国免费| 亚洲国产mv| 欧美淫片网站| 欧美日韩午夜| 亚洲激情偷拍| 久久美女性网| 中文国产亚洲喷潮| 欧美阿v一级看视频| 国产亚洲精品久久飘花| 亚洲丝袜av一区| 亚洲韩国一区二区三区| 午夜伦欧美伦电影理论片| 欧美精品日韩www.p站| 伊人久久男人天堂| 久久精品视频在线看| 一片黄亚洲嫩模| 欧美人妖另类| 快播亚洲色图| 国产午夜久久久久| 亚洲欧美激情在线视频| 亚洲激情网站| 欧美99在线视频观看| 永久免费视频成人| 久久久久久久综合日本| 亚洲中午字幕| 国产精品视频99| 亚洲欧美一级二级三级| 艳妇臀荡乳欲伦亚洲一区| 欧美日韩不卡一区| 亚洲图片欧美日产| 一本色道久久99精品综合| 欧美视频免费| 亚洲免费伊人电影在线观看av| 日韩视频在线一区二区| 欧美日韩午夜精品| 亚洲在线视频一区| 亚洲免费在线视频| 国产视频一区在线| 久久人91精品久久久久久不卡| 羞羞答答国产精品www一本| 国产一区二区三区免费不卡 | 久久女同精品一区二区| 激情欧美日韩| 欧美国产亚洲精品久久久8v| 免费看精品久久片| 一区二区福利| 亚洲在线1234| 狠狠久久五月精品中文字幕| 久久综合色8888| 欧美成人dvd在线视频| 一区二区三区蜜桃网| 宅男精品视频| 国产一级揄自揄精品视频| 能在线观看的日韩av| 欧美激情一区二区三区蜜桃视频 | 国产一区二区三区直播精品电影 | 欧美日韩一区二区三| 午夜亚洲一区| 久久精品中文字幕免费mv| 亚洲精品国精品久久99热一| 亚洲最新合集| 韩国自拍一区| 亚洲精品一区二区三区av| 国产欧美在线观看| 亚洲国产视频一区| 国产精品一国产精品k频道56| 久久亚洲精品一区二区| 日韩视频永久免费| 国产亚洲成精品久久| 欧美国产在线视频| 国产精品久久一级| 蘑菇福利视频一区播放| 欧美系列亚洲系列| 欧美高潮视频| 国产精品自拍视频| 亚洲美女网站| 亚洲电影在线看| 亚洲午夜极品| 99re66热这里只有精品3直播| 午夜在线观看免费一区| 一区二区精品在线| 免费成人高清视频| 久久亚洲电影| 国产日产欧产精品推荐色 | 久久乐国产精品| 欧美三级午夜理伦三级中视频| 久久亚洲高清| 国产日韩精品一区观看| 日韩一区二区精品| 99视频在线精品国自产拍免费观看 | 免费在线播放第一区高清av| 欧美午夜女人视频在线| 亚洲激情黄色| 亚洲国产一区在线| 久久狠狠亚洲综合| 久久国产精品久久久久久久久久 | 亚洲人精品午夜| 久久精品人人爽| 久久精品av麻豆的观看方式 | 国产日韩欧美一区在线| 妖精成人www高清在线观看| 亚洲人成艺术| 欧美激情一区二区三区在线视频| 久久久久国产精品一区二区| 国产精品久久久一区麻豆最新章节| 亚洲国产美女久久久久| 亚洲国产视频a| 欧美高清一区| 亚洲精品一区二区三区99| 日韩一级精品视频在线观看| 欧美成人福利视频| 亚洲黄色大片| 9i看片成人免费高清| 欧美日韩精品免费观看视频完整| 亚洲欧洲免费视频| 亚洲无人区一区| 国产精品日本欧美一区二区三区| 亚洲天堂偷拍| 久久深夜福利| 亚洲黄色三级| 欧美日韩在线直播| 亚洲欧美电影在线观看| 久久久久久电影| 欧美日韩精品免费| 一区二区欧美精品| 欧美夜福利tv在线| 国模私拍一区二区三区| 久久久国产一区二区三区| 亚洲第一区在线| 日韩午夜精品| 国产欧美日韩| 玖玖综合伊人| 一本色道久久综合精品竹菊 | 伊人狠狠色丁香综合尤物| 免费观看国产成人| 99国产精品久久久久久久久久| 欧美一区二区成人6969| 亚洲第一色中文字幕| 欧美久久视频| 久久成人羞羞网站| 亚洲精品视频在线播放| 久久成人国产精品| 日韩一级裸体免费视频| 国产欧美在线| 欧美日本免费一区二区三区| 欧美亚洲日本一区| 亚洲日韩欧美视频一区| 久久动漫亚洲| 夜夜嗨av一区二区三区| 狠狠久久五月精品中文字幕| 欧美日韩国产bt| 久久久之久亚州精品露出| 99精品99| 亚洲第一视频| 久久免费偷拍视频| 亚洲欧美在线网| 亚洲人成亚洲人成在线观看图片| 国产精品免费一区豆花| 欧美大色视频| 久久久精品一区| 亚洲欧美国产毛片在线| 亚洲精品乱码久久久久久蜜桃麻豆 | 亚洲卡通欧美制服中文| 国产色产综合色产在线视频| 欧美国产欧美亚洲国产日韩mv天天看完整 | 久久久91精品国产一区二区三区| 亚洲国产一成人久久精品| 久久人人爽国产| 久久se精品一区精品二区| 亚洲亚洲精品三区日韩精品在线视频| 在线播放日韩欧美| 黄色一区二区在线观看|