深入汇编理解缓冲区溢出攻击

1.基本知识
    子汇编程序里,调用函数使用CALL伪指令,原始的传递参数的方法可以是使用寄存器和全局标记(和高级语言,如C中的全局变量,在.data段定义的标记)。但是由于这样子函数不能模块化,而且如果程序功能稍大的话,代码将非常难于理解和维护,所以后来统一使用栈来管理函数调用,包括函数的参数传递,返回地址,局部变量。这样函数就可以模块化,并且可以写在另一个文件中。不过,在Linux内核中,系统调用都是使用寄存器传递参数的。
2.写一个简单的C程序stack.c,如下
------------------------stack.c--------------------------
#include<stdio.h>
void test_func(int,char,char);
int main(){
    test_func(1,'B','C');
    return 0;
}

void test_func(int a,char b,char c){
    int func_a=a;
    char func_b=b;
    char func_c=c;
}
------------------------------------------------------------
3.编译这个文件到汇编语言
    $gcc -S statk.c
    编译后生成汇编文件stack.s,我使用的是gcc4.4.1(Ubuntu-9.10(Karmic Koala)),内容如下
--------------------------stack.s----------------------------
    .file    "stack.c"
    .text
.globl main        #全局标记声明,gcc使用main标记作为程序入口,而gas使用_start标记。
    .type    main, @function
main:
    pushl    %ebp
    movl    %esp, %ebp     #规范化的函数调用开头:首先保存栈顶指针
    andl    $-16, %esp        #Intel推荐16字节对齐,为了更快的预取
    subl    $16, %esp          #栈向下增长16字节,留出新参的内存,栈很特别,它从高内存向低内存增长               
    movl    $66, 8(%esp)    #将3个参数入栈,入栈是反序的,参数1即在栈最上端(低地址)
    movl    $65, 4(%esp)
    movl    $1, (%esp)         #从这里可以知道,参数的增长方式是从低地址往高地址增长,否则
                                              #这个int参数1就要超过栈顶指针了,从另一方面理解,Intel是小端
                                             #对齐的,所有数据的地址都是低字节地址,所以其余字节都放在高内存
    call    test_func              #调用函数,call伪指令会把返回地址入栈
    movl    $0, %eax          #从这里算起要修改3行,否则使用gas,ld编译链接后的程序最后找不到出口,会发生段错误,但是并不影响我们。
    leave               
    ret
    .size    main, .-main
.globl test_func
    .type    test_func, @function
test_func:
    pushl    %ebp
    movl    %esp, %ebp
    subl    $24, %esp                 #在前面栈已经16字节对齐,所以这里不需要了
    movl    12(%ebp), %edx    #将参数'B'放入EDX寄存器,这里为什么会取得'A'将在后面用图
                            #来表示
    movl    16(%ebp), %eax    #将参数'C'放入EAX寄存器
    movb    %dl, -20(%ebp)    #'B'是字符,所以只取一个字节,暂存
    movb    %al, -24(%ebp)    #同上
    movl    8(%ebp), %eax     #取参数1,放入EAX寄存器
    movl    %eax, -8(%ebp)    #给func_a赋值
    movzbl    -20(%ebp), %eax    #扩展传送,表示将一个字节传送到寄存器EAX,EAX高24位补0
    movb    %al, -1(%ebp)            #赋值给func_b
    movzbl    -24(%ebp), %eax   
    movb    %al, -2(%ebp)            #赋值给func_c
    leave
    ret
    .size    test_func, .-test_func
    .ident    "GCC: (Ubuntu 4.4.1-4ubuntu8) 4.4.1"
    .section    .note.GNU-stack,"",@progbits
-------------------------------------------------------------------------
3.修改程序
    为了便于调试,我们将stack.s文件修改为gas格式,也就是把main换成_start即可,可以使用vi的替换功能方便的完成,并且还要修改__main的退出代码,见注释。修改以后的文件如下:
----------------------------------stack.s------------------------------
    .file    "stack.c"
    .text
.globl _start
    .type _start, @function
_start:
    pushl    %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $16, %esp
    movl    $67, 8(%esp)
    movl    $65, 4(%esp)
    movl    $1, (%esp)
    call    test_func
    pushl    $0                #这两行是修改后的
    call    exit
    .size    _start, .-_start
.globl test_func
    .type    test_func, @function
test_func:
    pushl    %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    movl    12(%ebp), %edx
    movl    16(%ebp), %eax
    movb    %dl, -20(%ebp)
    movb    %al, -24(%ebp)
    movl    8(%ebp), %eax
    movl    %eax, -8(%ebp)
    movzbl    -20(%ebp), %eax
    movb    %al, -1(%ebp)
    movzbl    -24(%ebp), %eax
    movb    %al, -2(%ebp)
    leave
    ret
    .size    test_func, .-test_func
    .ident    "GCC: (Ubuntu 4.4.1-4ubuntu8) 4.4.1"
    .section    .note.GNU-stack,"",@progbits
-------------------------------------------------------------------------
使用如下命令生成可以调试的程序
    $as -o stack.o stack.s -gstabs
    $ld -dynamic-linker /lib/ld-linux.so.2 -o stack stack.o -lc
解释:as即gas汇编器,-gstabs参数生成可供gdb调试的信息;
    ld是连接器,由于使用了C库,使用-l参数指定库libc,由于需要动态连接库,所以还要指定动态连接程序,使用参数-dynamic-linker来指定。
4.调试程序
    $gdb stack
    gdb的使用方法不再详细介绍,下面查看一些关键的结果。使用一个图来表示栈,如下(由于不能上传图片,见相册:Ubuntu

5.缓冲区溢出
    上面的程序,只是用来详细介绍函数调用过程中,堆栈的变化,缓冲区溢出攻击的原理就是使用超出缓冲区的字符来填充缓冲区,这样就有可能覆盖栈中存放的函数返回地址,覆盖以后,函数返回时就不能执行原来的程序,而是恶意攻击者安排的代码,为什么会覆盖?关键是函数局部变量的增长方式是从低地址向高地址增长,而这时候,call伪指令已经把函数的返回地址入栈了,所以它位于局部变量的高端,当局部变量错误的增长时,就可能覆盖返回地址。下面举例
-----------------------------overflow.c--------------------------------
#include<stdio.h>
void func();
int main(){
    func();
    return 0;
}
void func(){
    char input[4];
    gets(input);
}
-------------------------------------------------------------------------
    main函数中调用了一个有名的会发生缓冲区溢出的函数gets,所以不用我们自己去编写一个函数,使用如下命令
    $gcc -S overflow.c    //生成汇编文件overflow.s,修改汇编文件中main为_start以及程序出口,修改后如下
---------------------------------overflow.s-----------------------------
    .file    "overflow.c"
    .text
.globl _start
    .type    _start, @function
_start:
    pushl    %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    call    func
    movl    $0, %eax
    movl    %ebp, %esp
    popl    %ebp
    pushl    $0
    call    exit
    .size    _start, .-_start
.globl func
    .type    func, @function
func:
    pushl    %ebp
    movl    %esp, %ebp
    subl    $40, %esp
    leal    -12(%ebp), %eax    #leal表示将源操作数地址发送到目的,由于gets函数的参数是地址
    movl    %eax, (%esp)        #将参数(一个地址)放入栈中,前面说过,所有调用函数的参数都必须倒序入栈。
    call    gets                            #调用函数gets
    leave
    ret
    .size    func, .-func
    .ident    "GCC: (Ubuntu 4.4.1-4ubuntu8) 4.4.1"
    .section    .note.GNU-stack,"",@progbits
-----------------------------------------------------------------------
    $as -o overflow.o overflow.s -gstabs    //编译汇编文件到目标文件
    $ld -dynamic-linker /lib/ld-linux.so.2 -o overflow overflow.o -lc
    执行完上述命令以后,就可以使用gdb来调试程序overflow了,在链接文件的时候,会提示gets很危险,不应该使用它。
6.实验结果
    下面使用gdb来调试这个程序,gdb的具体使用方法就不介绍了,下面是一些截图,能够说明栈是怎样被覆盖的(由于不能上传图片,见相册Ubuntu)

图一,传递参数给函数gets

图二,使用call指令调用函数gets

图三,执行call指令后的栈布局

图四,输入过长的字符串

图五,返回地址被覆盖后,发生段错误

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 缓冲区溢出攻击是一种常见的计算机系统安全漏洞,攻击者利用程序在处理输入数据时未对输入长度进行正确检查的漏洞,向缓冲区中输入超出其容量的数据,导致内存越界,从而执行恶意代码或者破坏目标系统的程序执行流程。 在缓冲区溢出攻击实验中,level 3: rumble 是最高级别的实验。在这个实验中,攻击者需要面对更加复杂的目标系统和更加严格的安全措施。 首先,目标系统在level 3: rumble实验中会具有更多的防御机制,例如ASLR (地址空间布局随机化)和堆栈保护机制。这些防御机制会使攻击者更加困难地绕过目标系统的防御。 其次,在level 3: rumble 实验中,攻击者需要克服更多的挑战来进行缓冲区溢出攻击。例如,攻击者可能需要绕过更多的输入过滤和验证措施,以及发现程序中更加隐藏的漏洞。攻击者可能需要深入了解目标程序的内部机制和底层原理,以便针对性地构造恶意输入。 另外,level 3: rumble 实验中可能还涉及到其他的攻击技术,如返回导向编程(ROP)攻击、格式化字符串攻击等。攻击者需要具备更高的技术水平,才能成功进行这些更复杂的攻击。 总之,level 3: rumble 缓冲区溢出攻击实验是一个更高难度的实验,要求攻击者具备更丰富的知识和技术,能够绕过更复杂的防御机制,并能针对目标系统进行更深入的分析和攻击。 ### 回答2: 计算机系统基础缓冲区溢出攻击实验level 3: rumble是一个高级的缓冲区溢出攻击实验,需要一定的计算机系统知识和技术。缓冲区溢出攻击是一种常见的安全漏洞,攻击者通过向程序的输入缓冲区传递超出其容量的数据,覆盖其他内存区域的数据,实现非法的攻击目的。 在level 3: rumble实验中,攻击者需要深入了解目标程序的源代码和系统结构,包括缓冲区的分配与管理,函数的调用栈等。攻击者需要针对目标程序中的特定漏洞进行攻击,以获取系统权限或执行恶意操作。 具体而言,rumble实验可能涉及以下步骤: 1. 了解目标程序的源代码和漏洞:攻击者需要分析目标程序的源代码,找到其中存在的缓冲区溢出漏洞。这可能涉及到查找可疑函数的调用以及分析程序中的内存布局。 2. 构造恶意输入:攻击者需要构造一组数据,使其超出目标程序中对应缓冲区的容量。这可能需要了解目标程序中缓冲区的大小和分配方式,以及如何覆盖与之相邻的内存区域。 3. 利用缓冲区溢出漏洞:攻击者通过向目标程序输入恶意输入数据,触发缓冲区溢出漏洞。攻击者可能需要使溢出后的数据覆盖目标程序的返回地址或其他关键数据,以控制程序的执行流程。 4. 执行恶意操作:一旦攻击者成功控制了程序的执行流程,就可以执行自己定义的恶意操作。例如,获取系统权限、执行远程命令或者破坏系统的稳定性。 5. 躲避防御机制:为了增加攻击的成功几率,攻击者可能需要绕过目标程序中的防御机制,如栈保护、地址空间布局随机化(ASLR)等。这需要一些额外的技术和经验。 需要注意的是,缓冲区溢出攻击是一种非法的行为,可能导致系统崩溃、数据泄露或者其他安全问题。进行此类攻击需要事先获得合法授权,并在受控环境下进行。同时,在现实应用中,攻击者和开发者都应该重视安全,通过合理的代码编写和安全防护措施来提升系统的安全性。 ### 回答3: 计算机系统基础缓冲区溢出攻击实验level 3: rumble是一个相对复杂的实验,旨在进一步提高对缓冲区溢出攻击理解和应对能力。 在这个实验中,攻击者将试图通过利用缓冲区溢出漏洞来实现执行恶意代码的目的。与之前的实验不同的是,level 3的实验增加了更复杂的保护机制,包括地址随机化和堆栈不可执行等技术。 在开始实验之前,我们需要了解这种攻击方法的原理。缓冲区溢出攻击是指攻击者通过向程序输入过多的数据,超出了缓冲区本应能够接受数据的范围,从而改变了程序的正常行为。攻击者可以利用这一漏洞来执行恶意代码,例如注入恶意程序、获取权限等。 在level 3的实验中,我们需要实现对程序bug的利用,通过构造特定的输入,使程序运行时发生缓冲区溢出。然后,我们需要分析程序的内存布局,利用地址随机化技术确定目标地址,进而构造出能够执行恶意代码的输入。 与之前的实验不同的是,rumble增加了堆栈不可执行的保护措施。这意味着我们需要找到通常不可执行的存储区域来执行代码,例如函数指针等。 在实验中,我们需要深入理解程序的内存布局、栈和堆的使用,以及各种保护机制。同时,我们还需要使用一些工具和技巧,例如调试器和反汇编,来帮助我们分析程序的行为和构造有效的攻击输入。 通过这个实验,我们将能够更好地理解缓冲区溢出攻击的原理和实现方法。这不仅对于提高我们的安全意识和防御能力有帮助,也能够增加我们在计算机系统基础方面的知识和技能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值