头歌《深入理解计算机系统》系列实验二(下)

1.拆弹密码第五关

1.1解析题目

本关任务:在phase_5的汇编代码中找到本关拆弹密码。

1.2解决题目

首先完成开始之前的操作。

出现下述汇编代码

Dump of assembler code for function phase_5:
   0x0000000000401062 <+0>:     push   %rbx
   0x0000000000401063 <+1>:     sub    $0x20,%rsp
   0x0000000000401067 <+5>:     mov    %rdi,%rbx
   0x000000000040106a <+8>:     mov    %fs:0x28,%rax
   0x0000000000401073 <+17>:    mov    %rax,0x18(%rsp)
   0x0000000000401078 <+22>:    xor    %eax,%eax
   0x000000000040107a <+24>:    callq  0x40131b <string_length>
   0x000000000040107f <+29>:    cmp    $0x6,%eax
   0x0000000000401082 <+32>:    je     0x4010d2 <phase_5+112>
   0x0000000000401084 <+34>:    callq  0x40143a <explode_bomb>
   0x0000000000401089 <+39>:    jmp    0x4010d2 <phase_5+112>
   0x000000000040108b <+41>:    movzbl (%rbx,%rax,1),%ecx
   0x000000000040108f <+45>:    mov    %cl,(%rsp)
   0x0000000000401092 <+48>:    mov    (%rsp),%rdx
   0x0000000000401096 <+52>:    and    $0xf,%edx
   0x0000000000401099 <+55>:    movzbl 0x4024b0(%rdx),%edx
   0x00000000004010a0 <+62>:    mov    %dl,0x10(%rsp,%rax,1)
   0x00000000004010a4 <+66>:    add    $0x1,%rax
   0x00000000004010a8 <+70>:    cmp    $0x6,%rax
   0x00000000004010ac <+74>:    jne    0x40108b <phase_5+41>
   0x00000000004010ae <+76>:    movb   $0x0,0x16(%rsp)
   0x00000000004010b3 <+81>:    mov    $0x40245e,%esi
   0x00000000004010b8 <+86>:    lea    0x10(%rsp),%rdi
   0x00000000004010bd <+91>:    callq  0x401338 <strings_not_equal>
   0x00000000004010c2 <+96>:    test   %eax,%eax
   0x00000000004010c4 <+98>:    je     0x4010d9 <phase_5+119>
   0x00000000004010c6 <+100>:   callq  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>:   callq  0x400b30 <__stack_chk_fail@plt>
   0x00000000004010ee <+140>:   add    $0x20,%rsp
   0x00000000004010f2 <+144>:   pop    %rbx
   0x00000000004010f3 <+145>:   retq   
End of assembler dump.

第一个爆炸函数在+34,从此处开始回溯。

+24,调用string_length函数,从名字来看,应该是判断输入长度的。

+29,比较6和eax(eax是上述函数的返回值)

+32,如果eax=6,跳转到+112

+34,否则爆炸

为了不爆炸,我们假设输入的字符串长度为6,那么跳转到+112

+112,eax=0

+117,跳转到+41

+41,将(rbx+rax)处的值的传递给ecx的低位(这里rbx是字符串起始地址,+rax之后应该是字符串的第一个字符)

+45,将第一个字符保存

+48,将第一个字符保存在rdx

+52,将edx与15按位与,然后保存在edx(意思是截取了字符的低四位)

+55,从rdx+0x4024b0处读取数据保存在edx(注意这里的rdx其实是上述字符的低四位,这看起来像是以rdx为偏移量,读取了一个字符)

+62,将这个字符保存在rsp+rax+16

+66,rax=rax+1(在这里的作用是更新索引,也就是说原本是指向字符串的第一个字符,现在是指向第二个字符)

+70,比较6和rax(用来检测是否越界)

+74,如果6和rax不相等,跳转到+41(也就是一个循环)

+76,否则把rsp+24设置为0

+81,把esi设置为0x40245e

+86,rdi地址设置为rsp+16

+91,调用函数strings_not_equal(从名字来看,是判断字符串是否相等的),判断rdi和esi保存的字符串是否一致(rdi储存的是rsp+16,从前文可以看到rsp+16是我们储存的字符,而esi是0x40245e,需要查一下这个地址保存了什么值)

+96,测试返回值

+98,如果相等则跳转到+119

+100,如果不相等则爆炸

为了不爆炸,这里我们让两个字符串相等,那么跳转到+119

+119,把rsp+24储存在rax

+124,把fs+40的值与rax中保存的值异或,并储存在rax

+133,如果两个rax值一样就跳转到+140(我并不知道这个比较是起什么作用,但是没有很大的影响)

+135,否则调用函数__stack_chk_fail@plt(某种失败)

+140,rsp的值+32

+144,恢复rbx

+145,结束

似乎看起来还是有些不明确,我们梳理一下;

首先,输入的字符串长度必须是6,然后把每个字符的低四位(字符长度是8)用做偏移量,读取一个新的字符,最终把六个读取的新字符拼在一起,再和0x40245e保存的字符串比较一下,如果一样就成功解题了。

1.3解答题目

明确了这点,我们来看一下,最终需要比较的字符串是谁,在调试界面输入

x/s 0x40245e

可以看到结果是

"flyers"

也就是说,最终需要比较的字符串是flyers,我们要确保我们输入的字符串经过转换之后成为flyers,那我们来看一下转换关系,偏移发生在0x4024b0,所以我们可以查看一下此处的值,就可以知道偏移关系。

x/s 0x4024b0

结果是

0x4024b0 <array.3449>:  "maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?"

可以看出,代码包含了一个数组:maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?

把这个字符切割一下,分别是maduiersnfotvbyl和So you think you can stop the bomb with ctrl-c, do you?(所以你认为可以用ctrl+c来阻止爆炸,对吗?)

发现后面是一句完整的话,而前面是16个意义不明的字符,但是!!!

我们的字符低4位,正好可以构成16个数字,意味着和这16个字符一一对应。那么我们就可以得到一个对应表

二进制0000000100100011010001010110011110001001101010111100110111101111
字符maduiersnfotvbyl

想要获得flyers,就是1001 1111 1110 0101 0110 0111

你只需要保证你输入的字符的低四位是上述的二进制即可。

参考网站:https://www.asciim.cn/

比如,我们可以把高四位全设为0

第一个就是0000 1001 ,但是这个是水平制表符,这几个字符可能无法输入,对大家造成困扰。

所以我推荐前缀是0110(这个前缀是小写字母的开始)

这六个字符依次是

0110 1001:i

0110 1111 :o

0110 1110:n

0110 0101:e

0110 0110:f

0110 0111:g

输入

printf("i o n e f g");

恭喜你,通过你的聪明才智完成了这个题目!

2.拆弹密码第六关

2.1解析题目

本关任务:在phase_6的汇编代码中找到本关拆弹密码。

2.2解决题目

首先完成开始前的操作。

出现下列代码(由于本关代码量太大了,需要多按几次回车)

Dump of assembler code for function phase_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
   0x0000000000401103 <+15>:    mov    %rsp,%rsi
   0x0000000000401106 <+18>:    callq  0x40145c <read_six_numbers>
   0x000000000040110b <+23>:    mov    %rsp,%r14
   0x000000000040110e <+26>:    mov    $0x0,%r12d
   0x0000000000401114 <+32>:    mov    %r13,%rbp
   0x0000000000401117 <+35>:    mov    0x0(%r13),%eax
   0x000000000040111b <+39>:    sub    $0x1,%eax
   0x000000000040111e <+42>:    cmp    $0x5,%eax
   0x0000000000401121 <+45>:    jbe    0x401128 <phase_6+52>
   0x0000000000401123 <+47>:    callq  0x40143a <explode_bomb>
   0x0000000000401128 <+52>:    add    $0x1,%r12d
   0x000000000040112c <+56>:    cmp    $0x6,%r12d
   0x0000000000401130 <+60>:    je     0x401153 <phase_6+95>
   0x0000000000401132 <+62>:    mov    %r12d,%ebx
   0x0000000000401135 <+65>:    movslq %ebx,%rax
   0x0000000000401138 <+68>:    mov    (%rsp,%rax,4),%eax
   0x000000000040113b <+71>:    cmp    %eax,0x0(%rbp)
   0x000000000040113e <+74>:    jne    0x401145 <phase_6+81>
   0x0000000000401140 <+76>:    callq  0x40143a <explode_bomb>
   0x0000000000401145 <+81>:    add    $0x1,%ebx
   0x0000000000401148 <+84>:    cmp    $0x5,%ebx
   0x000000000040114b <+87>:    jle    0x401135 <phase_6+65>
   0x000000000040114d <+89>:    add    $0x4,%r13
   0x0000000000401151 <+93>:    jmp    0x401114 <phase_6+32>
   0x0000000000401153 <+95>:    lea    0x18(%rsp),%rsi
   0x0000000000401158 <+100>:   mov    %r14,%rax
   0x000000000040115b <+103>:   mov    $0x7,%ecx
   0x0000000000401160 <+108>:   mov    %ecx,%edx
   0x0000000000401162 <+110>:   sub    (%rax),%edx
   0x0000000000401164 <+112>:   mov    %edx,(%rax)
   0x0000000000401166 <+114>:   add    $0x4,%rax
   0x000000000040116a <+118>:   cmp    %rsi,%rax
   0x000000000040116d <+121>:   jne    0x401160 <phase_6+108>
   0x000000000040116f <+123>:   mov    $0x0,%esi
   0x0000000000401174 <+128>:   jmp    0x401197 <phase_6+163>
   0x0000000000401176 <+130>:   mov    0x8(%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
   0x0000000000401188 <+148>:   mov    %rdx,0x20(%rsp,%rsi,2)
   0x000000000040118d <+153>:   add    $0x4,%rsi
   0x0000000000401191 <+157>:   cmp    $0x18,%rsi
   0x0000000000401195 <+161>:   je     0x4011ab <phase_6+183>
   0x0000000000401197 <+163>:   mov    (%rsp,%rsi,1),%ecx
   0x000000000040119a <+166>:   cmp    $0x1,%ecx
   0x000000000040119d <+169>:   jle    0x401183 <phase_6+143>
   0x000000000040119f <+171>:   mov    $0x1,%eax
   0x00000000004011a4 <+176>:   mov    $0x6032d0,%edx
   0x00000000004011a9 <+181>:   jmp    0x401176 <phase_6+130>
   0x00000000004011ab <+183>:   mov    0x20(%rsp),%rbx
   0x00000000004011b0 <+188>:   lea    0x28(%rsp),%rax
   0x00000000004011b5 <+193>:   lea    0x50(%rsp),%rsi
   0x00000000004011ba <+198>:   mov    %rbx,%rcx
   0x00000000004011bd <+201>:   mov    (%rax),%rdx
   0x00000000004011c0 <+204>:   mov    %rdx,0x8(%rcx)
   0x00000000004011c4 <+208>:   add    $0x8,%rax
   0x00000000004011c8 <+212>:   cmp    %rsi,%rax
   0x00000000004011cb <+215>:   je     0x4011d2 <phase_6+222>
   0x00000000004011cd <+217>:   mov    %rdx,%rcx
   0x00000000004011d0 <+220>:   jmp    0x4011bd <phase_6+201>
   0x00000000004011d2 <+222>:   movq   $0x0,0x8(%rdx)
   0x00000000004011da <+230>:   mov    $0x5,%ebp
   0x00000000004011df <+235>:   mov    0x8(%rbx),%rax
   0x00000000004011e3 <+239>:   mov    (%rax),%eax
   0x00000000004011e5 <+241>:   cmp    %eax,(%rbx)
   0x00000000004011e7 <+243>:   jge    0x4011ee <phase_6+250>
   0x00000000004011e9 <+245>:   callq  0x40143a <explode_bomb>
   0x00000000004011ee <+250>:   mov    0x8(%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>:   retq   
End of assembler dump.

代码太多了,光看着就已经头疼了。

第一个爆炸出现在+47,从此开始回溯。

+18之前的都是对函数的一些准备

+18,调用函数read_six_numbers(读取六个数字)

+23,rsp(栈指针)保存在r14

+26,r12d初始化为0

+32,r13储存在rbp(也就是读取的数字的储存地址)

+35,第一个数保存在eax

+39,eax中的数字-1

+42,eax中的数字与5比较

+45,不大于5则跳转到+52

+47,大于5则爆炸

所以为了保证不爆炸,eax要<=5,由于eax是-1过的,所以我们输入的第一个数字要<=6,那么跳转到+52

+52,r12d中的数值+1

+56,比较r12d和6

+60,如果相等则跳转到+95(由此可以看出这是r12d其实是一个计数器,这里是一个循环)

+62,不相等则把r12d中的数字保存到ebx

+65,ebx中数字保存到rax

+68,把rsp+4*rax保存到eax(这里是把下一个数字保存到了eax)

+71,比较eax和rbp(其实就是把下一个数字和上一个数字比较)

+74,不相等则跳转到+81

+76,相等则爆炸

可以看出,这里其实是为了防止我们输入的六个数字有重复,为了不爆炸,我们要保证输入的数字不能重复,那么跳转到+81

+81到+93其实都是循环的一部分,为了节省字数,我们就忽略了。直接在+60处,完成循环,所以我们跳到了+95

+95,rsi设置为rsp+24的值

+100,r14的值保存在rax

+103,ecx设置为7

+108,edx设置为ecx

+110,7-rax中储存的值指向的地址的值(其实就是把读取的数字当作一个地址,然后减去这个地址的值)

+112,把edx中的值保存在一个新的地址(这个地址是读取的值)

+114,rax中的值+4(意思是下一个数字)

+118,比较rsi和rax

+121,不等则跳转到+108(其实也是个循环,这里检测循环是否结束)

那么上述这些代码,其实是以读取的数字为地址,在内存中建立了一个链表

+123,相等则esi设置为0

+128,跳转到+163

+163,rsp+rsi保存在ecx(应该是读取的数字)

+166,比较1和ecx

+169,ecx<=1时跳转到+143(分支1)

+171,否则eax=1(分支2)

分支1:

+143,edx设置为0x6032d0

+148,rdx保存在rsp+2*rsi+32(也就是把上面的0x6032d0保存了)

+153,rsi+4

+157,比较24和rsi

+161,相等则跳转到+183

+163,不相等则回到上面继续循环

分支2:

+176,edx设置为0x6032d0

+181,跳转到+130

+130,rdx设置为rdx+8

+134,eax+1

+137,比较eax和ecx

+139,不相等则跳转到+130,进入循环

+141,相等则跳转到+148,回到了分支1

上面这个大的循环是把读取的数字挨个与1比较,如果<=1就保存0x6032d0,>1则也会回到分支1,最终汇合在+183

+183,rbx设置为rsp+32的值(这里应该是链表第一个值)

+188,rax设置为rsp+40的值(这里应该是链表开始地址)

+193,rsi设置为rsp+80(这里应该是链表结束的地址)

+198,rbx发送到rcx

+201,rdx设置为以rax中的值为地址的值

+204,rdx发送到rcx+8(这里是把上述值储存在链表下一个位置了)

+208,rax+8(指向下一个地址)

+212,比较rax和rsi(判断是否到链表尽头)

+215,相等则跳转到+222

+217,否则rdx传送到rcx

+220,跳转到+201

完成链表的构建之后,也就是到了+222

+222,rdx+8设置为0

+230,ebp=5

+235,rax=rbx+8处的值

+239,eax=rax指向的值

+241,比较eax和rbx指向的值(其实就是比较链表中相邻的两个值)

+243,如果eax>=rbx(也就是说前一个值大于等于当前值)则跳转到+250

+245,否则爆炸

为了不爆炸,我们需要保证前一个值大于等于当前值

+250,rbx=rbx+8处的值

+254,ebp-1

+257,如果ebp不等于0,那么跳转到+235(又是循环)

那么这里循环的作用就是让整个链表是递减的

+259,rsp+80

从此处往后就不用看了,函数结束了

2.3解答问题

那么整个phase_6代码,其实就是要求输入六个数字,这六个数字介于1到6之间,并且不能重复,而且期间用7减去了每个数字构建了链表,最终的链表还是个递减的。那么构建的链表什么样呢?请注意,在代码里反复储存了一个数据0x6032d0,这应该是一个映射表,我们来查询一下

x/12xg 0x6032d0

结果是

0x6032d0 <node1>:       0x000000010000014c      0x00000000006032e0
0x6032e0 <node2>:       0x00000002000000a8      0x00000000006032f0
0x6032f0 <node3>:       0x000000030000039c      0x0000000000603300
0x603300 <node4>:       0x00000004000002b3      0x0000000000603310
0x603310 <node5>:       0x00000005000001dd      0x0000000000603320
0x603320 <node6>:       0x00000006000001bb      0x0000000000000000

可以看出每一行都给出了两个地址,经过查询得知:

第一个0x000000010000014c由两部分组成, 00000001表示是链表的第一个,0000014c是它的值,也就是十进制的332。

第二个0x00000000006032e0是下一个链表的地址(确实可以对上)

所以可以看出。六个链表的值为

序列123456
332168924691477443

所以为了保证最终的链表是递减的,我们应该排序为:924 691 477 443 332 168

所以序号的排序为:3 4 5 6 1 2(注意这是7减过的)

所以原始的序号应该为4 3 2 1 6 5

3.拆弹密码隐藏关

前面的写起来真是太费劲了,哄了自己一星期才愿意写这一关。

3.1解析题目

本关任务:先找到隐藏关,再找到本关拆弹密码。

3.2解决题目

开始前的操作不再赘述,作者在这里多了一个提示

叽里咕噜的说的甚么鸟话听不懂,只看到了最后的

disas secret_phase

然后显示汇编代码

Dump of assembler code for function secret_phase:
   0x0000000000401242 <+0>:     push   %rbx
   0x0000000000401243 <+1>:     callq  0x40149e <read_line>
   0x0000000000401248 <+6>:     mov    $0xa,%edx
   0x000000000040124d <+11>:    mov    $0x0,%esi
   0x0000000000401252 <+16>:    mov    %rax,%rdi
   0x0000000000401255 <+19>:    callq  0x400bd0 <strtol@plt>
   0x000000000040125a <+24>:    mov    %rax,%rbx
   0x000000000040125d <+27>:    lea    -0x1(%rax),%eax
   0x0000000000401260 <+30>:    cmp    $0x3e8,%eax
   0x0000000000401265 <+35>:    jbe    0x40126c <secret_phase+42>
   0x0000000000401267 <+37>:    callq  0x40143a <explode_bomb>
   0x000000000040126c <+42>:    mov    %ebx,%esi
   0x000000000040126e <+44>:    mov    $0x6030f0,%edi
   0x0000000000401273 <+49>:    callq  0x401204 <fun7>
   0x0000000000401278 <+54>:    cmp    $0x2,%eax
   0x000000000040127b <+57>:    je     0x401282 <secret_phase+64>
   0x000000000040127d <+59>:    callq  0x40143a <explode_bomb>
   0x0000000000401282 <+64>:    mov    $0x402438,%edi
   0x0000000000401287 <+69>:    callq  0x400b10 <puts@plt>
   0x000000000040128c <+74>:    callq  0x4015c4 <phase_defused>
   0x0000000000401291 <+79>:    pop    %rbx
   0x0000000000401292 <+80>:    retq   
End of assembler dump.

爆炸代码在+37,从这里开始回溯

+1,调用函数read_line(这个应该是读取用户的输入)

+6,edx设置为10

+11,esi设置为0

+16,把rax发送到rdi

+19,调用函数strtol@plt(这是一个把字符串变成数字的函数)

+24,把rax发送到rbx(rax也就是上述函数返回的整数)

+27,eax设置为rax-1

+30,比较1000和eax的值

+35,如果eax<=1000则跳转到+42

+37,否则爆炸

为了不爆炸,我们让eax<=1000,跳转到+42

+42,把ebx发送到esi

+44,把edi设置为0x6030f0

+49,调用函数fun7

+54,将函数返回值与2比较

+57,如果相等则跳转到+64

+59,不相等则爆炸

为了保证不爆炸,我们需要让函数返回值为2,那么跳转到+64

+64,edi设置为4203576

+69,调用函数puts@plt(这里应该是一个输出消息,应该不重要)

+74,调用函数phase_defused(函数解除,可能意味着我们成功了)

+79,从这开始到最后就是结束

所以我们只需要让函数返回值eax为2,并且eax一开始<=1000(也就是rax一开始<=1001)

我们来查看一下函数fun7的汇编代码

Dump of assembler code for function fun7:
   0x0000000000401204 <+0>:     sub    $0x8,%rsp
   0x0000000000401208 <+4>:     test   %rdi,%rdi
   0x000000000040120b <+7>:     je     0x401238 <fun7+52>
   0x000000000040120d <+9>:     mov    (%rdi),%edx
   0x000000000040120f <+11>:    cmp    %esi,%edx
   0x0000000000401211 <+13>:    jle    0x401220 <fun7+28>
   0x0000000000401213 <+15>:    mov    0x8(%rdi),%rdi
   0x0000000000401217 <+19>:    callq  0x401204 <fun7>
   0x000000000040121c <+24>:    add    %eax,%eax
   0x000000000040121e <+26>:    jmp    0x40123d <fun7+57>
   0x0000000000401220 <+28>:    mov    $0x0,%eax
   0x0000000000401225 <+33>:    cmp    %esi,%edx
   0x0000000000401227 <+35>:    je     0x40123d <fun7+57>
   0x0000000000401229 <+37>:    mov    0x10(%rdi),%rdi
   0x000000000040122d <+41>:    callq  0x401204 <fun7>
   0x0000000000401232 <+46>:    lea    0x1(%rax,%rax,1),%eax
   0x0000000000401236 <+50>:    jmp    0x40123d <fun7+57>
   0x0000000000401238 <+52>:    mov    $0xffffffff,%eax
   0x000000000040123d <+57>:    add    $0x8,%rsp
   0x0000000000401241 <+61>:    retq   
End of assembler dump.

我们来分析一下这个汇编代码

+0,分配空间

+4,检查参数rdi

+7,如果为0,那么跳转到+52

+52,eax设置为-1

+57,释放空间,函数结束

所以可以看出,如果参数rdi为0,那么函数直接结束,并且返回-1,这明显不符合我们的要求

所以我们要让参数rdi不为0,由于rdi是传入的0x6030f0,所以肯定是不为0的

+9,把rdi指向的值发送到edx

+11,比较esi和edx(esi是我们传入的参数,edx是我们当前指向的值)

+13,如果esi<=edx,那么跳转到+28(分支1)

+15,如果esi>edx,那么rdi设置为rdi+8处的值(应该是左节点,分支2)

分支1:

+28,eax设置为0

+33,比较esi和edx

+35,如果相等跳转到+57(也就是结束)

+37,不相等则把rdi设置为rdi+16处的值(应该是右节点)

很明显,如果相等的话,这里这里eax为0,不满足我们的要求,所以最开始esi和edx不能相等

+41,调用函数fun7(也就是调用自身,这是一个递归)

+46,递归结束之后eax设置为2*eax+1

分支2:

+19,调用函数fun7(也进入了递归)

+24,递归结束之后,eax设置为自身的两倍

对这个函数做一个总结

eax=eax*2+1(沿着右边寻找的时候)

eax=eax*2(沿着左边寻找的时候)

由于最终结果eax=2,所以我们按照回溯的顺序,有两种解:

解答1:

首先找到答案,然后eax=0

向上回溯,右侧,eax=1

向上回溯,左侧,eax=2

解答2:

首先找到答案,eax=0

向上回溯,左侧,eax=0

向上回溯,右侧,eax=1

向上回溯,左侧,eax=2

所以,我们来查看一下这个二叉树

x/128xg 0x6030f0

结果如下

0x6030f0 <n1>:                  0x0000000000000024      0x0000000000603110
0x603100 <n1+16>:               0x0000000000603130      0x0000000000000000
0x603110 <n21>:                 0x0000000000000008      0x0000000000603190
0x603120 <n21+16>:              0x0000000000603150      0x0000000000000000
0x603130 <n22>:                 0x0000000000000032      0x0000000000603170
0x603140 <n22+16>:              0x00000000006031b0      0x0000000000000000
0x603150 <n32>:                 0x0000000000000016      0x0000000000603270
0x603160 <n32+16>:              0x0000000000603230      0x0000000000000000
0x603170 <n33>:                 0x000000000000002d      0x00000000006031d0
0x603180 <n33+16>:              0x0000000000603290      0x0000000000000000
0x603190 <n31>:                 0x0000000000000006      0x00000000006031f0
0x6031a0 <n31+16>:              0x0000000000603250      0x0000000000000000
0x6031b0 <n34>:                 0x000000000000006b      0x0000000000603210
0x6031c0 <n34+16>:              0x00000000006032b0      0x0000000000000000
0x6031d0 <n45>:                 0x0000000000000028      0x0000000000000000
0x6031e0 <n45+16>:              0x0000000000000000      0x0000000000000000
0x6031f0 <n41>:                 0x0000000000000001      0x0000000000000000
0x603200 <n41+16>:              0x0000000000000000      0x0000000000000000
0x603210 <n47>:                 0x0000000000000063      0x0000000000000000
0x603220 <n47+16>:              0x0000000000000000      0x0000000000000000
0x603230 <n44>:                 0x0000000000000023      0x0000000000000000
0x603240 <n44+16>:              0x0000000000000000      0x0000000000000000
0x603250 <n42>:                 0x0000000000000007      0x0000000000000000
0x603260 <n42+16>:              0x0000000000000000      0x0000000000000000
0x603270 <n43>:                 0x0000000000000014      0x0000000000000000
0x603280 <n43+16>:              0x0000000000000000      0x0000000000000000
0x603290 <n46>:                 0x000000000000002f      0x0000000000000000
0x6032a0 <n46+16>:              0x0000000000000000      0x0000000000000000
0x6032b0 <n48>:                 0x00000000000003e9      0x0000000000000000
0x6032c0 <n48+16>:              0x0000000000000000      0x0000000000000000
0x6032d0 <node1>:               0x000000010000014c      0x00000000006032e0
0x6032e0 <node2>:               0x00000002000000a8      0x00000000006032f0
0x6032f0 <node3>:               0x000000030000039c      0x0000000000603300
0x603300 <node4>:               0x00000004000002b3      0x0000000000603310
0x603310 <node5>:               0x00000005000001dd      0x0000000000603320
0x603320 <node6>:               0x00000006000001bb      0x0000000000000000
0x603330:                       0x0000000000000000      0x0000000000000000
0x603340 <host_table>:          0x0000000000402629      0x0000000000402643
0x603350 <host_table+16>:       0x000000000040265d      0x0000000000000000
0x603360 <host_table+32>:       0x0000000000000000      0x0000000000000000
0x603370 <host_table+48>:       0x0000000000000000      0x0000000000000000
0x603380 <host_table+64>:       0x0000000000000000      0x0000000000000000
0x603390 <host_table+80>:       0x0000000000000000      0x0000000000000000
0x6033a0 <host_table+96>:       0x0000000000000000      0x0000000000000000
0x6033b0 <host_table+112>:      0x0000000000000000      0x0000000000000000
0x6033c0 <host_table+128>:      0x0000000000000000      0x0000000000000000
0x6033d0 <host_table+144>:      0x0000000000000000      0x0000000000000000
0x6033e0 <host_table+160>:      0x0000000000000000      0x0000000000000000
0x6033f0 <host_table+176>:      0x0000000000000000      0x0000000000000000
0x603400 <host_table+192>:      0x0000000000000000      0x0000000000000000
0x603410 <host_table+208>:      0x0000000000000000      0x0000000000000000
0x603420 <host_table+224>:      0x0000000000000000      0x0000000000000000
0x603430 <host_table+240>:      0x0000000000000000      0x0000000000000000
0x603440 <host_table+256>:      0x0000000000000000      0x0000000000000000
0x603450 <host_table+272>:      0x0000000000000000      0x0000000000000000
0x603460 <host_table+288>:      0x0000000000000000      0x0000000000000000
0x603470 <host_table+304>:      0x0000000000000000      0x0000000000000000
0x603480 <host_table+320>:      0x0000000000000000      0x0000000000000000
0x603490 <host_table+336>:      0x0000000000000000      0x0000000000000000
0x6034a0 <host_table+352>:      0x0000000000000000      0x0000000000000000
0x6034b0 <host_table+368>:      0x0000000000000000      0x0000000000000000
0x6034c0 <host_table+384>:      0x0000000000000000      0x0000000000000000
0x6034d0 <host_table+400>:      0x0000000000000000      0x0000000000000000
0x6034e0 <host_table+416>:      0x0000000000000000      0x0000000000000000

我们来分析一下结构,以第一个为例

0x6030f0 <n1>:                  0x0000000000000024      0x0000000000603110
0x603100 <n1+16>:               0x0000000000603130      0x0000000000000000

第一块 0x0000000000000024,指的是节点的值

第二块 0x0000000000603110,是左子树的地址

第三块,0x0000000000603130,是右子树的地址

第四块,0x0000000000000000,是无用的

那我们就可以把这个二叉树画出来

3.3解答问题

我们按照之前的两个解答,寻找答案

解答1:0x16

解答2:0x14

但是经过我的测试22没法通过测试(也就是0x16)

所以就输入20吧

printf("20");

恭喜你凭借你的聪明才智完成了整个系列实验,祝贺!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值