[UTCTF2020]babymips
0x00
MIPS的指令:
IDA可以直接反编译出MIPS的伪代码,所以我们直接看伪代码即可,可以看到是C++语法:
|
|
关键函数:sub_401164传入两个参数,一个是v7,将unk_4015F4复制,一个是v6,就是输入的flag
导出unk_4015F4:
|
|
sub_401164,整体逻辑很简单,显示判断a2的size是否为0x4E,是则进入for循环,对a2挨个字符进行异或与a1相比,最终都相等则输出correct:
|
|
根据以上即可写出exp:
|
|
得到:
|
|
到这里解题就结束了,接下来就是关于MIPS指令的学习
0x01 MIPS指令
首先需要知道MIPS和x86指令最本质的区别就是,MIPS属于RISC(精简指令集),x86属于CISC(复杂指令集)。
精简指令集与复杂指令集的区别就是指令的数量,精简指令集是以最大化程度减少指令的数量,那么相应的,某些功能如果通过精简指令集进行表达,则需要多条指令结合才能达到目的。而复杂指令集相反,增加指令的类型和数量,减少某些功能执行指令的数量。可以说前者是牺牲时间换空间,后者则是牺牲空间换时间,在某些简单操作上,精简指令集是比较占优势的,而对于复杂操作,复杂指令集的效率往往会更高,目前我们常用的Linux、Windows系统都是属于x86或者arm架构的复杂指令集。
MIPS,全称为Microcompute without InterLocked Pipeline Stages,中文翻译为无互锁流水级的微处理器
MIPS包含32个通用寄存器($0-$31):
编号 | 助记符 | 用法 |
---|---|---|
0 | zero | 不管传入什么数据,该寄存器的值永远都为0 |
1 | at | 用作汇编器的暂时变量 |
2-3 | v0, v1 | 子函数调用返回结果 |
4-7 | a0-a3 | 子函数调用的参数 |
8-15 | t0-t7 | 暂时变量,子函数调用时不需要保存与恢复 |
24-25 | t8-t9 | 暂时变量,子函数调用时不需要保存与恢复 |
16-23 | s0-s7 | 子函数寄存器变量,在返回之前子函数必须保存和恢复使用过的变量, |
26-27 | k0, k1 | 通常被中断或异常处理程序使用作为保存一些系统参数 |
28 | gp | 全局指针,一些运行系统维护这个指针来更方便的存取static和extern变量 |
29 | sp | 堆栈指针 |
30 | s8/fp | 第9个寄存器变量/框架指针 |
31 | ra | 子函数的返回地址 |
MIPS三种指令格式:
- R格式 Register Format 所有其他
- I格式 Immediate Format 用于有立即数的指令,lw,sw,
- J格式 Jump Format 无条件跳转j,并连接jal
R指令
0x02 实例
根据功能可以分为五类:
1.算数运算:
|
|
2.分支跳转:
|
|
3.跳转:
|
|
4.数据加载/存储
|
|
5.子函数调用
|
|
根据以上实例大概可以了解到,对于存在立即数的指令,指令的助记符必须加上i,例如addi $t0 $t1 5
、subi $t0 $t1 5
,对于无符号数操作,与x86中类似,在助记符后加u
即可,例如addu $t0 $t1
,如果同时存在,则加上iu
,例如 subiu $t0 $t1 5
0x03 实例练习
在该题中,首先根据graph流程图,找到循环的位置:
在进循环前执行:sw $zero, 0x28+var_C($fp)
,在反编译中,进循环前第一步就是int i = 0
,由于$zero
寄存器中的值永远为0,所以可以知道该指令就是对内存的赋值,对于0x28+var_c($fp)
中的值进行赋值,那么该变量就是此时的i
,可以发现sw
就是从左往右赋值。在每次循环结束时,i++
,根据关键字寻找可以发现,跳转前执行以下指令:
|
|
与sw
相反的就是lw
,从右往左赋值,将临时变量i存入寄存器v0
中,然后将v0
的值加1
,通过sw
将值存回i
中。
对于lw
称为数据加载,将数据从内存加载到寄存器中,而sw
称为数据存储,将数据从寄存器存储到内存中,然后调用分支指令b
跳转到判断的位置:
|
|
首先加载地址到a0
寄存器中,通过之前的了解我们知道a0-a3
都是子函数的参数,所以继续往下,la
应该就是将子函数地址给到v0
寄存器,然后赋值到t9
寄存器中,通过jalr
指令进行子函数调用,此时a0
作为参数,其实就是输入的flag
的地址,调用完之后,将strlen
的结果加载到v0
寄存器中,往下就是将v0
的结果复制到v1
,然后取i
加载到v0
,执行sltu
对v0
和v1
进行比较,根据前面的学习,sltu
无符号数操作,所以此时sltu $v0, $v1
的含义是,if $v0 < $v1, $v0 = 1, else $v0 = 0
,往下的beqz
就是如果v0=0
则跳转到loc_4012E0
的位置继续执行,该位置就是提示成功的位置了:
往下便如法炮制了,不再做深入了解,通过本题大概学习了MIPS的指令格式以及其与x86架构的一些区别,后续如果遇到其他相关题目,再做深入的学习
参考文献
[1]https://blog.csdn.net/qq_41191281/article/details/85933985
[2]https://blog.csdn.net/ben_chong/article/details/51794093