十二、缓冲区溢出漏洞原理

十二、缓冲区溢出漏洞原理
在x86计算机体系结构中,大部分编译器对函数的参数传递、局部变量分配和释放都是通过操作当前所在函数的栈帧实现的。在MIPS32架构中,函数调用时对函数栈帧的分配和使用方式与x86架构由很多相似之处,MIPS32架构中的栈可以结合一些寄存器完成诸如传递函数参数、局部变量保存、存储函数返回值、保存寄存器、恢复调用前处理器的状态等功能。

MIPS32架构堆栈

在MIPS32架构中,每次调用一个函数时,需要将当前调用栈的栈顶指针向下(也就是低地址方向)移动n个比特(也就是减去n个比特),这n个比特的空间就构成了当前被调用函数的栈帧。在这之后栈顶指针便不再移动,只能使用栈顶指针加上或减去一个偏移量来操作栈帧中的变量。最后,在函数返回时将栈顶指针再加上n个比特来恢复调用该函数前的栈现场。

读者可以查看文稿中的两幅图,分别代表了函数A调用函数B之前和函数B正在执行时栈顶指针(SP)的变化情况:

十二、缓冲区溢出漏洞原理
另外,在MIPS32架构中函数调用时的参数传递方式与x86有所不同,MIPS32中会将被调用函数的前4个参数通过 a 0   a0~ a0 a3传递。超过4个的部分被放到调用参数空间。

关于这个调用参数空间的概念,比如说,函数A调用函数B,函数A会在自己的函数栈空间中预留一部分空间来保存函数B的参数,这个预留的空间就是调用参数空间。

返回地址(RA寄存器)

对于理解MIPS32架构中的缓冲区溢出原理,还有一个很重要的知识点就是函数的返回地址,在MIPS架构下,假如函数A调用了strcpy函数时,会将调用strcpy函数后需要返回的地址存入RA寄存器。这样当执行完函数strcpy后,跳转指令会从RA寄存器取出返回地址,以便继续执行后续的指令调用。

同样的,在A函数中调用的其他函数(如函数B)也可能基于同样的原因对RA寄存器进行修改,所以,在函数B刚开始运行时,会首先将RA寄存器的值保存到函数B的栈空间中,以便在函数B结束运行时从栈空间中恢复刚刚保存的RA寄存器的值。

同样的,为了防止函数A调用的其他函数对RA寄存器进行修改,在函数A刚开始运行时,会首先将当前的RA寄存器的值保存到函数A的栈空间中,然后在函数A的末尾处将RA寄存器的值进行恢复。

我在文稿中列出了一段C语言代码,和使用mips-linux-gcc编译器对这段代码编译后的反汇编代码,读者可以看图体会一下。对于汇编代码,你可能不太熟悉,我在后面的分享中会涉及,为了便于理解,这里你可以只看红色方框部分的汇编代码注释:

十二、缓冲区溢出漏洞原理
这里我针对上面的示例代码中的函数A的栈空间画了一幅图,RA寄存器保存在栈空间的高地址处,缓冲区buf在栈空间的低地址处,如果使用strcpy函数时,并且在拷贝的数据长度超过了32字节时(比如64个字节的A),超出的部分就会将保存RA寄存器值的内存覆盖。

当函数A执行到末尾时,会从栈空间取出刚刚保存的RA地址对RA寄存器进行恢复:

十二、缓冲区溢出漏洞原理
当执行完恢复指令后,RA寄存器的值就变成了刚刚传入的“AAAA”。

最终在函数的末尾使用jr指令进行地址跳转时,就将程序的执行流程劫持到了AAAA地址处,本文中使用的示例传入的是“AAAA”,如果传入的不是“AAAA”,而是攻击者精心构造的存有恶意指令的地址,那么这些被精心构造的指令将会被执行。

今天我们结合示例代码讲解了路由器中缓冲区溢出的原理,在文稿中有一些反汇编代码,你可能对此有些陌生,但对于理解缓冲区溢出的原理并没有什么大的问题。不过,为了后面的ShellCode开发能够顺利进行,我在下次的分享中,将针对MIPS架构的汇编指令进行总结和分享。

最后,希望本次的分享能够给你带来帮助,谢谢大家。

来源:后知晚觉

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2020年2月6日
下一篇 2020年2月6日

相关推荐