堆栈缓冲区溢出的示例汇编语言程序

Example assembly language program for stack buffer overflow

我正在尝试编写一个示例程序来理解堆栈缓冲区溢出,并且我有以下程序。

overflow.s:

    .section .data
    .section .text
    .globl _start
    _start:
          call sum
          movl %eax, %ebx
          movl , %ebx
          movl , %eax
          int [=10=]x80

         .type sum, @function
    sum:
         pushl %ebp        # save the current base pointer
         movl %esp, %ebp   # store current stack pointer to %ebp
         subl , %esp     # inc the stack pointer by 4 bytes for local variable 
         movl , -8(%ebp) # store value 5 from 8 bytes of %ebp 4 bytes beyond stack pointer
         addl , -8(%ebp) # add 5 to the value store beyond of stack pointer 
         movl -8(%ebp), %eax # store the value in %eax
         movl %ebp, %esp
         popl %ebp
         ret

assemble和link程序:

   as -gstabs+ overflow.s -o oveflow.o
   ld overflow.o -o overflow
   ./overflow
   echo $?
   15 <============= the result

我预计我会得到一些垃圾或段错误。但它似乎按预期工作。所以在 sum 函数中,当我将堆栈指针增加 4 个字节并且当我从基指针存储值 5 8 个字节时,我期望这是溢出的模拟。上面的程序是否错误地用作堆栈缓冲区溢出的示例。 ?

%esp 下的内存可能会被异步破坏(通过信号处理程序1),但是您的程序的行为不依赖于read/write 和 addl , -8(%ebp)movl -8(%ebp), %eax 的值,在 ESP 正下方的 4 字节堆栈槽中。

在 Linux 上,触摸低于 ESP 的内存不是错误,一直到最大堆栈大小的限制(ulimit -s,默认为 8192 kiB)。如果内存尚未映射,则堆栈映射会自动扩展以映射那里和当前映射的具有最低地址的堆栈页面之间的所有页面。 (或者,对于线程堆栈,它已经被完全分配,而不是动态增长。但初始进程堆栈是特殊的。)


如果你 运行 push 在一个循环中 (没有 pop 或其他任何东西来平衡它),堆栈会增长直到 ESP 减少到指向一个未映射的页面,超出内核将为您增加堆栈的位置。然后下一个 push(或 call 或其他)将出现段错误,我们称之为堆栈溢出。


A 缓冲区溢出 如果您 sub , %espint arr[3] 保留 space,然后写入 int arr[5] ]:这将覆盖您的 return 地址,因此最终的 ret 将跳转到攻击者希望您跳转的任何位置。

 # arg in EAX: how many array elements to store into arr[3]
 vulnerable_function:
    sub  , %esp
    mov  %esp, %ecx
.Lloop:
    mov  %eax, (%ecx)
    add  , %ecx
    dec  %eax
    jnz  .Lloop

    add  , %esp
    ret

脚注 1: 您尚未安装任何信号处理程序,因此(在 Linux 上)没有任何东西可以异步使用堆栈内存,并且您在 ESP 下有无限的

但这是一个特殊情况,当你正在编写一个完整的程序并且不使用任何库时,如果保留和释放堆栈的额外指令 space 具有重要意义,通常你应该只内联小函数成本。

此外,如果您在 GDB 中执行 print foo() 或在某处的断点处停止时调用函数,调试器可以执行此操作 space。