需要比较扎实的漏洞分析技术,形成poc证明漏洞存在、编写exp利用漏洞攻击。主要还是堆栈溢出漏洞,之后会总结一些常见的漏洞类型和常用的保护机制绕过和分析技术。

MIPS 基本知识

特点

  • 大端模式 低存低 高存高
  • $s0这个寄存器里面的值永远是0,能让编码更简洁。
  • $ra($31)寄存器存放子程序的返回地址ret_addr,类似于arm的连接寄存器lr。
  • $a0˜$a3存放传递给子程序的前四个参数,多的放到堆栈。类似x86_64。
  • 流水线效应,即把每条指令分阶段,可以同时执行不同的指令的不同阶段,就是计组里学的典型的五指令流水线。其中最重要的效应是分支延迟槽效应,即分支指令的后一条指令总是会先将结果写回,MIPS就是这样规定的,因为早期没有分支预测,就把本来因为分支耽误的指令先执行了。如下面的汇编代码,函数func1用到的第一个参数$a0中的值是$s2,即分支指令后一条语句的执行结果。**同时,执行转移语句时,其后的第二条语句的地址被装入$pc。**

    1. move $a0,$s0
    2. jal func1
    3. move $a0,$s2 ;分支延迟槽
    4. li $v0,5 $v0一般存入返回结果 这条指令的地址是func1ret_addr
  • 没有栈底指针ebp,只有一个指向栈顶的堆栈指针$sp,不过还有个桢指针$fp经常拿来当ebp用。与x86一样都是从高地址往低地址方向生长,也是通过移动$sp来完成进入(减去该函数栈空间的大小)和退出函数(函数返回时加上这个偏移量,恢复到原来的栈顶。

  • 叶子函数与非叶子函数。叶子函数即不再调用其它函数的函数,非叶子函数还会调用其他函数。非叶子函数被调用时,其ret_ addr会被压入栈底。

    函数调用栈原理

    通过操纵栈的,完成函数调用,和x86类似。
    函数调用过程
    以函数funcA调用funcB为例
  1. funcA执行到调用funcB的指令时,根据流水线效应,此时转移语句后的第二条语句的地址已经装入$pc,即当前$pc中装的是funcB的ret_addr。函数将当前$pc中的值复制到$ra中,再把将要跳转的funcB的入口地址装入$pc中。
  2. funcA通过$a0~$a3传递前四个参数,超过四个的参数通过funcA栈顶预留的空间传递给被调用的函数funcB。注意:即使前四个参数不通过堆栈传递,调用者函数的栈顶依然预留四个空间。
  3. funcB首先开辟自己的栈空间,如addiu $sp,$sp,-32。然后funcB会将$a0˜$a3的值保存在funcA预留的四个参数空间中。
  4. 如果funcB是非叶子函数,则需要把$ra中的ret_addr压入栈底,将$ra留给被自己调用的函数。
  5. 返回时,如果funcB是非叶子函数,把堆栈中的ret_addr存入$ra,然后 jr $ra。funcB是叶子函数,则直接 jr $ra。

eg:下图为main调用more_arg()函数的栈空间,其中more_arg()需要5个参数,而且嵌套调用了需要7个参数的sprintf()。

5 漏洞的复现 - 图2基于栈的缓冲区溢出原理

通过覆盖ret_addr,

ARM 基本知识