C: printf()'s堆栈漏洞



我写了一个简单的C程序来查看printf()的堆栈框架

#include <stdio.h>
int main(void){
    printf("%s");
}

我认为堆栈的工作方式是main()首先将"%s"推到堆栈上,因此printf将seg fault或打印出垃圾。但是,在我的反汇编中,没有任何地方会将"%s"推到堆栈上。我打印出了介于%fp和%sp之间的所有值,但没有一个包含"%s"。

主组件转储:

0x00400950 <+0>:    lui gp,0x2
0x00400954 <+4>:    addiu   gp,gp,-32224
0x00400958 <+8>:    addu    gp,gp,t9
0x0040095c <+12>:   addiu   sp,sp,-32
0x00400960 <+16>:   sw  ra,28(sp)
0x00400964 <+20>:   sw  s8,24(sp)
0x00400968 <+24>:   move    s8,sp
0x0040096c <+28>:   sw  gp,16(sp)
0x00400970 <+32>:   lw  v0,-32744(gp)
0x00400974 <+36>:   nop
0x00400978 <+40>:   addiu   v0,v0,2864
0x0040097c <+44>:   move    a0,v0
0x00400980 <+48>:   lw  v0,-32688(gp)
0x00400984 <+52>:   nop
0x00400988 <+56>:   move    t9,v0
0x0040098c <+60>:   jalr    t9
0x00400990 <+64>:   nop
0x00400994 <+68>:   lw  gp,16(s8)
0x00400998 <+72>:   move    sp,s8
0x0040099c <+76>:   lw  ra,28(sp)
0x004009a0 <+80>:   lw  s8,24(sp)
0x004009a4 <+84>:   addiu   sp,sp,32
0x004009a8 <+88>:   jr  ra
0x004009ac <+92>:   nop

如果"%s"没有存储在堆栈上,它存储在哪里?此外,它从哪里获得要打印的相应字符串?

据我所知,mips-arch使用a0~a3作为函数调用的前四个参数。

通常,在实现级别将发生的事情是"%s"字符串文字位于某种静态存储中。当调用printf时,指向该字符串的指针将作为参数传递。这并不一定意味着这个指针被推到堆栈上。参数的传递方式取决于参数传递约定。它可以加载到寄存器中。

在您的特殊情况下,这里是"%s"准备通过的地方:

0x00400970 <+32>:   lw  v0,-32744(gp)
0x00400974 <+36>:   nop
0x00400978 <+40>:   addiu   v0,v0,2864
0x0040097c <+44>:   move    a0,v0

首先,从相对于全局指针寄存器的数据区域加载基地址。然后将该基地址偏移2864以获得CCD_ 4的地址。然后,地址被移动到a0,寄存器v0被重新用于计算printf的地址(由于它在共享库中,这一事实使其变得复杂)。

既然"%s"没有相应的char *自变量,那么行为当然是形式上未定义的。但实际行为是什么?

实际行为可能是printf将尝试以某种方式提取char *指针,可能是从堆栈中提取。(变元函数的尾部参数通常只放在堆栈中。)

现在,由于调用者没有在那里放置参数,printf提取一些"垃圾"字,并将其视为char *,将该字所指向的内存打印为以null结尾的字符串。也就是说,如果该单词指向有效内存。

如果您的目标是转储一些字节的堆栈内存,那么这根本不可靠。您不知道什么样的值作为char *指针得到解释器,也不知道它指向什么,或者它是否指向任何东西,更不用说堆栈了。

char *本身可能是从堆栈中提取的,但实际上您并没有打印该指针本身。

以下可能会为您获取几个字节的堆栈:

printf("%pn");

同样,任何没有参数的数字转换也可能如此。原因是%p%s不同,实际上打印指针本身。如果%p的自变量值是从堆栈中提取的,则该值的打印表示会泄露堆栈中一小部分的一些信息。

        .file   "1.c"
        .section        .rodata
.LC0:
        .string "%s"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    $.LC0, %edi
        movl    $0, %eax
        call    printf
        movl    $0, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010"
        .section        .note.GNU-stack,"",@progbits

我使用gcc生成程序集。字符串的存储方式与您想象的不同。字符串是静态存储的。

最新更新