CSAPP 实验:Bomb Lab
实验内容简述
- 作为实验目标的二进制炸弹 “Bomb Lab” Linux可执行程序包含了多个阶段(或关卡),在每个阶段程序要求输入一-个特定字符串,如果输入满足程序代码所定义的要求,该阶段的炸弹就被拆除了,否则程序输出 “炸弹爆炸BOOM!!!” 的提示并转到下一阶段再次等待对应的输入-实验的目标是设法得出解除尽可能多阶段的字符串。
- 为完成二进制炸弹拆除任务,需要通过反汇编并分析可执行炸弹文件程序的机器代码或使用
gdb
调试器跟踪机器代码的执行,从中理解关键机器指令的行为和作用,进而设法推断拆除炸弹所需的目标字符串。
实验环境
64 位 linux 操作系统
实验过程:phase 1
-
将可执行文件
bomb
反汇编objdump -d bomb > bomb.asm
-
查看源代码
可以了解到
phase_1
是调用函数名 -
在
bomb.asm
里搜索phase_1
,阅读源码含义
phase 1 调试过程
-
使用
gdb
调试,将断点设置在phase_1
,即0x400ee0
。运行后随便输入后查看0x402400
的内容(gdb) b *0x400ee0 Breakpoint 1 at 0x400ee0 (gdb) r Starting program: /mnt/d/OneDrive/csapp/bomb/bomb Welcome to my fiendish little bomb. You have 6 phases with which to blow yourself up. Have a nice day! dsasd Breakpoint 1, 0x0000000000400ee0 in phase_1 () (gdb) i r rip rip 0x400ee0 0x400ee0 <phase_1> (gdb) si 0x0000000000400ee4 in phase_1 () (gdb) si 0x0000000000400ee9 in phase_1 () (gdb) x/s 0x402400 0x402400: "Border relations with Canada have never been better."
-
可以从上面了解到字符串的答案就是:
Border relations with Canada have never been better.
实验过程:phase 2
-
查看
phase 2
汇编代码0000000000400efc <phase_2>: 400efc: 55 push %rbp 400efd: 53 push %rbx 400efe: 48 83 ec 28 sub $0x28,%rsp 400f02: 48 89 e6 mov %rsp,%rsi ----------------------分割线----------------------------------------;上面是函数的初始化 400f05: e8 52 05 00 00 callq 40145c <read_six_numbers>;读取六个数字 400f0a: 83 3c 24 01 cmpl $0x1,(%rsp);拿第一个数字与 1 进行比较 400f0e: 74 20 je 400f30 <phase_2+0x34>;如果相等就跳转到 400f30 400f10: e8 25 05 00 00 callq 40143a <explode_bomb>;否则,爆炸 400f15: eb 19 jmp 400f30 <phase_2+0x34> 400f17: 8b 43 fc mov -0x4(%rbx),%eax ;将第一个数字赋给 eax 400f1a: 01 c0 add %eax,%eax;将第一个数字*2 400f1c: 39 03 cmp %eax,(%rbx);将当前数字(eax) 与后一个数字 (rbx) 比较 400f1e: 74 05 je 400f25 <phase_2+0x29>;如果相等,就跳转到400f25 400f20: e8 15 05 00 00 callq 40143a <explode_bomb>;否则爆炸 400f25: 48 83 c3 04 add $0x4,%rbx;循环迭代条件 400f29: 48 39 eb cmp %rbp,%rbx;rbp 是栈底,也就是数组最后一个元素,作为循环判断的最后一个条件 400f2c: 75 e9 jne 400f17 <phase_2+0x1b>;如果没遍历到最后一个数字,就跳转到循环内400f17 400f2e: eb 0c jmp 400f3c <phase_2+0x40>;循环结束,跳转到最后函数结束后的处理400f3c 400f30: 48 8d 5c 24 04 lea 0x4(%rsp),%rbx ;将第二个数赋值给 rbx 400f35: 48 8d 6c 24 18 lea 0x18(%rsp),%rbp ;设置栈底,就是最后一个数字(0x18 == 24,输入的是6个数,所以是最后一个数) 400f3a: eb db jmp 400f17 <phase_2+0x1b>;跳到 400f17 进入循环开始的地方 ----------------------分割线----------------------------------------;之后都是函数结束后的处理 400f3c: 48 83 c4 28 add $0x28,%rsp 400f40: 5b pop %rbx 400f41: 5d pop %rbp 400f42: c3 retq
phase 2 调试过程
-
设置断点
(gdb) b phase_2 Breakpoint 1 at 0x400efc
-
运行并输入phase 1 的答案与 phase 2 推测出的答案
-
一直单步汇编运行,执行到
400f0a: cmpl $0x1,(%rsp);
此步说明第一个数字必须为1 -
比较第一个数字是否为 1
(gdb) x/wd $rsp 0x7ffffffedd70: 1
可以看到,
rsp
的值为1 ,就是我们输入的第一个值:1由此,通过
je 400f30
可知,两数相等,故跳转到400f30
-
通过单步汇编调试,查看
rsp
的值(gdb) si 0x0000000000400f30 in phase_2 () (gdb) si 0x0000000000400f35 in phase_2 ()
由此可以得出,
rbx
此时存放的是第二个数字2
,rbp
作为栈底,是最后一个数字的结尾,存放的是4199473
这个数字。 -
继续单步汇编调试,进入循环
400f17
此时执行完
400f17:mov -0x4(%rbx),%eax
eax` 的值为 1。如下图继续单步调试,执行到
add %eax,%eax
,对eax
进行 乘 2 的操作。 此时eax
为2 ,与我们输入的第二个数(rbx
存放) 相等 -
继续单步汇编调试,跳转到
400f25:add $0x4,%rbx
执行完
add $0x4,%rbx
,则选择了数组的下一个数字,判断循环是否是否结束。 -
之后一直执行到结束。
-
所以答案为:
1 2 4 8 16 32
实验过程:phase 3
- 查看
phase_3
汇编代码:
0000000000400f43 <phase_3>:
400f43: 48 83 ec 18 sub $0x18,%rsp
400f47: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx ;第二个参数
400f4c: 48 8d 54 24 08 lea 0x8(%rsp),%rdx ;第一个参数
400f51: be cf 25 40 00 mov $0x4025cf,%esi ;系统自带字符串"%d %d",2个参数
400f56: b8 00 00 00 00 mov $0x0,%eax
;----------------------------------------------------------------
400f5b: e8 90 fc ff ff callq 400bf0 <__isoc99_sscanf@plt>
400f60: 83 f8 01 cmp $0x1,%eax ;eax 与 1 比较。返回的是输入的参数个数
400f63: 7f 05 jg 400f6a <phase_3+0x27> ;如果大于 1,跳转400f6a
400f65: e8 d0 04 00 00 callq 40143a <explode_bomb>
400f6a: 83 7c 24 08 07 cmpl $0x7,0x8(%rsp) ;比较 7 与 第一个参数大小
400f6f: 77 3c ja 400fad <phase_3+0x6a> ;如果第一个参数大于7,爆炸
400f71: 8b 44 24 08 mov 0x8(%rsp),%eax ;把第一个参数赋给 eax
400f75: ff 24 c5 70 24 40 00 jmpq *0x402470(,%rax,8) ;跳转到 rax*8 + *0x402470
400f7c: b8 cf 00 00 00 mov $0xcf,%eax
400f81: eb 3b jmp 400fbe <phase_3+0x7b>
400f83: b8 c3 02 00 00 mov $0x2c3,%eax
400f88: eb 34 jmp 400fbe <phase_3+0x7b>
400f8a: b8 00 01 00 00 mov $0x100,%eax
400f8f: eb 2d jmp 400fbe <phase_3+0x7b>
400f91: b8 85 01 00 00 mov $0x185,%eax
400f96: eb 26 jmp 400fbe <phase_3+0x7b>
400f98: b8 ce 00 00 00 mov $0xce,%eax
400f9d: eb 1f jmp 400fbe <phase_3+0x7b>
400f9f: b8 aa 02 00 00 mov $0x2aa,%eax
400fa4: eb 18 jmp 400fbe <phase_3+0x7b>
400fa6: b8 47 01 00 00 mov $0x147,%eax
400fab: eb 11 jmp 400fbe <phase_3+0x7b>
400fad: e8 88 04 00 00 callq 40143a <explode_bomb>
400fb2: b8 00 00 00 00 mov $0x0,%eax
400fb7: eb 05 jmp 400fbe <phase_3+0x7b>
400fb9: b8 37 01 00 00 mov $0x137,%eax ;eax 为311(十进制)
400fbe: 3b 44 24 0c cmp 0xc(%rsp),%eax ;比较第二个参数与 eax
400fc2: 74 05 je 400fc9 <phase_3+0x86>;如果成功,拆弹成功
400fc4: e8 71 04 00 00 callq 40143a <explode_bomb>
;----------------------------------------------------------------
400fc9: 48 83 c4 18 add $0x18,%rsp
400fcd: c3 retq
分析:
我这个都是自己调试出来看到参数的,最开始第一个参数与第二个参数理解有误。结果最开始输入的竟然是第二个参数。
首先看到了调用函数是
__isoc99_sscanf
可以从中得知,返回的eax
为输入格式的个数,而最开始0x4025cf
的字符串就是%d %d
,可以了解到参数个数为 2 。之后让第一个参数与 7 比较,可以猜到第一个参数是一个选择做一个选择,选择是下面7个跳转(类似于
switch
)。最后可以在
400f75:jmpq *0x402470(,%rax,8)
观测,程序的跳转地址情况。
phase 3 调试过程
-
设置断点并运行
此时的
ans.txt
如下:Border relations with Canada have never been better. 1 2 4 8 16 32 1 311
-
单步汇编调试,直到执行完
400f5b:callq 400bf0 <__isoc99_sscanf@plt>
-
单步汇编调试,执行到
400f75:jmpq *0x402470(,%rax,8)
,观察对应0x402470
开始的 8 个单元对应的值。由
rax
保存的是第一个参数的值,我们输入的是 1,所以计算机结果是0x402470 + 1 * 8
即0x402478
单元对应的值0x400fb9
。 -
单步汇编调试,验证进入的单元。
-
要使第一个第二个参数与
0x137
相等,即十进制 311,最后拆除炸弹。
实验过程:phase 4
0000000000400fce <func4>:
400fce: 48 83 ec 08 sub $0x8,%rsp
400fd2: 89 d0 mov %edx,%eax ;eax = 14 (phase_4 输入的第三个参数)
400fd4: 29 f0 sub %esi,%eax ;eax = 14 - 0 = 14 (第二个参数减去第三个参数)
400fd6: 89 c1 mov %eax,%ecx ;ecx = 14
400fd8: c1 e9 1f shr $0x1f,%ecx;逻辑右移31 位,ecx 为0
400fdb: 01 c8 add %ecx,%eax ;eax = 14
400fdd: d1 f8 sar %eax ;算术右移1位,即÷2 eax = 7
400fdf: 8d 0c 30 lea (%rax,%rsi,1),%ecx ;ecx = 7
400fe2: 39 f9 cmp %edi,%ecx ;比较第一传入参数与 ecx = 7
400fe4: 7e 0c jle 400ff2 <func4+0x24> ;如果小于等于7,跳转400ff2
400fe6: 8d 51 ff lea -0x1(%rcx),%edx ;
400fe9: e8 e0 ff ff ff callq 400fce <func4>
400fee: 01 c0 add %eax,%eax
400ff0: eb 15 jmp 401007 <func4+0x39>
400ff2: b8 00 00 00 00 mov $0x0,%eax ;eax = 0
400ff7: 39 f9 cmp %edi,%ecx ;比较 7 与第一个传入参数的大小
400ff9: 7d 0c jge 401007 <func4+0x39>;如果第一个传入参数的大小大于等于7就正常返回
400ffb: 8d 71 01 lea 0x1(%rcx),%esi
400ffe: e8 cb ff ff ff callq 400fce <func4>
401003: 8d 44 00 01 lea 0x1(%rax,%rax,1),%eax
401007: 48 83 c4 08 add $0x8,%rsp
40100b: c3 retq
000000000040100c <phase_4>:
40100c: 48 83 ec 18 sub $0x18,%rsp
401010: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx;第二个参数
401015: 48 8d 54 24 08 lea 0x8(%rsp),%rdx;第一个参数
40101a: be cf 25 40 00 mov $0x4025cf,%esi;"%d %d"
40101f: b8 00 00 00 00 mov $0x0,%eax
;------------------------------------------------------------------------------------------
401024: e8 c7 fb ff ff callq 400bf0 <__isoc99_sscanf@plt>
401029: 83 f8 02 cmp $0x2,%eax;输入的参数个数是不是两个
40102c: 75 07 jne 401035 <phase_4+0x29>;不是,爆炸
40102e: 83 7c 24 08 0e cmpl $0xe,0x8(%rsp);将第一个参数和 0xe 比较
401033: 76 05 jbe 40103a <phase_4+0x2e>;如果小于等于,就继续
401035: e8 00 04 00 00 callq 40143a <explode_bomb>;否则爆炸
40103a: ba 0e 00 00 00 mov $0xe,%edx;调用函数第三个传入的实参,14
40103f: be 00 00 00 00 mov $0x0,%esi;调用函数第二个传入实参,0
401044: 8b 7c 24 08 mov 0x8(%rsp),%edi;调用函数第一个传入实参,也是输入的第一个参数
401048: e8 81 ff ff ff callq 400fce <func4>
40104d: 85 c0 test %eax,%eax ;递归返回值为0
40104f: 75 07 jne 401058 <phase_4+0x4c>;否则爆炸
401051: 83 7c 24 0c 00 cmpl $0x0,0xc(%rsp) ;经过func4 递归后,输入的第二个参数应为0
401056: 74 05 je 40105d <phase_4+0x51>;第二个参数不为0就爆炸
401058: e8 dd 03 00 00 callq 40143a <explode_bomb>
;------------------------------------------------------------------------------------------
40105d: 48 83 c4 18 add $0x18,%rsp
401061: c3 retq
分析:
首先从
callq 400bf0 <__isoc99_sscanf@plt>
可以知道又是调用sscanf
,上面的内容就是输入的参数。401010:lea 0xc(%rsp),%rcx
和401015:lea 0x8(%rsp),%rdx
可以得知,这是传入的第二个和第一个参数,传入的是两个参数。之后从
40103a:mov $0xe,%edx
可以得知,接下来这三行都是传递实参,为调用func4
做准备。func4
被调用的过程可以分析出第一个数字小于等于7,之后完成以后回到phase_4
的401051:cmpl $0x0,0xc(%rsp)
可以得知第二个参数为 0。故测试以下7 0
。
phase 4 调试过程
-
设置断点,并运行至
phase_4
此时的
ans.txt
如下:Border relations with Canada have never been better. 1 2 4 8 16 32 1 311 7 0
-
执行到调用
func4
的位置,中间确认输入参数 -
重点是
func4
的实现,这个过程需要的确定各个寄存器的值是否符合预期 -
最后拆弹成功
实验过程:phase 5
0000000000401062 <phase_5>:
401062: 53 push %rbx
401063: 48 83 ec 20 sub $0x20,%rsp
401067: 48 89 fb mov %rdi,%rbx ;第一个参数 -> rbx
40106a: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax ;接下来4行汇编是栈哨兵的检查(栈的保护机制,在编译时加上 -fno-stack-protector 就会被取消)
401071: 00 00
401073: 48 89 44 24 18 mov %rax,0x18(%rsp) ;将哨兵的值赋值到栈里
401078: 31 c0 xor %eax,%eax ;如果 eax 不为0,则说明栈被破坏
;---------------------------------------------------------------------------------
40107a: e8 9c 02 00 00 callq 40131b <string_length> ;判断字符串长度
40107f: 83 f8 06 cmp $0x6,%eax ;字符串长度必须为6
401082: 74 4e je 4010d2 <phase_5+0x70>
401084: e8 b1 03 00 00 callq 40143a <explode_bomb>
401089: eb 47 jmp 4010d2 <phase_5+0x70>
40108b: 0f b6 0c 03 movzbl (%rbx,%rax,1),%ecx ;ecx = Mem[rbx + rax(0) * 1] 此时ecx 指向字符串第一个字符a
40108f: 88 0c 24 mov %cl,(%rsp) ;取字符串的低8位值入栈
401092: 48 8b 14 24 mov (%rsp),%rdx ;输入字符串作为值(具体的ascii),之后在一长串字符串将输入字符串的值作为偏移量
401096: 83 e2 0f and $0xf,%edx ;与1111 取与运算后作为偏移量,也就是取低4位
401099: 0f b6 92 b0 24 40 00 movzbl 0x4024b0(%rdx),%edx ;edx = 'a' 0x4024b0 = "maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?"
;分别取第10位 f(I:74->0100 1001), 第 16 位 l(O:79->0100 1111), 第 15 位 y(N:78->0100 1110),第 6 位 e(E:69->0100 0101),第 7 位 r(F:70->0100 0110),第 8 位 s(G:71->0100 0111)
4010a0: 88 54 04 10 mov %dl,0x10(%rsp,%rax,1) ;rax = 0
4010a4: 48 83 c0 01 add $0x1,%rax ;rax = 1
4010a8: 48 83 f8 06 cmp $0x6,%rax ;循环终止的判断条件
4010ac: 75 dd jne 40108b <phase_5+0x29>
4010ae: c6 44 24 16 00 movb $0x0,0x16(%rsp)
4010b3: be 5e 24 40 00 mov $0x40245e,%esi ;第二个传入参数 0x40245e = "flyers"
4010b8: 48 8d 7c 24 10 lea 0x10(%rsp),%rdi ;第一个传入参数,rdi 0x10(%rsp)是保存的我们输入的地址,上面的操作就是将每个字符串赋值到0x10(%rsp)起始的地址单元里面
4010bd: e8 76 02 00 00 callq 401338 <strings_not_equal> ;要使得二者相等
4010c2: 85 c0 test %eax,%eax
4010c4: 74 13 je 4010d9 <phase_5+0x77> ;相等就结束
4010c6: e8 6f 03 00 00 callq 40143a <explode_bomb>
4010cb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
4010d0: eb 07 jmp 4010d9 <phase_5+0x77>
4010d2: b8 00 00 00 00 mov $0x0,%eax ;初始化 eax 为0
4010d7: eb b2 jmp 40108b <phase_5+0x29>
;---------------------------------------------------------------------------------
4010d9: 48 8b 44 24 18 mov 0x18(%rsp),%rax
4010de: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
4010e5: 00 00
4010e7: 74 05 je 4010ee <phase_5+0x8c>
4010e9: e8 42 fa ff ff callq 400b30 <__stack_chk_fail@plt>
4010ee: 48 83 c4 20 add $0x20,%rsp
4010f2: 5b pop %rbx
4010f3: c3 retq
分析:
首先能够判断是要输入 6 个字符串,然后随便输入6个字符进行调试,建议使用规律不同的字符串,这样能够知道传递的参数位置。我使用的是
abcdef
。接着当代码运行到
40108b:movzbl (%rbx,%rax,1),%ecx
以后,表示程序进入到循环体内,最后退出的条件是rax == 6
,而rax
的初始值为 0, 这个意思就是遍历完我们输入的字符串,循环体内的意思是取我们输入字符的值的低四位的值,我们输入的是字符,也就是该字符对应ascii
码的低四位。然后利用这个值作为一个索引,去0x4024b0
这个长的字符串中寻找f
、l
、y
、e
、r
、s
对应的下标,注意我注释里面添加说明的是第几位,但是具体下标要-1
操作。当代码运行到
4010b3:mov $0x40245e,%esi
的时候,可以看到这个就是在传递参数了,作为第二个实参,是系统赋予的,要将这个字符串flyers
和0x10(%rsp)
的值(也就是上面提及到的低四位ascii
码作为索引取出来的字符)进行比较,必须要二者相等。
综上,得出更新的ans.txt
Border relations with Canada have never been better.
1 2 4 8 16 32
1 311
7 0
IONEFG
phase 5 调试过程
-
设置断点在
phase_5
并运行 -
运行到进入循环体
40108b:movzbl (%rbx,%rax,1),%ecx
可以看到
movzbl (%rbx,%rax,1),%ecx
,注意rbx
最开始就用存储输入的字符串,此时rax
作为循环体初始变量 为 0。 -
执行到
401096
以后,完成了取第一个值的低四位的目的,此时再查看取到值是否为I
的低四位ascii
码的值 9可以看到值为 9,证明计算没错,之后可以直接运行
n
,直到结束。 -
运行
n
,直接验证是否正确
实验过程:phase 6
实在是太长了,暂时放弃😞
00000000004010f4 <phase_6>:
4010f4: 41 56 push %r14
4010f6: 41 55 push %r13
4010f8: 41 54 push %r12
4010fa: 55 push %rbp
4010fb: 53 push %rbx
4010fc: 48 83 ec 50 sub $0x50,%rsp
401100: 49 89 e5 mov %rsp,%r13
401103: 48 89 e6 mov %rsp,%rsi
401106: e8 51 03 00 00 callq 40145c <read_six_numbers>;读取六个数
;------------------------------------------------------------------------------------
40110b: 49 89 e6 mov %rsp,%r14 ;r14 最后一个数地址
40110e: 41 bc 00 00 00 00 mov $0x0,%r12d
401114: 4c 89 ed mov %r13,%rbp
401117: 41 8b 45 00 mov 0x0(%r13),%eax ;第一个数字
40111b: 83 e8 01 sub $0x1,%eax ; eax是返回的值的大小
40111e: 83 f8 05 cmp $0x5,%eax ; 第一个数字小于等于5就继续
401121: 76 05 jbe 401128 <phase_6+0x34>
401123: e8 12 03 00 00 callq 40143a <explode_bomb>
401128: 41 83 c4 01 add $0x1,%r12d ; 循环迭代器 i
40112c: 41 83 fc 06 cmp $0x6,%r12d ; 循环体,条件为6
401130: 74 21 je 401153 <phase_6+0x5f>
401132: 44 89 e3 mov %r12d,%ebx
401135: 48 63 c3 movslq %ebx,%rax
401138: 8b 04 84 mov (%rsp,%rax,4),%eax
40113b: 39 45 00 cmp %eax,0x0(%rbp)
40113e: 75 05 jne 401145 <phase_6+0x51>
401140: e8 f5 02 00 00 callq 40143a <explode_bomb>
401145: 83 c3 01 add $0x1,%ebx
401148: 83 fb 05 cmp $0x5,%ebx
40114b: 7e e8 jle 401135 <phase_6+0x41>
40114d: 49 83 c5 04 add $0x4,%r13
401151: eb c1 jmp 401114 <phase_6+0x20>
; ---------------------------------------------------------------------
; 循环分析比较麻烦,401151会跳转到401114这个是最外层,完成了%r13 + 0x4。即arr++
; %13初始的时候是数组首地址。
; 按照思路逻辑如下
; for( i=0;i<6;i++ ){
; if(arr[i] - 1 > 5) bomb()
; for(j=i+1;j<=5;j++){
; if(arr[j] == arr[i]) bomb()
; }
; }
401153: 48 8d 74 24 18 lea 0x18(%rsp),%rsi
401158: 4c 89 f0 mov %r14,%rax
40115b: b9 07 00 00 00 mov $0x7,%ecx
401160: 89 ca mov %ecx,%edx
401162: 2b 10 sub (%rax),%edx
401164: 89 10 mov %edx,(%rax)
401166: 48 83 c0 04 add $0x4,%rax
40116a: 48 39 f0 cmp %rsi,%rax
40116d: 75 f1 jne 401160 <phase_6+0x6c>
; -----------------------------------------------------------------
; rsi的值为rsp+0x18 = rsp+0x4*6即数组的结束位置。
; %rax = %r14往前看,是输入的数组首地址。
;
; for(int i=0;i<6;i++){
; arr[i] = 7 - arr[i];
; }
40116f: be 00 00 00 00 mov $0x0,%esi
401174: eb 21 jmp 401197 <phase_6+0xa3>
401176: 48 8b 52 08 mov 0x8(%rdx),%rdx
40117a: 83 c0 01 add $0x1,%eax
40117d: 39 c8 cmp %ecx,%eax
40117f: 75 f5 jne 401176 <phase_6+0x82>
401181: eb 05 jmp 401188 <phase_6+0x94>
401183: ba d0 32 60 00 mov $0x6032d0,%edx
401188: 48 89 54 74 20 mov %rdx,0x20(%rsp,%rsi,2)
40118d: 48 83 c6 04 add $0x4,%rsi
401191: 48 83 fe 18 cmp $0x18,%rsi
401195: 74 14 je 4011ab <phase_6+0xb7>
401197: 8b 0c 34 mov (%rsp,%rsi,1),%ecx
40119a: 83 f9 01 cmp $0x1,%ecx
40119d: 7e e4 jle 401183 <phase_6+0x8f>
40119f: b8 01 00 00 00 mov $0x1,%eax
4011a4: ba d0 32 60 00 mov $0x6032d0,%edx
4011a9: eb cb jmp 401176 <phase_6+0x82>
; --------------------------------------------------------------------------
; 借助流程图分析我们得到如下逻辑伪代码
; for(int i=0;i<6;i++){
; int cnt = arr[i];
; int add = 0x6032d0;
; for(int j=1;j<arr[i];j++){
; add = *(add+8);
; *(rsp + 2*i + 0x20) = add; // 8 字节
; }
; 查看下*(add+8)中的数据
; (gdb) x 0x6032d0+0x8
; 0x6032d8 <node1+8>: 0x006032e0
; (gdb) x 0x6032e0+0x8
; 0x6032e8 <node2+8>: 0x006032f0
; (gdb) x 0x6032f0+0x8
; 0x6032f8 <node3+8>: 0x00603300
; (gdb) x 0x603300+0x8
; 0x603308 <node4+8>: 0x00603310
; (gdb) x 0x603310+0x8
; 0x603318 <node5+8>: 0x00603320
4011ab: 48 8b 5c 24 20 mov 0x20(%rsp),%rbx
4011b0: 48 8d 44 24 28 lea 0x28(%rsp),%rax
4011b5: 48 8d 74 24 50 lea 0x50(%rsp),%rsi
4011ba: 48 89 d9 mov %rbx,%rcx
4011bd: 48 8b 10 mov (%rax),%rdx
4011c0: 48 89 51 08 mov %rdx,0x8(%rcx)
4011c4: 48 83 c0 08 add $0x8,%rax
4011c8: 48 39 f0 cmp %rsi,%rax
4011cb: 74 05 je 4011d2 <phase_6+0xde>
4011cd: 48 89 d1 mov %rdx,%rcx
4011d0: eb eb jmp 4011bd <phase_6+0xc9>
; ---------------------------------------------------------------
; narr 数组存放数据范围上面分析为 0x6032d0 - 0x603320
; int *narr = {};
; for(int i=1;i<6;i++){
; *(narr[i-1]+0x8) = narr[i]
; }
; 将narr[i-1]+0x8内存地址数据存放为narr[i]
4011d2: 48 c7 42 08 00 00 00 movq $0x0,0x8(%rdx)
4011d9: 00
4011da: bd 05 00 00 00 mov $0x5,%ebp
4011df: 48 8b 43 08 mov 0x8(%rbx),%rax
4011e3: 8b 00 mov (%rax),%eax
4011e5: 39 03 cmp %eax,(%rbx)
4011e7: 7d 05 jge 4011ee <phase_6+0xfa>
4011e9: e8 4c 02 00 00 callq 40143a <explode_bomb>
4011ee: 48 8b 5b 08 mov 0x8(%rbx),%rbx
4011f2: 83 ed 01 sub $0x1,%ebp
4011f5: 75 e8 jne 4011df <phase_6+0xeb>
;----------------------------------------------------------------------------------------
; eax : *(narr[0]+0x8) => narr[1] => *(narr[1])
; (%rbx) : *(narr[0])
; for(int i=1;i<=5;i++){
; if( *(narr[i-1]) < *(narr[i]) ) bomb()
; }
4011f7: 48 83 c4 50 add $0x50,%rsp
4011fb: 5b pop %rbx
4011fc: 5d pop %rbp
4011fd: 41 5c pop %r12
4011ff: 41 5d pop %r13
401201: 41 5e pop %r14
401203: c3 retq
一些其他收获
-
下载一些
cmu
的code
wget -r -np -nH --cut-dirs=3 -R index.html 网站地址
e.g.
wget -r -np -nH --cut-dirs=3 -R index.html https://www.cs.cmu.edu/~213/code/06-machine-control/