CSAPP实验-bomblab超详解

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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值