• 【csapp lab】lab2_bomblab


    前言

    刚做了csapp lab2,记录一下。

    我这里用的的系统环境是Ubuntu22.04,是64位系统,与用32位系统可能有所差异。

    实验共包括七个阶段,每个阶段考察机器级语言程序的不同方面,难度递增

    • 阶段一:字符串比较
    • 阶段二:循环
    • 阶段三:条件/分支,含switch语句
    • 阶段四:递归调用和栈
    • 阶段五:指针
    • 阶段六:链表/指针/结构
    • 隐藏阶段:第四阶段之后附加特定字符串后出现

    在实验过程中gdb调试起到了相当重要的作用,下面是我调试过程中频繁使用的几条指令,不熟悉gdb的小伙伴一定要熟悉一下:

    • r ans.txt - 开始运行,ans.txt为命令行参数,可以省略

    • c - 运行至下一个断点处

    • b \*addr - 在addr处的指令打断点

    • d num - 删除断点num

    • display /x %reg - 每次执行完指令时打印寄存器rgx的值

    • undisplay num - 删除监视变量num

    • ni - 执行下一条指令

    • x/nws addr - x是examine,表示进行内存检查;n表示显示n个数据项;w表示以8个字节为单位进行显示;s是以字符串的形式显示解释,可以替换成x,以十六进制的形式进行解释,可以替换成u,以十进制的形式进行解释

    为了方便,我们创建一个ans.txt文件,每做出来一题就把答案写进去,这样就避免了一条一条输入答案。一题占一行,要注意最后要多一个空行,目的是最后一题的输入以 换行符结尾,保证是正确输入:

    image-20231119105551853

    实验内容

    phase_1

    找到 phase_1 函数在 main 函数中被调用的位置:

     8048a6d:	c7 04 24 70 a0 04 08 	movl   $0x804a070,(%esp)
     8048a74:	e8 47 fd ff ff       	call   80487c0 
     8048a79:	e8 69 07 00 00       	call   80491e7 
     8048a7e:	89 04 24             	mov    %eax,(%esp)
     8048a81:	e8 aa 00 00 00       	call   8048b30 
     8048a86:	e8 5f 08 00 00       	call   80492ea 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在反汇编文件中继续查找 phase_1 的位置:

    08048b30 :
     8048b30:	55                  	push   %ebp
     8048b31:	89 e5                	mov    %esp,%ebp
     8048b33:	83 ec 10             	sub    $0x10,%esp
     8048b36:	68 ec a0 04 08       	push   $0x804a0ec
     8048b3b:	ff 75 08             	push   0x8(%ebp)
     8048b3e:	e8 3f 05 00 00       	call   8049082 
     8048b43:	83 c4 10             	add    $0x10,%esp
     8048b46:	85 c0                	test   %eax,%eax
     8048b48:	74 05                	je     8048b4f 
     8048b4a:	e8 36 06 00 00        	call   8049185 
     8048b4f:	c9                  	leave  
     8048b50:	c3                  	ret    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    $0x804a0ec: 数据区地址,也是 strings_not_equal 的第二个参数

    0x8(%ebp): phase_1 的参数,也是 strings_not_equal 的第一个参数

    main()函数的汇编代码中,可以进一步找到:

     8048a79:	e8 69 07 00 00       	call   80491e7 
     8048a7e:	89 04 24             	mov    %eax,(%esp)
    
    • 1
    • 2

    %eax 里存储的是调用 read_line 函数的返回值,也是用户输入的字符串首地址

    推测拆弹密码字符串的存储地址为 $0x804a0ec,因为调用 strings_not_equal前有语句:

    8048b36: 68 ec a0 04 08 push $0x804a0ec

    执行 gdb bomb 找到答案When a problem comes along, you must zip it!

    image-20231119023040251

    phase_2

    查看phase_2的代码:

    08048b51 :
     8048b51:	55                   	push   %ebp
     8048b52:	89 e5                	mov    %esp,%ebp
     8048b54:	53                   	push   %ebx
     8048b55:	83 ec 2c             	sub    $0x2c,%esp
     8048b58:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax
     8048b5e:	89 45 f4             	mov    %eax,-0xc(%ebp)
     8048b61:	31 c0                	xor    %eax,%eax
     8048b63:	8d 45 dc             	lea    -0x24(%ebp),%eax
     8048b66:	50                   	push   %eax
     8048b67:	ff 75 08             	push   0x8(%ebp)
     8048b6a:	e8 3e 06 00 00       	call   80491ad 
     8048b6f:	83 c4 10             	add    $0x10,%esp
     8048b72:	83 7d dc 00          	cmpl   $0x0,-0x24(%ebp)
     8048b76:	79 05                	jns    8048b7d 
     8048b78:	e8 08 06 00 00       	call   8049185 
     8048b7d:	bb 01 00 00 00       	mov    $0x1,%ebx			  
     8048b82:	89 d8                	mov    %ebx,%eax			 
     8048b84:	03 44 9d d8          	add    -0x28(%ebp,%ebx,4),%eax
     8048b88:	39 44 9d dc          	cmp    %eax,-0x24(%ebp,%ebx,4)
     8048b8c:	74 05                	je     8048b93 
     8048b8e:	e8 f2 05 00 00       	call   8049185 
     8048b93:	83 c3 01             	add    $0x1,%ebx
     8048b96:	83 fb 06             	cmp    $0x6,%ebx
     8048b99:	75 e7                	jne    8048b82 
     8048b9b:	8b 45 f4             	mov    -0xc(%ebp),%eax
     8048b9e:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
     8048ba5:	74 05                	je     8048bac 
     8048ba7:	e8 e4 fb ff ff       	call   8048790 <__stack_chk_fail@plt>
     8048bac:	8b 5d fc             	mov    -0x4(%ebp),%ebx
     8048baf:	c9                   	leave  
     8048bb0:	c3                   	ret  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    关注到8048b6a: e8 3e 06 00 00 call 80491ad ,这题应该是要输入六个数字。

    观察这部分汇编代码:

     8048b7d:	bb 01 00 00 00       	mov    $0x1,%ebx			  
     8048b82:	89 d8                	mov    %ebx,%eax			 
     8048b84:	03 44 9d d8          	add    -0x28(%ebp,%ebx,4),%eax
     8048b88:	39 44 9d dc          	cmp    %eax,-0x24(%ebp,%ebx,4)
     8048b8c:	74 05                	je     8048b93 
     8048b8e:	e8 f2 05 00 00       	call   8049185 
     8048b93:	83 c3 01             	add    $0x1,%ebx
     8048b96:	83 fb 06             	cmp    $0x6,%ebx
     8048b99:	75 e7                	jne    8048b82 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    可以看出这就是一段循环,循环次数为5,每次循环是做一次比较,%ebx是循环计数器,每轮循环结束后 +1。循环内会进行一次比较,如果两个数不相等就会爆炸。那么关键就是待比较的两个数。

    ebx比较数1比较数2
    11 + ebp-0x28+1*4ebp-0x24+1*4
    22 + ebp-0x28+2*4ebp-0x24+2*4
    33 + ebp-0x28+3*4ebp-0x24+3*4
    44 + ebp-0x28+4*4ebp-0x24+4*4
    55 + ebp-0x28+5*4ebp-0x24+5*4

    经过观察和不断地调试发现比较数1是第一个输入的数+ebx,比较数2是第二个输入的数。

    经过大量试错调试,最终得出这一题的答案:输入六个数,每两个数之间的差依次为1/2/3/4/5

    所以这样得到本题的一组答案:1 2 4 7 11 16,当然答案不唯一,比如符合条件的2 3 5 8 12 17也是可以通 过的。

    部分调试记录截图:

    image-20231119023438558

    image-20231119023442324

    这题后面的比较循环一开始看到以6结尾,想当然地认为循环比较六次,这样就多出来一个输入的数,百思不得其解,在网上查阅相关教程发现题目也不一样。最终就逐步调试,发现只是循环了5次,这样就对上了。在调试过程中也找到了本题的答案。

    phase_3

    先把phase_3的代码贴出来:

    08048bb1 :
     8048bb1:	55                   	push   %ebp
     8048bb2:	89 e5                	mov    %esp,%ebp
     8048bb4:	83 ec 24             	sub    $0x24,%esp
     8048bb7:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax
     8048bbd:	89 45 f4             	mov    %eax,-0xc(%ebp)
     8048bc0:	31 c0                	xor    %eax,%eax
     8048bc2:	8d 45 f0             	lea    -0x10(%ebp),%eax
     8048bc5:	50                   	push   %eax
     8048bc6:	8d 45 eb             	lea    -0x15(%ebp),%eax
     8048bc9:	50                   	push   %eax
     8048bca:	8d 45 ec             	lea    -0x14(%ebp),%eax
     8048bcd:	50                   	push   %eax
     8048bce:	68 42 a1 04 08       	push   $0x804a142
     8048bd3:	ff 75 08             	push   0x8(%ebp)
     8048bd6:	e8 35 fc ff ff       	call   8048810 <__isoc99_sscanf@plt>
     8048bdb:	83 c4 20             	add    $0x20,%esp
     8048bde:	83 f8 02             	cmp    $0x2,%eax
     8048be1:	7f 05                	jg     8048be8 
     8048be3:	e8 9d 05 00 00       	call   8049185 
     8048be8:	83 7d ec 07          	cmpl   $0x7,-0x14(%ebp)
     8048bec:	0f 87 ef 00 00 00    	ja     8048ce1 
     8048bf2:	8b 45 ec             	mov    -0x14(%ebp),%eax
     8048bf5:	ff 24 85 54 a1 04 08 	jmp    *0x804a154(,%eax,4)
     8048bfc:	b8 78 00 00 00       	mov    $0x78,%eax
     8048c01:	81 7d f0 44 01 00 00 	cmpl   $0x144,-0x10(%ebp)
     8048c08:	0f 84 dd 00 00 00    	je     8048ceb 
     8048c0e:	e8 72 05 00 00       	call   8049185 
     8048c13:	b8 78 00 00 00       	mov    $0x78,%eax
     8048c18:	e9 ce 00 00 00       	jmp    8048ceb 
     8048c1d:	b8 69 00 00 00       	mov    $0x69,%eax
     8048c22:	81 7d f0 99 01 00 00 	cmpl   $0x199,-0x10(%ebp)
     8048c29:	0f 84 bc 00 00 00    	je     8048ceb 
     8048c2f:	e8 51 05 00 00       	call   8049185 
     8048c34:	b8 69 00 00 00       	mov    $0x69,%eax
     8048c39:	e9 ad 00 00 00       	jmp    8048ceb 
     8048c3e:	b8 74 00 00 00       	mov    $0x74,%eax
     8048c43:	81 7d f0 f6 02 00 00 	cmpl   $0x2f6,-0x10(%ebp)
     8048c4a:	0f 84 9b 00 00 00    	je     8048ceb 
     8048c50:	e8 30 05 00 00       	call   8049185 
     8048c55:	b8 74 00 00 00       	mov    $0x74,%eax
     8048c5a:	e9 8c 00 00 00       	jmp    8048ceb 
     8048c5f:	b8 68 00 00 00       	mov    $0x68,%eax
     8048c64:	81 7d f0 37 02 00 00 	cmpl   $0x237,-0x10(%ebp)
     8048c6b:	74 7e                	je     8048ceb 
     8048c6d:	e8 13 05 00 00       	call   8049185 
     8048c72:	b8 68 00 00 00       	mov    $0x68,%eax
     8048c77:	eb 72                	jmp    8048ceb 
     8048c79:	b8 79 00 00 00       	mov    $0x79,%eax
     8048c7e:	81 7d f0 1c 03 00 00 	cmpl   $0x31c,-0x10(%ebp)
     8048c85:	74 64                	je     8048ceb 
     8048c87:	e8 f9 04 00 00       	call   8049185 
     8048c8c:	b8 79 00 00 00       	mov    $0x79,%eax
     8048c91:	eb 58                	jmp    8048ceb 
     8048c93:	b8 62 00 00 00       	mov    $0x62,%eax
     8048c98:	81 7d f0 bd 01 00 00 	cmpl   $0x1bd,-0x10(%ebp)
     8048c9f:	74 4a                	je     8048ceb 
     8048ca1:	e8 df 04 00 00       	call   8049185 
     8048ca6:	b8 62 00 00 00       	mov    $0x62,%eax
     8048cab:	eb 3e                	jmp    8048ceb 
     8048cad:	b8 64 00 00 00       	mov    $0x64,%eax
     8048cb2:	81 7d f0 5a 03 00 00 	cmpl   $0x35a,-0x10(%ebp)
     8048cb9:	74 30                	je     8048ceb 
     8048cbb:	e8 c5 04 00 00       	call   8049185 
     8048cc0:	b8 64 00 00 00       	mov    $0x64,%eax
     8048cc5:	eb 24                	jmp    8048ceb 
     8048cc7:	b8 65 00 00 00       	mov    $0x65,%eax
     8048ccc:	81 7d f0 50 02 00 00 	cmpl   $0x250,-0x10(%ebp)
     8048cd3:	74 16                	je     8048ceb 
     8048cd5:	e8 ab 04 00 00       	call   8049185 
     8048cda:	b8 65 00 00 00       	mov    $0x65,%eax
     8048cdf:	eb 0a                	jmp    8048ceb 
     8048ce1:	e8 9f 04 00 00       	call   8049185 
     8048ce6:	b8 6b 00 00 00       	mov    $0x6b,%eax
     8048ceb:	3a 45 eb             	cmp    -0x15(%ebp),%al
     8048cee:	74 05                	je     8048cf5 
     8048cf0:	e8 90 04 00 00       	call   8049185 
     8048cf5:	8b 45 f4             	mov    -0xc(%ebp),%eax
     8048cf8:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
     8048cff:	74 05                	je     8048d06 
     8048d01:	e8 8a fa ff ff       	call   8048790 <__stack_chk_fail@plt>
     8048d06:	c9                   	leave  
     8048d07:	c3                   	ret    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83

    首先观察到phase_3的代码中有一段:

     8048bce:	68 42 a1 04 08       	push   $0x804a142
     8048bd3:	ff 75 08             	push   0x8(%ebp)
     8048bd6:	e8 35 fc ff ff       	call   8048810 <__isoc99_sscanf@plt>
    
    • 1
    • 2
    • 3

    其中$0x804a142是输入格式字符串,这里要作为参数压栈,传递给sscanf,查看一下:

    (gdb) x/s 0x804a142
    0x804a142: "%d %c %d"
    
    • 1
    • 2

    这题的输入就是两个整数中间用字符隔开。

    紧接着的一段代码也印证了这一点:

     8048bdb:	83 c4 20             	add    $0x20,%esp
     8048bde:	83 f8 02             	cmp    $0x2,%eax
     8048be1:	7f 05                	jg     8048be8 
     8048be3:	e8 9d 05 00 00       	call   8049185 
    
    • 1
    • 2
    • 3
    • 4

    eax也就是sscanf的返回值,也就是输入的变量的个数,如果小于等于2就bomb。

    紧接着有一个判断:

     8048be8:	83 7d ec 07          	cmpl   $0x7,-0x14(%ebp)
     8048bec:	0f 87 ef 00 00 00    	ja     8048ce1 
    
    • 1
    • 2

    如果-0x14(%ebp) > 0x7,就会跳转,这里会跳转到call ,所以要求-0x14(%ebp) <= 7,那它是什么呢?观察调用sscanf之前的一段代码:

     8048bc2:	8d 45 f0             	lea    -0x10(%ebp),%eax
     8048bc5:	50                  	push   %eax
     8048bc6:	8d 45 eb             	lea    -0x15(%ebp),%eax
     8048bc9:	50                  	push   %eax
     8048bca:	8d 45 ec             	lea    -0x14(%ebp),%eax
     8048bcd:	50                  	push   %eax
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    根据参数传递的顺序应该是我们输入的第一个数,这也就要求我们输入的第一个数小于等于7。

    继续往下看,发现程序会跳转,并且跳转的位置和我们输入的数相关联:

     8048bf2:	8b 45 ec             	mov    -0x14(%ebp),%eax
     8048bf5:	ff 24 85 54 a1 04 08 	jmp    *0x804a154(,%eax,4)
    
    • 1
    • 2

    用第一个输入的数是1进行下一步调试:

    image-20231119023945131

    跳转到了:

     8048c1d:	b8 69 00 00 00       	mov    $0x69,%eax
     8048c22:	81 7d f0 99 01 00 00 	cmpl   $0x199,-0x10(%ebp)
     8048c29:	0f 84 bc 00 00 00    	je     8048ceb 
     8048c2f:	e8 51 05 00 00       	call   8049185 
    
    • 1
    • 2
    • 3
    • 4

    继续看,这里把eax更新成了0x69,并且比较了我们的第三个输入,也就是第二个整数和0x199也就是409,所以确定了我们要输入的第二个数是409,接下来程序跳转到了8048ceb

     8048ceb:	3a 45 eb             	cmp    -0x15(%ebp),%al
     8048cee:	74 05                	je     8048cf5 
     8048cf0:	e8 90 04 00 00       	call   8049185 
     8048cf5:	8b 45 f4             	mov    -0xc(%ebp),%eax
     8048cf8:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
     8048cff:	    74 05               je     8048d06 
     8048d01:	e8 8a fa ff ff       	call   8048790 <__stack_chk_fail@plt>
     8048d06:	c9                  	leave  
     8048d07:	c3                  	ret  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这里比较的是我们输入的字符和%al%al是累加器寄存器eax的低八位,如下图所示,转成十进制就是105,所以我们要输入的字符的ascii码值是105,也就是i。这样就得到了最终的答案1i409

    image-20231119024053668

    不过这道题应该有8个答案,因为输入的第一个整数有8个取值,所以会有八个分支,对应八个答案。

    phase_4

    先贴代码:

    08048d08 :
     8048d08:	55                   	push   %ebp
     8048d09:	89 e5                	mov    %esp,%ebp
     8048d0b:	56                   	push   %esi
     8048d0c:	53                   	push   %ebx
     8048d0d:	8b 55 08             	mov    0x8(%ebp),%edx
     8048d10:	8b 4d 0c             	mov    0xc(%ebp),%ecx
     8048d13:	8b 75 10             	mov    0x10(%ebp),%esi
     8048d16:	89 f0                	mov    %esi,%eax
     8048d18:	29 c8                	sub    %ecx,%eax
     8048d1a:	89 c3                	mov    %eax,%ebx
     8048d1c:	c1 eb 1f             	shr    $0x1f,%ebx
     8048d1f:	01 d8                	add    %ebx,%eax
     8048d21:	d1 f8                	sar    %eax
     8048d23:	8d 1c 08             	lea    (%eax,%ecx,1),%ebx
     8048d26:	39 d3                	cmp    %edx,%ebx
     8048d28:	7e 15                	jle    8048d3f 
     8048d2a:	83 ec 04             	sub    $0x4,%esp
     8048d2d:	8d 43 ff             	lea    -0x1(%ebx),%eax
     8048d30:	50                   	push   %eax
     8048d31:	51                   	push   %ecx
     8048d32:	52                   	push   %edx
     8048d33:	e8 d0 ff ff ff       	call   8048d08 
     8048d38:	83 c4 10             	add    $0x10,%esp
     8048d3b:	01 d8                	add    %ebx,%eax
     8048d3d:	eb 19                	jmp    8048d58 
     8048d3f:	89 d8                	mov    %ebx,%eax
     8048d41:	39 d3                	cmp    %edx,%ebx
     8048d43:	7d 13                	jge    8048d58 
     8048d45:	83 ec 04             	sub    $0x4,%esp
     8048d48:	56                   	push   %esi
     8048d49:	8d 43 01             	lea    0x1(%ebx),%eax
     8048d4c:	50                   	push   %eax
     8048d4d:	52                   	push   %edx
     8048d4e:	e8 b5 ff ff ff       	call   8048d08 
     8048d53:	83 c4 10             	add    $0x10,%esp
     8048d56:	01 d8                	add    %ebx,%eax
     8048d58:	8d 65 f8             	lea    -0x8(%ebp),%esp
     8048d5b:	5b                   	pop    %ebx
     8048d5c:	5e                   	pop    %esi
     8048d5d:	5d                   	pop    %ebp
     8048d5e:	c3                   	ret    
    
    08048d5f :
     8048d5f:	55                   	push   %ebp
     8048d60:	89 e5                	mov    %esp,%ebp
     8048d62:	83 ec 18             	sub    $0x18,%esp
     8048d65:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax
     8048d6b:	89 45 f4             	mov    %eax,-0xc(%ebp)
     8048d6e:	31 c0                	xor    %eax,%eax
     8048d70:	8d 45 f0             	lea    -0x10(%ebp),%eax
     8048d73:	50                   	push   %eax
     8048d74:	8d 45 ec             	lea    -0x14(%ebp),%eax
     8048d77:	50                   	push   %eax
     8048d78:	68 93 a2 04 08       	push   $0x804a293
     8048d7d:	ff 75 08             	push   0x8(%ebp)
     8048d80:	e8 8b fa ff ff       	call   8048810 <__isoc99_sscanf@plt>
     8048d85:	83 c4 10             	add    $0x10,%esp
     8048d88:	83 f8 02             	cmp    $0x2,%eax
     8048d8b:	75 06                	jne    8048d93 
     8048d8d:	83 7d ec 0e          	cmpl   $0xe,-0x14(%ebp)
     8048d91:	76 05                	jbe    8048d98 
     8048d93:	e8 ed 03 00 00       	call   8049185 
     8048d98:	83 ec 04             	sub    $0x4,%esp
     8048d9b:	6a 0e                	push   $0xe
     8048d9d:	6a 00                	push   $0x0
     8048d9f:	ff 75 ec             	push   -0x14(%ebp)
     8048da2:	e8 61 ff ff ff       	call   8048d08 
     8048da7:	83 c4 10             	add    $0x10,%esp
     8048daa:	83 f8 13             	cmp    $0x13,%eax
     8048dad:	75 06                	jne    8048db5 
     8048daf:	83 7d f0 13          	cmpl   $0x13,-0x10(%ebp)
     8048db3:	74 05                	je     8048dba 
     8048db5:	e8 cb 03 00 00       	call   8049185 
     8048dba:	8b 45 f4             	mov    -0xc(%ebp),%eax
     8048dbd:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
     8048dc4:	74 05                	je     8048dcb 
     8048dc6:	e8 c5 f9 ff ff       	call   8048790 <__stack_chk_fail@plt>
     8048dcb:	c9                   	leave  
     8048dcc:	c3                   	ret    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80

    还是先观察调用sscanf之前的代码确定输入格式:

     8048d70:	8d 45 f0             	lea    -0x10(%ebp),%eax
     8048d73:	50                  	push   %eax
     8048d74:	8d 45 ec             	lea    -0x14(%ebp),%eax
     8048d77:	50                  	push   %eax
     8048d78:	68 93 a2 04 08       	push   $0x804a293
     8048d7d:	ff 75 08             	push   0x8(%ebp)
     8048d80:	e8 8b fa ff ff       	call   8048810 <__isoc99_sscanf@plt>
     8048d85:	83 c4 10             	add    $0x10,%esp
     8048d88:	83 f8 02             	cmp    $0x2,%eax
     8048d8b:	75 06                	jne    8048d93 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    image-20231119024159720

    输入格式是两个整数,中间用空格隔开。

    其中第一个数存在-0x14(%ebp),第二个数存在-0x10(%ebp)

    继续向下看:

     8048d8d:	83 7d ec 0e          	cmpl   $0xe,-0x14(%ebp)
     8048d91:	76 05                	jbe    8048d98 
     8048d93:	e8 ed 03 00 00       	call   8049185 
    
    • 1
    • 2
    • 3

    这里比较了输入的第一个数和0xe,也就是14,说明第一个数要小于等于14,这里还是用输入1来进一步调试。继续看代码:

     8048d98:	83 ec 04             	sub    $0x4,%esp
     8048d9b:	6a 0e                	push   $0xe
     8048d9d:	6a 00                	push   $0x0
     8048d9f:	ff 75 ec             	push   -0x14(%ebp)
     8048da2:	e8 61 ff ff ff       	call   8048d08 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    调用了func4,先分析一下参数:第一个参数是输入的第一个整数,第二个参数是 0,第三个参数是0xe也就是14。继续分析func4的代码,先看参数的处理:

     8048d0d:	8b 55 08             	mov    0x8(%ebp),%edx
     8048d10:	8b 4d 0c             	mov    0xc(%ebp),%ecx
     8048d13:	8b 75 10             	mov    0x10(%ebp),%esi
    
    • 1
    • 2
    • 3

    这里把第一个参数,也就是输入的第一个整数1赋给了edx,把0赋给了ecx14赋给了esi,继续看代码:

     8048d16:	89 f0                	mov    %esi,%eax
     8048d18:	29 c8                	sub    %ecx,%eax
     8048d1a:	89 c3                	mov    %eax,%ebx
     8048d1c:	c1 eb 1f             	shr    $0x1f,%ebx
     8048d1f:	01 d8                	add    %ebx,%eax
     8048d21:	d1 f8                	sar    %eax
     8048d23:	8d 1c 08             	lea    (%eax,%ecx,1),%ebx
     8048d26:	39 d3                	cmp    %edx,%ebx
     8048d28:	7e 15                	jle    8048d3f 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    一直执行到比较之前,各个寄存器存放的值如下:

    img

    接下来过不了比较,会继续向下执行:

     8048d2a:	83 ec 04             	sub    $0x4,%esp
     8048d2d:	8d 43 ff             	lea    -0x1(%ebx),%eax
     8048d30:	50                   	push   %eax
     8048d31:	51                   	push   %ecx
     8048d32:	52                   	push   %edx
     8048d33:	e8 d0 ff ff ff       	call   8048d08 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这样就开始了递归,先看一下此时递归前各个寄存器存放的值:

    img

    从左到右三个参数依次为1、0、1,与上一次调用相比第三个参数发生了变化,继续调试到比较:

    img

    此时仍无法结束递归,下面会进行第三次调用,调用前各个寄存器的值如下:

    img

    第三个参数变成了2,继续调试到比较:

    img

    此时终于符合条件ebx <= edx,跳转到新阶段:

     8048d3f:	89 d8               mov    %ebx,%eax
     8048d41:	39 d3               cmp    %edx,%ebx
     8048d43:	7d 13               jge    8048d58 
    
    • 1
    • 2
    • 3

    接下来有个判断,要求ebx >= edx,先看一下此时各个寄存器的值:

    img

    符合条件,跳转到新位置:

     8048d58:	8d 65 f8             	lea    -0x8(%ebp),%esp
     8048d5b:	5b                  	pop    %ebx
     8048d5c:	5e                  	pop    %esi
     8048d5d:	5d                  	pop    %ebp
     8048d5e:	c3                  	ret   
    
    • 1
    • 2
    • 3
    • 4
    • 5

    此时跳出第三次调用,来到第二次调用的栈帧:

     8048d33:	e8 d0 ff ff ff       	call   8048d08 
     8048d38:	83 c4 10             	add    $0x10,%esp
     8048d3b:	01 d8                	add    %ebx,%eax
     8048d3d:	eb 19                	jmp    8048d58 
    
    • 1
    • 2
    • 3
    • 4

    跳转之前各个寄存器的值如下:

    img

    第二次调用随之也结束,来到第一次调用的栈帧,还是与第二次一样,返回phase_4之前eax变为11

    之后回到phase_4的栈帧:

     8048da2:	e8 61 ff ff ff       	call   8048d08 
     8048da7:	83 c4 10             	add    $0x10,%esp
     8048daa:	83 f8 13             	cmp    $0x13,%eax
     8048dad:	75 06                	jne    8048db5 
     8048daf:	83 7d f0 13          	cmpl   $0x13,-0x10(%ebp)
     8048db3:	74 05                	je     8048dba 
     8048db5:	e8 cb 03 00 00       	call   8049185 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    此时各个寄存器的值如下:

    img

    此时eax(11) != 0x13(19),所以会爆炸,所以拆弹的关键来到了eax。在经过函数递归之后,eax的值要变为19,那就观察一下eax的值是如何变化的。不过这里还能看出第二个输入的整数应该是0x13(19)

    在分析eax的过程中被绕晕了,索性摆烂,反正最大不超过14,一个个试,试出了正确答案4

    img

    所以这一题的最终答案就是:4 19

    phase_5

    贴代码:

    08048dcd :
     8048dcd:	55                   	push   %ebp
     8048dce:	89 e5                	mov    %esp,%ebp
     8048dd0:	53                   	push   %ebx
     8048dd1:	83 ec 20             	sub    $0x20,%esp
     8048dd4:	8b 5d 08             	mov    0x8(%ebp),%ebx
     8048dd7:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax
     8048ddd:	89 45 f4             	mov    %eax,-0xc(%ebp)
     8048de0:	31 c0                	xor    %eax,%eax
     8048de2:	53                   	push   %ebx
     8048de3:	e8 78 02 00 00       	call   8049060 
     8048de8:	83 c4 10             	add    $0x10,%esp
     8048deb:	83 f8 06             	cmp    $0x6,%eax
     8048dee:	74 05                	je     8048df5 
     8048df0:	e8 90 03 00 00       	call   8049185 
     8048df5:	b8 00 00 00 00       	mov    $0x0,%eax
     8048dfa:	0f b6 14 03          	movzbl (%ebx,%eax,1),%edx
     8048dfe:	83 e2 0f             	and    $0xf,%edx
     8048e01:	0f b6 92 74 a1 04 08 	movzbl 0x804a174(%edx),%edx
     8048e08:	88 54 05 ed          	mov    %dl,-0x13(%ebp,%eax,1)
     8048e0c:	83 c0 01             	add    $0x1,%eax
     8048e0f:	83 f8 06             	cmp    $0x6,%eax
     8048e12:	75 e6                	jne    8048dfa 
     8048e14:	c6 45 f3 00          	movb   $0x0,-0xd(%ebp)
     8048e18:	83 ec 08             	sub    $0x8,%esp
     8048e1b:	68 4b a1 04 08       	push   $0x804a14b
     8048e20:	8d 45 ed             	lea    -0x13(%ebp),%eax
     8048e23:	50                   	push   %eax
     8048e24:	e8 59 02 00 00       	call   8049082 
     8048e29:	83 c4 10             	add    $0x10,%esp
     8048e2c:	85 c0                	test   %eax,%eax
     8048e2e:	74 05                	je     8048e35 
     8048e30:	e8 50 03 00 00       	call   8049185 
     8048e35:	8b 45 f4             	mov    -0xc(%ebp),%eax
     8048e38:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
     8048e3f:	74 05                	je     8048e46 
     8048e41:	e8 4a f9 ff ff       	call   8048790 <__stack_chk_fail@plt>
     8048e46:	8b 5d fc             	mov    -0x4(%ebp),%ebx
     8048e49:	c9                   	leave  
     8048e4a:	c3                   	ret    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    先看部分代码:

     8048de3:	e8 78 02 00 00       	call   8049060 
     8048de8:	83 c4 10             	add    $0x10,%esp
     8048deb:	83 f8 06             	cmp    $0x6,%eax
     8048dee:	74 05                	je     8048df5 
     8048df0:	e8 90 03 00 00       	call   8049185 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    一开始调用了一次string_length,并且比较了一下字符串的长度和6,如果不相等就会爆炸,需要知道string_length是计算的谁的长度:

    img

    img

    经过测试发现是我们输入的字符串的长度,这样就确定了我们要输入的字符串的长度是6

    接下来是一段循环:

     8048df5:	b8 00 00 00 00       	mov    $0x0,%eax
     8048dfa:	0f b6 14 03          	movzbl  (%ebx,%eax,1),%edx
     8048dfe:	83 e2 0f             	and    $0xf,%edx
     8048e01:	0f b6 92 74 a1 04 08 	movzbl  0x804a174(%edx),%edx
     8048e08:	88 54 05 ed          	mov    %dl,-0x13(%ebp,%eax,1)
     8048e0c:	83 c0 01             	add    $0x1,%eax
     8048e0f:	83 f8 06             	cmp    $0x6,%eax
     8048e12:	75 e6                	jne    8048dfa 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    eax0开始,每次+1,直到等于6。分析一下这个循环做了什么。

    首先给edx赋值,且这个值随着循环次数改变而改变,然后保留edx低4位,把某个地址加上第四位作为偏移量,得到的地址处的值赋给edx,然后把这个值赋给-0x13(%ebp,%eax,1),只占一个字节空间,猜测每一次取了一个字符,并且后面调用了strings_not_equal

     8048e1b:	68 4b a1 04 08       	push   $0x804a14b
     8048e20:	8d 45 ed             	lea    -0x13(%ebp),%eax
     8048e23:	50                  	push   %eax
     8048e24:	e8 59 02 00 00       	call   8049082 
    
    • 1
    • 2
    • 3
    • 4

    六次循环得到一个字符串,并把这个字符串的首地址作为参数传给strings_not_equal,所以这六个字符串是什么呢,答案在0x804a14b

    img

    所以要通过循环构造出这么一个字符串。循环是从0x804a174加偏移得到的,先看一下0x804a174指向的内容:

    img

    很显然要从maduiersnfotvbylSo通过加偏移量的方式依次构造出f l a m e s,偏移量依次是9 15 1 0 5 7,偏移量从哪来呢:

    8048dd4: 8b 5d 08 mov 0x8(%ebp),%ebx

    很显然是传过来的参数,这个参数就是我们输入的字符串,所以是取我们输入的字符串对应的ascii码值的低四位作为偏移量,所以答案的要求就出来了:低四位分别是1001(9) 1111(15) 0001(1) 0000(0) 0101(5) 0111(7)

    令高位为0100(64),得到一组解I(73) O(79) A(65) @(64) E(69) G(71),所以正确答案就是IOA@EG

    phase_6

    贴代码:

    08048e4b :
     8048e4b:	55                   	push   %ebp
     8048e4c:	89 e5                	mov    %esp,%ebp
     8048e4e:	56                   	push   %esi
     8048e4f:	53                   	push   %ebx
     8048e50:	83 ec 48             	sub    $0x48,%esp
     8048e53:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax
     8048e59:	89 45 f4             	mov    %eax,-0xc(%ebp)
     8048e5c:	31 c0                	xor    %eax,%eax
     8048e5e:	8d 45 c4             	lea    -0x3c(%ebp),%eax
     8048e61:	50                   	push   %eax
     8048e62:	ff 75 08             	push   0x8(%ebp)
     8048e65:	e8 43 03 00 00       	call   80491ad 
     8048e6a:	83 c4 10             	add    $0x10,%esp
     8048e6d:	be 00 00 00 00       	mov    $0x0,%esi
     8048e72:	8b 44 b5 c4          	mov    -0x3c(%ebp,%esi,4),%eax
     8048e76:	83 e8 01             	sub    $0x1,%eax
     8048e79:	83 f8 05             	cmp    $0x5,%eax
     8048e7c:	76 05                	jbe    8048e83 
     8048e7e:	e8 02 03 00 00       	call   8049185 
    
     8048e83:	83 c6 01             	add    $0x1,%esi
     8048e86:	83 fe 06             	cmp    $0x6,%esi
     8048e89:	74 33                	je     8048ebe 
     8048e8b:	89 f3                	mov    %esi,%ebx
     8048e8d:	8b 44 9d c4          	mov    -0x3c(%ebp,%ebx,4),%eax
     8048e91:	39 44 b5 c0          	cmp    %eax,-0x40(%ebp,%esi,4)
     8048e95:	75 05                	jne    8048e9c 
     8048e97:	e8 e9 02 00 00       	call   8049185 
     8048e9c:	83 c3 01             	add    $0x1,%ebx
     8048e9f:	83 fb 05             	cmp    $0x5,%ebx
     8048ea2:	7e e9                	jle    8048e8d 
     8048ea4:	eb cc                	jmp    8048e72 
    
     8048ea6:	8b 52 08             	mov    0x8(%edx),%edx
     8048ea9:	83 c0 01             	add    $0x1,%eax
     8048eac:	39 c8                	cmp    %ecx,%eax
     8048eae:	75 f6                	jne    8048ea6 
     8048eb0:	89 54 b5 dc          	mov    %edx,-0x24(%ebp,%esi,4)
     8048eb4:	83 c3 01             	add    $0x1,%ebx
     8048eb7:	83 fb 06             	cmp    $0x6,%ebx
     8048eba:	75 07                	jne    8048ec3 
     8048ebc:	eb 1c                	jmp    8048eda 
    
     8048ebe:	bb 00 00 00 00       	mov    $0x0,%ebx
     8048ec3:	89 de                	mov    %ebx,%esi
     8048ec5:	8b 4 c 9d c4          	mov    -0x3c(%ebp,%ebx,4),%ecx
     8048ec9:	b8 01 00 00 00       	mov    $0x1,%eax
     8048ece:	ba 3c c1 04 08       	mov    $0x804c13c,%edx
     8048ed3:	83 f9 01             	cmp    $0x1,%ecx
     8048ed6:	7f ce                	jg     8048ea6 
     8048ed8:	eb d6                	jmp    8048eb0 
    
     8048eda:	8b 5d dc             	mov    -0x24(%ebp),%ebx
     8048edd:	8d 45 dc             	lea    -0x24(%ebp),%eax
     8048ee0:	8d 75 f0             	lea    -0x10(%ebp),%esi
     8048ee3:	89 d9                	mov    %ebx,%ecx
     8048ee5:	8b 50 04             	mov    0x4(%eax),%edx
     8048ee8:	89 51 08             	mov    %edx,0x8(%ecx)
     8048eeb:	83 c0 04             	add    $0x4,%eax
     8048eee:	89 d1                	mov    %edx,%ecx
     8048ef0:	39 f0                	cmp    %esi,%eax
     8048ef2:	75 f1                	jne    8048ee5 
    
     8048ef4:	c7 42 08 00 00 00 00 	movl   $0x0,0x8(%edx)
     8048efb:	be 05 00 00 00       	mov    $0x5,%esi
     8048f00:	8b 43 08             	mov    0x8(%ebx),%eax
     8048f03:	8b 00                	mov    (%eax),%eax
     8048f05:	39 03                	cmp    %eax,(%ebx)
     8048f07:	7d 05                	jge    8048f0e 
     8048f09:	e8 77 02 00 00       	call   8049185 
     8048f0e:	8b 5b 08             	mov    0x8(%ebx),%ebx
     8048f11:	83 ee 01             	sub    $0x1,%esi
     8048f14:	75 ea                	jne    8048f00 
    
     8048f16:	8b 45 f4             	mov    -0xc(%ebp),%eax
     8048f19:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
     8048f20:	74 05                	je     8048f27 
     8048f22:	e8 69 f8 ff ff       	call   8048790 <__stack_chk_fail@plt>
     8048f27:	8d 65 f8             	lea    -0x8(%ebp),%esp
     8048f2a:	5b                   	pop    %ebx
     8048f2b:	5e                   	pop    %esi
     8048f2c:	5d                   	pop    %ebp
     8048f2d:	c3                   	ret    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    先看:

     8048e5e:	8d 45 c4             	lea    -0x3c(%ebp),%eax
     8048e61:	50                  	push   %eax
     8048e62:	ff 75 08             	push   0x8(%ebp)
     8048e65:	e8 43 03 00 00       	call   80491ad 
    
    • 1
    • 2
    • 3
    • 4

    通过开始的一段代码可知是输入六个数字,并且首个元素存放地址为-0x3c(%ebp),假设输入的六个数字分别是a[0], a[1], a[2], a[3], a[4], a[5]

    之后有一个判断:

     8048e6a:	83 c4 10             	add    $0x10,%esp
     8048e6d:	be 00 00 00 00       	mov    $0x0,%esi
     8048e72:	8b 44 b5 c4          	mov    -0x3c(%ebp,%esi,4),%eax
     8048e76:	83 e8 01             	sub    $0x1,%eax
     8048e79:	83 f8 05             	cmp    $0x5,%eax
     8048e7c:	76 05                	jbe    8048e83 
     8048e7e:	e8 02 03 00 00       	call   8049185 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    跳转条件是(a[0] - 1 <= 5),这就限制了a[0] <= 6,后面以输入1进行尝试。

     8048e83:	83 c6 01             	add    $0x1,%esi
     8048e86:	83 fe 06             	cmp    $0x6,%esi
     8048e89:	74 33                	je     8048ebe 
     8048e8b:	89 f3                	mov    %esi,%ebx
     8048e8d:	8b 44 9d c4          	mov    -0x3c(%ebp,%ebx,4),%eax
     8048e91:	39 44 b5 c0          	cmp    %eax,-0x40(%ebp,%esi,4)
     8048e95:	75 05                	jne    8048e9c 
     8048e97:	e8 e9 02 00 00       	call   8049185 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    接下来esi + 16进行比较,可以猜想出这是一个循环,esi是计数器,循环六次。接下来又比较了a[0]和a[1],要求两个数不能相等。

     8048e9c:	83 c3 01             	add    $0x1,%ebx
     8048e9f:	83 fb 05             	cmp    $0x5,%ebx
     8048ea2:	7e e9                	jle    8048e8d 
     8048ea4:	eb cc                	jmp    8048e72 
    
    • 1
    • 2
    • 3
    • 4

    然后ebx成了新计数器,循环5次,这里是把a[0]依次与a[1] ~ a[5]各比较一次,保证不相等。可以发现其实这是两层循环,先判断循环到的a[i]是否小于等于6,然后判断它与后面几个数是否相等。

    整个大循环结束后来到新部分:

     8048ea6:	8b 52 08             	mov    0x8(%edx),%edx
     8048ea9:	83 c0 01             	add    $0x1,%eax
     8048eac:	39 c8                	cmp    %ecx,%eax
     8048eae:	75 f6                	jne    8048ea6 
     8048eb0:	89 54 b5 dc          	mov    %edx,-0x24(%ebp,%esi,4)
     8048eb4:	83 c3 01             	add    $0x1,%ebx
     8048eb7:	83 fb 06             	cmp    $0x6,%ebx
     8048eba:	75 07                	jne    8048ec3 
     8048ebc:	eb 1c                	jmp    8048eda 
    
     8048ebe:	bb 00 00 00 00       	mov    $0x0,%ebx
     8048ec3:	89 de                	mov    %ebx,%esi
     8048ec5:	8b 4 c 9d c4          	mov    -0x3c(%ebp,%ebx,4),%ecx
     8048ec9:	b8 01 00 00 00       	mov    $0x1,%eax
     8048ece:	ba 3c c1 04 08       	mov    $0x804c13c,%edx
     8048ed3:	83 f9 01             	cmp    $0x1,%ecx
     8048ed6:	7f ce                	jg     8048ea6 
     8048ed8:	eb d6                	jmp    8048eb0 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    程序从8048ebe开始执行,这里的计数器是ebx,从0开始循环到6,共循环7次。

    第一次cmp跳转之前各个寄存器的值如图:

    img

    比较的是ecx1ecxa[ebx],第一次是a[0],我输入的是1 2 3 4 5 6,显然jg不会跳转,程序来到了8048eb0。接下来把edx给到了-0x24(%ebp,%esi,4)地址处,推测ebp-0x24地址处存放的是一个指针804c13c

    可以看一下这个地址处存放的内容:

    img

    推测是链表,进一步查看:

    img

    链表一共有六个节点,每个节点存放三个数据,显然第三个数据是下一个节点的地址。第二个数据从1~6,推测是链表的节点编号,那第一个数据应该就是链表存放的有效数据。

    mov 0x8(%edx), %edx 操作其实是把edx更新成下个节点的指针,当eaxecx相等时把节点指针压栈,

    否则会进入一个循环:eax++; edx继续指向下个指针,直到eax == ecx,这也就意味着六个节点压栈的顺序与我们输入的六个数字相关,如果输入的2 4 1 3 6 5,则六个节点在栈中存放的顺序依次为node2 node4 node1 node3 node6 node5,存放地址依次为%ebp-0x24、%ebp-0x20、%ebp-0x1c、%ebp-0x18、%ebp-0x14、%ebp-0x10。其实就是node[a[0]] ~ node[a[6]]

    继续看代码:

     8048eda:	8b 5d dc             	mov    -0x24(%ebp),%ebx
     8048edd:	8d 45 dc             	lea    -0x24(%ebp),%eax
     8048ee0:	8d 75 f0             	lea    -0x10(%ebp),%esi
     8048ee3:	89 d9                	mov    %ebx,%ecx
     8048ee5:	8b 50 04             	mov    0x4(%eax),%edx
     8048ee8:	89 51 08             	mov    %edx,0x8(%ecx)
     8048eeb:	83 c0 04             	add    $0x4,%eax
     8048eee:	89 d1                	mov    %edx,%ecx
     8048ef0:	39 f0                	cmp    %esi,%eax
     8048ef2:	75 f1                	jne    8048ee5 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    这段代码也是循环,会改变链表指向,通过多次调试可以发现:如果输入的是1 2 3 4 5 6,则链表指向不变;如果输入的是6 5 4 3 2 1,则链表指向变为6>5>4>3>2>1>2,最后有个环;如果输入的是1 3 5 2 4 6,则链表指向变为1>3>5>2>4>6

    部分调试记录如下:

    img

    img

    以上发现了链表指向顺序会跟输入的六个数有关。

    来到最后一段代码:

     8048ef4:	c7 42 08 00 00 00 00 	movl   $0x0,0x8(%edx)
     8048efb:	be 05 00 00 00       	mov    $0x5,%esi
     8048f00:	8b 43 08             	mov    0x8(%ebx),%eax
     8048f03:	8b 00               	mov    (%eax),%eax
     8048f05:	39 03                	cmp    %eax,(%ebx)
     8048f07:	7d 05               	jge    8048f0e 
     8048f09:	e8 77 02 00 00       	call   8049185 
     8048f0e:	8b 5b 08             	mov    0x8(%ebx),%ebx
     8048f11:	83 ee 01             	sub    $0x1,%esi
     8048f14:	75 ea                	jne    8048f00 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    这段代码其实就是遍历链表,遍历顺序就是刚刚重新排列好的顺序,然后依次取相邻两个链表的值进行比较,这一点可以调试到 8048f05 cmp %eax, (%ebx) 之前,看一下eax的值和ebx指向的值,如果前者大于后者就能通过,然后继续遍历,否则bomb!

    所以链表的正确排列顺序就有依据了,先记录一下各个链表存放的数据都是多少:

    img

    1-995 2-959 3-779 4-921 5-853 6-363

    要求前者大于后者,所以正确顺序也就是正确答案应该是:1 2 4 5 3 6

    secret_phase

    还有一个函数名叫secret_phase的,代码如下:

    08048f80 :
     8048f80:	55                   	push   %ebp
     8048f81:	89 e5                	mov    %esp,%ebp
     8048f83:	53                   	push   %ebx
     8048f84:	83 ec 04             	sub    $0x4,%esp
     8048f87:	e8 5b 02 00 00       	call   80491e7 
     8048f8c:	83 ec 04             	sub    $0x4,%esp
     8048f8f:	6a 0a                	push   $0xa
     8048f91:	6a 00                	push   $0x0
     8048f93:	50                   	push   %eax
     8048f94:	e8 e7 f8 ff ff       	call   8048880 
     8048f99:	89 c3                	mov    %eax,%ebx
     8048f9b:	8d 40 ff             	lea    -0x1(%eax),%eax
     8048f9e:	83 c4 10             	add    $0x10,%esp
     8048fa1:	3d e8 03 00 00       	cmp    $0x3e8,%eax
     8048fa6:	76 05                	jbe    8048fad 
     8048fa8:	e8 d8 01 00 00       	call   8049185 
     8048fad:	83 ec 08             	sub    $0x8,%esp
     8048fb0:	53                   	push   %ebx
     8048fb1:	68 88 c0 04 08       	push   $0x804c088
     8048fb6:	e8 73 ff ff ff       	call   8048f2e 
     8048fbb:	83 c4 10             	add    $0x10,%esp
     8048fbe:	83 f8 03             	cmp    $0x3,%eax
     8048fc1:	74 05                	je     8048fc8 
     8048fc3:	e8 bd 01 00 00       	call   8049185 
     8048fc8:	83 ec 0c             	sub    $0xc,%esp
     8048fcb:	68 1c a1 04 08       	push   $0x804a11c
     8048fd0:	e8 eb f7 ff ff       	call   80487c0 
     8048fd5:	e8 10 03 00 00       	call   80492ea 
     8048fda:	83 c4 10             	add    $0x10,%esp
     8048fdd:	8b 5d fc             	mov    -0x4(%ebp),%ebx
     8048fe0:	c9                   	leave  
     8048fe1:	c3                   	ret   
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    phase_defused中有调用,所以贴一下phase_defused的代码:

    080492ea :
     80492ea:	55                   	push   %ebp
     80492eb:	89 e5                	mov    %esp,%ebp
     80492ed:	83 ec 68             	sub    $0x68,%esp
     80492f0:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax
     80492f6:	89 45 f4             	mov    %eax,-0xc(%ebp)
     80492f9:	31 c0                	xor    %eax,%eax
     80492fb:	83 3d cc c3 04 08 06 	cmpl   $0x6,0x804c3cc
     8049302:	75 6f                	jne    8049373 
     8049304:	83 ec 0c             	sub    $0xc,%esp
     8049307:	8d 45 a4             	lea    -0x5c(%ebp),%eax
     804930a:	50                   	push   %eax
     804930b:	8d 45 a0             	lea    -0x60(%ebp),%eax
     804930e:	50                   	push   %eax
     804930f:	8d 45 9c             	lea    -0x64(%ebp),%eax
     8049312:	50                   	push   %eax
     8049313:	68 ed a2 04 08       	push   $0x804a2ed
     8049318:	68 d0 c4 04 08       	push   $0x804c4d0
     804931d:	e8 ee f4 ff ff       	call   8048810 <__isoc99_sscanf@plt>
     8049322:	83 c4 20             	add    $0x20,%esp
     8049325:	83 f8 03             	cmp    $0x3,%eax
     8049328:	75 39                	jne    8049363 
     804932a:	83 ec 08             	sub    $0x8,%esp
     804932d:	68 f6 a2 04 08       	push   $0x804a2f6
     8049332:	8d 45 a4             	lea    -0x5c(%ebp),%eax
     8049335:	50                   	push   %eax
     8049336:	e8 47 fd ff ff       	call   8049082 
     804933b:	83 c4 10             	add    $0x10,%esp
     804933e:	85 c0                	test   %eax,%eax
     8049340:	75 21                	jne    8049363 
     8049342:	83 ec 0c             	sub    $0xc,%esp
     8049345:	68 bc a1 04 08       	push   $0x804a1bc
     804934a:	e8 71 f4 ff ff       	call   80487c0 
     804934f:	c7 04 24 e4 a1 04 08 	movl   $0x804a1e4,(%esp)
     8049356:	e8 65 f4 ff ff       	call   80487c0 
     804935b:	e8 20 fc ff ff       	call   8048f80 
     8049360:	83 c4 10             	add    $0x10,%esp
     8049363:	83 ec 0c             	sub    $0xc,%esp
     8049366:	68 1c a2 04 08       	push   $0x804a21c
     804936b:	e8 50 f4 ff ff       	call   80487c0 
     8049370:	83 c4 10             	add    $0x10,%esp
     8049373:	8b 45 f4             	mov    -0xc(%ebp),%eax
     8049376:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
     804937d:	74 05                	je     8049384 
     804937f:	e8 0c f4 ff ff       	call   8048790 <__stack_chk_fail@plt>
     8049384:	c9                   	leave  
     8049385:	c3                   	ret    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    phase_defused的代码中发现了这句:

    804935b: e8 20 fc ff ff call 8048f80

    这也就意味着每次拆除炸弹都有可能触发secret_phase,继续对phase_defused的代码进行分析,寻找触发条件,着重关注最这行:

     8049312:	50                  	push   %eax
     8049313:	68 ed a2 04 08       	push   $0x804a2ed
     8049318:	68 d0 c4 04 08       	push   $0x804c4d0
     804931d:	e8 ee f4 ff ff       	call   8048810 <__isoc99_sscanf@plt>
     8049322:	83 c4 20             	add    $0x20,%esp
     8049325:	83 f8 03             	cmp    $0x3,%eax
     8049328:	75 39                	jne    8049363 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    调用sscanf之前先看一下输入格式:

    img

    两个整数一个字符串,这里会判断输入了几个,如果输入的是两个数据并不会bomb,而是正常结束,输入两个的正好是phase_4,猜测phase_4多输一个字符串会触发secret_phase,下面继续分析这个字符串应该是什么:

     804932a:	83 ec 08             	sub    $0x8,%esp
     804932d:	68 f6 a2 04 08       	push   $0x804a2f6
     8049332:	8d 45 a4             	lea    -0x5c(%ebp),%eax
     8049335:	50                  	push   %eax
     8049336:	e8 47 fd ff ff       	call   8049082 
     804933b:	83 c4 10             	add    $0x10,%esp
     804933e:	85 c0                	test   %eax,%eax
     8049340:	75 21                	jne    8049363 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    看到一个关键地址0x804a2f6,查看内容:

    img

    这就找到了我们要多输入的字符串:DrEvil

    输入进去之后程序最后输出了小彩蛋,印证了它就是目标字符串;

    img

    回过头来对secret_phase进行分析:

     8048f8f:	6a 0a                	push   $0xa
     8048f91:	6a 00                	push   $0x0
     8048f93:	50                  	push   %eax
     8048f94:	e8 e7 f8 ff ff       	call   8048880 
     8048f99:	89 c3                	mov    %eax,%ebx
     8048f9b:	8d 40 ff             	lea    -0x1(%eax),%eax
     8048f9e:	83 c4 10             	add    $0x10,%esp
     8048fa1:	3d e8 03 00 00       	cmp    $0x3e8,%eax
     8048fa6:	76 05                	jbe    8048fad 
     8048fa8:	e8 d8 01 00 00       	call   8049185 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    开始会调用一个strtol函数,将字符串转成long,这里要转的字符串的首地址是eax,也就是上一次调用的函数的返回值,上一个函数是read_line,所以也就是把我们的输入转成long-1后与0x3e8(1000)进行比较,要求小于等于1000,也就意味着我们要输入一个数字,并且这个数字要小于等于1001

    继续向下看;

     8048fb0:	53                  	push   %ebx
     8048fb1:	68 88 c0 04 08       	push   $0x804c088
     8048fb6:	e8 73 ff ff ff       	call   8048f2e 
     8048fbb:	83 c4 10             	add    $0x10,%esp
     8048fbe:	83 f8 03             	cmp    $0x3,%eax
     8048fc1:	74 05                	je     8048fc8 
     8048fc3:	e8 bd 01 00 00       	call   8049185 
     8048fbb:	83 c4 10             	add    $0x10,%esp
     8048fbe:	83 f8 03             	cmp    $0x3,%eax
     8048fc1:	74 05                	je     8048fc8 
     8048fc3:	e8 bd 01 00 00       	call   8049185 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    看最后三句可以看出当调用完fun7后,eax==3时才不会爆炸,secret_phase也就会结束,所以关键看eaxfun7中的变化。

    调用fun7之前传递的参数为ebx0x804c088ebx根据之前的代码可知是我们输入的整数,看一下0x804c088存的是什么(64是显示64个单位,u是以十进制格式输出,换成x就是以十六进制格式输出,w是以8个字节为单位,我一开始是用默认的4个字节为单位,结果什么也看不出来,耽误了好久):

    img

    img

    数据有nxx,后面还有nodex,其实就是phase_6中的链表节点结构。不难看出nxx也有着异曲同工之妙。再看一眼,能看出nxx是以三个单位为一组,第一个存放的是一个较小的数,猜测为有效数据,后两个显然是指针,并且前七个节点中的两个指针都有指向,猜测为二叉树结构,一共15个节点,8个叶子结点,根据各个节点之间的连接关系和对应地址处的标识,可以推断出这棵树的形状如下:

    img

    整理出来发现这是一颗平衡搜索二叉树,左节点都比父节点小,右节点都比父节点大。

    那么fun7的参数就有了,第一个参数是这颗二叉树根节点的地址,第二个参数是我们输入的数,接下来可以继续分析fun7,先打出完整代码,然后顺着翻译一下:

    08048f2e :
     8048f2e:	55                   	push   %ebp
     8048f2f:	89 e5                	mov    %esp,%ebp
     8048f31:	53                   	push   %ebx
     8048f32:	83 ec 04             	sub    $0x4,%esp
     8048f35:	8b 55 08             	mov    0x8(%ebp),%edx
     8048f38:	8b 4d 0c             	mov    0xc(%ebp),%ecx
     8048f3b:	85 d2                	test   %edx,%edx
     8048f3d:	74 37                	je     8048f76 
    
     8048f3f:	8b 1a                	mov    (%edx),%ebx
     8048f41:	39 cb                	cmp    %ecx,%ebx
     8048f43:	7e 13                	jle    8048f58 
     8048f45:	83 ec 08             	sub    $0x8,%esp
     8048f48:	51                   	push   %ecx
     8048f49:	ff 72 04             	push   0x4(%edx)
     8048f4c:	e8 dd ff ff ff       	call   8048f2e 
    
     8048f51:	83 c4 10             	add    $0x10,%esp
     8048f54:	01 c0                	add    %eax,%eax
     8048f56:	eb 23                	jmp    8048f7b 
      
     8048f58:	b8 00 00 00 00       	mov    $0x0,%eax
     8048f5d:	39 cb                	cmp    %ecx,%ebx
     8048f5f:	74 1a                	je     8048f7b 
    
     8048f61:	83 ec 08             	sub    $0x8,%esp
     8048f64:	51                   	push   %ecx
     8048f65:	ff 72 08             	push   0x8(%edx)
     8048f68:	e8 c1 ff ff ff       	call   8048f2e 
    
     8048f6d:	83 c4 10             	add    $0x10,%esp
     8048f70:	8d 44 00 01          	lea    0x1(%eax,%eax,1),%eax
     8048f74:	eb 05                	jmp    8048f7b 
     
     8048f76:	b8 ff ff ff ff       	mov    $0xffffffff,%eax
    
     8048f7b:	8b 5d fc             	mov    -0x4(%ebp),%ebx
     8048f7e:	c9                   	leave  
     8048f7f:	c3                   	ret    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    顺着思路翻译一下还是很简单的:

    fun7 (node* root, int num) {
    	if (root == null) {
    		eax = 0xffffffff;
    		return
        }
    	if (root->val <= num) {
    		eax = 0;				# ①
    		if (root->val == num)
    			return;
    		fun7(root->right, num);
    		eax = eax * 2 + 1;		# ②
    		return;
        }
    	fun7(root->left, num);
    	eax *= 2;					# ③
    	return;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    本来是想一步步分析汇编的,但递归实在太绕了:

    img

    还是顺着把汇编翻译一下比较简单。

    最终目标是让eax变为3。这里有关eax的有效操作有①eax = 0;②eax = eax * 2 + 1;③eax *= 2

    所以一个可行的顺序是①②②,,对应的在递归第一层要执行②,第二层要执行②,第三层执行①。

    来到第一层,此时root->val = 36,第一层要走到②之前那一句进入二层调用,这就要求num > 36

    来到第二层,此时root->val = 50,仍需要走到②之前那一句进入三层调用,这就要求num > 50

    来到第三层,此时root->val = 107,这次需要走到①处然后返回,要求num == 107

    这样就推理出了最终答案:107

    测试一下完全通过:

    img

  • 相关阅读:
    vue2插件
    Vue3+vite打包配置及部分打包优化~
    【重拾C语言】六、批量数据组织(四)线性表—栈和队列
    策略模式(Strategy)
    awvs 中低危漏洞
    通过docker进行部署 Zookeeper、Kafka集群
    Toronto Research Chemicals加米霉素-d4说明书
    集成学习-Bagging和Boosting算法
    数据请求方式Fetch
    专为大模型训练优化,百度集合通信库 BCCL 万卡集群快速定位故障
  • 原文地址:https://blog.csdn.net/Ye_Ming_OUC/article/details/134488199