堆栈缓冲区溢出的示例汇编语言程序
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 , %esp
为 int 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 下有无限的 red-zone。
但这是一个特殊情况,当你正在编写一个完整的程序并且不使用任何库时,如果保留和释放堆栈的额外指令 space 具有重要意义,通常你应该只内联小函数成本。
此外,如果您在 GDB 中执行 print foo()
或在某处的断点处停止时调用函数,调试器可以执行此操作 space。
我正在尝试编写一个示例程序来理解堆栈缓冲区溢出,并且我有以下程序。
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 , %esp
为 int 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 下有无限的 red-zone。
但这是一个特殊情况,当你正在编写一个完整的程序并且不使用任何库时,如果保留和释放堆栈的额外指令 space 具有重要意义,通常你应该只内联小函数成本。
此外,如果您在 GDB 中执行 print foo()
或在某处的断点处停止时调用函数,调试器可以执行此操作 space。