CSAPP阅读笔记-变长栈帧,缓冲区溢出攻击-来自第三章3.10的笔记-P192-P204
一.几个关于指针的小知识点:
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的更多相关文章
- CSAPP阅读笔记-栈帧-来自第三章3.7的笔记-P164-P176
1.基本结构: 如上图所示,是通用的栈帧结构.大致分两块,调用者函数P和被调用者函数Q. 对P来说,要做的工作是把传递参数中多于6个的部分压栈,随后把Q返回时要执行的下一条指令的地址压栈. 对Q来说, ...
- CSAPP阅读笔记-汇编语言初探(控制类指令)-来自第三章3.6的笔记-P135-P163
1.正溢出与负溢出: 首先,一个正数与一个负数相加,不可能溢出,因为结果的绝对值一定小于两个加数的绝对值,既然两个加数能合理表示出来,结果一定也能合理表示出来. 其次,正溢出是由于两个很大的正数相加, ...
- CSAPP阅读笔记-gcc常用参数初探-来自第三章3.2的笔记-P113
gcc是一种C编译器,这次我们根据书上的代码尝试着使用它. 使用之前,先补充前置知识.编译器将源代码转换为可执行代码的流程:首先,预处理器对源代码进行处理,将#define指定的宏进行替换,将#inc ...
- CSAPP阅读笔记-struct, union, 数据对齐-来自第三章3.9的笔记-P183-P191
1.数据对齐 为什么要对齐:通俗点解释就是CPU对数据访问时,每次都是取固定数量的字节数,假如一次取4个字节,若有个int存在0x01-0x04,则一次就能取出,若存在0x03-0x06,则需要分两次 ...
- CSAPP阅读笔记-数组分配与访问-来自第三章3.8的笔记-P176-P183
这一节比较简单,仅记录几个比较重要的点: 1.C语言允许对指针进行运算,计算出的值会根据该指针引用的数据类型大小进行伸缩. 例子: 其中,xE是数组的起始地址.注意,指针运算时,若最终结果为指针,则指 ...
- CSAPP阅读笔记-汇编语言初探(算术和逻辑操作类指令)-来自第三章3.5的笔记-P128-P135
1.算术和逻辑操作类指令分四类:加载有效地址,一元操作,二元操作和移位,如下: 2. leaq指令,类似mov指令,它左侧的数看似是给出一个地址,在内存中从给定的地址取操作数,传给右边的目的地.但其实 ...
- CSAPP缓冲区溢出攻击实验(上)
CSAPP缓冲区溢出攻击实验(上) 下载实验工具.最新的讲义在这. 网上能找到的实验材料有些旧了,有的地方跟最新的handout对不上.只是没有关系,大体上仅仅是程序名(sendstring)或者參数 ...
- CSAPP缓冲区溢出攻击实验(下)
CSAPP缓冲区溢出攻击实验(下) 3.3 Level 2: 爆竹 实验要求 这一个Level的难度陡然提升,我们要让getbuf()返回到bang()而非test(),并且在执行bang()之前将g ...
- CSAPP:逆向工程【缓冲区溢出攻击】
逆向工程[缓冲区溢出攻击] 任务描述 掌握函数调用时的栈帧结构,利用输入缓冲区的溢出漏洞,将攻击代码嵌入当前程序的栈帧中,使程序执行我们所期望的过程. 主要方法 溢出的字符将覆盖栈帧上的数据,会覆盖程 ...
随机推荐
- activiti监听器
activiti使用的时候,通常需要跟业务紧密的结合在一起,有些业务非常的复杂,通常有如下一些场景: 1.activiti人员动态的分配. 2.当前任务节点完成的时候,指定需要指定下一个节点的处理人( ...
- Replication--发布属性immediate_sync
在创建发布时,如果选择立即初始化,会将immediate_sync属性设置为true.如果immediate_sync属性为true时,snapshot文件和发布事务及发布命令将一直保留到指定的事务保 ...
- owin Oauth
原文:http://www.cnblogs.com/richieyang/p/4918819.html 一.什么是OAuth OAuth是一个关于授权(Authorization)的开放网络标准,目前 ...
- Linux 下面screen命令的用法
最近在使用阿里云的Linux 云服务做毕业设计遇到一些问题,我把java的jar运行程序上传之后,使用java -jar server命令之后程序开始正常运行,但是当我关闭终端的时候程 ...
- Data Base Mysql迁移到SqlServer 2008工具使用方法
Data Base Mysql迁移到SqlServer 2008工具使用方法 一.下载及安装: 二.
- 2. Python的划分
解释型:当程序运行时,将代码从上至下,一句一句解释成二进制,在执行. 典型:python,php 优点:开发速度快,可以跨平台. 缺点:执行效率慢 编译型:将源码一次性转化成二进制文件,然后在执行. ...
- [NOI2010]能量采集(莫比乌斯反演)
题面: bzoj luogu NOI2010能量采集 题解 读完题之后我们发现在每个产生贡献的点\((x1,y1)\)中,它与原点之间的点\((x2,y2)\)都满足\(x2|x1\),\(y2|y1 ...
- 不可逆转(SDOI2010地精部落)
提供一个简单一点的思路,不需要滚动数组,也不需要一些奇奇怪怪的性质. 我们考虑设\(f[i]\)为\(1\)到\(i\)中有多少种波动数列. 我们可以注意到,波动数列分为先降后升和先升后降两种,但是我 ...
- boost::string 例题1
如果有一个语法正确的shader源文件,其包括若干关于uniform变量的定义.请写一个程序从某个shader源文件里提取其全部定义的uniform变量.要求记录其名称.数据类型和初始值(如果有定义) ...
- 【maven】---初识
前言 最近在研究自动化测试,其中的一个研究点儿就是maven,去download了一本书,看了看.下面是自己的从书中摘录的一些关于maven的概念性的东西. 内容 maven是什么? Maven是一个 ...