gdb调试知识
gdb bomb:使用gdb调试可执行文件bomb
break 函数名/行号:打断点
r(run):运行至断点
c(continue):继续执行到下一个断点
q(quit):退出gdb
si:单指令执行
n(next):单步跟踪程序,遇到调用函数不会进入函数内部
s(step):单步调试,遇到调用函数会进入函数内部
until:运行程序直到退出循环体
layout asm:显示反汇编窗口
layout regs:显示反汇编和寄存器窗口
disassemble:查看当前执行指令的汇编代码
i r 寄存器名:显示当前寄存器值
x/[count][format] [address]:打印指定格式指定数量的内存值
phase_1
反汇编代码:
0x00005555555555e7 <+0>: endbr64
0x00005555555555eb <+4>: sub $0x8,%rsp
0x00005555555555ef <+8>: lea 0x1b56(%rip),%rsi # 0x55555555714c
0x00005555555555f6 <+15>: call 0x555555555af3 <strings_not_equal>
0x00005555555555fb <+20>: test %eax,%eax
0x00005555555555fd <+22>: jne 0x555555555604 <phase_1+29>
0x00005555555555ff <+24>: add $0x8,%rsp
0x0000555555555603 <+28>: ret
0x0000555555555604 <+29>: call 0x555555555c07 <explode_bomb>
0x0000555555555609 <+34>: jmp 0x5555555555ff <phase_1+24>
<+0>指令前缀,目的是增强安全性
<+4>减少栈指针的八个字节,分配空间
前两行基本是固定的,对拆弹没什么作用。
<+8>lea这个指令在实际应用中是用来计算地址,而非访问内存。这里是将%rip内的地址加上0x1b56的偏移量加载到%rsi中,i r rip查看此处rip的值为0x5555555555e7,加上偏移量刚好等于注释中的0x55555555714c。
<+15>这里调用函数,看名字就能知道这是一个判断字符串是否相等的函数,我们先接着往下看。
<+20>这里是将eax的值自身按位与,eax是返回值,也就是判断<+15>处调用的函数的返回值是否为零。
<+22>:如果上一行返回值不等于0,则跳转到<phase_1+29>处,即爆炸。
<+24>:如果<+20>处返回值等于0,则增加栈指针八个字节,释放分配的空间。
<+28>:从函数返回,结束phase_1。
也就是说我们本题输入应为一个字符串,存在%rdi(第一个参数)中,与%rsi(第二个参数)内地址对应的字符串相等。
x/s 0x55555555714c查看内容得到"Public speaking is very easy."即为答案。
phase_2
反汇编代码:
0x000055555555560b <+0>: endbr64
0x000055555555560f <+4>: push %rbp
0x0000555555555610 <+5>: push %rbx
0x0000555555555611 <+6>: sub $0x28,%rsp
0x0000555555555615 <+10>: mov %fs:0x28,%rax
0x000055555555561e <+19>: mov %rax,0x18(%rsp)
0x0000555555555623 <+24>: xor %eax,%eax
0x0000555555555625 <+26>: mov %rsp,%rsi
0x0000555555555628 <+29>: call 0x555555555c33 <read_six_numbers>
0x000055555555562d <+34>: cmpl $0x0,(%rsp)
0x0000555555555631 <+38>: js 0x55555555563d <phase_2+50>
0x0000555555555633 <+40>: mov %rsp,%rbp
0x0000555555555636 <+43>: mov $0x1,%ebx
0x000055555555563b <+48>: jmp 0x555555555650 <phase_2+69>
0x000055555555563d <+50>: call 0x555555555c07 <explode_bomb>
0x0000555555555642 <+55>: jmp 0x555555555633 <phase_2+40>
0x0000555555555644 <+57>: add $0x1,%ebx
0x0000555555555647 <+60>: add $0x4,%rbp
0x000055555555564b <+64>: cmp $0x6,%ebx
0x000055555555564e <+67>: je 0x555555555661 <phase_2+86>
0x0000555555555650 <+69>: mov %ebx,%eax
0x0000555555555652 <+71>: add 0x0(%rbp),%eax
0x0000555555555655 <+74>: cmp %eax,0x4(%rbp)
0x0000555555555658 <+77>: je 0x555555555644 <phase_2+57>
0x000055555555565a <+79>: call 0x555555555c07 <explode_bomb>
0x000055555555565f <+84>: jmp 0x555555555644 <phase_2+57>
0x0000555555555661 <+86>: mov 0x18(%rsp),%rax
0x0000555555555666 <+91>: sub %fs:0x28,%rax
0x000055555555566f <+100>: jne 0x555555555678 <phase_2+109>
0x0000555555555671 <+102>: add $0x28,%rsp
0x0000555555555675 <+106>: pop %rbx
0x0000555555555676 <+107>: pop %rbp
0x0000555555555677 <+108>: ret
0x0000555555555678 <+109>: call 0x555555555250 <__stack_chk_fail@plt>
有了上边的经验,我们后边直接看关键点,调用了函数<read_six_numbers>可以推知我们本题需要输入六个数字,设为x1-x6。
<+34>:比较栈指针指向的内存中的数与0的大小,即判断正负,若为负数,跳转到<phase_2+50>爆炸。
此时栈指针指向的内存的数即x1。
然后将栈指针的值赋给%rbp,将立即数1赋给%ebx。
无条件跳转至<phase_2+69>,将%ebx的值(也就是1)赋给%eax,将%rbp指向的内存的值(即x1)加到%eax中,比较%rbp的值+0x4的偏移量所指向的内存的值(即x2)与%eax的值(1+x1)的大小。如果相等则跳转<phase_2+57>,不相等则爆炸。
所以有x2=x1+1。
跳转到<+57>:将%ebx的值加1,将%rbp的值加4,此时%rbp指向的内存的数变为x2。再判断6和%ebx的大小,不相等则继续进行<+69>的操作:将%ebx的值(此时为2)赋给%eax,将%rbp指向的内存的值(x2)加到%eax中,比较%rbp的值+0x4的偏移量所指向的内存的值(即x3)与%eax的值(2+x2)的大小。如果相等则跳转<phase_2+57>,不相等则爆炸。
即x3=x2+2。
只有当%ebx=6时才跳出这个循环,那么%ebx在这里我们可以理解为数字的索引n,必须保证Xn=X(n-1)+n-1。
又因为输入的数必须不能为负,我们设x1=1,则答案为1 2 4 7 11 16
phase_3
反汇编代码:
0x000055555555567d <+0>: endbr64
0x0000555555555681 <+4>: sub $0x18,%rsp
0x0000555555555685 <+8>: mov %fs:0x28,%rax
0x000055555555568e <+17>: mov %rax,0x8(%rsp)
0x0000555555555693 <+22>: xor %eax,%eax
0x0000555555555695 <+24>: lea 0x4(%rsp),%rcx
0x000055555555569a <+29>: mov %rsp,%rdx
0x000055555555569d <+32>: lea 0x1c73(%rip),%rsi # 0x555555557317
0x00005555555556a4 <+39>: call 0x555555555300 <__isoc99_sscanf@plt>
0x00005555555556a9 <+44>: cmp $0x1,%eax
0x00005555555556ac <+47>: jle 0x5555555556c8 <phase_3+75>
0x00005555555556ae <+49>: cmpl $0x7,(%rsp)
0x00005555555556b2 <+53>: ja 0x555555555719 <phase_3+156>
0x00005555555556b4 <+55>: mov (%rsp),%eax
0x00005555555556b7 <+58>: lea 0x1ac2(%rip),%rdx # 0x555555557180
0x00005555555556be <+65>: movslq (%rdx,%rax,4),%rax
0x00005555555556c2 <+69>: add %rdx,%rax
0x00005555555556c5 <+72>: notrack jmp *%rax
0x00005555555556c8 <+75>: call 0x555555555c07 <explode_bomb>
0x00005555555556cd <+80>: jmp 0x5555555556ae <phase_3+49>
0x00005555555556cf <+82>: mov $0xbd,%eax
0x00005555555556d4 <+87>: cmp %eax,0x4(%rsp)
0x00005555555556d8 <+91>: jne 0x55555555572c <phase_3+175>
0x00005555555556da <+93>: mov 0x8(%rsp),%rax
0x00005555555556df <+98>: sub %fs:0x28,%rax
0x00005555555556e8 <+107>: jne 0x555555555733 <phase_3+182>
0x00005555555556ea <+109>: add $0x18,%rsp
0x00005555555556ee <+113>: ret
0x00005555555556ef <+114>: mov $0x171,%eax
0x00005555555556f4 <+119>: jmp 0x5555555556d4 <phase_3+87>
0x00005555555556f6 <+121>: mov $0x3c0,%eax
0x00005555555556fb <+126>: jmp 0x5555555556d4 <phase_3+87>
0x00005555555556fd <+128>: mov $0x350,%eax
0x0000555555555702 <+133>: jmp 0x5555555556d4 <phase_3+87>
0x0000555555555704 <+135>: mov $0x143,%eax
0x0000555555555709 <+140>: jmp 0x5555555556d4 <phase_3+87>
0x000055555555570b <+142>: mov $0x43,%eax
0x0000555555555710 <+147>: jmp 0x5555555556d4 <phase_3+87>
0x0000555555555712 <+149>: mov $0x50,%eax
0x0000555555555717 <+154>: jmp 0x5555555556d4 <phase_3+87>
0x0000555555555719 <+156>: call 0x555555555c07 <explode_bomb>
0x000055555555571e <+161>: mov $0x0,%eax
0x0000555555555723 <+166>: jmp 0x5555555556d4 <phase_3+87>
0x0000555555555725 <+168>: mov $0x97,%eax
0x000055555555572a <+173>: jmp 0x5555555556d4 <phase_3+87>
0x000055555555572c <+175>: call 0x555555555c07 <explode_bomb>
0x0000555555555731 <+180>: jmp 0x5555555556da <phase_3+93>
0x0000555555555733 <+182>: call 0x555555555250 <__stack_chk_fail@plt>
x/s 打印了一下注释的0x555555557317,发现是“%d %d”,猜测本题输两个十进制数,设为x1,x2。
x/x 打印了0x555555557180,是0xa5。不知道是什么先放着。
直接从<+44>开始看,比较上边函数的返回值%eax与1的大小,如果小于等于1,爆炸。比较%rsp指向的内存的值(x1)与7的大小,如果大于7,爆炸。
x1<=7。
随便取了个2:
到<+55>:把x1赋给%eax,计算地址把0x555555557180加载到%rdx。将%rdx+4*%rax指向的内存地址的值符号扩展后赋给%rax,再将%rdx的值加到%rax中,无条件跳转到%rax指向的地址继续执行。
此时 i r rax得到0x5555555556ef,即跳转到<+114>处继续。
<+114>:将0x171赋给%eax,跳转到<+87>处继续。
<+87>:比较%eax(0x171)与%rsp的值+0x4的偏移量所指向的内存的值(x2),不相等则爆炸。
x2=0x171=369。
答案:2 369
答案不唯一,根据x1的不同跳转到不同地址。跳转地址的计算方式是(0x555555557180+4*x1)指向的内存的值+0x555555557180。
可以打印0x555555557180附近的值,分别对应了x1=0,1,2,3,4,5,6,7的值

再分别加上0x555555557180得到的地址即对应的跳转地址。跳转之后赋给%eax的值即为x2的值。
phase_4
0x000055555555576e <+0>: endbr64
0x0000555555555772 <+4>: sub $0x18,%rsp
0x0000555555555776 <+8>: mov %fs:0x28,%rax
0x000055555555577f <+17>: mov %rax,0x8(%rsp)
0x0000555555555784 <+22>: xor %eax,%eax
0x0000555555555786 <+24>: lea 0x4(%rsp),%rcx
0x000055555555578b <+29>: mov %rsp,%rdx
0x000055555555578e <+32>: lea 0x1b82(%rip),%rsi # 0x555555557317
0x0000555555555795 <+39>: call 0x555555555300 <__isoc99_sscanf@plt>
0x000055555555579a <+44>: cmp $0x2,%eax
0x000055555555579d <+47>: jne 0x5555555557a5 <phase_4+55>
0x000055555555579f <+49>: cmpl $0xe,(%rsp)
0x00005555555557a3 <+53>: jbe 0x5555555557aa <phase_4+60>
0x00005555555557a5 <+55>: call 0x555555555c07 <explode_bomb>
0x00005555555557aa <+60>: mov $0xe,%edx
0x00005555555557af <+65>: mov $0x0,%esi
0x00005555555557b4 <+70>: mov (%rsp),%edi
0x00005555555557b7 <+73>: call 0x555555555738 <func4>
0x00005555555557bc <+78>: cmp $0xa,%eax
0x00005555555557bf <+81>: jne 0x5555555557c8 <phase_4+90>
0x00005555555557c1 <+83>: cmpl $0xa,0x4(%rsp)
0x00005555555557c6 <+88>: je 0x5555555557cd <phase_4+95>
0x00005555555557c8 <+90>: call 0x555555555c07 <explode_bomb>
0x00005555555557cd <+95>: mov 0x8(%rsp),%rax
0x00005555555557d2 <+100>: sub %fs:0x28,%rax
0x00005555555557db <+109>: jne 0x5555555557e2 <phase_4+116>
0x00005555555557dd <+111>: add $0x18,%rsp
0x00005555555557e1 <+115>: ret
0x00005555555557e2 <+116>: call 0x555555555250 <__stack_chk_fail@plt>
同样先看了眼0x555555557317,还是“%d %d”。设输入的两个数为x1,x2。
<+49>比较%rsp指向内存的值(x1)与14的大小,小于等于跳转<+60>,否则爆炸。
x1<=14。
到<+60>:把14赋给%edx(第三个参数),0赋给%esi(第二个参数),%rsp指向内存的值(x1)赋给%edi(第一个参数)。调用func4函数。
先接着看,<+78>比较函数返回值%eax与10的大小,不相等则爆炸,所以func4函数的返回值必须等于10。比较%rsp的值+0x4的偏移量所指向内存的值(x2)与10的大小,如果相等跳转<+95>,否则爆炸。
x2=10。
返回头看func4。

将%rbx压栈。
%eax=%edx-%esi=14。
把%eax的值赋给%ebx。
<+11>:将%ebx的值逻辑右移31位,获取符号位0。将%eax的值加到%ebx上。
<+16>:将%ebx算术右移一位。1110->0111,%ebx=7。
把%esi加到%ebx上,比较%ebx和%edi的值,即比较7和x1。
若x1<7,跳到<+30>:令%edx=%rbx-1=6,再次调用func4。
若x1>7,跳到<+42>:令%esi=%rbx+1=8,再次调用func4。
若x1=7,则将%ebx的值赋给%eax(7),%rbx出栈,函数结束。
由于我们要求func4的返回值必须是10,所以不能简单的令x1=7,必须要经过递归。
而两个再次调用func4的情况结束后,均把返回值加到%ebx上,再将%ebx赋给func4的返回值。
已知%ebx=7,所以递归调用的func4返回值必须为3。
想让递归调用的func4结束且返回值为3,则需让%ebx=3(<+26>),而若想不再进入递归,需要x1=%ebx=3(<+20>处不进入两个跳转)。
x1=3。
答案:3 10
phase_5
0x00005555555557e7 <+0>: endbr64
0x00005555555557eb <+4>: sub $0x18,%rsp
0x00005555555557ef <+8>: mov %fs:0x28,%rax
0x00005555555557f8 <+17>: mov %rax,0x8(%rsp)
0x00005555555557fd <+22>: xor %eax,%eax
0x00005555555557ff <+24>: lea 0x4(%rsp),%rcx
0x0000555555555804 <+29>: mov %rsp,%rdx
0x0000555555555807 <+32>: lea 0x1b09(%rip),%rsi # 0x555555557317
0x000055555555580e <+39>: call 0x555555555300 <__isoc99_sscanf@plt>
0x0000555555555813 <+44>: cmp $0x1,%eax
0x0000555555555816 <+47>: jle 0x555555555872 <phase_5+139>
0x0000555555555818 <+49>: mov (%rsp),%eax
0x000055555555581b <+52>: and $0xf,%eax
0x000055555555581e <+55>: mov %eax,(%rsp)
0x0000555555555821 <+58>: cmp $0xf,%eax
0x0000555555555824 <+61>: je 0x555555555858 <phase_5+113>
0x0000555555555826 <+63>: mov $0x0,%ecx
0x000055555555582b <+68>: mov $0x0,%edx
0x0000555555555830 <+73>: lea 0x1969(%rip),%rsi # 0x5555555571a0 <array.0>
0x0000555555555837 <+80>: add $0x1,%edx
0x000055555555583a <+83>: cltq
0x000055555555583c <+85>: mov (%rsi,%rax,4),%eax
0x000055555555583f <+88>: add %eax,%ecx
0x0000555555555841 <+90>: cmp $0xf,%eax
0x0000555555555844 <+93>: jne 0x555555555837 <phase_5+80>
0x0000555555555846 <+95>: movl $0xf,(%rsp)
0x000055555555584d <+102>: cmp $0xf,%edx
0x0000555555555850 <+105>: jne 0x555555555858 <phase_5+113>
0x0000555555555852 <+107>: cmp %ecx,0x4(%rsp)
0x0000555555555856 <+111>: je 0x55555555585d <phase_5+118>
0x0000555555555858 <+113>: call 0x555555555c07 <explode_bomb>
0x000055555555585d <+118>: mov 0x8(%rsp),%rax
0x0000555555555862 <+123>: sub %fs:0x28,%rax
0x000055555555586b <+132>: jne 0x555555555879 <phase_5+146>
0x000055555555586d <+134>: add $0x18,%rsp
0x0000555555555871 <+138>: ret
0x0000555555555872 <+139>: call 0x555555555c07 <explode_bomb>
0x0000555555555877 <+144>: jmp 0x555555555818 <phase_5+49>
0x0000555555555879 <+146>: call 0x555555555250 <__stack_chk_fail@plt>
还是先看注释。
7317还是“%d %d”,还是设输入为x1,x2。
71a0后边写了<array.0>说明是个数组的开始。
<+44>开始,上一行函数的返回值%eax必须大于1,否则爆炸。
(一直没细看<__isoc99_sscanf@plt>这个函数到底是干嘛的,但自用到它的题目%eax打印出来都是2)
到<+49>:把x1赋给%eax,把%eax与0xf按位与,即保留低四位,再将%eax的值存回%rsp指向的内存地址处。比较%eax与15,如果相等,爆炸。
把0赋给%ecx和%edx,计算地址(71a0)存到%rsi里。
<+83>cltq是将32位寄存器中有符号整数扩展为64位。
把%rsi(71a0)+4*%rax(x1的低四位)所指向的内存的值赋给%eax。
注意!这里4*%rax换成十六进制再和%rsi相加。
打印了一下数组:

接着看<+88>:把%eax的值加到%ecx,比较%eax和15的大小,如果不相等,跳转到<+80>循环,也就是说循环结束需要让%eax=15,即数组中的f处,地址为71b8。
我们先不进入循环接着往后看<+95>,把f赋给%rsp指向的内存地址,比较%edx和15的大小,如果不相等,爆炸。所以%edx必须等于15,%edx表示什么?%edx在<+80>每次循环加一,记录了循环的次数。也就是说一共必须经过15次循环。
<+107>:比较%rsp的值+0x4的偏移量所指向的内存的值(即x2)和%ecx的值,不相等则爆炸,相等则结束循环。%ecx表示什么?每次循环都会将获取的%eax值加到%ecx上,也就是经过15次循环后,每次访问到的数组内的数的加和。
所以本题需倒推:根据第15次循环时访问到的数组内的数为f,倒推第一次%rax应为多少,第一次%rax的值即为x1(只考虑低四位),而每次访问到的数的加和即为x2。
简单说下怎么倒推,首先将数组的数和位置表示改为:

已知第15次访问的是f,即15,图中红6的位置。每次访问的数都是相对红0的位置+%rax的值(理解为红色标号的方式“去掉”了“4*”),也就是说第14次访问的是6,第13次访问的是14……(每次访问的数的红色标号都是上一次循环访问的值)
6,14,2,1,10,0,8,4,9,13,11,7,3,12
最后得到第一次访问的数为12,那么第一次的%rax就是5,即x1=5。
加和为115。
x1只考虑低四位,高位的取值无所谓,我们简单就取5。
答案:5 115
phase_6
0x000055555555587e <+0>: endbr64
0x0000555555555882 <+4>: push %r14
0x0000555555555884 <+6>: push %r13
0x0000555555555886 <+8>: push %r12
0x0000555555555888 <+10>: push %rbp
0x0000555555555889 <+11>: push %rbx
0x000055555555588a <+12>: sub $0x60,%rsp
0x000055555555588e <+16>: mov %fs:0x28,%rax
0x0000555555555897 <+25>: mov %rax,0x58(%rsp)
0x000055555555589c <+30>: xor %eax,%eax
0x000055555555589e <+32>: mov %rsp,%r13
0x00005555555558a1 <+35>: mov %r13,%rsi
0x00005555555558a4 <+38>: call 0x555555555c33 <read_six_numbers>
0x00005555555558a9 <+43>: mov $0x1,%r14d
0x00005555555558af <+49>: mov %rsp,%r12
0x00005555555558b2 <+52>: jmp 0x5555555558dc <phase_6+94>
0x00005555555558b4 <+54>: call 0x555555555c07 <explode_bomb>
0x00005555555558b9 <+59>: jmp 0x5555555558eb <phase_6+109>
0x00005555555558bb <+61>: add $0x1,%rbx
0x00005555555558bf <+65>: cmp $0x5,%ebx
0x00005555555558c2 <+68>: jg 0x5555555558d4 <phase_6+86>
0x00005555555558c4 <+70>: mov (%r12,%rbx,4),%eax
0x00005555555558c8 <+74>: cmp %eax,0x0(%rbp)
0x00005555555558cb <+77>: jne 0x5555555558bb <phase_6+61>
0x00005555555558cd <+79>: call 0x555555555c07 <explode_bomb>
0x00005555555558d2 <+84>: jmp 0x5555555558bb <phase_6+61>
0x00005555555558d4 <+86>: add $0x1,%r14
0x00005555555558d8 <+90>: add $0x4,%r13
0x00005555555558dc <+94>: mov %r13,%rbp
0x00005555555558df <+97>: mov 0x0(%r13),%eax
0x00005555555558e3 <+101>: sub $0x1,%eax
0x00005555555558e6 <+104>: cmp $0x5,%eax
0x00005555555558e9 <+107>: ja 0x5555555558b4 <phase_6+54>
0x00005555555558eb <+109>: cmp $0x5,%r14d
0x00005555555558ef <+113>: jg 0x5555555558f6 <phase_6+120>
0x00005555555558f1 <+115>: mov %r14,%rbx
0x00005555555558f4 <+118>: jmp 0x5555555558c4 <phase_6+70>
0x00005555555558f6 <+120>: mov $0x0,%esi
0x00005555555558fb <+125>: mov (%rsp,%rsi,4),%ecx
0x00005555555558fe <+128>: mov $0x1,%eax
0x0000555555555903 <+133>: lea 0x3906(%rip),%rdx # 0x555555559210 <node1>
0x000055555555590a <+140>: cmp $0x1,%ecx
0x000055555555590d <+143>: jle 0x55555555591a <phase_6+156>
0x000055555555590f <+145>: mov 0x8(%rdx),%rdx
0x0000555555555913 <+149>: add $0x1,%eax
0x0000555555555916 <+152>: cmp %ecx,%eax
0x0000555555555918 <+154>: jne 0x55555555590f <phase_6+145>
0x000055555555591a <+156>: mov %rdx,0x20(%rsp,%rsi,8)
0x000055555555591f <+161>: add $0x1,%rsi
0x0000555555555923 <+165>: cmp $0x6,%rsi
0x0000555555555927 <+169>: jne 0x5555555558fb <phase_6+125>
0x0000555555555929 <+171>: mov 0x20(%rsp),%rbx
0x000055555555592e <+176>: mov 0x28(%rsp),%rax
0x0000555555555933 <+181>: mov %rax,0x8(%rbx)
0x0000555555555937 <+185>: mov 0x30(%rsp),%rdx
0x000055555555593c <+190>: mov %rdx,0x8(%rax)
0x0000555555555940 <+194>: mov 0x38(%rsp),%rax
0x0000555555555945 <+199>: mov %rax,0x8(%rdx)
0x0000555555555949 <+203>: mov 0x40(%rsp),%rdx
0x000055555555594e <+208>: mov %rdx,0x8(%rax)
0x0000555555555952 <+212>: mov 0x48(%rsp),%rax
0x0000555555555957 <+217>: mov %rax,0x8(%rdx)
0x000055555555595b <+221>: movq $0x0,0x8(%rax)
0x0000555555555963 <+229>: mov $0x5,%ebp
0x0000555555555968 <+234>: jmp 0x555555555973 <phase_6+245>
0x000055555555596a <+236>: mov 0x8(%rbx),%rbx
0x000055555555596e <+240>: sub $0x1,%ebp
0x0000555555555971 <+243>: je 0x555555555984 <phase_6+262>
0x0000555555555973 <+245>: mov 0x8(%rbx),%rax
0x0000555555555977 <+249>: mov (%rax),%eax
0x0000555555555979 <+251>: cmp %eax,(%rbx)
0x000055555555597b <+253>: jle 0x55555555596a <phase_6+236>
0x000055555555597d <+255>: call 0x555555555c07 <explode_bomb>
0x0000555555555982 <+260>: jmp 0x55555555596a <phase_6+236>
0x0000555555555984 <+262>: mov 0x58(%rsp),%rax
0x0000555555555989 <+267>: sub %fs:0x28,%rax
0x0000555555555992 <+276>: jne 0x5555555559a1 <phase_6+291>
0x0000555555555994 <+278>: add $0x60,%rsp
0x0000555555555998 <+282>: pop %rbx
0x0000555555555999 <+283>: pop %rbp
0x000055555555599a <+284>: pop %r12
0x000055555555599c <+286>: pop %r13
0x000055555555599e <+288>: pop %r14
0x00005555555559a0 <+290>: ret
0x00005555555559a1 <+291>: call 0x555555555250 <__stack_chk_fail@plt>
看了前边寄存器的值的来源以后,后边就直说代表的值了。
观察一下代码,发现<read_six_numbers>,猜测本题需要输六个数,设为x1-x6。
9210似乎是个节点的开始。
<+43>:把1赋给%r14d,%rsp的值赋给%r12。
<+94>:把%r13的值赋给%rbp,把%r13指向的内存的值赋给%eax,即x1。将%eax-1与5比较大小,若%eax-1>5,爆炸。
x1<=6。
<+109>:比较%r14d和5的大小,如果%r14d>5,跳转<+120>,否则把%r14d的值赋给%rbx,跳转<+70>。
<+70>:把x2赋给%eax,比较x1和x2大小,如果相等,爆炸,不相等则跳到<+61>循环,直至%ebx>5,即循环判断x2-x6均不和x1相等。
跳出循环后至<+86>:%r14+=1,%r13+=4,%r13值赋给%rbp,把x2的值赋给%eax,判断%eax-1与5的大小,判断%r14的值是否大于5,否则再次进入循环。
也就是说从<+43>到<+118>为两个嵌套的循环,目的是检验输入的每个数都不相等,且<=6。
<+120>:%esi=0,%ecx=x1,%eax=1,%rdx=9210,比较%ecx和1的大小,若%ecx<=1,跳转<+156>,否则继续,我们先假设%ecx>1。

<+145>:如图,把9220赋给%rdx,%eax+=1,比较%ecx和%eax的大小,如果不相等,则把9230赋给%rdx,直至%ecx=%eax,也就是说,%ecx是几,就对应第几个节点。
<+171>-<+221>:把x1对应的节点赋给%rbx,x2对应的节点赋给%rax,将%rax链接到%rbx所指向的内存+8的位置……最后0链接到x6对于节点+8的位置,即构造链表x1->x2->x3->x4->x5->x6->0。
%ebp=5
<+245>:把x2对应的节点的值赋给%eax,比较(%rbx)(即x1对应的节点的值)和%eax的大小,如果(%rsp)>%eax,爆炸。否则跳转<+236>,即把x2对应节点的值赋给%rbx,把x3对应节点的值赋给%eax,比较(%rbx)(即x2对应的节点的值)和%eax的大小……直至%ebp为0,即比较完整个链表。
也就是说这个图的含义是:第一列为节点的值,第二列表示节点,第三列链接下一个节点。要求比较值的大小,根据第一列的从小到大,排列节点,即为输入的x1-x6。

node6在上边:查看node5的第三列地址:
![]()
答案:3 2 6 4 5 1
secret_phase
_(:з」∠)_写不动了,秘密关卡简单写写。
触发条件是做对前六题且在第四题的3 10答案后加 DrEvil。
二叉树是这样的:

输入的数必须在二叉树中,根据secret_phase要求的返回值倒推。
进入左子树,%eax=%eax*2,进入右子树,%eax=%eax*2+1。
我的secret_phase中要求返回5,即右左右,2f=47。
over over。

1万+

被折叠的 条评论
为什么被折叠?



