计算机系统原理的实验,参考了我的前舍友和不知道哪位同学的博客,不过写得也太简略了并且还有一些错误,更像是单纯的记录,对手头没代码的人大概没啥意义吧。所以就把我自己做时的带注释的代码贴住来吧,也不过是记录罢了。
两位同学的博客:
MIPS - 反汇编 - 拆炸弹 - bomb(如你所见直接把标题抄过来了)
bomb二进制炸弹拆除实验(MIPS)
如果有同校学弟做实验时看到这篇博客,还是希望可以先尽量尝试独立完成,实在没办法时再来参考。如有错误,欢迎指正。
前导知识
gdb操作请看:GDB汇编调试指令合集
MIPS相关知识请看:MIPS指令集及汇编完全解析
开拆前…
本实验含有六个炸弹(和一个隐藏炸弹 ),在运行程序开头,需要输入ID(学号),在之后的拆弹过程中会使用到。每个炸弹都需要输入一行数据,包括数字或字符串。需避开一个个引爆点来通关。
阅读反汇编代码时,最好能够前后联系,理解数行/一部分代码的功能。要逐渐熟练掌握MIPS指令集操作和寄存器含义,了解数据存储的位置和方式。
有???标记的是我暂时抱有疑惑的地方,可以忽视。
本文还未完成,随缘更新。
phase_1
第一个炸弹,主要是熟悉用。炸弹会调用函数strings_not_equal来比较输入字符串与存储的字符串是否相等,不需要理解该子函数的实现,只需要明白该函数的功能即可。读寄存器$a1中保存的字符串,为“Let’s begin now!”,即为我们要输入的内容。
00400900 <main>:
...
400ba4: 8fdc0010 lw gp,16(s8)
400ba8: 0c1007fb jal 401fec <read_line> //读取输入
400bac: 00000000 nop
400bb0: 8fdc0010 lw gp,16(s8)
400bb4: afc20020 sw v0,32(s8) //将读得的数据存入栈
400bb8: 8fc40020 lw a0,32(s8) //并放入$a0中供函数phase_1使用
400bbc: 0c10035b jal 400d6c <phase_1>
...
00400d6c <phase_1>:
400d6c: 27bdffe0 addiu sp,sp,-32 //为当前函数开辟栈空间
400d70: afbf001c sw ra,28(sp)
400d74: afbe0018 sw s8,24(sp)
400d78: 03a0f021 move s8,sp
400d7c: afc40020 sw a0,32(s8) //将$a0的内容存入栈
400d80: 8fc40020 lw a0,32(s8)
400d84: 3c020040 lui v0,0x40
400d88: 2445276c addiu a1,v0,10092 //加载参数到$a1
400d8c: 0c10073e jal 401cf8 <strings_not_equal> //比较字符串$a0和$a1,不相等则$v0 = 1,否则$v0 = 1
400d90: 00000000 nop
400d94: 10400003 beqz v0,400da4 <phase_1+0x38> //若$v0 == 0则跳至0x00400da4处
400d98: 00000000 nop
400d9c: 0c10087c jal 4021f0 <explode_bomb> //否则若$v0 == 0则爆炸
400da0: 00000000 nop
400da4: 03c0e821 move sp,s8
400da8: 8fbf001c lw ra,28(sp)
400dac: 8fbe0018 lw s8,24(sp)
400db0: 27bd0020 addiu sp,sp,32
400db4: 03e00008 jr ra
400db8: 00000000 nop
phase_2
该炸弹要求我们输入六个数字(看函数名称识功能,第1个数必须为1),之后进入循环中一个个对这些数字进行处理,将第i个数分别与学号倒数i位相乘结果与第i+1个数比较判断是否相等,意味着我们输入的六个数就是对ID后五位累乘的序列。为什么知道是ID后五位?读寄存器得到的。ID不够五位?那就相当于定义了一个变量却没有赋值,一样能解决这个炸弹。这个炸弹不理解代码强行在0x400e80处读$a0的值并反复尝试令其与$v0相等也能解决,当然不推荐就是了。
00400dbc <phase_2>:
400dbc: 27bdffc0 addiu sp,sp,-64
400dc0: afbf003c sw ra,60(sp)
400dc4: afbe0038 sw s8,56(sp)
400dc8: 03a0f021 move s8,sp
400dcc: 3c1c0042 lui gp,0x42
400dd0: 279cb190 addiu gp,gp,-20080
400dd4: afbc0010 sw gp,16(sp)
400dd8: afc40040 sw a0,64(s8)
400ddc: 27c2001c addiu v0,s8,28
400de0: 8fc40040 lw a0,64(s8)
400de4: 00402821 move a1,v0
400de8: 0c1006ea jal 401ba8 <read_six_numbers> //读入6个数字
400dec: 00000000 nop
400df0: 8fdc0010 lw gp,16(s8)
400df4: 8fc3001c lw v1,28(s8) //m[$s8+28]存有读入的第一个数,将其存入$v1
400df8: 24020001 li v0,1
400dfc: 10620004 beq v1,v0,400e10 <phase_2+0x54> //判定$v1是否为1,不是则引爆
400e00: 00000000 nop
400e04: 0c10087c jal 4021f0 <explode_bomb>
400e08: 00000000 nop
400e0c: 8fdc0010 lw gp,16(s8)
400e10: 24020001 li v0,1 //设定循环单次表达式$v0 = 1(i = 1)
400e14: afc20018 sw v0,24(s8) //将$v0(i)存入栈
400e18: 10000023 b 400ea8 <phase_2+0xec> //循环开始,跳至条件表达式
400e1c: 00000000 nop
400e20: 8fc20018 lw v0,24(s8) //将之前存在栈中的变量i取出
400e24: 00000000 nop
400e28: 2442ffff addiu v0,v0,-1 //$v0自减
400e2c: 00021080 sll v0,v0,0x2 //$v0 = $v0 * 4(拓到1 int长度)
400e30: 27c30018 addiu v1,s8,24 //第i个数的存放位置为m[$s8 + 24 + i * 4](一个int型变量占4个字节)
400e34: 00621021 addu v0,v1,v0
400e38: 8c440004 lw a0,4(v0) //将第i个数的值存入$a0(所以前面自减有意义吗)
400e3c: 2403000c li v1,12
400e40: 8fc20018 lw v0,24(s8) //将之前存在栈中的变量i取出
400e44: 00000000 nop
400e48: 00621023 subu v0,v1,v0 //$v0 = $v1(12) - $v0(i)
400e4c: 8f83806c lw v1,-32660(gp) //读得输入的学号
400e50: 00021080 sll v0,v0,0x2 //$v0 = $v0 * 4(拓到1 int长度)
400e54: 00621021 addu v0,v1,v0 //$v0 = $v1 + $v0
400e58: 8c420000 lw v0,0(v0) //将学号的倒数i位读入$v0中
400e5c: 00000000 nop
400e60: 00820018 mult a0,v0 //$a0与$v0相乘
400e64: 00002012 mflo a0 //将结果存入$a0
400e68: 8fc20018 lw v0,24(s8) //将之前存在栈中的变量i取出
400e6c: 00000000 nop
400e70: 00021080 sll v0,v0,0x2 //$v0 = $v0 * 4(拓到1 int长度)
400e74: 27c30018 addiu v1,s8,24 //第i个数得存放位置为m[$s8 + 24 + i * 4](一个int型变量占4个字节)
400e78: 00621021 addu v0,v1,v0 //得到第i个数的位置
400e7c: 8c420004 lw v0,4(v0) //将第i + 1个数的值存入$v0
400e80: 00000000 nop
400e84: 10820004 beq a0,v0,400e98 <phase_2+0xdc> //比较$a0(第i个数与学号的倒数i位相乘的结果)与$v0(第i + 1个数),若不相等则引爆
400e88: 00000000 nop
400e8c: 0c10087c jal 4021f0 <explode_bomb>
400e90: 00000000 nop
400e94: 8fdc0010 lw gp,16(s8) //末尾循环体
400e98: 8fc20018 lw v0,24(s8)
400e9c: 00000000 nop
400ea0: 24420001 addiu v0,v0,1 //i自加
400ea4: afc20018 sw v0,24(s8) //将i存入栈中
400ea8: 8fc20018 lw v0,24(s8)
400eac: 00000000 nop
400eb0: 28420006 slti v0,v0,6 //判断循环是否结束,相当于i != 6(i < 6)
400eb4: 1440ffda bnez v0,400e20 <phase_2+0x64>
400eb8: 00000000 nop
400ebc: 03c0e821 move sp,s8
400ec0: 8fbf003c lw ra,60(sp)
400ec4: 8fbe0038 lw s8,56(sp)
400ec8: 27bd0040 addiu sp,sp,64
400ecc: 03e00008 jr ra
400ed0: 00000000 nop
phase_3
考察分支结构(switch…case…结构)。要求输入格式为"%d %c %d"(看调用数据时的指令得到的,word/byte的区别),第一个数就是switch语句中的变量,跳至不同的case语句(0到7共八个,超出直接引爆)。除case 3外,其他数均是要求一个数与输入的第三个数相乘的结果为指定的立即数,而case 3要求相乘结果为0。读寄存器可知该数为输入ID的最后一位。另外在每个case结构的开头都将一个字符存入栈中,在switch…case…结束后与我们输入的字符比较,令两个字符相等即可。
00400ed4 <phase_3>:
400ed4: 27bdffc8 addiu sp,sp,-56
400ed8: afbf0034 sw ra,52(sp)
400edc: afbe0030 sw s8,48(sp)
400ee0: 03a0f021 move s8,sp
400ee4: 3c1c0042 lui gp,0x42
400ee8: 279cb190 addiu gp,gp,-20080
400eec: afbc0018 sw gp,24(sp)
400ef0: afc40038 sw a0,56(s8)
400ef4: 8fc40038 lw a0,56(s8)
400ef8: 3c020040 lui v0,0x40
400efc: 24452780 addiu a1,v0,10112
400f00: 27c3002c addiu v1,s8,44
400f04: 27c20028 addiu v0,s8,40
400f08: 27c60024 addiu a2,s8,36
400f0c: afa60010 sw a2,16(sp)
400f10: 00603021 move a2,v1
400f14: 00403821 move a3,v0
400f18: 8f828084 lw v0,-32636(gp)
400f1c: 00000000 nop
400f20: 0040c821 move t9,v0
400f24: 0320f809 jalr t9
400f28: 00000000 nop
400f2c: 8fdc0018 lw gp,24(s8)
400f30: 28420003 slti v0,v0,3 //判定输入的数据个数是否为3
400f34: 10400004 beqz v0,400f48 <phase_3+0x74>
400f38: 00000000 nop
400f3c: 0c10087c jal 4021f0 <explode_bomb>
400f40: 00000000 nop
400f44: 8fdc0018 lw gp,24(s8)
400f48: 8fc2002c lw v0,44(s8) //读入输入的第一个数至$v0
400f4c: 00000000 nop
400f50: 2c430008 sltiu v1,v0,8 //判断$v0是否小于8,是则继续,否则引爆
400f54: 1060008e beqz v1,401190 <phase_3+0x2bc>
400f58: 00000000 nop
400f5c: 00021880 sll v1,v0,0x2 //$v1 = $v0 * 4,拓至1 int型变量长度
400f60: 3c020040 lui v0,0x40
400f64: 2442278c addiu v0,v0,10124
400f68: 00621021 addu v0,v1,v0
400f6c: 8c420000 lw v0,0(v0) //$v0对应case语句的地址,用x $v0查看
400f70: 00000000 nop
400f74: 00400008 jr v0 //switch语句,跳至相应的case语句
400f78: 00000000 nop //case 0:
400f7c: 24020071 li v0,113 //q的ASCII码
400f80: a3c20020 sb v0,32(s8) //存入1byte的数据
400f84: 8f82806c lw v0,-32660(gp)
400f88: 00000000 nop
400f8c: 8c43002c lw v1,44(v0) //读入学号的最后一位
400f90: 8fc20024 lw v0,36(s8) //读入输入数据的第三个数
400f94: 00000000 nop
400f98: 00620018 mult v1,v0
400f9c: 00001812 mflo v1 //$v1 = $v1 * $v0
400fa0: 24020309 li v0,777 //判断是否为777(=3*=7*111),是则break,否则引爆
400fa4: 10620081 beq v1,v0,4011ac <phase_3+0x2d8>
400fa8: 00000000 nop
400fac: 0c10087c jal 4021f0 <explode_bomb>
400fb0: 00000000 nop
400fb4: 8fdc0018 lw gp,24(s8)
400fb8: 1000008f b 4011f8 <phase_3+0x324>
400fbc: 00000000 nop //case 1:
400fc0: 24020062 li v0,98 //b的ASCII码
400fc4: a3c20020 sb v0,32(s8) //存入1byte的数据
400fc8: 8f82806c lw v0,-32660(gp)
400fcc: 00000000 nop
400fd0: 8c43002c lw v1,44(v0) //读入学号的最后一位
400fd4: 8fc20024 lw v0,36(s8) //读入输入数据的第三个数
400fd8: 00000000 nop
400fdc: 00620018 mult v1,v0
400fe0: 00001812 mflo v1 //$v1 = $v1 * $v0
400fe4: 240200d6 li v0,214 //判断是否为214(=2*107),是则break,否则引爆
400fe8: 10620073 beq v1,v0,4011b8 <phase_3+0x2e4>
400fec: 00000000 nop
400ff0: 0c10087c jal 4021f0 <explode_bomb>
400ff4: 00000000 nop
400ff8: 8fdc0018 lw gp,24(s8)
400ffc: 1000007e b 4011f8 <phase_3+0x324>
401000: 00000000 nop //case 2:
401004: 24020062 li v0,98 //b的ASCII码
401008: a3c20020 sb v0,32(s8) //存入1byte的数据
40100c: 8f82806c lw v0,-32660(gp)
401010: 00000000 nop
401014: 8c43002c lw v1,44(v0) //读入学号的最后一位
401018: 8fc20024 lw v0,36(s8) //读入输入数据的第三个数
40101c: 00000000 nop
401020: 00620018 mult v1,v0
401024: 00001812 mflo v1 //$v1 = $v1 * $v0
401028: 240202f3 li v0,755 //判断是否为755(=5*151),是则break,否则引爆
40102c: 10620065 beq v1,v0,4011c4 <phase_3+0x2f0>
401030: 00000000 nop
401034: 0c10087c jal 4021f0 <explode_bomb>
401038: 00000000 nop
40103c: 8fdc0018 lw gp,24(s8)
401040: 1000006d b 4011f8 <phase_3+0x324>
401044: 00000000 nop //case 3:
401048: 2402006b li v0,107 //k的ASCII码
40104c: a3c20020 sb v0,32(s8) //存入1byte的数据
401050: 8f82806c lw v0,-32660(gp)
401054: 00000000 nop
401058: 8c43002c lw v1,44(v0) //读入学号的最后一位
40105c: 8fc20024 lw v0,36(s8) //读入输入数据的第三个数
401060: 00000000 nop
401064: 00620018 mult v1,v0
401068: 00001012 mflo v0 //$v0 = $v1 * $v0
40106c: 10400058 beqz v0,4011d0 <phase_3+0x2fc> //判断$v0是否等于0,是则break,否则引爆
401070: 00000000 nop
401074: 0c10087c jal 4021f0 <explode_bomb>
401078: 00000000 nop
40107c: 8fdc0018 lw gp,24(s8)
401080: 1000005d b 4011f8 <phase_3+0x324>
401084: 00000000 nop //case 4:
401088: 2402006f li v0,111 //o的ASCII码
40108c: a3c20020 sb v0,32(s8) //存入1byte的数据
401090: 8f82806c lw v0,-32660(gp)
401094: 00000000 nop
401098: 8c43002c lw v1,44(v0) //读入学号的最后一位
40109c: 8fc20024 lw v0,36(s8) //读入输入数据的第三个数
4010a0: 00000000 nop
4010a4: 00620018 mult v1,v0
4010a8: 00001812 mflo v1 //$v1 = $v1 * $v0
4010ac: 240200e4 li v0,228 //判断是否为228(=2*114=4*57),是则break,否则引爆 ???
4010b0: 1062004a beq v1,v0,4011dc <phase_3+0x308>
4010b4: 00000000 nop
4010b8: 0c10087c jal 4021f0 <explode_bomb>
4010bc: 00000000 nop
4010c0: 8fdc0018 lw gp,24(s8)
4010c4: 1000004c b 4011f8 <phase_3+0x324>
4010c8: 00000000 nop //case 5:
4010cc: 24020074 li v0,116 //t的ASCII码
4010d0: a3c20020 sb v0,32(s8) //存入1byte的数据
4010d4: 8f82806c lw v0,-32660(gp)
4010d8: 00000000 nop
4010dc: 8c43002c lw v1,44(v0) //读入学号的最后一位
4010e0: 8fc20024 lw v0,36(s8) //读入输入数据的第三个数
4010e4: 00000000 nop
4010e8: 00620018 mult v1,v0
4010ec: 00001812 mflo v1 //$v1 = $v1 * $v0
4010f0: 24020201 li v0,513 //判断是否为513(=3*171=9*57),是则break,否则引爆
4010f4: 1062003c beq v1,v0,4011e8 <phase_3+0x314>
4010f8: 00000000 nop
4010fc: 0c10087c jal 4021f0 <explode_bomb>
401100: 00000000 nop
401104: 8fdc0018 lw gp,24(s8)
401108: 1000003b b 4011f8 <phase_3+0x324>
40110c: 00000000 nop //case 6:
401110: 24020076 li v0,118 //v的ASCII码
401114: a3c20020 sb v0,32(s8) //存入1byte的数据
401118: 8f82806c lw v0,-32660(gp)
40111c: 00000000 nop
401120: 8c43002c lw v1,44(v0) //读入学号的最后一位
401124: 8fc20024 lw v0,36(s8) //读入输入数据的第三个数
401128: 00000000 nop
40112c: 00620018 mult v1,v0
401130: 00001812 mflo v1 //$v1 = $v1 * $v0
401134: 2402030c li v0,780 //判断是否为780(=2*390=3*260=4*195=5*156=6*130),是则break,否则引爆
401138: 10620004 beq v1,v0,40114c <phase_3+0x278>
40113c: 00000000 nop
401140: 0c10087c jal 4021f0 <explode_bomb>
401144: 00000000 nop //case 7:
401148: 8fdc0018 lw gp,24(s8) ???
40114c: 24020062 li v0,98 //b的ASCII码
401150: a3c20020 sb v0,32(s8) //存入1byte的数据
401154: 8f82806c lw v0,-32660(gp)
401158: 00000000 nop
40115c: 8c43002c lw v1,44(v0) //读入学号的最后一位
401160: 8fc20024 lw v0,36(s8) //读入输入数据的第三个数
401164: 00000000 nop
401168: 00620018 mult v1,v0
40116c: 00001812 mflo v1 //$v1 = $v1 * $v0
401170: 24020338 li v0,824 //判断是否为824(=2*412=4*206),是则break,否则引爆
401174: 1062001f beq v1,v0,4011f4 <phase_3+0x320>
401178: 00000000 nop
40117c: 0c10087c jal 4021f0 <explode_bomb>
401180: 00000000 nop
401184: 8fdc0018 lw gp,24(s8)
401188: 1000001b b 4011f8 <phase_3+0x324>
40118c: 00000000 nop
401190: 24020078 li v0,120
401194: a3c20020 sb v0,32(s8)
401198: 0c10087c jal 4021f0 <explode_bomb>
40119c: 00000000 nop
4011a0: 8fdc0018 lw gp,24(s8)
4011a4: 10000014 b 4011f8 <phase_3+0x324>
4011a8: 00000000 nop
4011ac: 00000000 nop
4011b0: 10000011 b 4011f8 <phase_3+0x324