一.几个关于指针的小知识点:

1.  malloc是在堆上动态分配内存,返回的是void *,使用时会配合显式/隐式类型转换,用完后需要用free手动释放。

alloca是标准库函数,可以在栈上分配任意字节数量的内存,用完自动释放。

2.指针的优先级较低:

  char (*p)[3],括号中优先级最高,所以p是一个指针,指向一个3个元素的char数组。

  char *p[3],  因为指针优先级较低,所以*与char结合,p代表一个3个元素的数组,每个元素都是一个char *。

3.函数指针:

它的语法类似指向数组的指针,char (*f)(int *),同之前的分析,*f代表f是一个指针,比起char (*p)[3],此时后面的是(int *),所以此指针指向的是一个以int*为参数的函数,返回值类型是char。

  若改成 char *f(int *),则f不再是一个指向函数的指针,而是一个以char*为返回值,参数为int*的函数。

二.缓冲区溢出

先看个例子:

      

左边为c代码,右边为汇编代码,c代码中我们为char数组buf设置了8个字节的缓冲区,虽然在汇编代码中为它在栈上分配了24个字节(还记得吗,在3.7那里讲过什么时候要压栈,这里属于第二种情况,当被保存的局部变量需要被访问到地址时,比如这里的数组,要把这个局部变量压栈,而不能仅仅用寄存器传递),剩下16个字节是不被使用的,而在gets中,由于未指定目标缓冲区大小,当输入超过8个字符时,显然会破坏后续的栈空间,在23个之前不会有太大影响,而一旦超过23个,会破坏返回地址的值,若超过32个字节,会破坏调用函数(即echo)里,调用caller之前存的值((还记得吗,3.7中讲过,返回地址是在存完多余参数,局部变量,被调用者保存寄存器后,由call存入的,可以参考书中3.7节的图3-29来理解)。

具体情况如下图:

给一道很有意思的例题:

这道题里有很多值得注意的点,首先,这儿的返回地址是调用get_line的那个函数压入的,跳转到get_line函数后,get_line先把%rbx压入了栈,这可能是因为后面get_line函数用到%rbx寄存器(还记得3.7讲的被调用者保存寄存器吗,%rbx是其中之一),所以要先压栈保存当前值,此时第二行是01 23 45 67 89 AB CD EF,随后要为局部变量buf数组分配栈空间,分配了0x10=16个字节,所以第4行是%rsp的位置,要注意的是,调用gets后,存入输入的字符串的顺序是先存到%rsp,再到1(%rsp),一直延续的,所以第4行对应存前8个字符,且从后向前存,结果是: 37 36 35 34 33 32 31 30,同理,第三行结果是: 35 34 33 32 31 30 39 38,再向上存,第二行保存的%rbx值被覆盖为: 33 32 31 30 39 38 37 36,最后仍剩2个字符,4和空,所以返回地址对应的第一行会被覆盖为: 00 00 00 00 00 40 00 34。

最后,E题,对malloc的调用应该是malloc(strlen(buf)+1),因为strlen计算长度时并未包含空字符,而且malloc后未检查返回值是否为NULL。

根据上述内容,可以看出缓冲区溢出攻击的原理:在给程序输入的字符串中包含了一些恶意的攻击代码,同时会用一个指向攻击代码的指针覆盖掉返回地址,这样,函数返回时会“迷路”,转而去执行攻击代码。

对抗这种攻击有几种常用方法:

1.栈随机化,即程序开始时,在栈上随机分配一段0-n字节间的随机大小的空间(可用alloca实现),程序不使用这段空间,这样,通过浪费一段空间,可以使程序每次执行时后续的栈位置发生变化。然而这种方式仍有着被破解的可能,攻击者可以在攻击代码前放许多nop指令,这些指令唯一的作用就是指向下一条指令,假设本来栈随机化后栈空间地址的变化范围达到了223个字节,本来要精确地将返回地址改到攻击代码入口的对应的地址需要“精确投放”,即要尝试枚举223种可能,现在攻击代码加上这一堆nop指令,假设达到了28=256个字节,代表只要返回地址指向这些指令中的任何一条,都会导致最后进入攻击代码,因此只要枚举215种可能就行了,因此栈随机化不大保险。

2.栈破坏检测,基本思路是在栈的局部缓冲区插入一个哨兵值,它在程序每次运行时随机产生(比如可以从内存中某个地方取得),在函数返回以及恢复寄存器的值之前,程序会先检测哨兵值是否被改变,若改变了则程序异常终止。

3.限制可执行代码区域,即限制只有保存编译器产生的代码的那部分内存才是可执行的,其他内存区域被限制为只允许读和写。

三.变长栈帧

当声明一个局部变长数组时,编译器无法一开始就确定栈帧的大小,要为之分配多少内存空间,因此需要用变长栈帧。

下面看一个实例,比较难:

左侧两个是相应的C和汇编代码,程序中声明的long *数组,其所占空间大小由传入的n决定,所以栈上至少需要8n个字节容纳这个数组,另外局部变量i由于用到了&i,也要存到栈上。首先%rbp是被调用者保存寄存器,要把%rbp原先的值存入栈,%rbp用来存初始的栈顶位置,它被称为“帧指针”。为什么要特意用一个被调用者保存寄存器来存初始的栈顶位置呢?是因为,在for循环中,i是不停变化的,因此局部变量i在被存到栈中后不能不管它,要在循环过程中修改它的值,而右图中可看到,i存放在初始栈顶向下的8个字节中,因此需要记录下初始栈顶位置以方便定位到i。第4行代码在栈上分配16个字节,其中8个存i,8个没用到。第5行计算得到8n+22,第6行andq $-16,%rax,注意-16补码是0x1111111111111110,即将%rax最后4位置0,也就是把8n+22向下舍入最接近的16的倍数,n是奇数时,结果是8n+8,n是偶数时,结果是8n+16,第7行开辟相应大小的栈空间,开辟后栈指针%rsp的地址由右图中的s1变到下方的s2。第8-10行计算(s2+7)/8 * 8,代表将s2向上舍入到最近的8的倍数,即对应右图中的p。注意左图显示的只是部分汇编代码,它们不是连续的,第11行和12行间有省略号,这里做了一些事情,比如注释中的 i in %rax,在上面的代码中没有操作过,因此这部分操作是被略过去的。13到19行用来完成for循环,没什么可说的。20行的leave指令,等价于 movq %rbp, %rsp  popq %rbp,即恢复寄存器%rbp的值,且栈被释放,恢复回之前的值。

完毕。

CSAPP阅读笔记-变长栈帧,缓冲区溢出攻击-来自第三章3.10的笔记-P192-P204的更多相关文章

  1. CSAPP阅读笔记-栈帧-来自第三章3.7的笔记-P164-P176

    1.基本结构: 如上图所示,是通用的栈帧结构.大致分两块,调用者函数P和被调用者函数Q. 对P来说,要做的工作是把传递参数中多于6个的部分压栈,随后把Q返回时要执行的下一条指令的地址压栈. 对Q来说, ...

  2. CSAPP阅读笔记-汇编语言初探(控制类指令)-来自第三章3.6的笔记-P135-P163

    1.正溢出与负溢出: 首先,一个正数与一个负数相加,不可能溢出,因为结果的绝对值一定小于两个加数的绝对值,既然两个加数能合理表示出来,结果一定也能合理表示出来. 其次,正溢出是由于两个很大的正数相加, ...

  3. CSAPP阅读笔记-gcc常用参数初探-来自第三章3.2的笔记-P113

    gcc是一种C编译器,这次我们根据书上的代码尝试着使用它. 使用之前,先补充前置知识.编译器将源代码转换为可执行代码的流程:首先,预处理器对源代码进行处理,将#define指定的宏进行替换,将#inc ...

  4. CSAPP阅读笔记-struct, union, 数据对齐-来自第三章3.9的笔记-P183-P191

    1.数据对齐 为什么要对齐:通俗点解释就是CPU对数据访问时,每次都是取固定数量的字节数,假如一次取4个字节,若有个int存在0x01-0x04,则一次就能取出,若存在0x03-0x06,则需要分两次 ...

  5. CSAPP阅读笔记-数组分配与访问-来自第三章3.8的笔记-P176-P183

    这一节比较简单,仅记录几个比较重要的点: 1.C语言允许对指针进行运算,计算出的值会根据该指针引用的数据类型大小进行伸缩. 例子: 其中,xE是数组的起始地址.注意,指针运算时,若最终结果为指针,则指 ...

  6. CSAPP阅读笔记-汇编语言初探(算术和逻辑操作类指令)-来自第三章3.5的笔记-P128-P135

    1.算术和逻辑操作类指令分四类:加载有效地址,一元操作,二元操作和移位,如下: 2. leaq指令,类似mov指令,它左侧的数看似是给出一个地址,在内存中从给定的地址取操作数,传给右边的目的地.但其实 ...

  7. CSAPP缓冲区溢出攻击实验(上)

    CSAPP缓冲区溢出攻击实验(上) 下载实验工具.最新的讲义在这. 网上能找到的实验材料有些旧了,有的地方跟最新的handout对不上.只是没有关系,大体上仅仅是程序名(sendstring)或者參数 ...

  8. CSAPP缓冲区溢出攻击实验(下)

    CSAPP缓冲区溢出攻击实验(下) 3.3 Level 2: 爆竹 实验要求 这一个Level的难度陡然提升,我们要让getbuf()返回到bang()而非test(),并且在执行bang()之前将g ...

  9. CSAPP:逆向工程【缓冲区溢出攻击】

    逆向工程[缓冲区溢出攻击] 任务描述 掌握函数调用时的栈帧结构,利用输入缓冲区的溢出漏洞,将攻击代码嵌入当前程序的栈帧中,使程序执行我们所期望的过程. 主要方法 溢出的字符将覆盖栈帧上的数据,会覆盖程 ...

随机推荐

  1. java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoader

    一直报这个错误 错误原因jar包没有导入到.classes文件下,需要导入到此文件下,因为用的是user library,所以只有逻辑导入,没有实际导入,切换下就好了,具体看如下文章 http://w ...

  2. 总结- H5项目常见问题汇总及解决方案(转)

    H5项目常见问题及注意事项 Meta基础知识: H5页面窗口自动调整到设备宽度,并禁止用户缩放页面 //一.HTML页面结构 <meta name="viewport" co ...

  3. 用word2013发布csdn博客

    目前大部分的博客作者在用Word写博客这件事情上都会遇到以下3个痛点: 1.所有博客平台关闭了文档发布接口,用户无法使用Word,Windows Live Writer等工具来发布博客.使用Word写 ...

  4. Postgresql导出指定的数据表

    两对双引号"\"\""是用于解决:pg_dump: no matching tables were found 问题. pg_dump -h localhost ...

  5. Smart3d 近景摄影测量与航空摄影测量

    实例:http://blog.sina.com.cn/s/blog_8f68d2350102wef4.html ContextCapture(Smart3d):https://www.bentley. ...

  6. 流程控制语句(if、for、while、do while、switch、 break、continue)

    3:流程控制语句    (1)顺序结构 从上往下,依次执行    (2)选择结构    按照不同的选择,执行不同的代码    (3)循环结构 做一些重复的代码 4:if语句    (1)三种格式   ...

  7. Alpha阶段项目复审(菜就完事了队)

    Alpha阶段项目复审 小组 优点 缺点 名次 天冷记得穿秋裤队 实现的功能完整,可以离线下载 下载不稳定,大文件无法下载 1 中午吃啥队 使用方便,操作简单 界面适应不够好 2 只会嘤嘤嘤队 游戏和 ...

  8. [SIP01]SIP Header Fields里面各字段用途

    INVITE sip:bob@biloxi.com SIP/2.0 Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds Max-Forw ...

  9. Djangorestframework编写post接口

    前提:已经有Django环境 一.安装 pip install djangorestframework 二.编写视图 import json from django.shortcuts import ...

  10. 提高SQL Server数据库效率常用方法

    1.没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 2.I/O吞吐量小,形成了瓶颈效应. 3.没有创建计算列导致查询不优化. 4.内存不足 5.网络速度慢 6.查询出的数据量过大 ...