代码之家  ›  专栏  ›  技术社区  ›  user3015238

C: printf()的堆栈漏洞?[已关闭]

  •  -2
  • user3015238  · 技术社区  · 8 年前

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

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

    我认为堆栈的工作方式是main()将首先将“%s”推到堆栈上,因此printf要么seg fault,要么打印垃圾。然而,在我的反汇编中,它没有将“%s”推到堆栈上。我打印了%fp和%sp之间的所有值,但没有一个包含“%s”。

    main的程序集转储:

    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”未存储在堆栈上,它存储在哪里?另外,它从哪里得到要打印的相应字符串?

    3 回复  |  直到 8 年前
        1
  •  1
  •   Pan Ruochen    8 年前

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

        2
  •  0
  •   Kaz    8 年前

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

    在你的特殊情况下,这里是 “%s” 正在准备通过:

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

    首先,从相对于全局指针寄存器的数据区域加载基址。然后将该基地址偏移2864,以获得 “%s” 。然后将地址移动到 a0 ,和寄存器 v0 用于计算 打印 (由于它位于一个共享库中,这一点很复杂)。

    从现在起 “%s” 没有相应的 char * 当然,行为在形式上是未定义的。但实际的行为是什么?

    实际的行为可能是 打印 将尝试提取 字符* 指针,可能来自堆栈。(变量函数的尾部参数通常只放在堆栈中。)

    既然打电话的人没有在那里争论, 打印 提取一些“垃圾”单词并将其作为 字符* ,将该字所指向的内存打印为以空结尾的字符串。也就是说,如果该单词指向有效内存。

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

    那个假的 字符* 可以从堆栈中提取指针本身,但实际上并没有打印该指针本身。

    以下内容 可以 给你几个字节的堆栈:

    printf("%p\n");
    

    同样,任何没有参数的数值转换也可能如此。原因是 %p 不像 %s ,实际上打印指针本身。如果 %第页 则该值的打印表示泄漏了关于堆栈中一小块的一些信息。

        3
  •  -1
  •   Shiv    8 年前
            .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生成程序集。字符串不是按您的想法存储的。字符串是静态存储的。