硬件安全初探-实战基础

MIPS32汇编

MIPS32寄存器分为通用寄存器GPR和特殊寄存器。MIPS体系结构有32个GPR,汇编中可用$0~$31表示,也可用寄存器名表示,堆栈从内存高地址向低地址增长。

编号 寄存器名 描述
0 zero 值始终为0
1 $at 保留
2~3 $v0~$v1 values,保存表达式或函数返回结果
4~7 $a0~$a3 arguments,作为函数前4个参数
8~15 $t0~$t7 temporaries,临时寄存器
16~23 $s0~$s7 saved values,子函数使用时需先保存原寄存器的值
24~25 $t8~$t9 temporaries,临时寄存器,补充$t0~$t7
26~27 $k0~$k1 保留,中断处理函数使用
28 $gp global pointer,全局指针
29 $sp stack pointer,堆栈指针,指向堆栈栈顶
30 $fp frame pointer,保存栈指针
31 $ra return address,返回地址

对于第0号寄存器,总返回0;第2~3号寄存器存放子程序返回值或非浮点结果,这俩不存放返回值时编译器通过内存完成;第4~7号寄存器将前4个参数传递给子程序,不够的用堆栈处理,$a0~$a3、$v0~$v1和$ra共同完成函数调用过程,分别用于传递参数、返回结果、存放返回地址;对于第8~15号寄存器,子函数不用保存随意使用,便于进行表达式计算,但当调用一个子函数时该寄存器值可能会被破坏;对于第16~23号寄存器,子函数必须保证函数返回时这些寄存器内容将恢复到函数调用前的值;全局指针gp访问静态变量,编译时数据需在以gp为基指针的64KB范围内;对于ra寄存器,当使用跳转链接jal指令时,调用程序把参数放在$a0~$a3中,jal X跳到X过程,被调过程完成后,结果放在$v0~$v1中,最后用jr $ra返回,调用时需要保存$a0~$a3、$s0~$s7、$gp、$sp、$fp、$ra。

MIPS32有仨特殊寄存器,分别为程序计数器PC、乘除结果高位寄存器HI、乘除结果低位寄存器LO。进行乘法运算时,HI存储高32位,LO存储低32位。进行除法运算时,HI保存余数、LO存储商。

MIPS固定4字节指令长度,内存中数据访问必须严格至少4字节对齐。跳转指令有26位目标地址,加上2位对齐位,可寻址28位空间,即256MB。条件分支指令有16位跳转指令,加上2位对齐位,共18位寻址空间,即256KB。

MIPS采用高度流水线效应,即几条指令同时执行,只是处于不同阶段,其中最重要的是分支延迟效应。分支跳转语句后那条语句叫分支延迟槽。在程序执行到分支语句时,当他纲要把跳转的地址填充到代码计数器中,还没完成本条指令时,分支延迟槽已经执行了。具体解释如下:

1
2
3
move $a0,$s2
jalr strrchr
move $a0,$s0

在执行第2行时,第3行已经执行完了,因此strchr的参数来自第3行$s0而不是第1行$s2。

MIPS指令的32位中,最高6位为Opcode码,剩下24为可将指令分为3种类型,有R、I、J型。R型指令用连续3个5位二进制码表示3个寄存器地址,用1个5位二进制码表示以为位数,最后6位为Function码,其与Opcode码共同决定R型指令具体操作方式。I型指令用连续2个5为二进制码表示2个寄存器地址,由1个16位二进制码表示1个立即数二进制码。J型指令用32位二进制码表示跳转目标指令地址,其中最低2位为00,最高4位由PC当前地址决定。

在汇编指令语法表述中,$Rd表示目的寄存器,$Rs表示源寄存器、$Rt表示中间缓存寄存器,imm表示立即数,MEM[]表示RAM中一段内存,offset表示偏移量。0

LOAD/STORE指令有14条,其中以l开头的为加载指令,以s开头的为存储指令。

1
2
3
4
5
la $Rd, Label ;load address 复制Label地址到$Rd寄存器中
li $Rd, imm ;load immediate 让$Rd赋值为imm 相当于addi $t1,$zero,40
lw $Rt,offset($Rs) ;laod word 取$Rs偏移offset处内存word场地的值到$Rt中
sw $Rt,offset($Rs) ;store word 将$Rt中一个word大小的值存入$Rs偏移offset处 若存入0($sp)则$sp自动抬栈
move $Rt,$Rs ;寄存器间传递值

算数运算指令的所有操作数必须为寄存器,不能直接用RAM地址或间接寻址。

1
2
3
4
5
6
7
8
9
add $t0,$t1,$t2 ;带符号数相加 $t0=$t1+$t2
sub $t0,$t1,$t2 ;带符号数相减 $t0=$t1-$t2
addi $t0,$t1,imm
addu $t0,$t1,$t2 ;无符号数相加
subu $t0,$t1,$t2 ;无符号数相减
mult $t3,$t4 ;(HI,LO)=$t3*$t4
div $t5,$t6 ;LO=$t5/$t6 HI=$t5 mod $t6
mfhi $t0 ;$t0=$HI
mflo $t1 ;t1=$LO

以下为常见SLT系列指令:

1
2
3
4
slt $Rd,$Rs,$Rt ;若$Rs<$Rt 设置$Rd为1 否则0 有符号比较
slti $Rt,$Rs,imm ;若$Rs<imm 设置$Rt为1 否则0 有符号比较
sltu $Rd,$Rs,$Rt ;若$Rs<$Rt 设置$Rd为1 否则0 无符号比较
sltiu $Rt,$Rs,imm ;若$Rs<imm 设置$Rt为1 否则0 无符号比较

例如:

1
2
3
4
li $v1,1
beq $v0,$v1,loc_41A394
slti $v0,$s2,2
;以上相当于 if($s2<2) goto loc_41A394

SYSCALL产生一个软中断,实现系统调用,系统调用号存放在$v0中,参数存放在$a0~$a3中。返回值放在$v0中,若出错则$a3中返回错误号。

分支跳转指令:

1
2
3
4
5
6
7
b target ;无条件
beq $t0,$t1,target ;$t0==$t1
blt $t0,$t1,target ;$t0<$t1
ble $t0,$t1,target ;$t0<=$t1
bgt $t0,$t1,target ;$t0>$t1
bge $t0,$t1,target ;$t0>=$1
bne $t0,$t1,target ;$t0!=$t1

跳转指令:

1
2
3
j target ;无条件
jr $t3 ;跳转到$t3指向的地址处
jal target ;跳转到target处 保存返回地址到$ra

函数调用和返回过程为:子函数调用jal sub_routine_label时,发生了:赋值PC到$ra、跳转到指定标签处;子函数返回jr $ra

自己去装插件:https://github.com/grayhatacademy/ida。

先鸽着,环境配置得找点现代化的…