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:逆向工程【缓冲区溢出攻击】
逆向工程[缓冲区溢出攻击] 任务描述 掌握函数调用时的栈帧结构,利用输入缓冲区的溢出漏洞,将攻击代码嵌入当前程序的栈帧中,使程序执行我们所期望的过程. 主要方法 溢出的字符将覆盖栈帧上的数据,会覆盖程 ...
随机推荐
- 初涉Runtime (一)
Objective-C 是一门动态语言,有很多东西都是运行时才确定的. 比如这句代码首先声明testObject是一个NSString,然后创建了一个NSData对象,并且将这个对象的内存地址保存在t ...
- 第十六章 IIC协议详解+UART串口读写EEPROM
十六.IIC协议详解+Uart串口读写EEPROM 本文由杭电网友曾凯峰根据小梅哥FPGA IIC协议基本概念公开课内容整理并最终编写Verilog代码实现使用串口读写EEPROM的功能. 以下为原文 ...
- linux 权限机制
系统每个文件都拥有特定的权限,所属用户及所属组,通过这样的机制限制哪些用户.哪些组可以对特定文件进行什么样的操作,一般分为读.写.执行三种权限. 每个进程都是以某个用户的身份运行,所以进程的权限与该用 ...
- loadrunner中对https证书的配置
1.准备好网站的证书,一般证书是cer格式: 2.因为loadrunner只支持pem格式的证书,所以要将证书转换格式,利用openssl工具:(或者直接让开发提供pem格式的证书) 3.得到pe ...
- laravel中使用mgirations创建和迁移数据库
使用php artisan make:migration create_links_table命令 编辑2016_04_11_095342_create_links_table public func ...
- Partition--分区切换
现有数据表[dbo].[staging_TB1_20131018-104722]和分区表[dbo].[TB1],需要将分区表和数据表中做数据交换 CREATE TABLE [dbo].[staging ...
- Nutch2.2.1 爬虫问题列表
http://www.cnblogs.com/cy163/archive/2013/02/14/2912630.html http://blog.csdn.net/wangzhaodong001/ar ...
- Linux中VMware虚拟机增加磁盘空间的扩容操作
用VMwareware虚拟机安装的Red Hat Enterprise Linux系统剩余空间不足,造成软件无法正常安装.如果重新装一遍系统就需要重新配置好开发环境和软件的安装配置.通过上网搜集的资料 ...
- 【转】基于Redis实现延时队列服务
背景 在业务发展过程中,会出现一些需要延时处理的场景,比如: a.订单下单之后超过30分钟用户未支付,需要取消订单b.订单一些评论,如果48h用户未对商家评论,系统会自动产生一条默认评论c.点我达订单 ...
- unity面试准备
最近有换工作的打算 所以上网看下面试题 自己做下总结 Q:ArrayList 和 List区别 A: 1:List大家都知道初始化的时候需要定义其类型,例如 List<int> listT ...