2015-05-21 63 views
6

我在閱讀Smashing the Stack for Fun and Profit(特別是,這篇文章是指「緩衝區溢出」部分)。該文章是爲 32位計算機編寫的,但是我正在使用64位,因此我在 中考慮了我的示例。一個特殊的例子是導致一些我不能解釋的問題。 example3.c具有覆蓋返回地址的功能,跳過主函數中的指令 。這裏是我的代碼:堆棧保護和使用GCC粉碎

gcc example3.c -o example3 

注意,默認情況下,gcc編譯器似乎執行一些堆棧 保護,如地址:

#include <stdio.h> 

void function(int a, int b, int c) 
{ 
    char buf1[5]; 
    char buf2[10]; 
    int *retptr; 

    retptr = (void*)(buf2 + 40); 
    (*retptr) += 8; 
} 

int main(void) 
{ 
    int x; 

    x = 0; 
    function(1,2,3); 
    x = 1; 
    printf("%d\n", x); 
    return 0; 
} 

我用gcc v4.8.2用以下命令編譯這個程序空間佈局隨機化和堆棧金絲雀。我在計算指針值時考慮了這些安全措施。這裏 是 gcc example3.c -S -fverbose-asm -o stack-protection.s產生相應的組件:

.file "example3.c" 
# GNU C (Ubuntu 4.8.2-19ubuntu1) version 4.8.2 (x86_64-linux-gnu) 
# compiled by GNU C version 4.8.2, GMP version 5.1.3, MPFR version 3.1.2-p3, MPC version 1.0.1 
# GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 
# options passed: -imultiarch x86_64-linux-gnu example3.c -mtune=generic 
# -march=x86-64 -auxbase-strip verbose-stack-pro.s -fverbose-asm 
# -fstack-protector -Wformat -Wformat-security 
# options enabled: -faggressive-loop-optimizations 
# -fasynchronous-unwind-tables -fauto-inc-dec -fbranch-count-reg -fcommon 
# -fdelete-null-pointer-checks -fdwarf2-cfi-asm -fearly-inlining 
# -feliminate-unused-debug-types -ffunction-cse -fgcse-lm -fgnu-runtime 
# -fident -finline-atomics -fira-hoist-pressure -fira-share-save-slots 
# -fira-share-spill-slots -fivopts -fkeep-static-consts 
# -fleading-underscore -fmath-errno -fmerge-debug-strings 
# -fmove-loop-invariants -fpeephole -fprefetch-loop-arrays 
# -freg-struct-return -fsched-critical-path-heuristic 
# -fsched-dep-count-heuristic -fsched-group-heuristic -fsched-interblock 
# -fsched-last-insn-heuristic -fsched-rank-heuristic -fsched-spec 
# -fsched-spec-insn-heuristic -fsched-stalled-insns-dep -fshow-column 
# -fsigned-zeros -fsplit-ivs-in-unroller -fstack-protector 
# -fstrict-volatile-bitfields -fsync-libcalls -ftrapping-math 
# -ftree-coalesce-vars -ftree-cselim -ftree-forwprop -ftree-loop-if-convert 
# -ftree-loop-im -ftree-loop-ivcanon -ftree-loop-optimize 
# -ftree-parallelize-loops= -ftree-phiprop -ftree-pta -ftree-reassoc 
# -ftree-scev-cprop -ftree-slp-vectorize -ftree-vect-loop-version 
# -funit-at-a-time -funwind-tables -fverbose-asm -fzero-initialized-in-bss 
# -m128bit-long-double -m64 -m80387 -maccumulate-outgoing-args 
# -malign-stringops -mfancy-math-387 -mfp-ret-in-387 -mfxsr -mglibc 
# -mieee-fp -mlong-double-80 -mmmx -mno-sse4 -mpush-args -mred-zone -msse 
# -msse2 -mtls-direct-seg-refs 

    .text 
    .globl function 
    .type function, @function 
function: 
.LFB0: 
    .cfi_startproc 
    pushq %rbp # 
    .cfi_def_cfa_offset 16 
    .cfi_offset 6, -16 
    movq %rsp, %rbp #, 
    .cfi_def_cfa_register 6 
    subq $64, %rsp #, 
    movl %edi, -52(%rbp) # a, a 
    movl %esi, -56(%rbp) # b, b 
    movl %edx, -60(%rbp) # c, c 
    movq %fs:40, %rax #, tmp65 
    movq %rax, -8(%rbp) # tmp65, D.2197 
    xorl %eax, %eax # tmp65 
    leaq -32(%rbp), %rax #, tmp61 
    addq $40, %rax #, tmp62 
    movq %rax, -40(%rbp) # tmp62, ret 
    movq -40(%rbp), %rax # ret, tmp63 
    movl (%rax), %eax # *ret_1, D.2195 
    leal 8(%rax), %edx #, D.2195 
    movq -40(%rbp), %rax # ret, tmp64 
    movl %edx, (%rax) # D.2195, *ret_1 
    movq -8(%rbp), %rax # D.2197, tmp66 
    xorq %fs:40, %rax #, tmp66 
    je .L2 #, 
    call __stack_chk_fail # 
.L2: 
    leave 
    .cfi_def_cfa 7, 8 
    ret 
    .cfi_endproc 
.LFE0: 
    .size function, .-function 
    .section .rodata 
.LC0: 
    .string "%d\n" 
    .text 
    .globl main 
    .type main, @function 
main: 
.LFB1: 
    .cfi_startproc 
    pushq %rbp # 
    .cfi_def_cfa_offset 16 
    .cfi_offset 6, -16 
    movq %rsp, %rbp #, 
    .cfi_def_cfa_register 6 
    subq $16, %rsp #, 
    movl $0, -4(%rbp) #, x 
    movl $3, %edx #, 
    movl $2, %esi #, 
    movl $1, %edi #, 
    call function # 
    movl $1, -4(%rbp) #, x 
    movl -4(%rbp), %eax # x, tmp61 
    movl %eax, %esi # tmp61, 
    movl $.LC0, %edi #, 
    movl $0, %eax #, 
    call printf # 
    movl $0, %eax #, D.2200 
    leave 
    .cfi_def_cfa 7, 8 
    ret 
    .cfi_endproc 
.LFE1: 
    .size main, .-main 
    .ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2" 
    .section .note.GNU-stack,"",@progbits 

執行示例3具有第二分配跳躍到x和程序輸出0期望的效果。

然而,如果代替我編譯使用-fno-stack-protector選項:

gcc -fno-stack-protector example3.c -S -fverbose-asm -o no-stack-protection.s 

我收到以下組件的文件:

.file "example3.c" 
# GNU C (Ubuntu 4.8.2-19ubuntu1) version 4.8.2 (x86_64-linux-gnu) 
# compiled by GNU C version 4.8.2, GMP version 5.1.3, MPFR version 3.1.2-p3, MPC version 1.0.1 
# GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 
# options passed: -imultiarch x86_64-linux-gnu example3.c -mtune=generic 
# -march=x86-64 -auxbase-strip verbose-no-stack-pro.s -fno-stack-protector 
# -fverbose-asm -Wformat -Wformat-security 
# options enabled: -faggressive-loop-optimizations 
# -fasynchronous-unwind-tables -fauto-inc-dec -fbranch-count-reg -fcommon 
# -fdelete-null-pointer-checks -fdwarf2-cfi-asm -fearly-inlining 
# -feliminate-unused-debug-types -ffunction-cse -fgcse-lm -fgnu-runtime 
# -fident -finline-atomics -fira-hoist-pressure -fira-share-save-slots 
# -fira-share-spill-slots -fivopts -fkeep-static-consts 
# -fleading-underscore -fmath-errno -fmerge-debug-strings 
# -fmove-loop-invariants -fpeephole -fprefetch-loop-arrays 
# -freg-struct-return -fsched-critical-path-heuristic 
# -fsched-dep-count-heuristic -fsched-group-heuristic -fsched-interblock 
# -fsched-last-insn-heuristic -fsched-rank-heuristic -fsched-spec 
# -fsched-spec-insn-heuristic -fsched-stalled-insns-dep -fshow-column 
# -fsigned-zeros -fsplit-ivs-in-unroller -fstrict-volatile-bitfields 
# -fsync-libcalls -ftrapping-math -ftree-coalesce-vars -ftree-cselim 
# -ftree-forwprop -ftree-loop-if-convert -ftree-loop-im -ftree-loop-ivcanon 
# -ftree-loop-optimize -ftree-parallelize-loops= -ftree-phiprop -ftree-pta 
# -ftree-reassoc -ftree-scev-cprop -ftree-slp-vectorize 
# -ftree-vect-loop-version -funit-at-a-time -funwind-tables -fverbose-asm 
# -fzero-initialized-in-bss -m128bit-long-double -m64 -m80387 
# -maccumulate-outgoing-args -malign-stringops -mfancy-math-387 
# -mfp-ret-in-387 -mfxsr -mglibc -mieee-fp -mlong-double-80 -mmmx -mno-sse4 
# -mpush-args -mred-zone -msse -msse2 -mtls-direct-seg-refs 

    .text 
    .globl function 
    .type function, @function 
function: 
.LFB0: 
    .cfi_startproc 
    pushq %rbp # 
    .cfi_def_cfa_offset 16 
    .cfi_offset 6, -16 
    movq %rsp, %rbp #, 
    .cfi_def_cfa_register 6 
    movl %edi, -36(%rbp) # a, a 
    movl %esi, -40(%rbp) # b, b 
    movl %edx, -44(%rbp) # c, c 
    leaq -32(%rbp), %rax #, tmp61 
    addq $40, %rax #, tmp62 
    movq %rax, -8(%rbp) # tmp62, ret 
    movq -8(%rbp), %rax # ret, tmp63 
    movl (%rax), %eax # *ret_1, D.2195 
    leal 8(%rax), %edx #, D.2195 
    movq -8(%rbp), %rax # ret, tmp64 
    movl %edx, (%rax) # D.2195, *ret_1 
    popq %rbp # 
    .cfi_def_cfa 7, 8 
    ret 
    .cfi_endproc 
.LFE0: 
    .size function, .-function 
    .section .rodata 
.LC0: 
    .string "%d\n" 
    .text 
    .globl main 
    .type main, @function 
main: 
.LFB1: 
    .cfi_startproc 
    pushq %rbp # 
    .cfi_def_cfa_offset 16 
    .cfi_offset 6, -16 
    movq %rsp, %rbp #, 
    .cfi_def_cfa_register 6 
    subq $16, %rsp #, 
    movl $0, -4(%rbp) #, x 
    movl $3, %edx #, 
    movl $2, %esi #, 
    movl $1, %edi #, 
    call function # 
    movl $1, -4(%rbp) #, x 
    movl -4(%rbp), %eax # x, tmp61 
    movl %eax, %esi # tmp61, 
    movl $.LC0, %edi #, 
    movl $0, %eax #, 
    call printf # 
    movl $0, %eax #, D.2196 
    leave 
    .cfi_def_cfa 7, 8 
    ret 
    .cfi_endproc 
.LFE1: 
    .size main, .-main 
    .ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2" 
    .section .note.GNU-stack,"",@progbits 

和相應的可執行不產生0但一個所需的值 隨機值,我不能與彙編文件調和。

我在-fno-stack-protector情況下棧幀的心理畫面(SFP =保存的幀指針,RET =返回地址):

low memory address  buf2 (16 bytes) buf1 (8 bytes) retptr (8 bytes) sfp (8 bytes) ret  high memory address 
<---     [    ][    ][    ][    ][ ] ... 
top of stack                      bottom of stack 

我的問題:

我是錯估的位置在不受保護的情況下的返回地址?

+0

使用'-S -fverbose-asm'編譯,也可能使用'-O' –

+0

我已更新我的問題以包含'-fverbose-asm'的輸出。 '-O'似乎抹去了輸出,使得無堆棧保護版本沒有'功能'。我無法從其他註釋中看到該選項在程序集文件中出現了什麼問題。這兩個版本似乎都對'retptr'變量執行相同的操作。 – incomplete

+0

最好的辦法就是單步執行代碼,以便觀察寄存器/內存值。自從我做了asm之後已經有一段時間了,但是rsp寄存器中有一個sub qword指令,它在非保護版本中不存在。 –

回答

2

我是否錯誤地計算了無保護情況下返回地址的位置?

這部分是正確的,至少只要地址適合int。 retptr的正確類型爲long,x86-64 asm,因此指針可以保存64位地址。

你可以仔細檢查通過運行下面的程序:

#include <stdio.h> 

void function(int a, int b, int c) 
{ 
    char buf1[5]; 
    char buf2[10]; 
    int *retptr; 

    retptr = (void*)(buf2 + 40); 
    printf("retptr points to: %p\n", (long*)(long)*retptr); 
    (*retptr) += 8; 
} 

int main(void) 
{ 
    int x; 


    printf("ret address is %p\n", &&label); 
    x = 0; 
    function(1,2,3); 
label: 
    x = 1; 
    printf("%d\n", x); 

    return 0; 
} 

通過運行這個,你應該能夠確認的是,在function之後的地址是一個也retptr舉行。

我相信你沒有得到預期的0的原因,就在於這一行:

(*retptr) += 8; 

在我的64位系統,x = 1被編譯爲:

40058a: c7 45 fc 01 00 00 00 movl $0x1,-0x4(%rbp) 
    400591: 8b 45 fc    mov -0x4(%rbp),%eax 
    400594: 89 c6     mov %eax,%esi 

第一線加載1,x,另外兩行將x的值作爲自變量傳遞給printf()。請注意,這是7個字節,而不是8個。如果將增量更改爲7,則應該看到0,正如您所期望的那樣。

實際上,通過加上8,ret指令設置指令指針指向45,而不是8b。 該代碼就變成了:

45 fc     rex.RB cld 
    89 c6     mov %eax,%esi 

我不能完全肯定在這一點上發生了什麼,我懷疑這取決於CPU型號。 Mine似乎跳過指令,直到mov %eax,%esi,因此printf顯示%eax的值。如果您看看function()的反彙編,事實證明%rax用於存儲retptr的值,並且這是打印的看似隨機的值。

+0

啊,我不好使用'long'。看來GCC總是爲指針分配8個字節。我對7字節的指令長度和爲什麼8字節增量在受保護的情況下工作有些困惑。你如何獲得實現'x = 1'的指令的佈局?我使用了GDB的反彙編命令,但我沒有獲得與此處顯示的相同級別的細節。一點: 」...'ret'指令設置指令指針指向'45',而不是'c7'',應該讀取'8b'而不是'c7'?最後的問題:你如何獲得最終的彙編代碼片段? – incomplete

+1

我在可執行文件中使用了'objdump -D'來獲得反彙編。關於'c7'而不是'8b',我的意思是'c7':當跳到'c7 45 fc 01 00 00 00'時,因爲的額外字節添加到返回地址,我們最終跳到'45 fc 01 00 00 ...'。關於最終的代碼片段,我寫了一個小程序:'char asm_snippet [] = {0x45,0xfc, 0x01,0x00,0x00 ...',然後使用'objdump -D',也可以在gdb中使用'x/32i asm_snippet',我從這裏學到了這個:https://lkml.org/lkml/2008/ 1/7/406 –

+0

是不是我們想跳過的想法通過了'c7 45 fc 01 00 00 00'?因此,增加7個字節會使我們轉向'8b 45 fc'並且增加一個額外的字節會導致我們跳過'8b'到'45 fc'? – incomplete