2015-05-21 67 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