计算机系统基础实验三(解了但尽量理解)

news2024/12/30 3:08:15

一.准备阶段

1、下载好32位的实验代码后,将文件解压缩并且通过共享文件夹操作将文件添加到虚拟机中,双击查看bomb.c代码,将c代码完整看了一遍,发现看这里的c代码是无从下手的,代码中只含有主函数,触发炸弹的过程被隐藏了起来。阅读老师给出的readme文件后,得知本题需要在Linux终端中对汇编代码进行分析并且调试才能得出答案。
2、输入objdump -d bomb,将可执行文件反汇编成汇编文件,为方便查看,可以使用objdump -d bomb >1.txt将汇编代码输出到txt文档里
3、试探一下炸弹爆炸的效果,gdb调试该可执行文件,随便输入一个字符串,果然爆炸了

二.实验开始

 一.实验一phase_1

0000000000400ee0 <phase_1>:
  400ee0:    48 83 ec 08              sub    $0x8,%rsp
  400ee4:    be 00 24 40 00           mov    $0x402400,%esi
  400ee9:    e8 4a 04 00 00           callq  401338 <strings_not_equal>
  400eee:    85 c0                    test   %eax,%eax
  400ef0:    74 05                    je     400ef7 <phase_1+0x17>
  400ef2:    e8 43 05 00 00           callq  40143a <explode_bomb>
  400ef7:    48 83 c4 08              add    $0x8,%rsp
  400efb:    c3                       retq  
对上面汇编语言进行分析:

这段汇编代码实现了一个函数phase_1,它检查一个字符串是否与某个预期值相匹配,如果不匹配,则会调用一个函数explode_bomb。下面是对每一行代码的解释:

  1. 400ee0: 48 83 ec 08 sub $0x8,%rsp

    • 函数开始时,调整栈指针%rsp,为局部变量分配8个字节的空间。这通常是为了保持栈的对齐或为局部变量腾出空间。
  2. 400ee4: be 00 24 40 00 mov $0x402400,%esi

    • 将常量地址0x402400加载到寄存器%esi中。这个地址通常指向一个字符串常量。
  3. 400ee9: e8 4a 04 00 00 callq 401338 <strings_not_equal>

    • 调用函数strings_not_equal,传递参数%esi(即字符串常量地址0x402400)。该函数将检查传递给phase_1的字符串与位于0x402400的字符串是否不同。
  4. 400eee: 85 c0 test %eax,%eax

    • 测试寄存器%eax的值。strings_not_equal函数的返回值存储在%eax中。如果返回值是0(表示字符串相同),则test %eax,%eax将设置ZF(零标志)。
  5. 400ef0: 74 05 je 400ef7 <phase_1+0x17>

    • 如果ZF(零标志)被设置(即%eax为0,表示字符串相同),跳转到地址400ef7,跳过explode_bomb调用。
  6. 400ef2: e8 43 05 00 00 callq 40143a <explode_bomb>

    • 如果字符串不相同,调用explode_bomb函数,这通常会导致程序失败或某种惩罚措施(比如在某些安全竞赛中模拟炸弹爆炸)。
  7. 400ef7: 48 83 c4 08 add $0x8,%rsp

    • 恢复栈指针%rsp,将之前减去的8个字节加回来。
  8. 400efb: c3 retq

    • 函数返回。

总结一下,这段汇编的逻辑是:

  • 从栈中分配一些空间。
  • 加载一个字符串常量地址。
  • 调用一个函数检查输入字符串是否与预期字符串相同。
  • 如果不相同,调用explode_bomb
  • 恢复栈指针并返回。

接下来我们看string_not_equal这个函数:
 0000000000401338 <strings_not_equal>:
  401338:    41 54                    push   %r12
  40133a:    55                       push   %rbp
  40133b:    53                       push   %rbx
  40133c:    48 89 fb                 mov    %rdi,%rbx
  40133f:    48 89 f5                 mov    %rsi,%rbp
  401342:    e8 d4 ff ff ff           callq  40131b <string_length>
  401347:    41 89 c4                 mov    %eax,%r12d
  40134a:    48 89 ef                 mov    %rbp,%rdi
  40134d:    e8 c9 ff ff ff           callq  40131b <string_length>
  401352:    ba 01 00 00 00           mov    $0x1,%edx
  401357:    41 39 c4                 cmp    %eax,%r12d
  40135a:    75 3f                    jne    40139b <strings_not_equal+0x63>
  40135c:    0f b6 03                 movzbl (%rbx),%eax
  40135f:    84 c0                    test   %al,%al
  401361:    74 25                    je     401388 <strings_not_equal+0x50>
  401363:    3a 45 00                 cmp    0x0(%rbp),%al
  401366:    74 0a                    je     401372 <strings_not_equal+0x3a>
  401368:    eb 25                    jmp    40138f <strings_not_equal+0x57>
  40136a:    3a 45 00                 cmp    0x0(%rbp),%al
  40136d:    0f 1f 00                 nopl   (%rax)
  401370:    75 24                    jne    401396 <strings_not_equal+0x5e>
  401372:    48 83 c3 01              add    $0x1,%rbx
  401376:    48 83 c5 01              add    $0x1,%rbp
  40137a:    0f b6 03                 movzbl (%rbx),%eax
  40137d:    84 c0                    test   %al,%al
  40137f:    75 e9                    jne    40136a <strings_not_equal+0x32>
  401381:    ba 00 00 00 00           mov    $0x0,%edx
  401386:    eb 13                    jmp    40139b <strings_not_equal+0x63>
  401388:    ba 00 00 00 00           mov    $0x0,%edx
  40138d:    eb 0c                    jmp    40139b <strings_not_equal+0x63>
  40138f:    ba 01 00 00 00           mov    $0x1,%edx
  401394:    eb 05                    jmp    40139b <strings_not_equal+0x63>
  401396:    ba 01 00 00 00           mov    $0x1,%edx
  40139b:    89 d0                    mov    %edx,%eax
  40139d:    5b                       pop    %rbx
  40139e:    5d                       pop    %rbp
  40139f:    41 5c                    pop    %r12
  4013a1:    c3                       retq 

下面是对每一行汇编的解释:
 

  1. 401338: 41 54 push %r12

    • 将寄存器 %r12 的值压入栈中,保存它的原始值。
  2. 40133a: 55 push %rbp

    • 将寄存器 %rbp 的值压入栈中,保存它的原始值。
  3. 40133b: 53 push %rbx

    • 将寄存器 %rbx 的值压入栈中,保存它的原始值。
  4. 40133c: 48 89 fb mov %rdi,%rbx

    • 将寄存器 %rdi 的值移动到 %rbx%rdi 是第一个字符串的地址。
  5. 40133f: 48 89 f5 mov %rsi,%rbp

    • 将寄存器 %rsi 的值移动到 %rbp%rsi 是第二个字符串的地址。
  6. 401342: e8 d4 ff ff ff callq 40131b <string_length>

    • 调用 string_length 函数来计算第一个字符串的长度。
  7. 401347: 41 89 c4 mov %eax,%r12d

    • string_length 返回的值(第一个字符串的长度)存储到 %r12d
  8. 40134a: 48 89 ef mov %rbp,%rdi

    • %rbp(第二个字符串的地址)移动到 %rdi,为下一次调用 string_length 准备参数。
  9. 40134d: e8 c9 ff ff ff callq 40131b <string_length>

    • 调用 string_length 函数来计算第二个字符串的长度。
  10. 401352: ba 01 00 00 00 mov $0x1,%edx

    • 将常量 1 移动到 %edx。如果字符串不相等,该值将用作返回值。
  11. 401357: 41 39 c4 cmp %eax,%r12d

    • 比较第二个字符串的长度(存储在 %eax 中)与第一个字符串的长度(存储在 %r12d 中)。
  12. 40135a: 75 3f jne 40139b <strings_not_equal+0x63>

    • 如果长度不同,跳转到 40139b,设置返回值为 1,表示字符串不相等。
  13. 40135c: 0f b6 03 movzbl (%rbx),%eax

    • %rbx 指向的第一个字符串的字符加载到 %eax 中,扩展为零填充。
  14. 40135f: 84 c0 test %al,%al

    • 测试 %al 是否为零(字符串是否结束)。
  15. 401361: 74 25 je 401388 <strings_not_equal+0x50>

    • 如果 %al 为零(字符串结束),跳转到 401388,设置返回值为 0,表示字符串相等。
  16. 401363: 3a 45 00 cmp 0x0(%rbp),%al

    • 比较 %al%rbp 指向的第二个字符串的字符。
  17. 401366: 74 0a je 401372 <strings_not_equal+0x3a>

    • 如果字符相同,跳转到 401372,继续比较下一个字符。
  18. 401368: eb 25 jmp 40138f <strings_not_equal+0x57>

    • 如果字符不同,跳转到 40138f,设置返回值为 1,表示字符串不相等。
  19. 40136a: 3a 45 00 cmp 0x0(%rbp),%al

    • 再次比较 %al%rbp 指向的第二个字符串的字符。
  20. 40136d: 0f 1f 00 nopl (%rax)

    • 无操作指令,可能用于对齐。
  21. 401370: 75 24 jne 401396 <strings_not_equal+0x5e>

    • 如果字符不同,跳转到 401396,设置返回值为 1,表示字符串不相等。
  22. 401372: 48 83 c3 01 add $0x1,%rbx

    • 增加 %rbx,指向下一个字符。
  23. 401376: 48 83 c5 01 add $0x1,%rbp

    • 增加 %rbp,指向下一个字符。
  24. 40137a: 0f b6 03 movzbl (%rbx),%eax

    • %rbx 指向的第一个字符串的下一个字符加载到 %eax 中,扩展为零填充。
  25. 40137d: 84 c0 test %al,%al

    • 测试 %al 是否为零(字符串是否结束)。
  26. 40137f: 75 e9 jne 40136a <strings_not_equal+0x32>

    • 如果 %al 不为零,跳转回去继续比较下一个字符。
  27. 401381: ba 00 00 00 00 mov $0x0,%edx

    • 将常量 0 移动到 %edx,表示字符串相等。
  28. 401386: eb 13 jmp 40139b <strings_not_equal+0x63>

    • 跳转到 40139b,将结果返回。
  29. 401388: ba 00 00 00 00 mov $0x0,%edx

    • 将常量 0 移动到 %edx,表示字符串相等。
  30. 40138d: eb 0c jmp 40139b <strings_not_equal+0x63>

    • 跳转到 40139b,将结果返回。
  31. 40138f: ba 01 00 00 00 mov $0x1,%edx

    • 将常量 1 移动到 %edx,表示字符串不相等。
  32. 401394: eb 05 jmp 40139b <strings_not_equal+0x63>

    • 跳转到 40139b,将结果返回。
  33. 401396: ba 01 00 00 00 mov $0x1,%edx

    • 将常量 1 移动到 %edx,表示字符串不相等。
  34. 40139b: 89 d0 mov %edx,%eax

    • %edx 的值移动到 %eax,准备返回结果。
  35. 40139d: 5b pop %rbx

    • 恢复之前保存的 %rbx 的值。
  36. 40139e: 5d pop %rbp

    • 恢复之前保存的 %rbp 的值。
  37. 40139f: 41 5c pop %r12

    • 恢复之前保存的 %r12 的值。
  38. 4013a1: c3 retq

    • 返回到调用者。

 最后我们看一下string_length的汇编语言:

000000000040131b <string_length>:
  40131b:    80 3f 00                 cmpb   $0x0,(%rdi)
  40131e:    74 12                    je     401332 <string_length+0x17>
  401320:    48 89 fa                 mov    %rdi,%rdx
  401323:    48 83 c2 01              add    $0x1,%rdx
  401327:    89 d0                    mov    %edx,%eax
  401329:    29 f8                    sub    %edi,%eax
  40132b:    80 3a 00                 cmpb   $0x0,(%rdx)
  40132e:    75 f3                    jne    401323 <string_length+0x8>
  401330:    f3 c3                    repz retq 
  401332:    b8 00 00 00 00           mov    $0x0,%eax
  401337:    c3                       retq  

下面我们看一下每一行的解释:

  1. 40131b: 80 3f 00 cmpb $0x0,(%rdi)

    • 比较寄存器 %rdi 指向的内存地址中的字节与 0%rdi 通常指向一个字符串的起始地址。
  2. 40131e: 74 12 je 401332 <string_length+0x17>

    • 如果字符串的第一个字节为 0(即空字符串),则跳转到 401332,直接返回字符串长度 0
  3. 401320: 48 89 fa mov %rdi,%rdx

    • %rdi 的值(字符串的起始地址)复制到 %rdx
  4. 401323: 48 83 c2 01 add $0x1,%rdx

    • %rdx 增加 1,指向下一个字符。
  5. 401327: 89 d0 mov %edx,%eax

    • %rdx 的值(当前字符地址)复制到 %eax
  6. 401329: 29 f8 sub %edi,%eax

    • %rdi(字符串起始地址)的值从 %eax 中减去,这样 %eax 中保存的就是当前字符的偏移量,即字符串的长度。
  7. 40132b: 80 3a 00 cmpb $0x0,(%rdx)

    • 比较 %rdx 指向的当前字符与 0
  8. 40132e: 75 f3 jne 401323 <string_length+0x8>

    • 如果当前字符不是 0,则跳回 401323,继续处理下一个字符。
  9. 401330: f3 c3 repz retq

    • 返回。这个指令等同于 retq,因为 repz 前缀对 retq 没有影响。这里可能是为了对齐或编译器生成的优化。
  10. 401332: b8 00 00 00 00 mov $0x0,%eax

    • 0 移动到 %eax,表示字符串长度为 0
  11. 401337: c3 retq

    • 返回到调用者。

 分析一遍可以知道,phase1就是把一个准备好的string和我输入的string进行比较,如果两者一致,那就不会爆炸。所以我们将刚刚读取到的0x402400中的那个string拿出来,输入,然后就成功了。

 

二.实验二phase_2

0000000000400efc <phase_2>:
  400efc:    55                       push   %rbp
  400efd:    53                       push   %rbx
  400efe:    48 83 ec 28              sub    $0x28,%rsp
  400f02:    48 89 e6                 mov    %rsp,%rsi
  400f05:    e8 52 05 00 00           callq  40145c <read_six_numbers>
  400f0a:    83 3c 24 01              cmpl   $0x1,(%rsp)
  400f0e:    74 20                    je     400f30 <phase_2+0x34>
  400f10:    e8 25 05 00 00           callq  40143a <explode_bomb>
  400f15:    eb 19                    jmp    400f30 <phase_2+0x34>
  400f17:    8b 43 fc                 mov    -0x4(%rbx),%eax
  400f1a:    01 c0                    add    %eax,%eax
  400f1c:    39 03                    cmp    %eax,(%rbx)
  400f1e:    74 05                    je     400f25 <phase_2+0x29>
  400f20:    e8 15 05 00 00           callq  40143a <explode_bomb>
  400f25:    48 83 c3 04              add    $0x4,%rbx
  400f29:    48 39 eb                 cmp    %rbp,%rbx
  400f2c:    75 e9                    jne    400f17 <phase_2+0x1b>
  400f2e:    eb 0c                    jmp    400f3c <phase_2+0x40>
  400f30:    48 8d 5c 24 04           lea    0x4(%rsp),%rbx
  400f35:    48 8d 6c 24 18           lea    0x18(%rsp),%rbp
  400f3a:    eb db                    jmp    400f17 <phase_2+0x1b>
  400f3c:    48 83 c4 28              add    $0x28,%rsp
  400f40:    5b                       pop    %rbx
  400f41:    5d                       pop    %rbp
  400f42:    c3                       retq 

下面我们看一下每一行对应的解释:

  1. 400efc: 55 push %rbp

    • %rbp 寄存器的值压入栈中,保存它的原始值。
  2. 400efd: 53 push %rbx

    • %rbx 寄存器的值压入栈中,保存它的原始值。
  3. 400efe: 48 83 ec 28 sub $0x28,%rsp

    • 调整栈指针,分配 40 字节的栈空间(0x28 = 40)。
  4. 400f02: 48 89 e6 mov %rsp,%rsi

    • 将当前栈指针的值存入 %rsi,为调用 read_six_numbers 函数准备参数。
  5. 400f05: e8 52 05 00 00 callq 40145c <read_six_numbers>

    • 调用 read_six_numbers 函数,从用户输入中读取六个整数并存储在栈中。
  6. 400f0a: 83 3c 24 01 cmpl $0x1,(%rsp)

    • 比较栈中第一个整数是否等于 1。
  7. 400f0e: 74 20 je 400f30 <phase_2+0x34>

    • 如果第一个整数是 1,则跳转到 400f30
  8. 400f10: e8 25 05 00 00 callq 40143a <explode_bomb>

    • 否则,调用 explode_bomb 函数。
  9. 400f15: eb 19 jmp 400f30 <phase_2+0x34>

    • 跳转到 400f30,继续执行。
  10. 400f17: 8b 43 fc mov -0x4(%rbx),%eax

    • %rbx 前一个整数的值(即当前整数的前一个整数)存储到 %eax
  11. 400f1a: 01 c0 add %eax,%eax

    • %eax 的值加倍(乘以 2)。
  12. 400f1c: 39 03 cmp %eax,(%rbx)

    • 比较 %eax(前一个整数的两倍)与 %rbx 当前指向的整数。
  13. 400f1e: 74 05 je 400f25 <phase_2+0x29>

    • 如果相等,跳转到 400f25,继续检查下一个整数。
  14. 400f20: e8 15 05 00 00 callq 40143a <explode_bomb>

    • 否则,调用 explode_bomb 函数。
  15. 400f25: 48 83 c3 04 add $0x4,%rbx

    • %rbx 增加 4,指向下一个整数。
  16. 400f29: 48 39 eb cmp %rbp,%rbx

    • 比较 %rbx%rbp 的值(指向第六个整数的地址)。
  17. 400f2c: 75 e9 jne 400f17 <phase_2+0x1b>

    • 如果 %rbx 未达到 %rbp,跳回到 400f17,继续检查下一个整数。
  18. 400f2e: eb 0c jmp 400f3c <phase_2+0x40>

    • 跳转到 400f3c,清理栈并返回。
  19. 400f30: 48 8d 5c 24 04 lea 0x4(%rsp),%rbx

    • 将栈中第一个整数之后的地址(第二个整数的地址)加载到 %rbx
  20. 400f35: 48 8d 6c 24 18 lea 0x18(%rsp),%rbp

    • 将栈中最后一个整数的地址(第六个整数的地址)加载到 %rbp
  21. 400f3a: eb db jmp 400f17 <phase_2+0x1b>

    • 跳回到 400f17,开始检查整数序列。
  22. 400f3c: 48 83 c4 28 add $0x28,%rsp

    • 恢复栈指针,释放之前分配的 40 字节栈空间。
  23. 400f40: 5b pop %rbx

    • 恢复 %rbx 的原始值。
  24. 400f41: 5d pop %rbp

    • 恢复 %rbp 的原始值。
  25. 400f42: c3 retq

    • 返回到调用者。

接下来我们看一下read_six_numbers这个函数: 

000000000040145c <read_six_numbers>:
  40145c:    48 83 ec 18              sub    $0x18,%rsp
  401460:    48 89 f2                 mov    %rsi,%rdx
  401463:    48 8d 4e 04              lea    0x4(%rsi),%rcx
  401467:    48 8d 46 14              lea    0x14(%rsi),%rax
  40146b:    48 89 44 24 08           mov    %rax,0x8(%rsp)
  401470:    48 8d 46 10              lea    0x10(%rsi),%rax
  401474:    48 89 04 24              mov    %rax,(%rsp)
  401478:    4c 8d 4e 0c              lea    0xc(%rsi),%r9
  40147c:    4c 8d 46 08              lea    0x8(%rsi),%r8
  401480:    be c3 25 40 00           mov    $0x4025c3,%esi
  401485:    b8 00 00 00 00           mov    $0x0,%eax
  40148a:    e8 61 f7 ff ff           callq  400bf0 <__isoc99_sscanf@plt>
  40148f:    83 f8 05                 cmp    $0x5,%eax
  401492:    7f 05                    jg     401499 <read_six_numbers+0x3d>
  401494:    e8 a1 ff ff ff           callq  40143a <explode_bomb>
  401499:    48 83 c4 18              add    $0x18,%rsp
  40149d:    c3                       retq 
下面我们解释一下每一行汇编:

  1. 40145c: 48 83 ec 18 sub $0x18,%rsp

    • 调整栈指针,分配 24 字节的栈空间(0x18 = 24)。
  2. 401460: 48 89 f2 mov %rsi,%rdx

    • %rsi 的值(指向存储六个整数的缓冲区的指针)复制到 %rdx
  3. 401463: 48 8d 4e 04 lea 0x4(%rsi),%rcx

    • %rsi 加上 4 的地址加载到 %rcx,即第二个整数的位置。
  4. 401467: 48 8d 46 14 lea 0x14(%rsi),%rax

    • %rsi 加上 20 的地址加载到 %rax,即第六个整数的位置。
  5. 40146b: 48 89 44 24 08 mov %rax,0x8(%rsp)

    • %rax(第六个整数的位置)存储到栈中偏移 8 的位置。
  6. 401470: 48 8d 46 10 lea 0x10(%rsi),%rax

    • %rsi 加上 16 的地址加载到 %rax,即第五个整数的位置。
  7. 401474: 48 89 04 24 mov %rax,(%rsp)

    • %rax(第五个整数的位置)存储到栈中的偏移 0 的位置。
  8. 401478: 4c 8d 4e 0c lea 0xc(%rsi),%r9

    • %rsi 加上 12 的地址加载到 %r9,即第四个整数的位置。
  9. 40147c: 4c 8d 46 08 lea 0x8(%rsi),%r8

    • %rsi 加上 8 的地址加载到 %r8,即第三个整数的位置。
  10. 401480: be c3 25 40 00 mov $0x4025c3,%esi

    • 0x4025c3(格式字符串的地址)加载到 %esi
  11. 401485: b8 00 00 00 00 mov $0x0,%eax

    • 0 加载到 %eax,准备调用 sscanf
  12. 40148a: e8 61 f7 ff ff callq 400bf0 <__isoc99_sscanf@plt>

    • 调用 __isoc99_sscanf 函数,读取六个整数。格式字符串是 "%d %d %d %d %d %d"
  13. 40148f: 83 f8 05 cmp $0x5,%eax

    • 比较 sscanf 返回的值(读取到的整数数量)是否大于 5。
  14. 401492: 7f 05 jg 401499 <read_six_numbers+0x3d>

    • 如果读取到的整数数量大于 5(即正好是 6),跳转到 401499
  15. 401494: e8 a1 ff ff ff callq 40143a <explode_bomb>

    • 否则,调用 explode_bomb 函数。
  16. 401499: 48 83 c4 18 add $0x18,%rsp

    • 恢复栈指针,释放之前分配的 24 字节栈空间。
  17. 40149d: c3 retq

    • 返回到调用者。

这个phase是考循环的,输入六个数,第一个数强制要求是1,并且每次倍增,自然而然,得出这六个数是1 2 4 8 16 32。 

三.实验三phase_3 

0000000000400f43 <phase_3>:
  400f43:    48 83 ec 18              sub    $0x18,%rsp
  400f47:    48 8d 4c 24 0c           lea    0xc(%rsp),%rcx
  400f4c:    48 8d 54 24 08           lea    0x8(%rsp),%rdx
  400f51:    be cf 25 40 00           mov    $0x4025cf,%esi
  400f56:    b8 00 00 00 00           mov    $0x0,%eax
  400f5b:    e8 90 fc ff ff           callq  400bf0 <__isoc99_sscanf@plt>
  400f60:    83 f8 01                 cmp    $0x1,%eax
  400f63:    7f 05                    jg     400f6a <phase_3+0x27>
  400f65:    e8 d0 04 00 00           callq  40143a <explode_bomb>
  400f6a:    83 7c 24 08 07           cmpl   $0x7,0x8(%rsp)
  400f6f:    77 3c                    ja     400fad <phase_3+0x6a>
  400f71:    8b 44 24 08              mov    0x8(%rsp),%eax
  400f75:    ff 24 c5 70 24 40 00     jmpq   *0x402470(,%rax,8)
  400f7c:    b8 cf 00 00 00           mov    $0xcf,%eax
  400f81:    eb 3b                    jmp    400fbe <phase_3+0x7b>
  400f83:    b8 c3 02 00 00           mov    $0x2c3,%eax
  400f88:    eb 34                    jmp    400fbe <phase_3+0x7b>
  400f8a:    b8 00 01 00 00           mov    $0x100,%eax
  400f8f:    eb 2d                    jmp    400fbe <phase_3+0x7b>
  400f91:    b8 85 01 00 00           mov    $0x185,%eax
  400f96:    eb 26                    jmp    400fbe <phase_3+0x7b>
  400f98:    b8 ce 00 00 00           mov    $0xce,%eax
  400f9d:    eb 1f                    jmp    400fbe <phase_3+0x7b>
  400f9f:    b8 aa 02 00 00           mov    $0x2aa,%eax
  400fa4:    eb 18                    jmp    400fbe <phase_3+0x7b>
  400fa6:    b8 47 01 00 00           mov    $0x147,%eax
  400fab:    eb 11                    jmp    400fbe <phase_3+0x7b>
  400fad:    e8 88 04 00 00           callq  40143a <explode_bomb>
  400fb2:    b8 00 00 00 00           mov    $0x0,%eax
  400fb7:    eb 05                    jmp    400fbe <phase_3+0x7b>
  400fb9:    b8 37 01 00 00           mov    $0x137,%eax
  400fbe:    3b 44 24 0c              cmp    0xc(%rsp),%eax
  400fc2:    74 05                    je     400fc9 <phase_3+0x86>
  400fc4:    e8 71 04 00 00           callq  40143a <explode_bomb>
  400fc9:    48 83 c4 18              add    $0x18,%rsp
  400fcd:    c3                       retq     

下面我们看一下每一行的解释:

  1. 400f43: 48 83 ec 18 sub $0x18,%rsp

    • 调整栈指针,分配 24 字节的栈空间(0x18 = 24)。
  2. 400f47: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx

    • 计算栈上偏移 0xC(12)的地址,并将其加载到 %rcx
  3. 400f4c: 48 8d 54 24 08 lea 0x8(%rsp),%rdx

    • 计算栈上偏移 0x8(8)的地址,并将其加载到 %rdx
  4. 400f51: be cf 25 40 00 mov $0x4025cf,%esi

    • 将立即数 0x4025cf(格式字符串的地址)加载到 %esi
  5. 400f56: b8 00 00 00 00 mov $0x0,%eax

    • 将 0 加载到 %eax,准备调用 sscanf
  6. 400f5b: e8 90 fc ff ff callq 400bf0 <__isoc99_sscanf@plt>

    • 调用 __isoc99_sscanf 函数,读取两个整数。格式字符串是 "%d %d"
  7. 400f60: 83 f8 01 cmp $0x1,%eax

    • 比较 sscanf 返回的值是否大于 1(即正好是 2)。
  8. 400f63: 7f 05 jg 400f6a <phase_3+0x27>

    • 如果读取到的整数数量大于 1,跳转到 400f6a
  9. 400f65: e8 d0 04 00 00 callq 40143a <explode_bomb>

    • 否则,调用 explode_bomb 函数。
  10. 400f6a: 83 7c 24 08 07 cmpl $0x7,0x8(%rsp)

    • 比较栈上偏移 0x8(第二个整数)与 7。
  11. 400f6f: 77 3c ja 400fad <phase_3+0x6a>

    • 如果第二个整数大于 7,跳转到 400fad
  12. 400f71: 8b 44 24 08 mov 0x8(%rsp),%eax

    • 将栈上偏移 0x8(第二个整数)加载到 %eax
  13. 400f75: ff 24 c5 70 24 40 00 jmpq *0x402470(,%rax,8)

    • 根据 %eax 的值,从地址 0x402470 开始按索引跳转到相应的分支。
  14. 400f7c: b8 cf 00 00 00 mov $0xcf,%eax

    • 将立即数 0xcf(207)加载到 %eax
  15. 400f81: eb 3b jmp 400fbe <phase_3+0x7b>

    • 跳转到 400fbe
  16. 400f83: b8 c3 02 00 00 mov $0x2c3,%eax

    • 将立即数 0x2c3(707)加载到 %eax
  17. 400f88: eb 34 jmp 400fbe <phase_3+0x7b>

    • 跳转到 400fbe
  18. 400f8a: b8 00 01 00 00 mov $0x100,%eax

    • 将立即数 0x100(256)加载到 %eax
  19. 400f8f: eb 2d jmp 400fbe <phase_3+0x7b>

    • 跳转到 400fbe
  20. 400f91: b8 85 01 00 00 mov $0x185,%eax

    • 将立即数 0x185(389)加载到 %eax
  21. 400f96: eb 26 jmp 400fbe <phase_3+0x7b>

    • 跳转到 400fbe
  22. 400f98: b8 ce 00 00 00 mov $0xce,%eax

    • 将立即数 0xce(206)加载到 %eax
  23. 400f9d: eb 1f jmp 400fbe <phase_3+0x7b>

    • 跳转到 400fbe
  24. 400f9f: b8 aa 02 00 00 mov $0x2aa,%eax

    • 将立即数 0x2aa(682)加载到 %eax
  25. 400fa4: eb 18 jmp 400fbe <phase_3+0x7b>

    • 跳转到 400fbe
  26. 400fa6: b8 47 01 00 00 mov $0x147,%eax

    • 将立即数 0x147(327)加载到 %eax
  27. 400fab: eb 11 jmp 400fbe <phase_3+0x7b>

    • 跳转到 400fbe
  28. 400fad: e8 88 04 00 00 callq 40143a <explode_bomb>

    • 调用 explode_bomb 函数。
  29. 400fb2: b8 00 00 00 00 mov $0x0,%eax

    • 将立即数 0 加载到 %eax
  30. 400fb7: eb 05 jmp 400fbe <phase_3+0x7b>

    • 跳转到 400fbe
  31. 400fb9: b8 37 01 00 00 mov $0x137,%eax

    • 将立即数 0x137(311)加载到 %eax
  32. 400fbe: 3b 44 24 0c cmp 0xc(%rsp),%eax

    • 将栈上偏移 0xC(第一个整数)与 %eax 进行比较。
  33. 400fc2: 74 05 je 400fc9 <phase_3+0x86>

    • 如果相等,跳转到 400fc9
  34. 400fc4: e8 71 04 00 00 callq 40143a <explode_bomb>

    • 否则,调用 explode_bomb 函数。
  35. 400fc9: 48 83 c4 18 add $0x18,%rsp

    • 恢复栈指针,释放之前分配的 24 字节栈空间。
  36. 400fcd: c3 retq

    • 返回到调用者。

根据上面的分析可以知道,这是一个Switch语句类似的跳转表的结构,输入的第一个数字决定了后面跳到哪里,也就决定了第二个数字是多少。第一个数字可以是0~7的任意一个数字,我们可以选0,对应的是0xcf (207),输入这两个数就可以不引爆炸弹。

综上,此题多解,而且只要前两个数是对的,再多输入几个数也不影响,比如可以输0 207。

 

 四.实验四phase_4

我们先看一下phase_4的代码:

000000000040100c <phase_4>: 40100c: 48 83 ec 18 sub $0x18,%rsp

这行代码将栈指针 %rsp 减去 0x18 (24),为函数调用分配 24 字节的栈空间。

401010: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx

%rsp 加上 0xc (12),然后将结果加载到 %rcx。这通常用于准备参数。

401015: 48 8d 54 24 08 lea 0x8(%rsp),%rdx

%rsp 加上 0x8 (8),然后将结果加载到 %rdx。这也通常用于准备参数。

40101a: be cf 25 40 00 mov $0x4025cf,%esi

将立即数 0x4025cf 移动到 %esi 寄存器。这是一个地址,可能是一个格式字符串。

40101f: b8 00 00 00 00 mov $0x0,%eax

将 0 移动到 %eax。这是为 sscanf 准备的调用约定的一部分。

401024: e8 c7 fb ff ff callq 400bf0 <__isoc99_sscanf@plt>

调用 __isoc99_sscanf 函数,使用之前准备的参数。这个函数从字符串中解析输入。

401029: 83 f8 02 cmp $0x2,%eax

%eax 与 2 比较,检查 sscanf 返回的值是否是 2 (表示成功解析了两个值)。

40102c: 75 07 jne 401035 <phase_4+0x29>

如果 %eax 不等于 2,跳转到 401035,即执行炸弹爆炸的函数。

40102e: 83 7c 24 08 0e cmpl $0xe,0x8(%rsp)

将栈上偏移 0x8 处的值与 0xe (14) 比较。

401033: 76 05 jbe 40103a <phase_4+0x2e>

如果栈上偏移 0x8 处的值小于或等于 0xe,则继续执行,否则跳转到炸弹爆炸函数。

401035: e8 00 04 00 00 callq 40143a <explode_bomb>

调用 explode_bomb 函数,表示条件不满足,程序失败。

40103a: ba 0e 00 00 00 mov $0xe,%edx

将 0xe (14) 移动到 %edx。这是为后续函数调用准备的参数。

40103f: be 00 00 00 00 mov $0x0,%esi

将 0 移动到 %esi,这也是为后续函数调用准备的参数。

401044: 8b 7c 24 08 mov 0x8(%rsp),%edi

将栈上偏移 0x8 处的值移动到 %edi,这也是为后续函数调用准备的参数。

401048: e8 81 ff ff ff callq 400fce <func4>

调用 func4 函数,使用之前准备的参数。func4 是一个自定义函数,具体功能不详。

40104d: 85 c0 test %eax,%eax

测试 %eax 的值,看是否为零。func4 的返回值存储在 %eax 中。

40104f: 75 07 jne 401058 <phase_4+0x4c>

如果 %eax 不为零,跳转到炸弹爆炸函数。

401051: 83 7c 24 0c 00 cmpl $0x0,0xc(%rsp)

将栈上偏移 0x0c 处的值与 0 比较。

401056: 74 05 je 40105d <phase_4+0x51>

如果栈上偏移 0x0c 处的值为 0,则继续执行;否则跳转到炸弹爆炸函数。

401058: e8 dd 03 00 00 callq 40143a <explode_bomb>

调用 explode_bomb 函数,表示条件不满足,程序失败。

40105d: 48 83 c4 18 add $0x18,%rsp

将栈指针 %rsp 加上 0x18 (24),释放之前分配的栈空间。

401061: c3 retq

返回调用者,phase_4 函数结束。

 再看一下func4的汇编及解释:

0000000000400fce <func4>: 400fce: 48 83 ec 08 sub $0x8,%rsp

这行代码将栈指针 %rsp 减去 0x8 (8),为函数调用分配 8 字节的栈空间。

400fd2: 89 d0 mov %edx,%eax

%edx 的值移动到 %eax

400fd4: 29 f0 sub %esi,%eax

%esi 的值从 %eax 中减去。

400fd6: 89 c1 mov %eax,%ecx

%eax 的值移动到 %ecx

400fd8: c1 e9 1f shr $0x1f,%ecx

%ecx 右移 31 位(0x1f 是十六进制的31)。这是为了获取符号位。

400fdb: 01 c8 add %ecx,%eax

%ecx 的值加到 %eax 上。这是为了处理有符号整数的算术右移。

400fdd: d1 f8 sar %eax

%eax 算术右移一位(即右移一位并保持符号位)。

400fdf: 8d 0c 30 lea (%rax,%rsi,1),%ecx

%rax%rsi 相加,并将结果存储到 %ecx。这相当于 %ecx = %rax + %rsi

400fe2: 39 f9 cmp %edi,%ecx

%ecx%edi 比较。

400fe4: 7e 0c jle 400ff2 <func4+0x24>

如果 %ecx 小于或等于 %edi,跳转到 400ff2。

400fe6: 8d 51 ff lea -0x1(%rcx),%edx

%rcx - 1 的值存储到 %edx

400fe9: e8 e0 ff ff ff callq 400fce <func4>

递归调用 func4,传递更新后的参数。

400fee: 01 c0 add %eax,%eax

%eax 的值加到 %eax 上(即 %eax = 2 * %eax)。

400ff0: eb 15 jmp 401007 <func4+0x39>

跳转到 401007,函数的结束部分。

400ff2: b8 00 00 00 00 mov $0x0,%eax

将 0 移动到 %eax

400ff7: 39 f9 cmp %edi,%ecx

%ecx%edi 比较。

400ff9: 7d 0c jge 401007 <func4+0x39>

如果 %ecx 大于或等于 %edi,跳转到 401007。

400ffb: 8d 71 01 lea 0x1(%rcx),%esi

%rcx + 1 的值存储到 %esi

400ffe: e8 cb ff ff ff callq 400fce <func4>

递归调用 func4,传递更新后的参数。

401003: 8d 44 00 01 lea 0x1(%rax,%rax,1),%eax

2 * %rax + 1 的值存储到 %eax

401007: 48 83 c4 08 add $0x8,%rsp

将栈指针 %rsp 加上 0x8 (8),释放之前分配的栈空间。

40100b: c3 retq

返回调用者,func4 函数结束。

其中,可以最快走出递归且让返回值为 0的一条道路可以是一直走图中最左侧的道路,即第一次调用就满足%ecx<=%edi且%ecx>=%edi也就是%ecx=%edi,追踪寄存器可以知道此时的%ecx=%edi=7(其实这是由于调用函数之前放里边的14经过算数右移1位得到的),当func4运行到结尾时,rax中的值还是0,满足条件。

此题是多解的,若不使用第一个数是7的方法,继续分析,从左向右数第二条路中,会继续递归func4函数且将返回值赋值为1,故此路不通。

其他情况下,只有走最右侧道路是合理的,即第一个传入的数(此时在%rdi里边)必须满足小于7,又因为函数递归中的  shr  $0x1f,%ecx这一条指令是取符号位,当负数的时候就和正数的时候不同,所以我们可以在0~6这七个数中直接试正确性。经试验,得知6, 3, 1, 0 等都可被接受。

故,其中一种可行的答案是7 0。

五.实验五phase_5 

我们看一下下面几行的汇编语言:

0000000000401062 <phase_5>: 401062: 53 push %rbx

将寄存器 %rbx 的当前值压入栈中,保存它的值以便稍后恢复。

401063: 48 83 ec 20 sub $0x20,%rsp

将栈指针 %rsp 减去 32(0x20)字节,分配栈空间。

401067: 48 89 fb mov %rdi,%rbx

将传递给 phase_5 函数的第一个参数(在 %rdi 中)保存到 %rbx 中。

40106a: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax 401071: 00 00

从线程本地存储中加载值(存储在 %fs 段寄存器的偏移 0x28 处)到 %rax 中。这通常用于栈保护。

401073: 48 89 44 24 18 mov %rax,0x18(%rsp)

%rax 的值保存到栈上偏移 0x18 处,保存栈保护的值。

401078: 31 c0 xor %eax,%eax

%eax 置零。

40107a: e8 9c 02 00 00 callq 40131b <string_length>

调用 string_length 函数,计算 %rdi 指向的字符串的长度。

40107f: 83 f8 06 cmp $0x6,%eax

将字符串长度与 6 进行比较。

401082: 74 4e je 4010d2 <phase_5+0x70>

如果字符串长度等于 6,则跳转到 4010d2。

401084: e8 b1 03 00 00 callq 40143a <explode_bomb>

否则,调用 explode_bomb 函数。

401089: eb 47 jmp 4010d2 <phase_5+0x70>

无条件跳转到 4010d2。

40108b: 0f b6 0c 03 movzbl (%rbx,%rax,1),%ecx

将字符串中的一个字符(无符号扩展为 32 位)加载到 %ecx 中,索引为 %rax

40108f: 88 0c 24 mov %cl,(%rsp)

%ecx 的低 8 位(即 %cl)存储到栈顶。

401092: 48 8b 14 24 mov (%rsp),%rdx

将刚才存储的值加载到 %rdx 中。

401096: 83 e2 0f and $0xf,%edx

%rdx 的值与 0xf 做与运算,只保留低 4 位。

401099: 0f b6 92 b0 24 40 00 movzbl 0x4024b0(%rdx),%edx

将偏移 0x4024b0 处 %rdx 的值(无符号扩展为 32 位)加载到 %edx 中。这是一个查表操作,使用 %rdx 的值作为索引。

4010a0: 88 54 04 10 mov %dl,0x10(%rsp,%rax,1)

%dl%edx 的低 8 位)存储到栈上偏移 0x10 加上 %rax 的位置。

4010a4: 48 83 c0 01 add $0x1,%rax

%rax 加 1,准备处理下一个字符。

4010a8: 48 83 f8 06 cmp $0x6,%rax

%rax 与 6 进行比较。

4010ac: 75 dd jne 40108b <phase_5+0x29>

如果 %rax 不等于 6,则跳转回 40108b 处理下一个字符。

4010ae: c6 44 24 16 00 movb $0x0,0x16(%rsp)

将 0 存储到栈上偏移 0x16 处,作为字符串的终止符。

4010b3: be 5e 24 40 00 mov $0x40245e,%esi

将字符串 0x40245e 的地址存储到 %esi 中。

4010b8: 48 8d 7c 24 10 lea 0x10(%rsp),%rdi

将栈上偏移 0x10 的地址加载到 %rdi 中,这是转换后的字符串的地址。

4010bd: e8 76 02 00 00 callq 401338 <strings_not_equal>

调用 strings_not_equal 函数,比较两个字符串。

4010c2: 85 c0 test %eax,%eax

测试比较结果(strings_not_equal 的返回值)。

4010c4: 74 13 je 4010d9 <phase_5+0x77>

如果字符串相等(%eax 为 0),跳转到 4010d9。

4010c6: e8 6f 03 00 00 callq 40143a <explode_bomb>

否则,调用 explode_bomb 函数。

4010cb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)

无操作指令,用于对齐。

4010d0: eb 07 jmp 4010d9 <phase_5+0x77>

无条件跳转到 4010d9。

4010d2: b8 00 00 00 00 mov $0x0,%eax

将 0 存储到 %eax 中。

4010d7: eb b2 jmp 40108b <phase_5+0x29>

无条件跳转回 40108b,重新开始处理下一个字符。

4010d9: 48 8b 44 24 18 mov 0x18(%rsp),%rax

将栈上偏移 0x18 处的值加载到 %rax 中,恢复栈保护值。

4010de: 64 48 33 04 25 28 00 xor %fs:0x28,%rax 4010e5: 00 00

%fs:0x28 处的值与 %rax 异或,检查栈保护是否被修改。

4010e7: 74 05 je 4010ee <phase_5+0x8c>

如果栈保护未被修改,跳转到 4010ee。

4010e9: e8 42 fa ff ff callq 400b30 <__stack_chk_fail@plt>

如果栈保护被修改,调用 __stack_chk_fail 函数,表示栈破坏错误。

4010ee: 48 83 c4 20 add $0x20,%rsp

将栈指针 %rsp 加 32(0x20)字节,释放栈空间。

4010f2: 5b pop %rbx

将栈顶的值弹出到 %rbx 中,恢复 %rbx 的值。

4010f3: c3 retq

返回调用者,phase_5 函数结束。

 

  1. 从 phase_4 结尾处继续,随便输入一些字符,回车,进入 phase_5 断点,layout asm 显示反汇编窗口
    0x40106a 有指令 mov %fs:0x28,%rax ,可以阅读 StackOverflow 的相关问题,这就是书中 3.10.4 对抗缓冲区溢出攻击 (Thwarting Buffer Overflow Attacks) 中的第二种方法 栈破坏检测 (Stack Corruption Detection) 的哨兵值/金丝雀值。
  2. 浏览 phase_5 函数,能判断出它的正确输入是 长度为 7 的字符串,和前面一样,行尾的换行算一个,所以字符数量共有 6 个。
  3. 由 phase_1 经验可知,在调用 strings_not_equal 时, %rsi 中存放的是正确答案字符串地址,%rdi 中存放的是用户输入字符串地址。在 0x4010b3 处有 mov $0x40245e,%esi 、lea 0x10(%rsp),%rdi ,后面就调用了 strings_not_equal ,所以我们可以推出 0x40245e 处的字符串就是正确答案字符串,0x10(%rsp) 处起 6 字节存储的是用户输入字符串。查看 0x40245e 处内容,可以知道最终字符串为 flyers

 

已知待匹配字符串是“flyers”

原料字符串是“maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?”

想要拼出flyer可以分别取原料字符串的第(十六进制表示)9 f e 5 6 7个位置的字符,

即用4bit表示这六个数是 1001;1111;1110;0101;0110;01111;

查询ASCII码表可以得到后4bit对应的六个字符(不唯一)

1001->i I y Y 9 )

1111->? / _ o O

1110->> n N .

0101->u U e E %

0110->f F v V &

0111->’ 7 g G w W

上面字符任意组合即可,以“)?>%&’”为例,输入后可见正确。

现在,我们能够大概理解这个过程了,用户输入字符只有最低四位(假设为 x)才有意义,用来在 0x4024b0 处挑选第 x 个字符,然后这些挑选出的字符组合成一个字符串,这个字符串应该是 flyers ,这样才能与答案字符串对上。
flyers 的对应数字是 9H FH EH 5H 6H 7H ,从 ASCII 码表 中选择低位满足要求的字符组合,不妨取小写字母 ionefg。

 六.实验六phase_6

  1. 从 phase_5 结尾处继续,随便输入一些字符,回车,进入 phase_6 断点,layout asm 显示反汇编窗口
    虽然这个比前面 5 个都要长上不少,但不要畏惧,这并不复杂
  2. 从 read_six_numbers 可知,我们需要输入的依旧是六个以空格隔开的数字,而且这六个数分别存储在 %rsp 、%rsp+4 、%rsp+8 、%rsp+12 、%rsp+16 、%rsp+20 位置
    0x401117 处对第一个数字做出要求:小于等于 6,不然就引爆炸弹
    0x401138 处开始遍历第二个到第六个数字,要求它们均与第一个数字不相等,不然就引爆炸弹
    0x401121 处用的是 jbe ,说明这些数字是无符号数
    0x40111b 处令输入的数字首先减去 1,如果是 0 的话,会得到 FFFF...FFFF,引爆炸弹,故每个数字均需大于 0
    跳回到 0x401114 发现,这是对每个数字都有相同的要求:小于等于 6,大于 0,且两两不相等,故 6 个数字是 1、2、3 、4、5、6 的排列组合之一
  3. 满足上面条件后,跳转到 0x401153 ,到 0x40116d ,这部分实现的是"7- 第 x 个数的值后,把该值放在第 x 个数原来的位置, x 从 0 到 6 遍历一遍",用高级语言就是 a[i] = 7 - a[i]
  4. 0x40116f 到 0x4011a9 这一部分,这部分一眼看上去非常难以理解,实际上它确实挺复杂。
    我们注意到中间出现了一个地址 0x6032d0,先看一下内容再说

可以看出,一共有六个节点,每个节点占据 16 个字节内存空间,前 8 个字节存储内容值,后 8 个字节存储地址。而且从方框中的数据能够猜测这应该是个链表(前一个节点保存着后一个节点的地址)
该部分流程图如下:

 

 接下来看 0x4011ab 到 0x 4011d 2 处指令,这是把节点链接起来,即第一个数对应的结点的指针域(后 8 字节)存储第二个数对应节点的地址,依此类推,最后让 数6对应节点的指针域指向 Null

0x4011d9 后的指令完成一件事:数 1 对应节点的内容(取 4 个字节,而不是 8 个字节)大于数 2 对应节点的内容,数 2 对应节点的内容大于数 3 对应节点的内容,以此类推。

 

根据第 6 步的结论"数 1 对应的节点的内容 > 数 2 对应节点的内容 > ... > 数 6 对应节点的内容",节点链接情况是:node3 -> node4-> node5-> node6-> node1-> node2
再根据第 4 步得出的结论"如果 某处存储的数大小是 m,那么它对应的就是第 m 个节点",所以数 1 到数 6 分别是:3、4、5、6、1、2。
又由于在第 3 步中用 7 减去输入的数,所以输入的数应该是 4 3 2 1 6 5。 

七.隐藏实验

  1. 还记得 bomb.c 速览 时最后几行注释吗? 它暗示了作者还藏了暗雷!
    但是我们通过正常手段无法直接运行到这个暗雷相关的代码,那要怎么找出呢? 如果你比较细心的话,会发现在 phase_6 代码后面有个 fun7 函数,我们记得 phase_4 阶段调用过 func4 函数,所以这似乎是在强烈暗示我们暗雷会调用 func7
  2. 这样找 fun7 实在是太麻烦了,能不能把整个可执行文件反汇编成汇编语言文件,然后用 VS Code 等编辑器阅读? 当然可以,使用命令 objdump -d bomb >> bomb.s (如果没有 objdump 工具可自行安装),在当前目录得到 bomb.s 汇编语言文件。
  3. 使用搜索功能找到 fun7

 

可以看到 func 7 下方有个 secret_phase,看来它就是那个暗雷了
搜索 secret_phase 可以发现是在 phase_defused 函数中调用的,phase_defused 在每次 phase_i 调用结束后被调用。

 

从图中可以看出,0x 4015d8 处注释 num_input_strings 给了我们很大提示,当输入字符串数量不等于 6 时,不进行其他操作,直接返回;当等于 6 时,通过一系列判断是否调用 secret_phase。即在 phase_6 调用结束后,才能执行 0x 4015e1 后的若干条指令。

把汇编代码中设计到的字符串使用 x/s 地址 查看:

 其中 0x402619 处的 %d %d %s 给了我们一个暗示,前两个是数字,与第 3、4 个密码格式相同(我是没想到,在这试了半天答案都没能让 0x 4015ff 处的 %eax 变成 3,然后就看别人帖子去了,至于怎么联想到第 3、4 个密码,我没看到特别好的解释,可能是这个神奇的 __isoc99_sscanf@plt 函数调用有我不清楚的细节吧...)
第三个是字符串,结合下面的 0x402622 处内容和后面的 strings_not_equal 调用能推出字符串为 DrEvil
分别在第 3、4 个密码后添上空格、DrEvil,经过测试,第 4 个密码为 0 0 DrEvil (看到这个 0 0 ,我突然意识到 0x603870 处内容也是 0 0,所以这也算一个强暗示?)时能正确进入 secret phase。

  1. 分析 secret_phase 部分,首先是 read_line 函数读取用户输入,然后调用 strtol@plt 函数将用户输入转换成一个长整型数返回到 %rax。然后调用 fun7(参数分别是 %edi(0x6030f0),%esi(用户输入整数)),只有当 fun7 的返回值为 2 时才能避免炸弹爆炸
  2. 分析 fun7 部分,首先查看 0x6030f0 处内容。

 可以发现,这个数据分布非常有规律性,结合我们在 phase_6 的经验,可以发现这是若干个节点,每个节点占据 32 节点,分别是 内容值(4 字节)、填充(4 字节)、地址(8 字节)、地址(8 字节)、填充(8 字节)。每个节点有两个地址,合理猜测这是一个二叉树。如果第一个地址是该节点的左子树,第二个地址是该节点的右子树,把这棵二叉树画出来,如下图

 

 

其逆向等价 C 语言

int fun7(int cmp, Node* addr){
  if(addr == 0){
    return -1;
  }
  int v = addr->value;
  if (v == cmp){
    return 0;
  }else if( v < cmp){
        return 1 + 2*fun7(cmp, addr->right);
  }else{
    return 2*func7(cmp, addr->left);
  }
}


当用户输入的值在树中时,本次 fun7 的返回值(%eax ) 是 0,然后返回到 主调函数 调用 本次 fun7 后
当用户输入的值不在树中时,叶子节点处返回一个 -1(0xffffffff),层层返回,最终返回的还是一个负数值
每次调用进入左子树返回后,%eax 的值都会翻倍;调用右子树返回后,%eax 的值都会翻倍并 +1
我们最终希望返回值是 2,所以倒推一下,我们输入的数一定在树中(否则就返回负值),然后经过这个节点是其父节点的右子节点(02+1=1),然后他的父节点是祖父节点的子节点(1*2 = 2),此时返回值恰好是 2,故祖父节点就是根节点。所以目标节点(0x24)是根节点的左子节点(0x8)的右子节点(0x16),所以最终期待用户输入值是 0x16,即 22

当前目录下新建一个文件(不妨叫做 secret.txt ),内容如下

 

和 ans.txt 的区别主要是第 4 行后添加了 DrEvil 字符串,第 7 行是 secret_phase 的拆解密码,注意第 7 行后需要回车新建一行(不然第 7 行的数读不进去)

删除或禁用相关断点(注意保留阻止炸弹爆炸的那个断点以防不测),r secret.txt。

​​​​​​​

 注意到比解开 phase_6 时多了三行输出,证明我们成功发现并解开暗雷,所有炸弹都拆解完毕!

完结撒花!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1715191.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

19 - grace数据处理 - 补充 - 地下水储量计算过程分解 - 冰后回弹(GIA)改正

19 - grace数据处理 - 补充 - 地下水储量计算过程分解 - 冰后回弹(GIA)改正 0 引言1 gia数据处理过程0 引言 由水量平衡方程可以将地下水储量的计算过程分解为3个部分,第一部分计算陆地水储量变化、第二部分计算地表水储量变化、第三部分计算冰后回弹改正、第四部分计算地下…

【已解决】使用token登录机制,token获取不到,blog_list.html界面加载不出来

Bug产生 今天使用token完成用户登录信息的存储的时候被卡了大半天。 因为登录的功能写的已经很多了&#xff0c;所以今天就没有写一点验一点&#xff0c;而是在写完获取博客列表功功能&#xff0c;验证完它的后端后&#xff0c;了解完令牌的基本使用以及Jwt的基本使用方式——…

【高校科研前沿】南科大姜丽光课题组在地球物理学领域TOP期刊Geophys. Res. Lett.发表极端气候频发下水库蓄水状态的相关研究成果

文章简介 论文名称&#xff1a;Reservoir Filling Up Problems in a Changing Climate:Insights From CryoSat‐2 Altimetry 第一作者及单位&#xff1a;汪志伟&#xff08;硕士研究生 南方科技大学环境学院&#xff09; 通讯作者及单位&#xff1a;姜丽光&#xff08;助理教…

ABB 控制柜

1,主计算机:相当于电脑的主机,用于存放系统和数据,需要24V直流电才能工作。执行用户编写的程序,控制机器人进行响应的动作。主计算机有很多接口,比如与编程PC连接的服务网口、用于连接示教器的网口、连接轴计算机板的接口、连接安全面板的接口、不同的现场总线卡接口(比…

Docker安装nginx详细教程

详细教程如下&#xff1a; 1. 拉取Nginx镜像 docker pull nginx默认拉最新的&#xff08;也可以根据自己的需求指定版本&#xff09; 2. 运行Nginx容器 docker run --name my-nginx -d -p 80:80 nginx--name my-nginx&#xff1a;容器名称&#xff0c;便于管理。-d&#xf…

降价潮背后:中国产业大模型落地的卡点到底在哪?

“技术是不会以任何商业行为或者人们的意愿所改变它的上限和下限的&#xff0c;它需要的时间是恒定的。 ” 作者|思杭 编辑|皮爷 出品|产业家 如果说中国大模型市场最核心的话题是什么&#xff1f;降价则必然是其中之一。 从目前的参赛玩家来看&#xff0c;不论是字节豆…

基于python flask的旅游数据大屏实现,有爬虫有数据库

背景 随着旅游行业的快速发展&#xff0c;数据在旅游决策和规划中的重要性日益凸显。基于 Python Flask 的旅游数据大屏实现研究旨在结合爬虫技术和数据库存储&#xff0c;为用户提供全面、实时的旅游信息展示平台。 爬虫技术作为数据采集的重要手段&#xff0c;能够从各种网…

自定义数据集上的3D目标检测:使用OpenPCDet训练CenterPointPillar模型

前言 在自动驾驶和机器人领域&#xff0c;3D目标检测是关键技术之一。它能够提供关于周围环境中物体的精确位置和尺寸信息。OpenPCDet是一个基于PyTorch的开源3D目标检测框架&#xff0c;支持多种3D检测网络。在本文中&#xff0c;我们将探讨如何使用OpenPCDet框架和CenterPoi…

redis 集群 底层原理以及实操

前言 上篇我们讲解了哨兵集群是怎么回事 也说了对应的leader选举raft算法 也说了对应的slave节点是怎么被leader提拔的 主要是比较优先级 比较同步偏移量 比较runid等等 今天我们再说说,其实哨兵也有很多缺点 虽然在master挂了之后能很快帮我们选举出新的master 但是对于单个ma…

计算机毕业设计 | SpringBoot个人博客管理系统(附源码)

1&#xff0c;绪论 1.1 背景调研 在互联网飞速发展的今天&#xff0c;互联网已经成为人们快速获取、发布和传递信息的重要渠道&#xff0c;它在人们政治、经济、生活等各个方面发挥着重要的作用。互联网上发布信息主要是通过网站来实现的&#xff0c;获取信息也是要在互联网中…

【Qt】初识

一、使用Label显示Hello World 1.ui设计 可以在Qt Designer中拖拽方式进行创建 2.代码方式 在myqwidget.cpp文件中添加下列代码 二、对象树 我们在堆上创建了QLabel类的对象。但是我们没有去delete&#xff0c;这样会产生内存泄漏吗&#xff1f; 答案是不会。label对象会在…

2024 京麟ctf -MazeCodeV1

文章目录 检查代码思路一个字节的指令注意附上S1uM4i佬们的exp https://www.ctfiot.com/184181.html 检查 代码 __int64 __fastcall check_solve(char *a1) {__int64 result; // rax__int64 v2; // rax__int64 index_step; // rax__int64 v4; // rax__int64 v5; // rax__int64…

LPDDR6带宽预计将翻倍增长:应对低功耗挑战与AI时代能源需求激增

在当前科技发展的背景下&#xff0c;低能耗问题成为了业界关注的焦点。国际能源署(IEA)近期报告显示&#xff0c;日常的数字活动对电力消耗产生显著影响——每次Google搜索平均消耗0.3瓦时&#xff08;Wh&#xff09;&#xff0c;而向OpenAI的ChatGPT提出的每一次请求则消耗2.9…

继电器的选型和英应用

如何保证信号的稳定&#xff1f; 怎么消除继电器触点的电弧&#xff1f; 危害&#xff1a; 继电器的触点在动作时容易产生电弧&#xff0c;电弧具有热效应容易导致触点烧蚀粘接&#xff0c;缩短继电器的寿命&#xff0c;并且产生电弧的过程中会对外进行电磁辐射&#xff0c;…

鸿蒙开发接口图形图像:【WebGL】

WebGL WebGL提供图形绘制的能力&#xff0c;包括对当前绘制图形的位置、颜色等进行处理。 WebGL标准图形API&#xff0c;对应OpenGL ES 2.0特性集。 说明&#xff1a; 开发前请熟悉鸿蒙开发指导文档&#xff1a; gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md…

Django 做migrations时出错,解决方案

在做migrations的时候&#xff0c;偶尔会出现出错。 在已有数据的表中新增字段时&#xff0c;会弹出下面的信息 运行这个命令时 python manage.py makemigrationsTracking file by folder pattern: migrations It is impossible to add a non-nullable field ‘example’ to …

Spring OAuth2:开发者的安全盾牌!(下)

上文我们教了大家如何像海盗一样寻找宝藏&#xff0c;一步步解锁令牌的奥秘&#xff0c;今天将把更加核心的技巧带给大家一起学习&#xff0c;共同进步&#xff01; 文章目录 6. 客户端凭证与密码模式6.1 客户端凭证模式应用适用于后端服务间通信 6.2 密码模式考量直接传递用户…

小短片创作-理论知识(三)

1、抗锯齿 1.相机移动的时候出现锯齿 2.当1个像素在三角形边缘的时候&#xff0c;可能取值为白色&#xff0c;也可能取值为黑色&#xff0c;表现出来就是闪烁&#xff0c;或锯齿 3.如果我们通过超采样将1个像素变成4个像素进行计算&#xff0c;得到的结果就会更准确&#x…

文件IO(二)

文件IO&#xff08;二&#xff09; 标准IO缓冲类型全缓冲行缓冲不缓冲 打开文件fopen 操作文件按字符读写(fgetc fputc)按行读写&#xff08;fgets fputs&#xff09;按块&#xff08;对象&#xff09;读写&#xff08;fread fwrite&#xff09;按格式化读写&#xff08;fscanf…

【考研数学】李艳芳900比李林880难吗?值得做吗?

差不多&#xff0c;只能说基础没搞好刷这两个都很费劲 李艳芳900题把每个章节题目划分为ABC三个难度级别&#xff0c;题目选取的难度较大也比较新颖&#xff0c;计算量也非常接近考研趋势&#xff0c;原创性很高&#xff0c;比较适合过完一轮的同学继续做补充和强化 880算是比…