CSAPP:Bomb_Lab
准备工作
-
一些命令
evince name.pdf //查看PDF文件 (gdb) disas phase_1//查看汇编 -
下载脚本:(Ubuntu 22.04)
wget https://gitee.com/lin-xi-269/csapplab/raw/origin/installAll.sh # 下载脚本 bash installAll.sh # 运行脚本 -
tmux使用
tmux new -s name//新建 tmux ls tmux a -t name tmux attach -t tmux a -t name //简写 tmux kill-session -t name # 选择需要跳转的session会话 C + b s # 断开当前session C + b d # 在当前session中多加一个window C + b c C + b p//向上切换 C + b n//向下切换 # 关闭当前session中的当前window C + b x C + x 关闭当前窗格 # 常用快捷键 C + b ! 关闭一个session中所有窗口 C + b % 将当前窗口分成左右两分 C + b " 将当前窗口分成上下两分 C + b 方向键 #让光标在不同的窗口中跳转 C + b 方向键 #按住C+b不放,同时按住方向键,可以调节光标所在窗口的大小
phase_1
-
汇编分析
000000000400ee0 <phase_1>: 400ee0: 48 83 ec 08 sub $0x8,%rsp #栈偏移减少:为此函数在栈上开内存 400ee4: be 00 24 40 00 mov $0x402400,%esi #将某个神秘的地址存入%esi 400ee9: e8 4a 04 00 00 call 401338 <strings_not_equal> #调用函数:字符串比较函数 400eee: 85 c0 test %eax,%eax #检测两个寄存器的值是否相等 400ef0: 74 05 je 400ef7 <phase_1+0x17> #如果相等跳转到400ef7 400ef2: e8 43 05 00 00 call 40143a <explode_bomb> #不相等调用函数:炸弹爆炸 400ef7: 48 83 c4 08 add $0x8,%rsp #指针加8,指向下一个指令 400efb: c3 ret -
思路:
-
莫名其妙出现的地址0x402400是关键:需要找出此地址中存储的字符串,但如何以字符输出数据是个问题,而且字符串长度不确定。
-
推断:%eax应该是作为中间寄存器沟通phase_1和strings_not_equal两个函数。
-
-
解决方案:
(gdb) x/s 402400 0x402400: "Border relations with Canada have never been better."//答案 -
知识获取:
-
examine 命令(简写是 x )来查看内存地址中的值 x/nfu addr n 是一个正整数,表示显示内存的长度,从当前地址向后显示几个地址的内容 f 表示显示的格式:x、d、u、s等 u 表示从当前地址往后请求的字节数
-
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 call 40145c <read_six_numbers> #密码是六个数字 400f0a: 83 3c 24 01 cmpl $0x1,(%rsp) #观察因果关系,所以第一个数字应该是1 400f0e: 74 20 je 400f30 <phase_2+0x34> 400f10: e8 25 05 00 00 call 40143a <explode_bomb> 400f15: eb 19 jmp 400f30 <phase_2+0x34> 400f17: 8b 43 fc mov -0x4(%rbx),%eax #将前一个数字读出放入%rax寄存器内 400f1a: 01 c0 add %eax,%eax #2倍 400f1c: 39 03 cmp %eax,(%rbx) #判定,如果成功继续循环,证明第二个数是第一个数二倍 400f1e: 74 05 je 400f25 <phase_2+0x29> 400f20: e8 15 05 00 00 call 40143a <explode_bomb> 400f25: 48 83 c3 04 add $0x4,%rbx 400f29: 48 39 eb cmp %rbp,%rbx #循环条件 400f2c: 75 e9 jne 400f17 <phase_2+0x1b> #循环 400f2e: eb 0c jmp 400f3c <phase_2+0x40> #循环终止,因为看到400f3c之后直接pop了 400f30: 48 8d 5c 24 04 lea 0x4(%rsp),%rbx #%rbx存储下一个数字 400f35: 48 8d 6c 24 18 lea 0x18(%rsp),%rbp #类似于设置循环终止条件,不能超过分配的28字节 400f3a: eb db jmp 400f17 <phase_2+0x1b> #循环主体 400f3c: 48 83 c4 28 add $0x28,%rsp 400f40: 5b pop %rbx 400f41: 5d pop %rbp 400f42: c3 ret -
思路:
- 首先需要大胆猜出第一个数字是1,其次需要勇敢往下尝试(勇敢地走入循环)。
- 对%eax寄存器存储的值要时刻关注。
-
答案
1 2 4 8 16 32 -
知识获取
-
400efc: 55 push %rbp为什么要push这两个寄存器 400efd: 53 push %rbx 函数A调用了函数B,寄存器rbx在函数B中被修改了,逻辑上%rbx内容在调用函数B的前后应该保持一致, 解决这个问题有两个策略, (1)在函数A在调用函数B之前提前保存寄存器%rbx的内容,执行完函数B之后再恢复%rbx的内容,这个 策略就称为调用者保存; (2)函数B在使用寄存器%rbx,先保存寄存器%rbx的值,在函数B返回之前,要恢复寄存器%rbx原来存储 的内容,这种策略被称之为被调用者保存。
-
phase_3
-
汇编分析
0000000000400f43 <phase_3>: 400f43: 48 83 ec 18 sub $0x18,%rsp 400f47: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx #神秘数字a 400f4c: 48 8d 54 24 08 lea 0x8(%rsp),%rdx #神秘数字b 400f51: be cf 25 40 00 mov $0x4025cf,%esi #关键!!! 400f56: b8 00 00 00 00 mov $0x0,%eax #p1 400f5b: e8 90 fc ff ff call 400bf0 <__isoc99_sscanf@plt> #函数调用 400f60: 83 f8 01 cmp $0x1,%ea #p2:判断输入的值是否大于2 400f63: 7f 05 jg 400f6a <phase_3+0x27> 400f65: e8 d0 04 00 00 call 40143a <explode_bomb> 400f6a: 83 7c 24 08 07 cmpl $0x7,0x8(%rsp) 400f6f: 77 3c ja 400fad <phase_3+0x6a> #说明a值小于7,否则爆炸 400f71: 8b 44 24 08 mov 0x8(%rsp),%eax #将a值存入%rax 400f75: ff 24 c5 70 24 40 00 jmp *0x402470(,%rax,8) #关键!!! 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 call 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 400fbe: 3b 44 24 0c cmp 0xc(%rsp),%eax #比较b和之前存入的值 400fc2: 74 05 je 400fc9 <phase_3+0x86 #跳转 400fc4: e8 71 04 00 00 call 40143a <explode_bomb> 400fc9: 48 83 c4 18 add $0x18,%rsp 400fcd: c3 ret -
思路:(菜鸡思路)
-
对于神秘数字a和b:请先看下一条;推测出a和b可能存储的是我们输入的两个数字。
-
突破口
400f51: be cf 25 40 00 mov $0x4025cf,%esi (gdb) x/s 0x4025cf 0x4025cf "%d %d"首先我承认我是有蒙的成分在这里,我想看一下这块内存里存入的是什么,然后运用x命令试了亿下,出现能看懂的结果,再结合call了scanf,推测出我们输入的密码会是两个数字。
-
p1,p2:看到p2时,觉得很奇怪,如果没有任何操作的情况下,这两条指令后半部分表示的内存是等价的,所以在p1时,%eax肯定写入了什么东西,再结合中间的函数调用,所以推出%eax存了scanf的返回值。更加证明了应该输入两个数字。
-
400f75: ff 24 c5 70 24 40 00 jmp *0x402470(,%rax,8) (gdb) x/8xg 0x402470//找出跳转表 0x402470: 0x0000000000400f7c 0x0000000000400fb9 0x402480: 0x0000000000400f83 0x0000000000400f8a 0x402490: 0x0000000000400f91 0x0000000000400f98 0x4024a0: 0x0000000000400f9f 0x0000000000400fa6这块真是卡了好久,此命令意思为跳转到(8×%rax+0x402470)处,但是眼瞎的我没注意到这个寄存器的名字是 ax,与上一条中的eax是一样的,所以就清楚了,是将b的值乘以8加上首地址(看到那么多mov、jmp应该很容易理解到这是个switch语句,0x402470处是跳转表),接着x,根据结果进行匹配跳转。
-
-
答案(不唯一)
1 311
phase_4
-
汇编分析
0x000000000040100c <+0>: sub $0x18,%rsp 0x0000000000401010 <+4>: lea 0xc(%rsp),%rcx 0x0000000000401015 <+9>: lea 0x8(%rsp),%rdx 0x000000000040101a <+14>: mov $0x4025cf,%esi #好熟悉 0x000000000040101f <+19>: mov $0x0,%eax 0x0000000000401024 <+24>: call 0x400bf0 <__isoc99_sscanf@plt> 0x0000000000401029 <+29>: cmp $0x2,%eax 0x000000000040102c <+32>: jne 0x401035 <phase_4+41> #输入不等于2个数时跳转到bomb 0x000000000040102e <+34>: cmpl $0xe,0x8(%rsp) 0x0000000000401033 <+39>: jbe 0x40103a <phase_4+46> #a小于等于14跳转,否则bomb 0x0000000000401035 <+41>: call 0x40143a <explode_bomb> 0x000000000040103a <+46>: mov $0xe,%edx 0x000000000040103f <+51>: mov $0x0,%esi 0x0000000000401044 <+56>: mov 0x8(%rsp),%edi #a-->%edi 0x0000000000401048 <+60>: call 0x400fce <func4> 0x000000000040104d <+65>: test %eax,%eax 0x000000000040104f <+67>: jne 0x401058 <phase_4+76> #%eax等于0则不会爆炸 0x0000000000401051 <+69>: cmpl $0x0,0xc(%rsp) 0x0000000000401056 <+74>: je 0x40105d <phase_4+81> #说明b值为0 0x0000000000401058 <+76>: call 0x40143a <explode_bomb> 0x000000000040105d <+81>: add $0x18,%rsp 0x0000000000401061 <+85>: ret 0x0000000000400fce <+0>: sub $0x8,%rsp 0x0000000000400fd2 <+4>: mov %edx,%eax #14-->%eax 0x0000000000400fd4 <+6>: sub %esi,%eax #14-0-->%eax 0x0000000000400fd6 <+8>: mov %eax,%ecx #14-->%ecx 0x0000000000400fd8 <+10>: shr $0x1f,%ecx #逻辑右移31位%ecx-->0 0x0000000000400fdb <+13>: add %ecx,%eax #0+14-->%eax 0x0000000000400fdd <+15>: sar %eax #算术右移1位%eax-->7 0x0000000000400fdf <+17>: lea (%rax,%rsi,1),%ecx #7-->%ecx 0x0000000000400fe2 <+20>: cmp %edi,%ecx #a,7 0x0000000000400fe4 <+22>: jle 0x400ff2 <func4+36> #小于等于则跳转 0x0000000000400fe6 <+24>: lea -0x1(%rcx),%edx #7-1-->%rcx 0x0000000000400fe9 <+27>: call 0x400fce <func4> #递归调用 0x0000000000400fee <+32>: add %eax,%eax #2倍 0x0000000000400ff0 <+34>: jmp 0x401007 <func4+57> #跳转到出口 0x0000000000400ff2 <+36>: mov $0x0,%eax #0-->%eax 0x0000000000400ff7 <+41>: cmp %edi,%ecx 0x0000000000400ff9 <+43>: jge 0x401007 <func4+57> #大于等于则跳转 0x0000000000400ffb <+45>: lea 0x1(%rcx),%esi 0x0000000000400ffe <+48>: call 0x400fce <func4> 0x0000000000401003 <+53>: lea 0x1(%rax,%rax,1),%eax 0x0000000000401007 <+57>: add $0x8,%rsp 0x000000000040100b <+61>: ret- 思路:解题关键就是理清if-else的关系,而%eax只有为0时才不会bomb,所以结合func4函数的结构反推a的值,当然也可以不用推直接看出来7是答案之一。
我用一种汇编和c结合出语言解释一下func4函数 if(a <= 7) { %eax = 0; if(a >= 7) { return;//直接猜出第一个是7 } else { 0x1(%rcx),%esi; 0x400fce <func4>; 0x1(%rax,%rax,1),%eax; return; } } else { -0x1(%rcx),%edx 0x400fce <func4>; %eax,%eax return; }由于func4函数没有对b进行任何操作,所以根据代码
0x0000000000401051 <+69>: cmpl $0x0,0xc(%rsp) 0x0000000000401056 <+74>: je 0x40105d <phase_4+81> #说明b值为0 0x0000000000401058 <+76>: call 0x40143a <explode_bomb> 0x000000000040105d <+81>: add $0x18,%rsp -
答案
7 0
phase_5
-
汇编分析
0x0000000000401062 <+0>: push %rbx 0x0000000000401063 <+1>: sub $0x20,%rsp 0x0000000000401067 <+5>: mov %rdi,%rbx 0x000000000040106a <+8>: mov %fs:0x28,%rax #fs是一个段寄存器,将fs段中偏移量为0x28的值传入rax中 0x0000000000401073 <+17>: mov %rax,0x18(%rsp) 0x0000000000401078 <+22>: xor %eax,%eax #置0 0x000000000040107a <+24>: call 0x40131b <string_length> 0x000000000040107f <+29>: cmp $0x6,%eax #%eax存储字符串长度,且必须等于6 0x0000000000401082 <+32>: je 0x4010d2 <phase_5+112> 0x0000000000401084 <+34>: call 0x40143a <explode_bomb> 0x0000000000401089 <+39>: jmp 0x4010d2 <phase_5+112> 0x000000000040108b <+41>: movzbl (%rbx,%rax,1),%ecx #经测试,%rbx存储的是输入字符串的地址。%rax 为0,%ecx存储的是输入第一个字符的ASCII码值 0x000000000040108f <+45>: mov %cl,(%rsp) #cx寄存器的低四位存入memory 0x0000000000401092 <+48>: mov (%rsp),%rdx #存入%rdx 0x0000000000401096 <+52>: and $0xf,%edx #低四位和0xf进行与运算 0x0000000000401099 <+55>: movzbl 0x4024b0(%rdx),%edx #关键 0x00000000004010a0 <+62>: mov %dl,0x10(%rsp,%rax,1) #将取出的字符存入栈中 0x00000000004010a4 <+66>: add $0x1,%rax #偏移值加1 0x00000000004010a8 <+70>: cmp $0x6,%rax 0x00000000004010ac <+74>: jne 0x40108b <phase_5+41> #不等于6,则一直循环 0x00000000004010ae <+76>: movb $0x0,0x16(%rsp) 0x00000000004010b3 <+81>: mov $0x40245e,%esi #关键 0x00000000004010b8 <+86>: lea 0x10(%rsp),%rdi #取出相对应的字符存入到%rdi 0x00000000004010bd <+91>: call 0x401338 <strings_not_equal> 0x00000000004010c2 <+96>: test %eax,%eax 0x00000000004010c4 <+98>: je 0x4010d9 <phase_5+119> #相等则跳转 0x00000000004010c6 <+100>: call 0x40143a <explode_bomb> 0x00000000004010cb <+105>: nopl 0x0(%rax,%rax,1) 0x00000000004010d0 <+110>: jmp 0x4010d9 <phase_5+119> 0x00000000004010d2 <+112>: mov $0x0,%eax 0x00000000004010d7 <+117>: jmp 0x40108b <phase_5+41> 0x00000000004010d9 <+119>: mov 0x18(%rsp),%rax 0x00000000004010de <+124>: xor %fs:0x28,%rax 0x00000000004010e7 <+133>: je 0x4010ee <phase_5+140> 0x00000000004010e9 <+135>: call 0x400b30 <__stack_chk_fail@plt> 0x00000000004010ee <+140>: add $0x20,%rsp 0x00000000004010f2 <+144>: pop %rbx 0x00000000004010f3 <+145>: ret -
思路
(gdb) x/s 0x4024b0 0x4024b0 <array.3449>:"maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?"字符串1 (gdb) x/s 0x40245e 0x40245e: "flyers"//字符串2 这道题的意思是输入六个字符,将对应的ASCII码的二进制低四位取成一个数字作为一个偏移量,再根据各个偏移量在字符串1中找出对应的字符,找出来的字符应该是字符串2. flyers --> 9 15 14 5 6 7(对应字符串1的位置) ch[1] & 0xf = 9 ch[2] & 0xf = 15 .... ch[6] & 0xf = 7 9 --> 1001 --> 0100 1001 --> I <-- ch[1] ........(其实只要是后四位是1001就可以,但是为了方便,将前四位选择0100,来控制对应的字符是英文字母) -
答案(不唯一)
IONEFG
phase_6
-
汇编分析(测试数字1 2 3 4 5 6)
0x00000000004010f4 <+0>: push %r14 0x00000000004010f6 <+2>: push %r13 0x00000000004010f8 <+4>: push %r12 0x00000000004010fa <+6>: push %rbp 0x00000000004010fb <+7>: push %rbx 0x00000000004010fc <+8>: sub $0x50,%rsp 0x0000000000401100 <+12>: mov %rsp,%r13 #rsp --> r13 0x0000000000401103 <+15>: mov %rsp,%rsi #rsp --> rsi 0x0000000000401106 <+18>: call 0x40145c <read_six_numbers> #6个数字 0x000000000040110b <+23>: mov %rsp,%r14 #rsp --> r14 0x000000000040110e <+26>: mov $0x0,%r12d #0 --> r12d ------------------------------------------------------------------------------ 0x0000000000401114 <+32>: mov %r13,%rbp #rsp --> r13 --> rbp -- num[1] 0x0000000000401117 <+35>: mov 0x0(%r13),%eax #经检查eax存储的是输入是数字 0x000000000040111b <+39>: sub $0x1,%eax #num[1] - 1 0x000000000040111e <+42>: cmp $0x5,%eax #num[1] - 1 <= 5 0x0000000000401121 <+45>: jbe 0x401128 <phase_6+52> 0x0000000000401123 <+47>: call 0x40143a <explode_bomb> 0x0000000000401128 <+52>: add $0x1,%r12d #r12d:0 --> 1 0x000000000040112c <+56>: cmp $0x6,%r12d 0x0000000000401130 <+60>: je 0x401153 <phase_6+95> #等于6则跳转 0x0000000000401132 <+62>: mov %r12d,%ebx #r12d --> ebx -- 1 ----------------------------------------------------------------------------- 0x0000000000401135 <+65>: movslq %ebx,%rax #ebx --> rax -- 1 0x0000000000401138 <+68>: mov (%rsp,%rax,4),%eax #(sp + 1×4) --> eax -- num[2] 0x000000000040113b <+71>: cmp %eax,0x0(%rbp) 0x000000000040113e <+74>: jne 0x401145 <phase_6+81> #num[1] ≠ num[2] 则跳转 0x0000000000401140 <+76>: call 0x40143a <explode_bomb> 0x0000000000401145 <+81>: add $0x1,%ebx #1 + ebx -- 1 + 1 -- 2 0x0000000000401148 <+84>: cmp $0x5,%ebx 0x000000000040114b <+87>: jle 0x401135 <phase_6+65> #ebx ≠ 5则跳转 ----------------------------------------------------------------------------- 0x000000000040114d <+89>: add $0x4,%r13 #r13 + 4 0x0000000000401151 <+93>: jmp 0x401114 <phase_6+32> #循环 ----------------------------------------------------------------------------- 0x0000000000401153 <+95>: lea 0x18(%rsp),%rsi #rsi指向栈的某一位置 0x0000000000401158 <+100>: mov %r14,%rax #r14 --> rax 0x000000000040115b <+103>: mov $0x7,%ecx #7 --> ecx 0x0000000000401160 <+108>: mov %ecx,%edx #ecx --> edx --7 0x0000000000401162 <+110>: sub (%rax),%edx #7 - (%rax) 0x0000000000401164 <+112>: mov %edx,(%rax) #edx --> (rax) 0x0000000000401166 <+114>: add $0x4,%rax #rax + 4 --指向下一个数字 0x000000000040116a <+118>: cmp %rsi,%rax #循环终止条件 0x000000000040116d <+121>: jne 0x401160 <phase_6+108> #不等则跳转 ---------------------------------------------------------------------------- 0x000000000040116f <+123>: mov $0x0,%esi #置0 0x0000000000401174 <+128>: jmp 0x401197 <phase_6+163> ---------------------------------------------------------------------------- 0x0000000000401176 <+130>: mov 0x8(%rdx),%rdx #将下一节点的指针存入rdx 0x000000000040117a <+134>: add $0x1,%eax 0x000000000040117d <+137>: cmp %ecx,%eax 0x000000000040117f <+139>: jne 0x401176 <phase_6+130> #不等则跳转 --------------------------------------------------------------------------- 0x0000000000401181 <+141>: jmp 0x401188 <phase_6+148> #开始重复进行 0x0000000000401183 <+143>: mov $0x6032d0,%edx #edx存储链表首地址 0x0000000000401188 <+148>: mov %rdx,0x20(%rsp,%rsi,2) #rdx --> (rsp+rsi*2)+32 0x000000000040118d <+153>: add $0x4,%rsi #rsi + 4 0x0000000000401191 <+157>: cmp $0x18,%rsi 0x0000000000401195 <+161>: je 0x4011ab <phase_6+183> #rsi = 24时跳转 0x0000000000401197 <+163>: mov (%rsp,%rsi,1),%ecx #将rsp指向的值传给ecx 0x000000000040119a <+166>: cmp $0x1,%ecx 0x000000000040119d <+169>: jle 0x401183 <phase_6+143> #ecx小于等于1则跳转 0x000000000040119f <+171>: mov $0x1,%eax #当大于1时,1 --> eax 0x00000000004011a4 <+176>: mov $0x6032d0,%edx #链表首地址存入edx 0x00000000004011a9 <+181>: jmp 0x401176 <phase_6+130> #将第{7-num[i]}个节点的地址存入到0x20(%rsp+%rsi*2)栈中,中间会为指针空出位置 ------------------------------------------------------------------------------ 0x00000000004011ab <+183>: mov 0x20(%rsp),%rbx #rbx约定为初节点 0x00000000004011b0 <+188>: lea 0x28(%rsp),%rax #rax约定为下一个节点 0x00000000004011b5 <+193>: lea 0x50(%rsp),%rsi #循环结束条件 0x00000000004011ba <+198>: mov %rbx,%rcx ----------------------------------------------------------------------------- 0x00000000004011bd <+201>: mov (%rax),%rdx #下一个节点的值放入rdx 0x00000000004011c0 <+204>: mov %rdx,0x8(%rcx) #rcx指向下一个节点 0x00000000004011c4 <+208>: add $0x8,%rax #下一个数据 0x00000000004011c8 <+212>: cmp %rsi,%rax #判断循环是否结束 0x00000000004011cb <+215>: je 0x4011d2 <phase_6+222> 0x00000000004011cd <+217>: mov %rdx,%rcx #rcx后移 0x00000000004011d0 <+220>: jmp 0x4011bd <phase_6+201> ----------------------------------------------------------------------------- 0x00000000004011d2 <+222>: movq $0x0,0x8(%rdx) #尾节点中的指针指向NULL 0x00000000004011da <+230>: mov $0x5,%ebp ---------------------------------------------------------------------------- 0x00000000004011df <+235>: mov 0x8(%rbx),%rax #将rax指向rbx下一个链表结点 0x00000000004011e3 <+239>: mov (%rax),%eax 0x00000000004011e5 <+241>: cmp %eax,(%rbx) #比较每个节点中value的大小 0x00000000004011e7 <+243>: jge 0x4011ee <phase_6+250> #前大于后则跳转,否则bomb 0x00000000004011e9 <+245>: call 0x40143a <explode_bomb> 0x00000000004011ee <+250>: mov 0x8(%rbx),%rbx #rbx向后移动,为了使它降序排列 0x00000000004011f2 <+254>: sub $0x1,%ebp 0x00000000004011f5 <+257>: jne 0x4011df <phase_6+235> #循环 ------------------------------------------------------------------------------- 0x00000000004011f7 <+259>: add $0x50,%rsp 0x00000000004011fb <+263>: pop %rbx 0x00000000004011fc <+264>: pop %rbp 0x00000000004011fd <+265>: pop %r12 0x00000000004011ff <+267>: pop %r13 0x0000000000401201 <+269>: pop %r14 0x0000000000401203 <+271>: ret 条件统计: num[1] - 1 <= 5 num[1] ≠ num[2] -
思路
(gdb) x/20 0x6032d0 0x6032d0 <node1>: 332 1 6304480 0 0x6032e0 <node2>: 168 2 6304496 0 0x6032f0 <node3>: 924 3 6304512 0 0x603300 <node4>: 691 4 6304528 0 0x603310 <node5>: 477 5 6304544 0 (gdb) x/20 6304480 0x6032e0 <node2>: 168 2 6304496 0 0x6032f0 <node3>: 924 3 6304512 0 0x603300 <node4>: 691 4 6304528 0 0x603310 <node5>: 477 5 6304544 0 0x603320 <node6>: 443 6 0 0//尾节点 我们看到0x6032d0,标记出node,所以想到了这是个链表,而相邻节点差16字节,所以时前8字节存储value,后八字节存储*next这个程序干了什么呢?
1. 存储了6个数字,且每个数字大于0小于6 2. 将6个数字变成7-num[i] 3. 将第(7-num[i])个链表元素根据相对位置存入栈中 4. 将节点链接起来,单向链表 5. 检测数值是否符合降序 -
答案
根据链表的前四字节存储的值,我们降序排序 node[3]>node[4]>node[5]>node[6]>node[1]>node[2] 3 4 5 6 1 2 又因为上面的值为(7-num[i]) 4 3 2 1 6 5(最终答案)