c - 调用 printf 时的堆栈 View ?

标签 c assembly stack calling-convention

我刚刚了解格式字符串漏洞,这让我提出了这个问题

考虑以下简单的程序:

#include<stdio.h>
void main(int argc, char **argv)
{
char *s="SomeString";
printf(argv[1]);
}

现在很明显,这段代码容易受到格式字符串漏洞的影响。 IE。当命令行参数为 %s 时,则打印值 SomeString,因为 printf 会弹出堆栈一次。

我不明白的是调用 printf 时堆栈的结构

在我的脑海中,我想象堆栈如下:

从左到右增长----->

main()                                                                  ---> printf()-->
RET to libc_main | address of 's' | current registers| ret ptr to main | ptr to format string| 

如果是这种情况,向程序输入%s,如何导致s的值被弹出?

(或)如果我对堆栈结构完全错误,请纠正我

最佳答案

堆栈内容很大程度上取决于以下内容:

  • CPU
  • 编译器
  • 调用约定(即如何在寄存器和堆栈中传递参数)
  • 编译器执行的代码优化

这是我通过使用 gcc stk.c -S -o stk.s 使用 x86 mingw 编译你的小程序得到的结果:

        .file   "stk.c"
        .def    ___main;        .scl    2;      .type   32;     .endef
        .section .rdata,"dr"
LC0:
        .ascii "SomeString\0"
        .text
        .globl  _main
        .def    _main;  .scl    2;      .type   32;     .endef
_main:
LFB6:
        .cfi_startproc
        pushl   %ebp
        .cfi_def_cfa_offset 8
        .cfi_offset 5, -8
        movl    %esp, %ebp
        .cfi_def_cfa_register 5
        andl    $-16, %esp
        subl    $32, %esp
        call    ___main
        movl    $LC0, 28(%esp)
        movl    12(%ebp), %eax
        addl    $4, %eax
        movl    (%eax), %eax
        movl    %eax, (%esp)
        call    _printf
        leave
        .cfi_restore 5
        .cfi_def_cfa 4, 4
        ret
        .cfi_endproc
LFE6:
        .def    _printf;        .scl    2;      .type   32;     .endef

这就是我使用 gcc stk.c -S -O2 -o stk.s 得到的结果,即启用优化:

        .file   "stk.c"
        .def    ___main;        .scl    2;      .type   32;     .endef
        .section        .text.startup,"x"
        .p2align 2,,3
        .globl  _main
        .def    _main;  .scl    2;      .type   32;     .endef
_main:
LFB7:
        .cfi_startproc
        pushl   %ebp
        .cfi_def_cfa_offset 8
        .cfi_offset 5, -8
        movl    %esp, %ebp
        .cfi_def_cfa_register 5
        andl    $-16, %esp
        subl    $16, %esp
        call    ___main
        movl    12(%ebp), %eax
        movl    4(%eax), %eax
        movl    %eax, (%esp)
        call    _printf
        leave
        .cfi_restore 5
        .cfi_def_cfa 4, 4
        ret
        .cfi_endproc
LFE7:
        .def    _printf;        .scl    2;      .type   32;     .endef

如您所见,在后一种情况下,堆栈上没有指向“SomeString”的指针。事实上,该字符串甚至不存在于编译的代码中。

在这个简单的代码中,堆栈上没有保存任何寄存器,因为没有任何分配给寄存器的变量需要在调用 printf() 时保留。 .

因此,您在堆栈上获得的唯一内容是字符串指针(可选)、由于堆栈对齐而未使用的空间( andl $-16, %esp + subl $32, %esp 对齐堆栈并为局部变量分配空间,此处没有), printf()的参数,从printf()返回的返回地址返回main() .

在前一种情况下,指向“SomeString”的指针和 printf()的参数( argv[1] 的值)彼此相距甚远:

        movl    $LC0, 28(%esp) ; address of "SomeString" is at esp+28
        movl    12(%ebp), %eax
        addl    $4, %eax
        movl    (%eax), %eax
        movl    %eax, (%esp) ; address of a copy of argv[1] is at esp
        call    _printf

要使两个地址在堆栈上一个接一个地存储,如果这是您想要的,您需要使用代码、编译/优化选项或使用不同的编译器。

或者您可以在 argv[1] 中提供格式字符串这样printf()会达到它。例如,您可以在格式字符串中包含许多假参数。

例如,如果我使用 gcc stk.c -o stk.exe 编译这段代码并将其运行为 stk.exe %u%u%u%u%u%u%s ,我将从中得到以下输出:

4200532268676042006264200532880015253SomeString

所有这些都非常棘手,要使其正常工作并非易事。

关于c - 调用 printf 时的堆栈 View ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12223118/

相关文章:

c - 为什么预定义 gtk 函数会出现隐式声明错误?

c - 将变量作为参数传递并获得所需的返回值与传递指针基本相同吗?

c - 从 int 函数返回 char * 而不使用指针?

stack - 我无法理解堆栈是如何工作的

assembly - x87 FP 堆栈仍然相关吗?

c++ - 阿桑 : heap-use-after-free when flattening a binary tree to a linked list

c - C中的快速随机数生成函数

c - C中的汇编代码是什么?

c - 重新编码 memset 时出现“段错误”

c - 程序集 64 位无效有效地址