我是靠谱客的博主 粗心心情,最近开发中收集的这篇文章主要介绍linux 函数栈溢出,64位Linux下的栈溢出,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

今天在看《捉虫日记》,其中讲解的Linux下的栈缓冲区溢出是32位机器的,和我的64位机器有些不同之处。下面是我在64位Linux (Ubuntu14) 下的栈缓冲区溢出实验的记录。

首先是有溢出漏洞的程序,这个程序来自于《捉虫日记》。

#include

void overflow(char *arg){

char buf[12];

strcpy(buf, arg);

}

int main(int argc, char *argv[]){

if(argc==2)

overflow(argv[1]);

return 0;

}

禁用堆栈保护编译

gcc -m64 stackoverflow.c -o stackoverflow -z execstack -fno-stack-protector

生成汇编文件

gcc -m64 stackoverflow.c -S -masm=intel -o stackoverflow.s -z execstack -fno-stack-protector

生成的.stackoverflow.s文件内容如下所示

.file "stackoverflow.c"

.intel_syntax noprefix

.text

.globl overflow

.type overflow, @function

overflow:

.LFB0:

.cfi_startproc

push rbp

.cfi_def_cfa_offset 16

.cfi_offset 6, -16

mov rbp, rsp

.cfi_def_cfa_register 6

sub rsp, 32

mov QWORD PTR [rbp-24], rdi

mov rdx, QWORD PTR [rbp-24]

lea rax, [rbp-16]

mov rsi, rdx

mov rdi, rax

call strcpy

leave

.cfi_def_cfa 7, 8

ret

.cfi_endproc

.LFE0:

.size overflow, .-overflow

.globl main

.type main, @function

main:

.LFB1:

.cfi_startproc

push rbp

.cfi_def_cfa_offset 16

.cfi_offset 6, -16

mov rbp, rsp

.cfi_def_cfa_register 6

sub rsp, 16

mov DWORD PTR [rbp-4], edi

mov QWORD PTR [rbp-16], rsi

cmp DWORD PTR [rbp-4], 2

jne .L3

mov rax, QWORD PTR [rbp-16]

add rax, 8

mov rax, QWORD PTR [rax]

mov rdi, rax

call overflow

.L3:

mov eax, 0

leave

.cfi_def_cfa 7, 8

ret

.cfi_endproc

.LFE1:

.size main, .-main

.ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4"

.section .note.GNU-stack,"",@progbits

在主函数中,38、39行将命令行参数放入栈中,第40行比较第一个参数(argc)的值是否等于2,若不等于则跳转(第41行)到L3,随后退出,若等于2,则取第二个参数加8后的地址单元中的值存放到寄存器rdi中。(第二个参数argv是char **型的,加8是因为我们传给函数overflow的参数是argv数组中的第二个值argv[1],它是一个字符串,也就是指向字符的指针。)在64位机器中,参数的传递会使用寄存器而不是堆栈。处理好参数之后便调用函数:call overflow。

在 函数overflow中,先执行“push rbp,mov rbp, rsp”(9-12行)称之为序幕(prolog)工作,对应的有第21行的leave指令完成收尾(epilog)工作。第14行的“sub rsp, 32”为局部变量留出空间。15-19行的指令为调用函数strcpy准备好了参数,参数依旧存在寄存器中(rsi是arg,rdi是buf,都是字符指针)。由于内存只能以字为单位寻址,64位机器中一个字是8个字节,buf[12]占12个字节,所以需要两个字的存储空间,也就是16字节,所以17行“lea rax, [rbp–16]”中减去了16 (最接近rbp的空间是留给buf的,因为它在函数中最先定义)。运行到第17行时,堆栈如下图所示:

0ebf4d1deb309b25bb0fe256a70aa69c.png

函数strcpy会将rdi的值当做指针,将其指向的字符串复制到buf中,从上图可以看出,buf的大小很有限,若超出其长度,则会覆盖掉老rbp,老rip,使函数无法正常返回。尝试运行程序:

当参数字符串的长度小于等于15时运行不会出错

./stackoverflow 0123456789abcde

当参数字符串的长度大于15时运行才会报错

./stackoverflow 0123456789abcdef

Segmentation fault (core dumped)

为何不是16?C语言中的字符串是以作为结束标志的,所以实际的存储空间会比可见的长度多一个字节。

由于64位机器中的rip被设计成前47位有效,当指定一个大于0x00007fffffffffff的地址时会抛出异常,所以经典的0x4141414141414141无法实现。那就让我们把rip变成0x0000414141414141吧。

用gdb调试程序(-q使得gdb不输出gdb程序的版本等信息):

gdb -q ./stackoverflow

然后在gdb中输入命令

run $(python -c "print 'A'*30')

以30个大写字母A作为输入参数,其中16个用于填满buf,8个用于覆盖“老rbp”,最后6个用于覆盖rip。一运行就会看到这样的信息

Program received signal SIGSEGV, Segmentation fault.

0x0000414141414141 in ?? ()

可见rip的确被覆盖为了0x0000414141414141。若是想将rip覆盖为0x00007fffffffffff,则需要修改参数为

run $(python -c "print 'A'*24+'xff'*5+'x7f'")

运行结果为

Program received signal SIGSEGV, Segmentation fault.

0x00007fffffffffff in ?? ()

最后

以上就是粗心心情为你收集整理的linux 函数栈溢出,64位Linux下的栈溢出的全部内容,希望文章能够帮你解决linux 函数栈溢出,64位Linux下的栈溢出所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(64)

评论列表共有 0 条评论

立即
投稿
返回
顶部