binary bombs

binary bombs

准备阶段

反汇编

objdump -d bomb > bomb.s

通过gdb调试程序

gdb ./bomb

设置断点:

(gdb) b phase_1

以字符串形式显示内存内容:

(gdb) x/s 0x80497c0

在GDB(GNU调试器)中,x/s 0x80497c0命令的含义是从内存地址0x80497c0开始,以字符串(string)的形式显示内存内容。

这里的x是“examine”的缩写,表示检查内存内容。/s表示以字符串的形式显示结果。0x80497c0是要检查的内存地址。

这个命令通常用于查看某个内存地址中存储的字符串内容。

阶段1 字符串比较

源代码文本

08048b20 <phase_1>:
 8048b20:	55                   	push   %ebp
 8048b21:	89 e5                	mov    %esp,%ebp
 8048b23:	83 ec 08             	sub    $0x8,%esp
 8048b26:	8b 45 08             	mov    0x8(%ebp),%eax
 8048b29:	83 c4 f8             	add    $0xfffffff8,%esp
 8048b2c:	68 c0 97 04 08       	push   $0x80497c0
 8048b31:	50                   	push   %eax
 8048b32:	e8 f9 04 00 00       	call   8049030 <strings_not_equal>
 8048b37:	83 c4 10             	add    $0x10,%esp
 8048b3a:	85 c0                	test   %eax,%eax
 8048b3c:	74 05                	je     8048b43 <phase_1+0x23>
 8048b3e:	e8 b9 09 00 00       	call   80494fc <explode_bomb>
 8048b43:	89 ec                	mov    %ebp,%esp
 8048b45:	5d                   	pop    %ebp
 8048b46:	c3                   	ret    
 8048b47:	90                   	nop

分段解读

函数的初始化操作
 8048b20:	55                   	push   %ebp
 8048b21:	89 e5                	mov    %esp,%ebp
 8048b23:	83 ec 08             	sub    $0x8,%esp
 8048b26:	8b 45 08             	mov    0x8(%ebp),%eax
 8048b29:	83 c4 f8             	add    $0xfffffff8,%esp
  1. push %ebp:将基指针寄存器(ebp)的值压入栈中。这是为了保存调用者的基指针。
  2. mov %esp,%ebp:将栈指针寄存器(esp)的值复制到基指针寄存器(ebp)。这是为了让ebp指向当前的栈顶,作为这个函数的基指针。
  3. sub $0x8,%esp:将栈指针寄存器(esp)的值减8。这是为了在栈上分配8个字节的空间,用于存储局部变量或者临时数据。
  4. mov 0x8(%ebp),%eax:将基指针寄存器(ebp)加8的地址处的值复制到累加器寄存器(eax)。这是为了获取函数的第一个参数。
  5. add $0xfffffff8,%esp:将栈指针寄存器(esp)的值减8(0xfffffff8是-8的补码表示)。这是为了在栈上再分配8个字节的空间。
高地址
-----------------
|   参数1      |  <- ebp + 8
----------------- 
|   返回地址   |  <- ebp + 4
-----------------
|   调用者ebp  |  <- ebp (<- 开始esp)
-----------------
|   未知       |  <- esp(执行sub $0x8,%esp指令后)
-----------------
|   未知       |  <- esp(执行add $0xfffffff8,%esp指令后)
-----------------
低地址
高地址
-----------------
|   参数n      |  <- ebp + 8 + 4 * (n - 1)
-----------------
|   ...        |
-----------------
|   参数2      |  <- ebp + 12
-----------------
|   参数1      |  <- ebp + 8
-----------------
|   返回地址   |  <- ebp + 4
-----------------
|   调用者ebp  |  <- ebp
-----------------
低地址
函数调用的准备工作
 8048b29:	83 c4 f8             	add    $0xfffffff8,%esp
 8048b2c:	68 c0 97 04 08       	push   $0x80497c0
 8048b31:	50                   	push   %eax
 8048b32:	e8 f9 04 00 00       	call   8049030 <strings_not_equal>
  1. add $0xfffffff8,%esp:将栈指针寄存器(esp)的值减8(0xfffffff8是-8的补码表示)。这是为了在栈上分配8个字节的空间。
  2. push $0x80497c0:将立即数0x80497c0压入栈中。这可能是一个字符串的地址,将作为strings_not_equal函数的参数。
  3. push %eax:将累加器寄存器(eax)的值压入栈中。这是strings_not_equal函数的另一个参数。
  4. call 8049030 <strings_not_equal>:调用strings_not_equal函数。这会将返回地址压入栈中,并将指令指针(eip)设置为strings_not_equal函数的地址。
高地址
-----------------
|   参数1      |  <- ebp + 8
-----------------
|   返回地址   |  <- ebp + 4
-----------------
|   调用者ebp  |  <- ebp
-----------------
|   未知       |  <- esp(执行sub $0x8,%esp指令后)
-----------------
|   未知       |  
-----------------
|   0x80497c0  |  <- esp(执行push $0x80497c0指令后)
-----------------
|   参数1      |  <- esp(执行push %eax指令后)
-----------------
低地址

寻找目标字符串

思路:执行<strings_not_equal>函数前栈中压入了两个参数,一个是调用者给出的,另一个是地址,猜测另一个地址上就是需要匹配的字符串,通过gdb调试,使用x/s命令将那个参数地址中的值转化为字符串,得到我们最终的答案


阶段2 循环

源代码文本

08048b48 <phase_2>:
 8048b48:	55                   	push   %ebp
 8048b49:	89 e5                	mov    %esp,%ebp
 8048b4b:	83 ec 20             	sub    $0x20,%esp
 8048b4e:	56                   	push   %esi
 8048b4f:	53                   	push   %ebx
 8048b50:	8b 55 08             	mov    0x8(%ebp),%edx
 8048b53:	83 c4 f8             	add    $0xfffffff8,%esp
 8048b56:	8d 45 e8             	lea    -0x18(%ebp),%eax
 8048b59:	50                   	push   %eax
 8048b5a:	52                   	push   %edx
 8048b5b:	e8 78 04 00 00       	call   8048fd8 <read_six_numbers>
 8048b60:	83 c4 10             	add    $0x10,%esp
 8048b63:	83 7d e8 01          	cmpl   $0x1,-0x18(%ebp)
 8048b67:	74 05                	je     8048b6e <phase_2+0x26>
 8048b69:	e8 8e 09 00 00       	call   80494fc <explode_bomb>
 8048b6e:	bb 01 00 00 00       	mov    $0x1,%ebx
 8048b73:	8d 75 e8             	lea    -0x18(%ebp),%esi
 8048b76:	8d 43 01             	lea    0x1(%ebx),%eax
 8048b79:	0f af 44 9e fc       	imul   -0x4(%esi,%ebx,4),%eax
 8048b7e:	39 04 9e             	cmp    %eax,(%esi,%ebx,4)
 8048b81:	74 05                	je     8048b88 <phase_2+0x40>
 8048b83:	e8 74 09 00 00       	call   80494fc <explode_bomb>
 8048b88:	43                   	inc    %ebx
 8048b89:	83 fb 05             	cmp    $0x5,%ebx
 8048b8c:	7e e8                	jle    8048b76 <phase_2+0x2e>
 8048b8e:	8d 65 d8             	lea    -0x28(%ebp),%esp
 8048b91:	5b                   	pop    %ebx
 8048b92:	5e                   	pop    %esi
 8048b93:	89 ec                	mov    %ebp,%esp
 8048b95:	5d                   	pop    %ebp
 8048b96:	c3                   	ret    
 8048b97:	90                   	nop

分段分析

初始化阶段

esi

esi是一个32位的寄存器,很多情况下,它被用作源地址索引,特别是在一些数据移动的操作中。另外,esi寄存器也经常被用作循环中的计数器或者索引,这在处理数组或者缓冲区时非常有用。

esi 寄存器的值压入栈中。在函数调用过程中,通常会保存那些可能会被改变的寄存器的值,以便在函数返回时可以恢复它们的原始值。

ebx:

​ 同esi

在这个程序中作用:

esi寄存器被用来存储数组的地址,ebx寄存器被用来作为数组的索引,并计算下一个数组元素的预期值。

  1. mov 0x8(%ebp),%edx:这条指令将存储在 ebp + 8 的值(也就是 phase_2 函数的第一个参数)复制到 edx 寄存器。

  2. lea -0x18(%ebp),%eax:这条指令计算 ebp - 0x18 的地址,并将结果存储在 eax 寄存器中。这个地址将被用作 read_six_numbers 函数的第一个参数。

  3. call 8048fd8 <read_six_numbers>:这条指令调用了 read_six_numbers 函数。这个函数的作用是读取六个数字并将它们存储在由第一个参数指定的地址开始的内存中。

  4. add $0x10,%esp:这条指令将 esp 寄存器的值增加16(0x10)。这实际上是在清理栈上的参数。因为之前在调用 read_six_numbers 函数之前压入了两个参数,每个参数占用4字节,所以现在需要增加16字节来恢复 esp 寄存器的值。

  5. cmpl $0x1,-0x18(%ebp):这条指令比较存储在 ebp - 0x18 的值(也就是数组的第一个元素)和1。如果它们相等,ZF(零标志)会被设置。

  6. je 8048b6e:这条指令检查 ZF 是否被设置。如果是,程序就跳转到地址 8048b6e。这实际上是在检查数组的第一个元素是否为1。如果不是,程序就会继续执行下一条指令。

循环阶段
  1. mov $0x1,%ebx:这条指令将 ebx 寄存器的值设置为1。ebx 寄存器在这个循环中被用作索引。
  2. lea -0x18(%ebp),%esi:这条指令将 esi 寄存器的值设置为数组的起始地址(ebp - 0x18)。
  3. lea 0x1(%ebx),%eax:这条指令将 eax 寄存器的值设置为 ebx + 1
  4. imul -0x4(%esi,%ebx,4),%eax:这条指令将数组的当前元素(-0x4(%esi,%ebx,4))乘以 eax 寄存器的值,并将结果存储在 eax 寄存器中。
  5. cmp %eax,(%esi,%ebx,4):这条指令比较 eax 寄存器的值和数组的下一个元素。如果它们相等,ZF(零标志)会被设置。
  6. je 8048b88:这条指令检查 ZF 是否被设置。如果是,程序就跳转到地址 8048b88。这实际上是在检查数组的下一个元素是否等于当前元素乘以 ebx + 1。如果不是,程序就会调用 explode_bomb 函数。
  7. inc %ebx:这条指令将 ebx 寄存器的值增加1。
  8. cmp $0x5,%ebx:这条指令比较 ebx 寄存器的值和5。如果 ebx 小于或等于5,ZF 会被设置。
  9. jle 8048b76:这条指令检查 ZF 是否被设置。如果是,程序就跳回到地址 8048b76,也就是循环的开始。否则,程序就会退出循环。

寻找输入数组

  1. cmpl $0x1,-0x18(%ebp):这条指令比较存储在 ebp - 0x18 的值(也就是数组的第一个元素)和1。知,首数组元素为1。

  2. lea 0x1(%ebx),%eax
    imul -0x4(%esi,%ebx,4),%eax
    cmp %eax,(%esi,%ebx,4)
    inc %ebx
    

    知相邻数组之间的关系。

得:1 2 6 24 120 720


阶段3 条件/分支

源代码文本

08048b98 <phase_3>:
 8048b98:	55                   	push   %ebp
 8048b99:	89 e5                	mov    %esp,%ebp
 8048b9b:	83 ec 14             	sub    $0x14,%esp
 8048b9e:	53                   	push   %ebx
 8048b9f:	8b 55 08             	mov    0x8(%ebp),%edx
 8048ba2:	83 c4 f4             	add    $0xfffffff4,%esp
 8048ba5:	8d 45 fc             	lea    -0x4(%ebp),%eax
 8048ba8:	50                   	push   %eax
 8048ba9:	8d 45 fb             	lea    -0x5(%ebp),%eax
 8048bac:	50                   	push   %eax
 8048bad:	8d 45 f4             	lea    -0xc(%ebp),%eax
 8048bb0:	50                   	push   %eax
 8048bb1:	68 de 97 04 08       	push   $0x80497de
 8048bb6:	52                   	push   %edx
 8048bb7:	e8 a4 fc ff ff       	call   8048860 <sscanf@plt>
 8048bbc:	83 c4 20             	add    $0x20,%esp
 8048bbf:	83 f8 02             	cmp    $0x2,%eax
 8048bc2:	7f 05                	jg     8048bc9 <phase_3+0x31>
 8048bc4:	e8 33 09 00 00       	call   80494fc <explode_bomb>
 8048bc9:	83 7d f4 07          	cmpl   $0x7,-0xc(%ebp)
 8048bcd:	0f 87 b5 00 00 00    	ja     8048c88 <phase_3+0xf0>
 8048bd3:	8b 45 f4             	mov    -0xc(%ebp),%eax
 8048bd6:	ff 24 85 e8 97 04 08 	jmp    *0x80497e8(,%eax,4)
 8048bdd:	8d 76 00             	lea    0x0(%esi),%esi
 8048be0:	b3 71                	mov    $0x71,%bl
 8048be2:	81 7d fc 09 03 00 00 	cmpl   $0x309,-0x4(%ebp)
 8048be9:	0f 84 a0 00 00 00    	je     8048c8f <phase_3+0xf7>
 8048bef:	e8 08 09 00 00       	call   80494fc <explode_bomb>
 8048bf4:	e9 96 00 00 00       	jmp    8048c8f <phase_3+0xf7>
 8048bf9:	8d b4 26 00 00 00 00 	lea    0x0(%esi,%eiz,1),%esi
 8048c00:	b3 62                	mov    $0x62,%bl
 8048c02:	81 7d fc d6 00 00 00 	cmpl   $0xd6,-0x4(%ebp)
 8048c09:	0f 84 80 00 00 00    	je     8048c8f <phase_3+0xf7>
 8048c0f:	e8 e8 08 00 00       	call   80494fc <explode_bomb>
 8048c14:	eb 79                	jmp    8048c8f <phase_3+0xf7>
 8048c16:	b3 62                	mov    $0x62,%bl
 8048c18:	81 7d fc f3 02 00 00 	cmpl   $0x2f3,-0x4(%ebp)
 8048c1f:	74 6e                	je     8048c8f <phase_3+0xf7>
 8048c21:	e8 d6 08 00 00       	call   80494fc <explode_bomb>
 8048c26:	eb 67                	jmp    8048c8f <phase_3+0xf7>
 8048c28:	b3 6b                	mov    $0x6b,%bl
 8048c2a:	81 7d fc fb 00 00 00 	cmpl   $0xfb,-0x4(%ebp)
 8048c31:	74 5c                	je     8048c8f <phase_3+0xf7>
 8048c33:	e8 c4 08 00 00       	call   80494fc <explode_bomb>
 8048c38:	eb 55                	jmp    8048c8f <phase_3+0xf7>
 8048c3a:	8d b6 00 00 00 00    	lea    0x0(%esi),%esi
 8048c40:	b3 6f                	mov    $0x6f,%bl
 8048c42:	81 7d fc a0 00 00 00 	cmpl   $0xa0,-0x4(%ebp)
 8048c49:	74 44                	je     8048c8f <phase_3+0xf7>
 8048c4b:	e8 ac 08 00 00       	call   80494fc <explode_bomb>
 8048c50:	eb 3d                	jmp    8048c8f <phase_3+0xf7>
 8048c52:	b3 74                	mov    $0x74,%bl
 8048c54:	81 7d fc ca 01 00 00 	cmpl   $0x1ca,-0x4(%ebp)
 8048c5b:	74 32                	je     8048c8f <phase_3+0xf7>
 8048c5d:	e8 9a 08 00 00       	call   80494fc <explode_bomb>
 8048c62:	eb 2b                	jmp    8048c8f <phase_3+0xf7>
 8048c64:	b3 76                	mov    $0x76,%bl
 8048c66:	81 7d fc 0c 03 00 00 	cmpl   $0x30c,-0x4(%ebp)
 8048c6d:	74 20                	je     8048c8f <phase_3+0xf7>
 8048c6f:	e8 88 08 00 00       	call   80494fc <explode_bomb>
 8048c74:	eb 19                	jmp    8048c8f <phase_3+0xf7>
 8048c76:	b3 62                	mov    $0x62,%bl
 8048c78:	81 7d fc 0c 02 00 00 	cmpl   $0x20c,-0x4(%ebp)
 8048c7f:	74 0e                	je     8048c8f <phase_3+0xf7>
 8048c81:	e8 76 08 00 00       	call   80494fc <explode_bomb>
 8048c86:	eb 07                	jmp    8048c8f <phase_3+0xf7>
 8048c88:	b3 78                	mov    $0x78,%bl
 8048c8a:	e8 6d 08 00 00       	call   80494fc <explode_bomb>
 8048c8f:	3a 5d fb             	cmp    -0x5(%ebp),%bl
 8048c92:	74 05                	je     8048c99 <phase_3+0x101>
 8048c94:	e8 63 08 00 00       	call   80494fc <explode_bomb>
 8048c99:	8b 5d e8             	mov    -0x18(%ebp),%ebx
 8048c9c:	89 ec                	mov    %ebp,%esp
 8048c9e:	5d                   	pop    %ebp
 8048c9f:	c3                   	ret   

分段解读

初始化阶段
  1. push %ebp; mov %esp,%ebp; sub $0x14,%esp; push %ebx:这些指令是函数的开头,它们设置了新的堆栈帧并保存了 ebx 寄存器的值。

  2. mov 0x8(%ebp),%edx:这条指令将函数的第一个参数(存储在ebp+8的位置)移动到edx寄存器。

  3. add $0xfffffff4,%esp:这条指令将栈指针esp减少了12(0xfffffff4是-12的补码表示),为接下来的操作预留了空间。

  4. lea -0x4(%ebp),%eax; push %eax; lea -0x5(%ebp),%eax; push %eax; lea -0xc(%ebp),%eax; push %eax:这些指令将ebp寄存器减去4、5和12的地址分别加载到eax寄存器,并将这些地址压入栈中。

  5. push $0x80497de; push %edx:这两条指令将一个常量地址和 edx 寄存器的值压入栈中。这可能是 sscanf 函数的输入参数。

  6. push %edx:这条指令将edx寄存器的值(即函数的第一个参数)压入栈中。

  7. add $0x20,%esp:这条指令将 esp 寄存器的值增加32(0x20)。这实际上是在清理栈上的参数。因为之前在调用 sscanf 函数之前压入了5个参数,每个参数占用4字节,所以现在需要增加20字节来恢复 esp 寄存器的值。

  8. cmp $0x2,%eax:这条指令比较 eax 寄存器的值和2。eax 寄存器的值是 sscanf 函数的返回值,表示成功读取的项数。(知读入了两个以上个数据)

  9. cmpl $0x7,-0xc(%ebp):这条指令比较存储在 ebp - 0xc 的值和7。这可能是检查第一个变量是否大于7。

高地址
|-----------------|
|     %ebp        |  <- 通过 push %ebp 压入,此时的 %ebp 指向这里
|-----------------|
|     未知        |  <- 通过 sub $0x14,%esp 创建的空间
|-----------------|
|     %ebx        |  <- 通过 push %ebx 压入
|-----------------|
|     未知        |  <- 通过 add $0xfffffff4,%esp 创建的空间
|-----------------|
|  参数:&var3    |  <- 通过 lea -0x4(%ebp),%eax 和 push %eax 压入,此处为 %ebp - 0x4
|-----------------|
|  参数:&var2    |  <- 通过 lea -0x5(%ebp),%eax 和 push %eax 压入,此处为 %ebp - 0x5
|-----------------|
|  参数:&var1    |  <- 通过 lea -0xc(%ebp),%eax 和 push %eax 压入,此处为 %ebp - 0xc
|-----------------|
|  参数:0x80497de|  <- 通过 push $0x80497de 压入
|-----------------|
|  参数:%edx     |  <- 通过 push %edx 压入
|-----------------|
|     ...         |
低地址
条件分支阶段
  1. jmp *0x80497e8(,%eax,4):这是一个间接跳转指令。它将跳转到地址 0x80497e8 + eax * 4。这通常用于实现基于表的跳转,例如 switch 语句。eax 的值决定了跳转到哪个地址。

  2. lea 0x0(%esi),%esi:这条指令将 esi 寄存器的值加载到 esi 寄存器。这实际上没有改变 esi 寄存器的值,可能是用于调试或其他目的。

  3. mov $0x71,%bl:这条指令将常量 0x71(ASCII 码中的 ‘q’)移动到 bl 寄存器。(第二个参数)

  4. cmpl $0x309,-0x4(%ebp):这条指令比较存储在 ebp - 0x4 的值和 0x309。这是第三个参数是否等于 0x309

  5. cmp -0x5(%ebp),%bl:比较存储在 ebp - 0x5 的值和 bl 寄存器的值。这可能是第二个参数是否等于 bl 寄存器的值。

  6. mov -0x18(%ebp),%ebx:将存储在 ebp - 0x18 的值移动到 ebx 寄存器。这可能是恢复 ebx 寄存器的原始值,因为 ebx 寄存器在函数调用中需要被保留。

  7. 其他分支同理

寻找输入

通过x/s将0x80497de这个地址存的字符串解析出来
结果

  1. 根据 cmpl $0x7,-0xc(%ebp)判断出第一个参数大小为0到7之间的数。
  2. 根据mov $0x71,%bl得出当第一个参数为0时,第二个参数为q。
  3. 根据cmpl $0x309,-0x4(%ebp) 得出第一个参数为0时,第三个参数为0x309(777)。
  4. 输入可为 0 q 777

阶段4 递归调用栈

源代码文本

phase_4:

08048ce0 <phase_4>:
 8048ce0:	55                   	push   %ebp
 8048ce1:	89 e5                	mov    %esp,%ebp
 8048ce3:	83 ec 18             	sub    $0x18,%esp
 8048ce6:	8b 55 08             	mov    0x8(%ebp),%edx
 8048ce9:	83 c4 fc             	add    $0xfffffffc,%esp
 8048cec:	8d 45 fc             	lea    -0x4(%ebp),%eax
 8048cef:	50                   	push   %eax
 8048cf0:	68 08 98 04 08       	push   $0x8049808
 8048cf5:	52                   	push   %edx
 8048cf6:	e8 65 fb ff ff       	call   8048860 <sscanf@plt>
 8048cfb:	83 c4 10             	add    $0x10,%esp
 8048cfe:	83 f8 01             	cmp    $0x1,%eax
 8048d01:	75 06                	jne    8048d09 <phase_4+0x29>
 8048d03:	83 7d fc 00          	cmpl   $0x0,-0x4(%ebp)
 8048d07:	7f 05                	jg     8048d0e <phase_4+0x2e>
 8048d09:	e8 ee 07 00 00       	call   80494fc <explode_bomb>
 8048d0e:	83 c4 f4             	add    $0xfffffff4,%esp
 8048d11:	8b 45 fc             	mov    -0x4(%ebp),%eax
 8048d14:	50                   	push   %eax
 8048d15:	e8 86 ff ff ff       	call   8048ca0 <func4>
 8048d1a:	83 c4 10             	add    $0x10,%esp
 8048d1d:	83 f8 37             	cmp    $0x37,%eax
 8048d20:	74 05                	je     8048d27 <phase_4+0x47>
 8048d22:	e8 d5 07 00 00       	call   80494fc <explode_bomb>
 8048d27:	89 ec                	mov    %ebp,%esp
 8048d29:	5d                   	pop    %ebp
 8048d2a:	c3                   	ret    
 8048d2b:	90                   	nop

func4:

08048ca0 <func4>:
 8048ca0:	55                   	push   %ebp
 8048ca1:	89 e5                	mov    %esp,%ebp
 8048ca3:	83 ec 10             	sub    $0x10,%esp
 8048ca6:	56                   	push   %esi
 8048ca7:	53                   	push   %ebx
 8048ca8:	8b 5d 08             	mov    0x8(%ebp),%ebx
 8048cab:	83 fb 01             	cmp    $0x1,%ebx
 8048cae:	7e 20                	jle    8048cd0 <func4+0x30>
 8048cb0:	83 c4 f4             	add    $0xfffffff4,%esp
 8048cb3:	8d 43 ff             	lea    -0x1(%ebx),%eax
 8048cb6:	50                   	push   %eax
 8048cb7:	e8 e4 ff ff ff       	call   8048ca0 <func4>
 8048cbc:	89 c6                	mov    %eax,%esi
 8048cbe:	83 c4 f4             	add    $0xfffffff4,%esp
 8048cc1:	8d 43 fe             	lea    -0x2(%ebx),%eax
 8048cc4:	50                   	push   %eax
 8048cc5:	e8 d6 ff ff ff       	call   8048ca0 <func4>
 8048cca:	01 f0                	add    %esi,%eax
 8048ccc:	eb 07                	jmp    8048cd5 <func4+0x35>
 8048cce:	89 f6                	mov    %esi,%esi
 8048cd0:	b8 01 00 00 00       	mov    $0x1,%eax
 8048cd5:	8d 65 e8             	lea    -0x18(%ebp),%esp
 8048cd8:	5b                   	pop    %ebx
 8048cd9:	5e                   	pop    %esi
 8048cda:	89 ec                	mov    %ebp,%esp
 8048cdc:	5d                   	pop    %ebp
 8048cdd:	c3                   	ret    
 8048cde:	89 f6                	mov    %esi,%esi

分段分析

初始化阶段
  1. mov 0x8(%ebp),%edx:将存储在 ebp + 0x8 的值(即函数的第一个参数)移动到 edx 寄存器。
  2. lea -0x4(%ebp),%eaxpush %eax:计算地址 ebp - 0x4 并将其压入堆栈。这是 sscanf 函数的一个输出参数。
高地址
|-----------------|
|     旧 %ebp     |  <- 通过 push %ebp 压入,此时的 %ebp 指向这里
|-----------------|
|     未知        |  <- 通过 sub $0x18,%esp 创建的空间
|-----------------|
|     未知        |  <- 通过 add $0xfffffffc,%esp 创建的空间
|-----------------|
|  参数:&var     |  <- 通过 lea -0x4(%ebp),%eax 和 push %eax 压入,此处为 %ebp - 0x4
|-----------------|
|  参数:0x8049808|  <- 通过 push $0x8049808 压入
|-----------------|
|  参数:%edx     |  <- 通过 push %edx 压入
|-----------------|
|     ...         |
低地址
基础判断阶段
  1. add $0x10,%esp:将 esp 寄存器的值增加 16(0x10)。这是在清理堆栈上之前为 sscanf 函数调用分配的空间。
  2. cmp $0x1,%eax:比较 eax 寄存器的值和 1。eax 寄存器通常用于存储函数的返回值,所以这可能是检查 sscanf 函数的返回值是否为 1。得出输入的项数一定为1。即只有一个输入。
  3. cmpl $0x0,-0x4(%ebp):比较存储在 ebp - 0x4 的值和 0。这可能是检查输入值是否大于 0。(输入值一定大于0)
  4. add $0xfffffff4,%esp:将 esp 寄存器的值减 12(0xfffffff4)。这是在堆栈上分配更多的空间。
  5. mov -0x4(%ebp),%eaxpush %eax:将存储在 ebp - 0x4 的值移动到 eax 寄存器,然后将 eax 寄存器的值压入堆栈。这可能是 func4 函数的一个参数。
调用func4
  1. push %esipush %ebx:保存 esiebx 寄存器的值。这是因为这些寄存器在函数调用中需要被保留。
  2. mov 0x8(%ebp),%ebx:将存储在 ebp + 0x8 的值(即函数的第一个参数)移动到 ebx 寄存器。
  3. cmp $0x1,%ebx:比较 ebx 寄存器的值和 1。这可能是检查输入参数是否大于 1。(得出如果小于等于1直接跳到8048cd0,即返回值直接为1)(这里是递归的边界条件)
  4. lea -0x1(%ebx),%eaxpush %eax:计算 ebx - 1 的值并将其压入堆栈。
  5. call 8048ca0 <func4>:递归调用 func4 函数。
  6. mov %eax,%esi:将 eax 寄存器的值(即 func4 函数的返回值)移动到 esi 寄存器。
  7. add $0xfffffff4,%esplea -0x2(%ebx),%eaxpush %eax:再次在堆栈上分配空间,并计算 ebx - 2 的值并将其压入堆栈。这可能是另一个 func4 函数的参数。
  8. call 8048ca0 <func4>:再次递归调用 func4 函数。
  9. add %esi,%eax:将 esi 寄存器的值(即第一次 func4 调用的返回值)加到 eax 寄存器的值(即第二次 func4 调用的返回值)。
  10. jmp 8048cd5:无条件跳转到地址 8048cd5。这可能是函数的结束部分。
  11. mov $0x1,%eax:将 eax 寄存器的值设置为 1。这可能是函数的默认返回值。
  12. lea -0x18(%ebp),%esp:将 esp 寄存器的值设置为 ebp - 0x18。这是在清理堆栈上之前为函数调用分配的空间。
回到phase_4中
  1. add $0x10,%esp:将 esp 寄存器的值增加 16(0x10)。这是在清理堆栈上之前为 func4 函数调用分配的空间。
  2. cmp $0x37,%eax:比较 eax 寄存器的值和 55(0x37)。eax 寄存器通常用于存储函数的返回值,所以这可能是检查 func4 函数的返回值是否为 55。(得出函数返回值为55)

寻找输入

通过分析func4知,其实是求斐波那契数列的值为55时是第几个元素。

求解得9

这个函数的c语言代码:

int f(int n)
{
    if(n<=1) return 1;
    else return f(n-1) + f(n-2);
}

阶段5 指针

源代码文本

08048d2c <phase_5>:
 8048d2c:	55                   	push   %ebp
 8048d2d:	89 e5                	mov    %esp,%ebp
 8048d2f:	83 ec 10             	sub    $0x10,%esp
 8048d32:	56                   	push   %esi
 8048d33:	53                   	push   %ebx
 8048d34:	8b 5d 08             	mov    0x8(%ebp),%ebx
 8048d37:	83 c4 f4             	add    $0xfffffff4,%esp
 8048d3a:	53                   	push   %ebx
 8048d3b:	e8 d8 02 00 00       	call   8049018 <string_length>
 8048d40:	83 c4 10             	add    $0x10,%esp
 8048d43:	83 f8 06             	cmp    $0x6,%eax
 8048d46:	74 05                	je     8048d4d <phase_5+0x21>
 8048d48:	e8 af 07 00 00       	call   80494fc <explode_bomb>
 8048d4d:	31 d2                	xor    %edx,%edx
 8048d4f:	8d 4d f8             	lea    -0x8(%ebp),%ecx
 8048d52:	be 20 b2 04 08       	mov    $0x804b220,%esi
 8048d57:	8a 04 1a             	mov    (%edx,%ebx,1),%al
 8048d5a:	24 0f                	and    $0xf,%al
 8048d5c:	0f be c0             	movsbl %al,%eax
 8048d5f:	8a 04 30             	mov    (%eax,%esi,1),%al
 8048d62:	88 04 0a             	mov    %al,(%edx,%ecx,1)
 8048d65:	42                   	inc    %edx
 8048d66:	83 fa 05             	cmp    $0x5,%edx
 8048d69:	7e ec                	jle    8048d57 <phase_5+0x2b>
 8048d6b:	c6 45 fe 00          	movb   $0x0,-0x2(%ebp)
 8048d6f:	83 c4 f8             	add    $0xfffffff8,%esp
 8048d72:	68 0b 98 04 08       	push   $0x804980b
 8048d77:	8d 45 f8             	lea    -0x8(%ebp),%eax
 8048d7a:	50                   	push   %eax
 8048d7b:	e8 b0 02 00 00       	call   8049030 <strings_not_equal>
 8048d80:	83 c4 10             	add    $0x10,%esp
 8048d83:	85 c0                	test   %eax,%eax
 8048d85:	74 05                	je     8048d8c <phase_5+0x60>
 8048d87:	e8 70 07 00 00       	call   80494fc <explode_bomb>
 8048d8c:	8d 65 e8             	lea    -0x18(%ebp),%esp
 8048d8f:	5b                   	pop    %ebx
 8048d90:	5e                   	pop    %esi
 8048d91:	89 ec                	mov    %ebp,%esp
 8048d93:	5d                   	pop    %ebp
 8048d94:	c3                   	ret    
 8048d95:	8d 76 00             	lea    0x0(%esi),%esi

分段分析

初始化阶段
  1. mov 0x8(%ebp),%ebx 将函数的第一个参数(存储在基指针加8的位置)复制到 %ebx 寄存器。

  2. add $0x10,%esp 是在调用完函数后清理栈,移除之前为函数调用所压入栈的参数。

  3. cmp $0x6,%eax%eax(通常用于存储函数返回值)与6进行比较。(得出字符串长度为6)

字符串处理阶段
  1. xor %edx,%edx%edx 寄存器清零。

  2. lea -0x8(%ebp),%ecx%ebp 减 8 的地址加载到 %ecx 寄存器,这可能是一个局部变量的地址。

  3. mov $0x804b220,%esi 将地址 0x804b220 加载到 %esi 寄存器,这可能是另一个字符串数组的起始地址。

  4. 接下来的代码是一个循环,它会执行以下操作:

    • mov (%edx,%ebx,1),%al%ebx%edx 的地址加载一个字节到 %al 寄存器,这可能是从输入字符串中读取一个字符。
    • and $0xf,%al%al 寄存器的值与 0xf 进行与操作。(取出字节后四位)
    • movsbl %al,%eax%al 寄存器的值符号扩展到 %eax 寄存器。
    • mov (%eax,%esi,1),%al%esi%eax 的地址加载一个字节到 %al 寄存器,这可能是从数组中读取一个元素。(以eax为索引从esi为首地址的字符串中取出字符)
    • mov %al,(%edx,%ecx,1)%al 寄存器的值存储到 %ecx%edx 的地址,这可能是将字符存储到输出字符串。
    • inc %edx%edx 寄存器的值加 1。
    • cmp $0x5,%edx%edx 寄存器的值与 5 进行比较。
    • jle 8048d57 <phase_5+0x2b> 如果 %edx 寄存器的值小于或等于 5,则跳转到地址 8048d57 继续执行循环。

这段代码的主要功能是将输入字符串的每个字符转换为另一个字符。转换的方式是取字符的 ASCII 值的低四位,然后用这个值作为索引从一个位于 0x804b220 的数组中取出一个新的字符。

字符串配对阶段
  1. movb $0x0,-0x2(%ebp) 将一个字节的 0 存储到 %ebp 减 2 的地址,这可能是在新字符串的末尾添加一个 null 字符。
  2. push $0x804980b 将地址 0x804980b 压入栈,这可能是一个预定义的字符串的地址。
  3. lea -0x8(%ebp),%eax%ebp 减 8 的地址加载到 %eax 寄存器,这可能是新字符串的地址。
  4. push %eax%eax 寄存器的值压入栈,这是 strings_not_equal 函数的第二个参数。
  5. add $0x10,%esp%esp 加 16,这可能是为了清理栈上的空间。
  6. test %eax,%eax 测试 %eax 寄存器的值,这可能是检查 strings_not_equal 函数的返回值。
  7. je 8048d8c <phase_5+0x60> 如果 %eax 寄存器的值为 0,即两个字符串相等,那么跳转到地址 8048d8c

这段代码的目的是比较新字符串和某个预定义的字符串是否相等。如果相等,就跳转到另一个地址。

寻找输入

mov $0x804b220,%esi :通过x/s 找到0x804b220中的字符数组

(gdb) x/s 0x804b220
0x804b220:	"isrveawhobpnutfg\260\001"

push $0x804980b :通过x/s找到0x804980b中的预定义的字符串数组。

(gdb) x/s 0x804980b
0x804980b:	"giants"

根据提供的数组 “isrveawhobpnutfg”,我们可以推断出原始的输入字符串。“giants” 中的每个字符在数组中的位置如下:

  • ‘g’ 在数组中的索引是 15
  • ‘i’ 在数组中的索引是 0
  • ‘a’ 在数组中的索引是 5
  • ‘n’ 在数组中的索引是 11
  • ‘t’ 在数组中的索引是 13
  • ‘s’ 在数组中的索引是 1

因为这个程序取的是字符 ASCII 值的低四位作为索引,所以我们需要找到一组字符,它们的 ASCII 值的低四位分别是 15, 0, 6, 11, 13, 1。这组字符就是原始的输入字符串。

这样的字符有很多组,因为 ASCII 值的高四位可以是任何值。

得出opekma就是其中一个符合的答案。

阶段6 链表/指针/结构

:对于这题还不太理解,主要是阅读汇编代码能力太弱,还是等我深入学习后回来解决本题!

源代码文本

08048d98 <phase_6>:
 8048d98:	55                   	push   %ebp
 8048d99:	89 e5                	mov    %esp,%ebp
 8048d9b:	83 ec 4c             	sub    $0x4c,%esp
 8048d9e:	57                   	push   %edi
 8048d9f:	56                   	push   %esi
 8048da0:	53                   	push   %ebx
 8048da1:	8b 55 08             	mov    0x8(%ebp),%edx
 8048da4:	c7 45 cc 6c b2 04 08 	movl   $0x804b26c,-0x34(%ebp)
 8048dab:	83 c4 f8             	add    $0xfffffff8,%esp
 8048dae:	8d 45 e8             	lea    -0x18(%ebp),%eax
 8048db1:	50                   	push   %eax
 8048db2:	52                   	push   %edx
 8048db3:	e8 20 02 00 00       	call   8048fd8 <read_six_numbers>
 8048db8:	31 ff                	xor    %edi,%edi
 8048dba:	83 c4 10             	add    $0x10,%esp
 8048dbd:	8d 76 00             	lea    0x0(%esi),%esi
 8048dc0:	8d 45 e8             	lea    -0x18(%ebp),%eax
 8048dc3:	8b 04 b8             	mov    (%eax,%edi,4),%eax
 8048dc6:	48                   	dec    %eax
 8048dc7:	83 f8 05             	cmp    $0x5,%eax
 8048dca:	76 05                	jbe    8048dd1 <phase_6+0x39>
 8048dcc:	e8 2b 07 00 00       	call   80494fc <explode_bomb>
 8048dd1:	8d 5f 01             	lea    0x1(%edi),%ebx
 8048dd4:	83 fb 05             	cmp    $0x5,%ebx
 8048dd7:	7f 23                	jg     8048dfc <phase_6+0x64>
 8048dd9:	8d 04 bd 00 00 00 00 	lea    0x0(,%edi,4),%eax
 8048de0:	89 45 c8             	mov    %eax,-0x38(%ebp)
 8048de3:	8d 75 e8             	lea    -0x18(%ebp),%esi
 8048de6:	8b 55 c8             	mov    -0x38(%ebp),%edx
 8048de9:	8b 04 32             	mov    (%edx,%esi,1),%eax
 8048dec:	3b 04 9e             	cmp    (%esi,%ebx,4),%eax
 8048def:	75 05                	jne    8048df6 <phase_6+0x5e>
 8048df1:	e8 06 07 00 00       	call   80494fc <explode_bomb>
 8048df6:	43                   	inc    %ebx
 8048df7:	83 fb 05             	cmp    $0x5,%ebx
 8048dfa:	7e ea                	jle    8048de6 <phase_6+0x4e>
 8048dfc:	47                   	inc    %edi
 8048dfd:	83 ff 05             	cmp    $0x5,%edi
 8048e00:	7e be                	jle    8048dc0 <phase_6+0x28>
 8048e02:	31 ff                	xor    %edi,%edi
 8048e04:	8d 4d e8             	lea    -0x18(%ebp),%ecx
 8048e07:	8d 45 d0             	lea    -0x30(%ebp),%eax
 8048e0a:	89 45 c4             	mov    %eax,-0x3c(%ebp)
 8048e0d:	8d 76 00             	lea    0x0(%esi),%esi
 8048e10:	8b 75 cc             	mov    -0x34(%ebp),%esi
 8048e13:	bb 01 00 00 00       	mov    $0x1,%ebx
 8048e18:	8d 04 bd 00 00 00 00 	lea    0x0(,%edi,4),%eax
 8048e1f:	89 c2                	mov    %eax,%edx
 8048e21:	3b 1c 08             	cmp    (%eax,%ecx,1),%ebx
 8048e24:	7d 12                	jge    8048e38 <phase_6+0xa0>
 8048e26:	8b 04 0a             	mov    (%edx,%ecx,1),%eax
 8048e29:	8d b4 26 00 00 00 00 	lea    0x0(%esi,%eiz,1),%esi
 8048e30:	8b 76 08             	mov    0x8(%esi),%esi
 8048e33:	43                   	inc    %ebx
 8048e34:	39 c3                	cmp    %eax,%ebx
 8048e36:	7c f8                	jl     8048e30 <phase_6+0x98>
 8048e38:	8b 55 c4             	mov    -0x3c(%ebp),%edx
 8048e3b:	89 34 ba             	mov    %esi,(%edx,%edi,4)
 8048e3e:	47                   	inc    %edi
 8048e3f:	83 ff 05             	cmp    $0x5,%edi
 8048e42:	7e cc                	jle    8048e10 <phase_6+0x78>
 8048e44:	8b 75 d0             	mov    -0x30(%ebp),%esi
 8048e47:	89 75 cc             	mov    %esi,-0x34(%ebp)
 8048e4a:	bf 01 00 00 00       	mov    $0x1,%edi
 8048e4f:	8d 55 d0             	lea    -0x30(%ebp),%edx
 8048e52:	8b 04 ba             	mov    (%edx,%edi,4),%eax
 8048e55:	89 46 08             	mov    %eax,0x8(%esi)
 8048e58:	89 c6                	mov    %eax,%esi
 8048e5a:	47                   	inc    %edi
 8048e5b:	83 ff 05             	cmp    $0x5,%edi
 8048e5e:	7e f2                	jle    8048e52 <phase_6+0xba>
 8048e60:	c7 46 08 00 00 00 00 	movl   $0x0,0x8(%esi)
 8048e67:	8b 75 cc             	mov    -0x34(%ebp),%esi
 8048e6a:	31 ff                	xor    %edi,%edi
 8048e6c:	8d 74 26 00          	lea    0x0(%esi,%eiz,1),%esi
 8048e70:	8b 56 08             	mov    0x8(%esi),%edx
 8048e73:	8b 06                	mov    (%esi),%eax
 8048e75:	3b 02                	cmp    (%edx),%eax
 8048e77:	7d 05                	jge    8048e7e <phase_6+0xe6>
 8048e79:	e8 7e 06 00 00       	call   80494fc <explode_bomb>
 8048e7e:	8b 76 08             	mov    0x8(%esi),%esi
 8048e81:	47                   	inc    %edi
 8048e82:	83 ff 04             	cmp    $0x4,%edi
 8048e85:	7e e9                	jle    8048e70 <phase_6+0xd8>
 8048e87:	8d 65 a8             	lea    -0x58(%ebp),%esp
 8048e8a:	5b                   	pop    %ebx
 8048e8b:	5e                   	pop    %esi
 8048e8c:	5f                   	pop    %edi
 8048e8d:	89 ec                	mov    %ebp,%esp
 8048e8f:	5d                   	pop    %ebp
 8048e90:	c3                   	ret    
 8048e91:	8d 76 00             	lea    0x0(%esi),%esi

分段分析

读入阶段
  1. mov 0x8(%ebp),%edx:将函数的第一个参数(通常是一个指向输入字符串的指针)复制到 %edx 寄存器。
  2. movl $0x804b26c,-0x34(%ebp):将地址 0x804b26c 存储到 %ebp 减 0x34 的位置,这可能是为了在后续的代码中使用。
  3. add $0xfffffff8,%esp:将 %esp 减 8,这可能是为了在栈上预留空间。
  4. lea -0x18(%ebp),%eax:将 %ebp 减 0x18 的地址加载到 %eax 寄存器,这可能是为了存储 read_six_numbers 函数的结果。
  5. push %eax:将 %eax 寄存器的值压入栈,这是 read_six_numbers 函数的第二个参数。
  6. push %edx:将 %edx 寄存器的值压入栈,这是 read_six_numbers 函数的第一个参数。
双重循环阶段
  1. xor %edi,%edi:将 %edi 寄存器清零,这个寄存器在后面的循环中用作计数器。
  2. add $0x10,%esp:将 %esp 加 16,这可能是为了清理栈。
  3. lea 0x0(%esi),%esi:这是一个无操作指令,可能是编译器优化的结果。
  4. 外层循环从 lea -0x18(%ebp),%eax 开始,到 jle 8048dc0 <phase_6+0x28> 结束。这个循环遍历用户输入的六个数字。
  5. 在外层循环中,mov (%eax,%edi,4),%eaxdec %eax 获取用户输入的一个数字并减 1。
  6. cmp $0x5,%eaxjbe 8048dd1 <phase_6+0x39> 检查这个数字是否在 1 到 6 的范围内。如果不在这个范围内,就会调用 explode_bomb 函数。
  7. 内层循环从 lea 0x1(%edi),%ebx 开始,到 jle 8048de6 <phase_6+0x4e> 结束。这个循环检查当前的数字是否与其他的数字相同。
  8. 在内层循环中,cmp (%esi,%ebx,4),%eaxjne 8048df6 <phase_6+0x5e> 检查当前的数字是否与其他的数字相同。如果有相同的数字,就会调用 explode_bomb 函数。
内层循环
  1. lea 0x1(%edi),%ebx:将 %edi 加 1 并将结果存储在 %ebx 中,这是内部循环的计数器。
  2. cmp $0x5,%ebxjg 8048dfc <phase_6+0x64>:检查 %ebx 是否大于 5。如果大于 5,就跳出内部循环。
  3. lea 0x0(,%edi,4),%eaxmov %eax,-0x38(%ebp):计算 %edi 乘以 4 的结果并将其存储在 %ebp 减 0x38 的位置。
  4. lea -0x18(%ebp),%esi:将 %ebp 减 0x18 的地址加载到 %esi 寄存器,这可能是为了存储 read_six_numbers 函数的结果。
  5. mov -0x38(%ebp),%edxmov (%edx,%esi,1),%eax:获取当前的数字。
  6. cmp (%esi,%ebx,4),%eaxjne 8048df6 <phase_6+0x5e>:检查当前的数字是否与其他的数字相同。如果有相同的数字,就会调用 explode_bomb 函数。
  7. inc %ebxcmp $0x5,%ebxjle 8048de6 <phase_6+0x4e>:增加 %ebx 的值并检查是否小于等于 5。如果小于等于 5,就继续内部循环。
  8. inc %edicmp $0x5,%edijle 8048dc0 <phase_6+0x28>:增加 %edi 的值并检查是否小于等于 5。如果小于等于 5,就继续外部循环。

这段代码的目的是检查用户输入的六个数字是否都是唯一的,如果不是,就会调用 explode_bomb 函数。

遍历链表
  1. xor %edi,%edi:将 %edi 清零,这是外部循环的计数器。
  2. lea -0x18(%ebp),%ecx:将 %ebp 减 0x18 的地址加载到 %ecx 寄存器,这可能是为了存储 read_six_numbers 函数的结果。
  3. lea -0x30(%ebp),%eaxmov %eax,-0x3c(%ebp):将 %ebp 减 0x30 的地址加载到 %eax 寄存器,并将其存储在 %ebp 减 0x3c 的位置。
  4. mov -0x34(%ebp),%esi:将 %ebp 减 0x34 的值加载到 %esi 寄存器,这可能是链表的头节点。
  5. mov $0x1,%ebx:将 1 存储在 %ebx 寄存器,这是内部循环的计数器。
  6. lea 0x0(,%edi,4),%eaxmov %eax,%edx:计算 %edi 乘以 4 的结果并将其存储在 %edx 寄存器。
  7. cmp (%eax,%ecx,1),%ebxjge 8048e38 <phase_6+0xa0>:检查 %ebx 是否大于等于 (%eax,%ecx,1) 的值。如果大于等于,就跳出内部循环。
  8. mov (%edx,%ecx,1),%eax:获取当前的数字。
  9. mov 0x8(%esi),%esi:获取链表的下一个节点。
  10. inc %ebxcmp %eax,%ebxjl 8048e30 <phase_6+0x98>:增加 %ebx 的值并检查是否小于 %eax。如果小于,就继续内部循环。
  11. mov -0x3c(%ebp),%edx:将 %ebp 减 0x3c 的值加载到 %edx 寄存器。
  12. mov %esi,(%edx,%edi,4):将找到的节点存储在数组中。
  13. inc %edicmp $0x5,%edijle 8048e10 <phase_6+0x78>:增加 %edi 的值并检查是否小于等于 5。如果小于等于,就继续外部循环。

这段代码的目的是在链表中查找特定的节点,如果找到,就将其存储在数组中,然后继续查找下一个节点。如果找不到,就会继续查找下一个节点。

…(还未完结)


  • 26
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值