MIPS栈溢出原理

MIPS的函数调用

小知识:

Mips 调用函数时不会将返回地址放入栈中,而在非叶子函数中,为了调用下一个函数,会将上一个函数的返回地址压栈

叶子函数,即该函数中不会调用任何其他函数

非叶子函数,即该函数需要调用其他函数

mips的函数调用过程

当函数A执行到调用函数B的指令时,函数调用指令复制当前pc寄存器的值到ra寄存器中,即ra中存放返回地址

他这个没有专门的控制ra的指令,在ja指令执行的时候,给ra赋值了

程序跳转到函数B的时候,如果是非叶子函数,函数B会先把函数A的返回地址压栈(即ra寄存器的值压栈),叶子函数没有这个操作,返回地址就只存在于ra寄存器中

main函数一般是一个非叶子函数,我们几乎可以在任何mian开头看到 sw $ra,0x20+var_s4($sp)这条指令

image-20220402141121629

函数B执行完之后,叶子函数直接使用jr $ra指令返回函数A,而非叶子函数则需要,从堆栈中取出返回地址,然后将返回地址放入ra寄存器,再使用jr $ra指令

下面是两个程序(叶子和非叶子)的分析

叶子函数

image-20220402141131544

这个程序中,add函数为叶子函数,我们去分析add函数调用之前做了什么。

这里使用mipsel-linux-gnu-gcc -o tree tree.c -static进行编译

然后使用qemu-mipsel -g 1234 tree与ida连用的动态调试

IDA remote另一篇文章会详细讲,这里就不赘述

下断点到main函数,能看到在400578处有看到将ra压栈的操作,这个就是操作系统的某个地址。

image-20220402141137056

重点在0040059c这个行,调用add函数,先不关心参数调用,只看ra寄存器的相关操作

执行完00400578后,寄存器以及栈中的内容

image-20210825095723949

继续执行至0040059c,此时ra值没有改变

image-20220402141148995

执行jal add

image-20220402141156455

跳转过来之后,ra的值变了,值正好是jr add的下一条可用指令(nop的目的只是为了对齐)

image-20220402141203617

再看add的所有指令,会发现,只有跳回main函数指令出现了ra寄存器

image-20220402141210131

非叶子函数

image-20220402141216124

add中调用了printf()函数。

image-20220402141223617

执行进入add函数,与叶子函数相同

image-20220402141230705

进入之后,抬高堆栈后,执行了ra压栈操作,即00400544这一行的操作

image-20220402141238248

上图是函数执行完之后,准备返回main,在0040059c这行把main函数的返回地址放回ra。

然后通过jr $ra返回main。

参数

mips函数调用传递参数规则,前四个参数通过$a1- $a3寄存器传递,其他参数通过栈传递。

image-20220402141246086

从main函数中来看,先将参数数字放到临时栈中(蓝色框中),然后将第五个参数去取出,放入add的栈中,然后将前四个栈放到a0-a3寄存器中(红框)

image-20220402141304900

我们来尝试画出其栈图,下图是main函数的栈图

image-20220402141314906

红框为上个函数的返回地址,蓝框为局部变量,绿的是第五个参数。

栈溢出

我们使用下面代码来做实验

image-20220402141342106

代码大意就是从passwd这个文件读取文件。

尝试使用大量字符串

image-20220402141349030

运行发现报错

image-20220402141355070

栈溢出的目的是覆盖返回地址,上面说过,main一般是一个典型的非叶子函数,而且passwd文件的读取是在main中执行的,

所以我们目标应该在main刚进来的ra位置,使用ida+qemuGdb调试。

image-20220402141401755

运行至main

image-20220402141409333

并且关注ra的值存放的位置,右键 -> jump a new window

image-20220402141418631

执行完ra压栈之后,栈中的数据

image-20220402141427263

下面让程序读完passwd文件,这个地方有个小方法,读取一般是在循环中一个字符一个字符读的,所以向下单步执行,如果遇到多次循环,就可以尝试吧断点下载循环执行完的下一行。

image-20220402141436143

上图发现循环,尝试在循环外下断,不要断在nop上,情况允许就尽量断在nop下一行

image-20220402141447106

ida下使用f9可以继续运行,直到下一个断点,运行到00400500后可以看到,地址存放的地方已经被覆盖

image-20220402141454519

接下来可以使用cyclic等工具来计算长度,编写poc或exp利用。


MIPS栈溢出原理
http://example.com/article/bd2e4070.html
Author
p1yang
Posted on
August 12, 2021
Licensed under