MIPS架构简介
MIPS架构是一种采取精简指令集(RISC)的处理器架构
- MIPS和MIPSEL是两种架构MIPS是大端序、MIPSEL是小端序。一般来说大端序列是主流的(和x86和arm相反),不过很多CTF题目都是小端序的。(大端调试需要在gdb和pwntools都特别设置,否则默认小端)
- 不支持NX(即使编译选项添加了也没有用)不支持NX即函数的栈/bss都是可执行的,当我们的写入栈中的shellcode能够被执行,大大降低了利用难度。
- 叶子函数和非叶子函数
- 在MIPS体系架构下,函数分为叶子函数和非叶子函数。MIPS函数的调用过程与x86不同,x86中函数A调用函数B时,会将A函数的地址压入堆栈中,等到函数B执行完毕返回A函数时候,再从堆栈中弹出函数A的地址。而MIPS中,如果是叶子函数,与x86是不同的,函数的返回地址是不会压入栈中的,而是会直接存入寄存器$ra中。如果是非叶子函数(即函数中还调用了其他函数),则和x86类似,将地址存入栈中。
- 另外Mips是没有栈底指针的,只有一个
$
sp指向栈顶,并且不会像x86那样通过pop或者push调整指针,而是采用偏移寻址来访问变量。非叶子函数如图所示,在函数头部会将调用函数的返回地址即$ra存放在栈底(偏移4字节),而在函数快结束时会重新将该值取去出来,放入ra。在这个间段内,如果覆盖了函数栈底,就能够控制程序的流程。
寄存器
MIPS 架构中共包含 32 个通用寄存器:
REGISTER | NAME | USAGE |
---|---|---|
$0 |
$zero |
常量0(constant value 0) |
$1 |
$at |
保留给汇编器(Reserved for assembler) |
$2-$3 |
$v0-$v1 |
函数调用返回值(values for results and expression evaluation) |
$4-$7 |
$a0-$a3 |
函数调用参数(arguments) |
$8-$15 |
$t0-$t7 |
暂时的(或随便用的) |
$16-$23 |
$s0-$s7 |
保存的(或如果用,需要SAVE/RESTORE的)(saved) |
$24-$25 |
$t8-$t9 |
暂时的(或随便用的) |
$28 |
$gp |
全局指针(Global Pointer) |
$29 |
$sp |
堆栈指针(Stack Pointer) |
$30 |
$fp |
帧指针(Frame Pointer) |
$31 |
$ra |
返回地址(return address) |
MIPS32架构中定义了3个特殊寄存器。分别为PC(程序计数器)、HI(乘除结果高位寄存器)和LO(乘除结果低位寄存器)。在进行乘法运算时,HI和LO保存乘法的运算结果,其中HI存储高32位,LO存储低32位;而在进行除法运算时,HI保存余数,LO存储商。
汇编指令
指令 | 功能 | 应用实例 |
---|---|---|
LB | 从存储器中读取一个字节的数据到寄存器中 | LB R1, 0(R2) |
LH | 从存储器中读取半个字的数据到寄存器中 | LH R1, 0(R2) |
LW | 从存储器中读取一个字的数据到寄存器中 | LW R1, 0(R2) |
LD | 从存储器中读取双字的数据到寄存器中 | LD R1, 0(R2) |
L.S | 从存储器中读取单精度浮点数到寄存器中 | L.S R1, 0(R2) |
L.D | 从存储器中读取双精度浮点数到寄存器中 | L.D R1, 0(R2) |
LBU | 功能与LB指令相同,但读出的是不带符号的数据 | LBU R1, 0(R2) |
LHU | 功能与LH指令相同,但读出的是不带符号的数据 | LHU R1, 0(R2) |
LWU | 功能与LW指令相同,但读出的是不带符号的数据 | LWU R1, 0(R2) |
SB | 把一个字节的数据从寄存器存储到存储器中 | SB R1, 0(R2) |
SH | 把半个字节的数据从寄存器存储到存储器中 | SH R1,0(R2) |
SW | 把一个字的数据从寄存器存储到存储器中 | SW R1, 0(R2) |
SD | 把两个字节的数据从寄存器存储到存储器中 | SD R1, 0(R2) |
S.S | 把单精度浮点数从寄存器存储到存储器中 | S.S R1, 0(R2) |
S.D | 把双精度数据从存储器存储到存储器中 | S.D R1, 0(R2) |
DADD | 把两个定点寄存器的内容相加,也就是定点加 | DADD R1,R2,R3 |
DADDI | 把一个寄存器的内容加上一个立即数 | DADDI R1,R2,#3 |
DADDU | 不带符号的加 | DADDU R1,R2,R3 |
DADDIU | 把一个寄存器的内容加上一个无符号的立即数 | DADDIU R1,R2,#3 |
ADD.S | 把一个单精度浮点数加上一个双精度浮点数,结果是单精度浮点数 | ADD.S F0,F1,F2 |
ADD.D | 把一个双精度浮点数加上一个单精度浮点数,结果是双精度浮点数 | ADD.D F0,F1,F2 |
ADD.PS | 两个单精度浮点数相加,结果是单精度浮点数 | ADD.PS F0,F1,F2 |
DSUB | 两个寄存器的内容相减,也就是定点数的减 | DSUB R1,R2,R3 |
DSUBU | 不带符号的减 | DSUBU R1,R2,R3 |
SUB.S | 一个双精度浮点数减去一个单精度浮点数,结果为单精度 | SUB.S F1,F2,F3 |
SUB.D | 一个双精度浮点数减去一个单精度浮点数,结果为双精度浮点数 | SUB.D F1,F2,F3 |
SUB.PS | 两个单精度浮点数相减 | SUB.SP F1,F2,F3 |
DDIV | 两个定点寄存器的内容相除,也就是定点除 | DDIV R1,R2,R3 |
DDIVU | 不带符号的除法运算 | DDIVU R1,R2,R3 |
DIV.S | 一个双精度浮点数除以一个单精度浮点数,结果为单精度浮点数 | DIV.S F1,F2,F3 |
DIV.D | 一个双精度浮点数除以一个单精度浮点数,结果为双精度浮点数 | DIV.D F1,F2,F3 |
DIV.PS | 两个单精度浮点数相除,结果为单精度 | DIV.PS F1,F2,F3 |
DMUL | 两个定点寄存器的内容相乘,也就是定点乘 | DMUL R1,R2,R3 |
DMULU | 不带符号的乘法运算 | DMULU R1,R2,R3 |
MUL.S | 一个双精度浮点数乘以一个单精度浮点数,结果为单精度浮点数 | DMUL.S F1,F2,F3 |
MUL.D | 一个双精度浮点数乘以一个单精度浮点数,结果为双精度浮点数 | DMUL.D F1,F2,F3 |
MUL.PS | 两个单精度浮点数相乘,结果为单精度浮点数 | DMUL.PS F1,F2,F3 |
AND | 与运算,两个寄存器中的内容相与 | ANDR1,R2,R3 |
ANDI | 一个寄存器中的内容与一个立即数相与 | ANDIR1,R2,#3 |
OR | 或运算,两个寄存器中的内容相或 | ORR1,R2,R3 |
ORI | 一个寄存器中的内容与一个立即数相或 | ORIR1,R2,#3 |
XOR | 异或运算,两个寄存器中的内容相异或 | XORR1,R2,R3 |
XORI | 一个寄存器中的内容与一个立即数异或 | XORIR1,R2,#3 |
BEQZ | 条件转移指令,当寄存器中内容为0时转移发生 | BEQZ R1,0 |
BENZ | 条件转移指令,当寄存器中内容不为0时转移发生 | BNEZ R1,0 |
BEQ | 条件转移指令,当两个寄存器内容相等时转移发生 | BEQ R1,R2 |
BNE | 条件转移指令,当两个寄存器中内容不等时转移发生 | BNE R1,R2 |
J | 直接跳转指令,跳转的地址在指令中 | J name |
JR | 使用寄存器的跳转指令,跳转地址在寄存器中 | JR R1 |
JAL | 直接跳转指令,并带有链接功能,指令的跳转地址在指令中,跳转发生时要把返回地址存放到R31这个寄存器中 | JAL R1 name |
JALR | 使用寄存器的跳转指令,并且带有链接功能,指令的跳转地址在寄存器中,跳转发生时指令的放回地址放在R31这个寄存器中 | JALR R1 |
MOV.S | 把一个单精度浮点数从一个浮点寄存器复制到另一个浮点寄存器 | MOV.S F0,F1 |
MOV.D | 把一个双精度浮点数从一个浮点寄存器复制到另一个浮点寄存器 | MOV.D F0,F1 |
MFC0 | 把一个数据从通用寄存器复制到特殊寄存器 | MFC0 R1,R2 |
MTC0 | 把一个数据从特殊寄存器复制到通用寄存器 | MTC0 R1,R2 |
MFC1 | 把一个数据从定点寄存器复制到浮点寄存器 | MFC1 R1,F1 |
MTC1 | 把一个数据从浮点寄存器复制到定点寄存器 | MTC1 R1,F1 |
LUI | 把一个16位的立即数填入到寄存器的高16位,低16位补零 | LUI R1,#42 |
DSLL | 双字逻辑左移 | DSLL R1,R2,#2 |
DSRL | 双字逻辑右移 | DSRL R1,R2,#2 |
DSRA | 双字算术右移 | DSRA R1,R2,#2 |
DSLLV | 可变的双字逻辑左移 | DSLLV R1,R2,#2 |
DSRLV | 可变的双字罗伊右移 | DSRLV R1,R2,#2 |
DSRAV | 可变的双字算术右移 | DSRAV R1,R2,#2 |
SLT | 如果R2的值小于R3,那么设置R1的值为1,否则设置R1的值为0 | SLT R1,R2,R3 |
SLTI | 如果寄存器R2的值小于立即数,那么设置R1的值为1,否则设置寄存器R1的值为0 | SLTI R1,R2,#23 |
SLTU | 功能与SLT一致,但是带符号的 | SLTU R1,R2,R3 |
SLTUI | 功能与SLT一致,但不带符号 | SLTUI R1,R2,R3 |
MOVN | 如果第三个寄存器的内容为负,那么复制一个寄存器的内容到另外一个寄存器 | MOVN R1,R2,R3 |
MOVZ | 如果第三个寄存器的内容为0,那么复制一个寄存器的内容到另外一个寄存器 | MOVZ R1,R2,R3 |
TRAP | 根据地址向量转入管态 | |
ERET | 从异常中返回到用户态 | |
MADD.S | 一个双精度浮点数与单精度浮点数相乘加,结果为单精度 | |
MADD.D | 一个双精度浮点数与单精度浮点数相乘加,结果为双精度 | |
MADD.PS | 两个单精度浮点数相乘加,结果为单精度 |
ARM架构简介
ARM架构使用了与Intel/AMD架构所不同的精简指令集(RISC)。
寄存器
- 子程序间通过寄存器R0~R3来传递参数。这时,寄存器R0~R3可记作arg0~arg3。被调用的子程序在返回前无需恢复寄存器R0~R3的内容,R0被用来存储函数调用的返回值。
- 在子程序中,使用寄存器R4~R11来保存局部变量。这时,寄存器R4~R11可以记作var1~var8。如果在子程序中使用了寄存器v1~v8中的某些寄存器,则子程序进入时必须保存这些寄存器的值,在返回前必须恢复这些寄存器的值。R7经常被用作存储系统调用号,R11存放着帮助我们找到栈帧边界的指针,记作FP。在Thumb程序中,通常只能使用寄存器R4~R7来保存局部变量。
- 寄存器R12用作过程调用中间临时寄存器,记作IP。在子程序之间的连接代码段中常常有这种使用规则。
- 寄存器R13用作堆栈指针,记作SP。在子程序中寄存器R13不能用作其他用途。寄存器SP在进入子程序时的值和退出子程序时的值必须相等。
- 寄存器R14称为连接寄存器,记作LR。它用于保存子程序的返回地址。如果在子程序中保存了返回地址,寄存器R14则可以用作其他用途。
- 寄存器R15是程序计数器,记作PC。它不能用作其它用途。当执行一个分支指令时,PC存储目的地址。在程序执行中,ARM模式下的PC存储着当前指令加8(两条ARM指令后)的位置,Thumb(v1)模式下的PC存储着当前指令加4(两条Thumb指令后)的位置。
ARM与Intel寄存器对比:
ARM架构 寄存器名 | 寄存器描述 | Intel架构 寄存器名 |
---|---|---|
R0 | 通用寄存器 | EAX |
R1~R5 | 通用寄存器 | EBX、ECX、EDX、EDI、ESI |
R6~R10 | 通用寄存器 | 无 |
R11(FP) | 栈帧指针 | EBP |
R12(IP) | 内部程序调用 | 无 |
R13(SP) | 堆栈指针 | ESP |
R14(LP) | 链接寄存器 | 无 |
R15(PC) | 程序计数器 | EIP |
CPSR | 程序状态寄存器 | EFLAGS |
指令集
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 0bs3rver的小屋!