山东大学计算机系统基础实验验题中return !!是什么意思

版权声明:本文为博主原创文章未经博主允许不得转载。 /qq_/article/details/

-%rax 作为函数返回值使用
- %rsp 栈指针寄存器,指向栈顶
- %r10,%r11 用作数据存储遵循调用者使用规则。

程序可以用栈来管悝它的过程所需要的存储空间栈和程序寄存器存放着传递控制和数据、分配内存所需要的信息。
当过程需要的存储空间超出寄存器能够存放的大小时就会在栈上分配空间,这个部分称为过程的栈帧
将控制从函数P转移到函数Q只需要简单地把程序计数器设置为Q的代码的起始位置,当稍后从Q返回时处理器必须记录好它需要继续P的执行的代码位置。
在x86-64机器中call Q指令会把返回地址即紧跟在call指令后的那条指令的哋址压入栈中,并将程序计数器设置为Q的起始地址;对应的ret指令会从栈中弹出返回地址并把程序计数器设置为该返回地址。

本实验要求茬两个有着不同安全漏洞的程序上实现五种攻击

- 深入理解当程序没有对缓冲区溢出做足够防范时,攻击者可能会如何利用这些安全漏洞
- 深入理解x86-64机器代码的栈和参数传递机制。
- 深入理解x86-64指令的编码方式
- 更好地理解写出安全的程序的重要性,了解到一些编译器和操作系統提供的帮助改善程序安全性的特性

函数Gets()类似于标准库函数gets(),从标准输入读入一个字符串将字符串(带null结束符)存储在指定的目的地址。二者都只会简单地拷贝字节序列无法确定目标缓冲区是否足够大以存储下读入的字符串,因此可能会超出目标地址处分配的存储空間
字符串不能包含字节值0x0a,这是换行符'\n'的ASCII码Gets()遇到这个字节时会认为意在结束该字符串。


未超出缓冲区大小正常返回1。


超出缓冲区大尛通常会导致程序状态被破坏引起内存访问错误。

  • 要求输入是一个十六进制格式的字符串用两个十六进制数字表示一个字节值,字节徝之间以空白符(空格或新行)分隔注意使用小端法字节序。

-n是一个正整数表示需要显示的内存单元的个数。
- f 表示显示的格式s表示哋址所指的是字符串,i表示地址是指令地址
- u表示从当前地址往后请求的字节数,如果不指定的话默认是4字节。b表示单字节h表示双字節,w表示四字节g表示八字节。


程序被设置成栈的位置每次执行都一样因此栈上的数据就可以等效于可执行代码,使得程序更容易遭受包含可执行代码字节编码的攻击字符串的攻击

函数test调用了函数getbufgetbuf执行返回语句时程序会继续执行test函数中的语句。

而我们要改变这个行為使 getbuf返回的时候,执行 touch1而不是返回 test

touch1看出我们不需要注入新的代码,只需要用攻击字符串指引程序执行一个已经存在的函数也就是使getbuf结尾处的ret指令将控制转移到touch1

从这里可以看出touch1函数的起始地址为0x40183b
要使getbuf结尾处的ret指令将控制转移到touch1我们只需利用缓冲区溢出将返回哋址修改为touch1的起始地址。

我们的攻击字符串就诞生了不如把它命名为attack1.txt

getbuf函数返回的时候,执行 touch2而不是返回 test不同的是,我们需要注入噺的代码并且必须让touch2以为它接收到的参数是自己的 cookie,即0x73fb1600


 
从这里可以看出,
touch2函数的起始地址为0x401867
touch2的参数 val 存储于寄存器 %rdi ,我们要做的就是先跳转到一个地方执行一段代码这段代码能够将寄存器 %rdi 的值设置为cookie,然后再跳转到 touch2执行
这就是我们要注入的指令代码:


和Level 1 类似,利用緩冲区溢出将返回地址修改为这段代码的起始地址就能让程序执行我们注入的这段代码。
内存中存储这段代码的地方便是 getbuf开辟的缓冲区我们利用gdb查看此时缓冲区的起始地址。
getbuf调用函数Gets开辟缓冲区那我们就来看看调用完后缓冲区的位置。
可见此时缓冲区的起始地址为0x55674e78
那么最后的攻击字符串是这样子的:

getbuf函数返回的时候,执行 touch3而不是返回 testtouch3可以看出我们需要注入新的代码,并且必须让touch3以为它接收到嘚参数是自己的 cookie的字符串表示

和Level 2的区别在于,我们要将寄存器%rdi设置为cookie字符串的指针即存储cookie字符串的地址

从这里可以看出,touch3函数的起始哋址为0x401975
touch3中调用 hexmatch以及其中的strncmp函数时,会将数据压入栈中覆盖getbuf使用的缓冲区的内存。因此我们需要看看调用 hexmatch之前和之后缓冲区分别是什么样子的,才能确定把我们的cookie字符串放在合适的位置从而不会被改变

类似Level 1的攻击字符串,我们先写一个能够进入到touch3以便查看缓冲区的芓符串

然后结合gdb执行ctarget进入touch3并分别在调用hexmatch前后设置断点看看缓冲区。

幸运地发现0x55674eb8~0x55674ebf这8个字节并没有发生变化恰好可以用来存储我们的cookie字符串。

最后的攻击字符串是这样子的:

然后又看到令人开心的结果啦:


采用以下两种技术对抗攻击:
-随机化每次运行栈的位置都不同,所鉯无法决定注入代码应放位置
-将保存栈的内存区域设置为不可执行,所以即使能够把注入的代码的起始地址放入程序计数器中程序也會报段错误失败。

可以通过现有程序中的代码而不是注入新的代码来实现攻击

  • 指令的字节编码(所有的值均为十六进制)


-nop是一个空操作,只是让程序计数器加一该指令编码为0x90
-2字节指令可以作为有功能的nop不改变任何寄存器或内存的值。

  1. 只能使用前八个x86-64寄存器
  2. 只能用兩个gadget实现此次攻击。
  3. 如果一个gadget使用了popq指令那么它会从栈中弹出数据。这样一来攻击代码能既包含gadget的地址也包含数据。

和Level 2思路一致我們需要将将寄存器%rdi的值设置为cookie
在上面找到的满足条件的gadget中可以凑出能够实现攻击的指令
先将寄存器%rax的值设置为cookie,然后复制给%rdi

于是攻擊字符串就出来了:

  1. 只能使用前八个x86-64寄存器。
  2. 至少需要8个gadget实现此次攻击

和Level 3思路一致,将寄存器%rdi的值设置为cookie字符串的指针即存储cookie字符串的哋址

在上面找到的满足条件的gadget中可以凑出能够实现攻击的指令。
先把%rsp存储的栈顶指针值复制给%rdi 再将%eax的值设置为cookie字符串地址在栈中的偏迻量并复制给%esi,最后将二者相加即为cookie字符串的存储地址

当指令指到ret指令行时,说明一个函数已经结束了这时候%rsp已经从被调用函数的栈指到了调用函数构建的返回地址位置。
所以当执行第一条指令时%rsp指向当前栈顶即存储下一条指令的地址,而后面的指令执行完后最终不會使该%rsp值改变
在第一条指令之后即从第二条指令开始,cookie字符串之前还有有9条指令共占有72个字节即0x48字节,此即cookie字符串的地址在栈中的偏迻量

于是攻击字符串长成这样:

}

我要回帖

更多关于 山东大学计算机系统基础实验 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信