《Intro to Computer Systems》(csapp)LAB2(Bomb lab)

本文详细讲述了作者通过gcc和gdb工具逐层解析编译系统中的寄存器操作、数据传送指令,以及在lab1到phase_6阶段的深入分析过程,最终揭示了phase_4和phase_5中关键函数的秘密。隐藏关卡的突破涉及递归函数和字符串处理,解密了secret_phase的密码22。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

编译系统

在这里插入图片描述
对于源代码hello.c

  • gcc -Og -S hello.c,则生成hello.s汇编程序。
  • gcc -Og -c hello.c,则生成生成hello.o可重定位文件。
  • gcc -Og hello.c,则生成可执行文件a.out.
  • 可以使用objdump -d a.out对.o文件或.out文件进行反汇编。

常用寄存器

在这里插入图片描述

操作数格式

在这里插入图片描述

数据传送指令

在这里插入图片描述

gcc 和 gdb

gdb常用指令

在这里插入图片描述
layout 指令非常好用 。

lab1

之前做了该实验的前三部分博客。方法有些问题,过于纠结一些库函数的汇编代码。本次重新写一遍,并且做完该实验。

phase_1

$gdb bomb 调试bomb程序
(gdb) b phase_1在函数phase_1处打上断点
(gdb) r运行
(gdb) layout regs 查看汇编代码及寄存器
在这里插入图片描述
如下,可以发现phase_1偏移9的位置的strings_not_equl方法是关键l,再使用si进入strings_not_equal方法。
在这里插入图片描述
如上图,在strings_not_equal运行到第4行以及第18行的时候,通过查看内存位置是$rdi(调用string_length的参数)的内容,即可找到我们要比较的字符串。如下图
在这里插入图片描述
因此,第一个密码是Border relations with Canada have never been better.

phase_2

编辑input文件内容如下,方便每次调试不用重新输入之前的密码。

Border relations with Canada have never been better.

$gdb bomb
(gdb) b phase_2 断点打在phase_2函数上
(gdb) r input 在此步骤以后随便输入6个数字> 1 2 3 4 5 6
(gdb) layout regs
在这里插入图片描述
在这里插入图片描述

在此不对read_six_numbers进行详细分析可以尝试不同的输入跟踪该方法(该方法如果长度不满足要求也会引爆炸弹),从结果看,可以发现read_six_numbers方法读入6个数字到栈顶,紧接着判断第一个元素是否是1。

在这里插入图片描述
通过上述分析,需要判断下一个数字是否是上一个数字的两倍,即最终正确的输入需要是>1 2 4 8 16 32

phase_3

分析

本次lab3比上两个要难,在此之前,让我们搞清楚一些问题。

  • read_line读入的数据在哪?(以输入>1 3 4为例子)
    在main 函数中可以发现char *input;,并没有分配数组,同时,通过后面的DEBUG,可以发现这些数据实际上是通过read_line函数分配在了堆区(根据地址位置)。在这里插入图片描述
  • phase_x中调用函数时候传入的参数为char*指针input,即寄存器edi。

solution

phase_3的反汇编代码进行分析。
$gdb bomb
$(gdb) disas phase_3
结合下面三个图,非常容易分析得到结果。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述那么,可以获得最终结果是上述任意一个分支都可以。

0 207
1 311
2 707
3 256
4 389
5 206 
6 682
7 327

phase_4

首先进行反汇编phase_4函数,如果phase_3理解了,可以轻松明白phase_4的汇编代码。
在这里插入图片描述

可以看到,该函数的关键在于func4函数,可以接着对func4进行反汇编。
在这里插入图片描述
很容易发现,func4是个递归函数,果然递归的反汇编难读。我在参考了别的BLOG之后得到思路是先写成C函数,再求解,因此,我得到了如下代码。

//第一次调用时,y = 0, z = 14
int func4(int x, int y, int z) {
	//x = edi; y = esi; z = edx;
	int a = z - y;
	a = (a + (a >> 31)) >> 1;
	int c = a + y;	//第一次调用时 c = 7
	if (x <= c) {
		a = 0;
		if (x >= c) {
			return a;
		} 
		else {
			a = func4(x, c + 1, z);
			a = a + a + 1;
			return a;
		}
	}
	else {
		a = func4(x, y , c - 1);
		a = a + a;
	}
	return a;
}

如果想让func4返回0,则必须x = 7。因此最后拆炸弹的密码为7 0

phase_5

该步难度还行,直接反汇编看看代码,纠结了一会,再DEBUG就可以搞定。代码关键部分要搞清楚即可(用--------分割)

   0x0000000000401062 <+0>:	push   %rbx
   0x0000000000401063 <+1>:	sub    $0x20,%rsp
   0x0000000000401067 <+5>:	mov    %rdi,%rbx			//bx = rdi
   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					//输入的长度必须是6
   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		//cx为输入的字符ASCII用char表示
   0x000000000040108f <+45>:	mov    %cl,(%rsp)				//栈顶 = char
   0x0000000000401092 <+48>:	mov    (%rsp),%rdx				//rdx = char
   0x0000000000401096 <+52>:	and    $0xf,%edx				//edx = char保留低4位
   0x0000000000401099 <+55>:	movzbl 0x4024b0(%rdx),%edx		//(rdx这里用作偏移地址)取到的这个字符是什么????不知道就运行时候DEBUG看看
   0x00000000004010a0 <+62>:	mov    %dl,0x10(%rsp,%rax,1)	//6个不知名字符放在栈偏移0x10~0x15上
   0x00000000004010a4 <+66>:	add    $0x1,%rax
   0x00000000004010a8 <+70>:	cmp    $0x6,%rax				//循环6次
   0x00000000004010ac <+74>:	jne    0x40108b <phase_5+41>
---------------------------------------------------------------------------------------------
   0x00000000004010ae <+76>:	movb   $0x0,0x16(%rsp)
   0x00000000004010b3 <+81>:	mov    $0x40245e,%esi
   0x00000000004010b8 <+86>:	lea    0x10(%rsp),%rdi			//把上面核心段中的0x10~0x15字符串和$0x40245e位置上的字符串进行比较
   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					//ax = 0;
   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   

通过上述反汇编代码,可以发现我们需要关注两个东西,如下。(红色标注的是需要构造出来的各个字符的偏移地址)
在这里插入图片描述
实际上这题的意思就是让我们用输入的6个字符的低4位作为偏移地址去取6个字符,构成flyers。那查查ASCII表挑几个字母即可解决。
需要注意的是,因为取低4位,因此我们只能在字符串前16位中找我们需要的字符串。
flyers对应的位置分别是9 F E 5 6 7
我取了ionuvw字符串作为结果。

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				//r13栈顶
   0x0000000000401103 <+15>:	mov    %rsp,%rsi
   0x0000000000401106 <+18>:	callq  0x40145c <read_six_numbers>
   0x000000000040110b <+23>:	mov    %rsp,%r14				//r14存储6个数字的位置
   0x000000000040110e <+26>:	mov    $0x0,%r12d				
   0x0000000000401114 <+32>:	mov    %r13,%rbp				//rbp栈顶
   0x0000000000401117 <+35>:	mov    0x0(%r13),%eax			//ax为第一个数字
   0x000000000040111b <+39>:	sub    $0x1,%eax				
   0x000000000040111e <+42>:	cmp    $0x5,%eax
   0x0000000000401121 <+45>:	jbe    0x401128 <phase_6+52>	//需要ax <=5 
   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				//ebx和abx是偏移地址
   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>	//偏移地址<=5
    0x000000000040114d <+89>:	add    $0x4,%r13							
   0x0000000000401151 <+93>:	jmp    0x401114 <phase_6+32>	//大循环,依次判断每一个数字

该段代码的作用是读入6个数字到栈顶,并且每一个数必须小于等于6代码地址<+45>,并且这6个数字互不相同代码地址<+65>~<+87>。这一段就像是一个冒泡比较(第一个数字比后5个,第二个数比后4个。。。最后一个数字不需要再比了),因此需要读入的1 2 3 4 5 6,后面考虑考虑排列顺序。

   0x0000000000401153 <+95>:	lea    0x18(%rsp),%rsi				rsi = end(input)(4 * 6 = 24)
   0x0000000000401158 <+100>:	mov    %r14,%rax					ax = input;
   0x000000000040115b <+103>:	mov    $0x7,%ecx					cx = 7;
   0x0000000000401160 <+108>:	mov    %ecx,%edx					dx = 7;
   0x0000000000401162 <+110>:	sub    (%rax),%edx					dx -= input[0]
   0x0000000000401164 <+112>:	mov    %edx,(%rax)					input[0] = dx
   0x0000000000401166 <+114>:	add    $0x4,%rax					ax = input++
   0x000000000040116a <+118>:	cmp    %rsi,%rax					rsi != input	
   0x000000000040116d <+121>:	jne    0x401160 <phase_6+108>		//input[i] = 7-input[i]

这一段是将input[i]替换成7-input[i]

   0x000000000040116f <+123>:	mov    $0x0,%esi					esi = 0
   0x0000000000401174 <+128>:	jmp    0x401197 <phase_6+163>		
   0x0000000000401176 <+130>:	mov    0x8(%rdx),%rdx				即dx = dx.next
   0x000000000040117a <+134>:	add    $0x1,%eax					ax++;
   0x000000000040117d <+137>:	cmp    %ecx,%eax					input[esi] == ax
   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)		//node中的第input[esi]个节点放在第rsi个
   0x000000000040118d <+153>:	add    $0x4,%rsi
   0x0000000000401191 <+157>:	cmp    $0x18,%rsi
   0x0000000000401195 <+161>:	je     0x4011ab <phase_6+183>		//这一段循环6次
   0x0000000000401197 <+163>:	mov    (%rsp,%rsi,1),%ecx			cx = input[esi]
   0x000000000040119a <+166>:	cmp    $0x1,%ecx					
   0x000000000040119d <+169>:	jle    0x401183 <phase_6+143>		input[esi] <= 1
   0x000000000040119f <+171>:	mov    $0x1,%eax					ax = 1
   0x00000000004011a4 <+176>:	mov    $0x6032d0,%edx				dx = 0x6032d0
   0x00000000004011a9 <+181>:	jmp    0x401176 <phase_6+130>

在这里插入图片描述

实际效果啊参考这位老哥的。注意下图他的输入数据应该是翻转以后的即真实输入时4,5,3,1,2,6(7 - input[i]),这位老哥最后一步分析出了错误
在这里插入图片描述

   0x00000000004011ab <+183>:	mov    0x20(%rsp),%rbx				//rbx为栈上存node的地址
   0x00000000004011b0 <+188>:	lea    0x28(%rsp),%rax				//rax = rbx + 0x20 + 0x8
   0x00000000004011b5 <+193>:	lea    0x50(%rsp),%rsi				//rax = rbx + 0x20 + 0x30
   0x00000000004011ba <+198>:	mov    %rbx,%rcx					//cx = rbx
   0x00000000004011bd <+201>:	mov    (%rax),%rdx					//dx = *ax
   0x00000000004011c0 <+204>:	mov    %rdx,0x8(%rcx)				//重新修改node节点的next指针
   0x00000000004011c4 <+208>:	add    $0x8,%rax
   0x00000000004011c8 <+212>:	cmp    %rsi,%rax					//继续再栈上遍历下一个node
   0x00000000004011cb <+215>:	je     0x4011d2 <phase_6+222>
   0x00000000004011cd <+217>:	mov    %rdx,%rcx					//cx指向下一个
   0x00000000004011d0 <+220>:	jmp    0x4011bd <phase_6+201>
   0x00000000004011d2 <+222>:	movq   $0x0,0x8(%rdx)				//修改最后一个节点的尾指针
   0x00000000004011da <+230>:	mov    $0x5,%ebp					//ebp = 5
   0x00000000004011df <+235>:	mov    0x8(%rbx),%rax				//ax = rbx.next
   0x00000000004011e3 <+239>:	mov    (%rax),%eax					
   0x00000000004011e5 <+241>:	cmp    %eax,(%rbx)					//比较node的第一个域
   0x00000000004011e7 <+243>:	jge    0x4011ee <phase_6+250>		//(rbx) > rbx.next 降序!
   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  

最后一段较为简单,就是判断按照重新排列node,第一个域为降序即可。
在这里插入图片描述
渴望顺序3 4 5 6 1 2,计算7-input[i],真实输入为4 3 2 1 6 5
至此,拆除炸弹成功!

结果

input

Border relations with Canada have never been better.
1 2 4 8 16 32
3 256
7 0
ionuvw
4 3 2 1 6 5

在这里插入图片描述

secret_phase(隐藏关?)

根据源代码提示有隐藏关,并且在函数中不停的调用一个phase_defused没什么影响的方法,那说明这个和隐藏关有关。

    /* Wow, they got it!  But isn't something... missing?  Perhaps
     * something they overlooked?  Mua ha ha ha ha! */

进入隐藏关

找到和隐藏关对应的函数phase_defused,反汇编它。先进行注释,下面接着分析。

   0x00000000004015c4 <+0>:	sub    $0x78,%rsp
   0x00000000004015c8 <+4>:	mov    %fs:0x28,%rax
   0x00000000004015d1 <+13>:	mov    %rax,0x68(%rsp)
   0x00000000004015d6 <+18>:	xor    %eax,%eax
   0x00000000004015d8 <+20>:	cmpl   $0x6,0x202181(%rip)        # 0x603760 <num_input_strings>
   0x00000000004015df <+27>:	jne    0x40163f <phase_defused+123>	//断点调试发现通过第六关才能进入下段代码
   0x00000000004015e1 <+29>:	lea    0x10(%rsp),%r8				//参数5
   0x00000000004015e6 <+34>:	lea    0xc(%rsp),%rcx				//参数4
   0x00000000004015eb <+39>:	lea    0x8(%rsp),%rdx				//参数3
   0x00000000004015f0 <+44>:	mov    $0x402619,%esi				//参数2"%d %d %s"		
   0x00000000004015f5 <+49>:	mov    $0x603870,%edi				//参数1"7 0"
   0x00000000004015fa <+54>:	callq  0x400bf0 <__isoc99_sscanf@plt>
   0x00000000004015ff <+59>:	cmp    $0x3,%eax			
   0x0000000000401602 <+62>:	jne    0x401635 <phase_defused+113>	//这里必须读了3个数
   0x0000000000401604 <+64>:	mov    $0x402622,%esi				//"DrEvil"
   0x0000000000401609 <+69>:	lea    0x10(%rsp),%rdi
   0x000000000040160e <+74>:	callq  0x401338 <strings_not_equal>	//必须读一个 DrEvil
   0x0000000000401613 <+79>:	test   %eax,%eax
   0x0000000000401615 <+81>:	jne    0x401635 <phase_defused+113>
   0x0000000000401617 <+83>:	mov    $0x4024f8,%edi
   0x000000000040161c <+88>:	callq  0x400b10 <puts@plt>
   0x0000000000401621 <+93>:	mov    $0x402520,%edi
   0x0000000000401626 <+98>:	callq  0x400b10 <puts@plt>
   0x000000000040162b <+103>:	mov    $0x0,%eax
   0x0000000000401630 <+108>:	callq  0x401242 <secret_phase>
   0x0000000000401635 <+113>:	mov    $0x402558,%edi
   0x000000000040163a <+118>:	callq  0x400b10 <puts@plt>
   0x000000000040163f <+123>:	mov    0x68(%rsp),%rax
   0x0000000000401644 <+128>:	xor    %fs:0x28,%rax
   0x000000000040164d <+137>:	je     0x401654 <phase_defused+144>
   0x000000000040164f <+139>:	callq  0x400b30 <__stack_chk_fail@plt>
   0x0000000000401654 <+144>:	add    $0x78,%rsp
   0x0000000000401658 <+148>:	retq  
  • <+27>行代码发现只有第六个关卡才可以进入(应该还有其他途径分析得出)
  • <+27>~<+49>行代码发现调用了sscanf("7 0", "%d %d %s", 0x8(%rsp), 0x10(%rsp),0x10(%rsp))
  • <+64>~<+74>行代码发现0x10(%rsp)中存储的字符串需要是DrEvil

到此,如果敏感可以看出来7 0 这两个数是我们为过第四关的输入。那么我们可以尝试在这之后在输入一个字符出DrEvil。即7 0 DrEvil
如果说猜的不靠谱,那我们可以打给phase_4打断点,如下图,很显然,readline动态分配的地址就是6305904。
在这里插入图片描述

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>	//将输入rdi转成长整型
   0x000000000040125a <+24>:	mov    %rax,%rbx		
   0x000000000040125d <+27>:	lea    -0x1(%rax),%eax
   0x0000000000401260 <+30>:	cmp    $0x3e8,%eax			//0x3e8 = 1000
   0x0000000000401265 <+35>:	jbe    0x40126c <secret_phase+42>	//该整数需要小于等于1001
   0x0000000000401267 <+37>:	callq  0x40143a <explode_bomb>
   0x000000000040126c <+42>:	mov    %ebx,%esi		//参数2 输入的数字
   0x000000000040126e <+44>:	mov    $0x6030f0,%edi	//参数1	存的值是24
   0x0000000000401273 <+49>:	callq  0x401204 <fun7>
   0x0000000000401278 <+54>:	cmp    $0x2,%eax			//fun7函数需要返回2
   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   

上述代码较容易理解,下面看看fun7的反汇编代码。(显然是个递归函数,那不多说,直接翻译成c语言),需要fun7函数需要返回2。

   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  

对应的C代码如下:

//x = rdi, y = esi 
//初始 x = 0x6030f0 *x = 24, esi = input
int func(* x, int y) {
	dx = *x;
	if (dx <= y) {
		ax = 0;
		dx = y;
		if (dx == y) {
			return ax;
		} else {	
			x = *(x + 0x10);
			ax = func(x, y)
			ax = 2*ax + 1;
		}
	} else {
		x = *(x + 0x8);
		ax = func(x, y);
		ax = ax + ax;
		return ax;
	}
}

继续往下分析很困惑,我只看出来x指针含有3个域,而不同的分支分别进入了x = *(x + 0x8);*(x + 0x10)。其实这个数据结构是有val,left和right的树。那就可以稍作修改(没什么别要)。如下:

int fun7(node *rt, int y) {
	int ans;
	int val = rt->val;
	if (val <= y) {
		if (val == y)
			return 0;
		else {
			ans = fun7(x.right, y);
			return 2 * ans + 1;
		}
	} else {
		ans = fun7(rt->left, y);
		return 2 * ans;
	}
}

需要初始调用fun7函数返回2。找到一个调用栈为(val > y) -> (val < y) -> (val == y)

根节点(val > y) -> 左孩子(val < y) -> 左孩子的右边孩子(val == y)
在这里插入图片描述
综上,最后的结果为0x16,即22。

最终答案:

Border relations with Canada have never been better.
1 2 4 8 16 32
3 256
7 0 DrEvil
ionuvw
4 3 2 1 6 5
22

通关截图
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值