CSAPP 第3章 bomb lab 含gdb的介绍

csapp

资料

CSAPP导读,视频版

CSAPP重点解读 (gitbook.io),文字版

(34 封私信 / 81 条消息) 如何阅读《深入理解计算机系统》这本书? - 知乎 (zhihu.com)

lab讲解

第3章 程序的机器级表示

lab_2 bomb_lab

(22条消息) 深入理解计算机系统bomb实验_ECNU__YZ的博客-CSDN博客_bomb实验解析

GDB使用详解 - 知乎 (zhihu.com)

gdb的使用

做lab反汇编时,一些小技巧

  1. gdb 中输入一个命令后,再直接按回车,会重复上面的命令
  2. 在查看汇编代码的时候,可以通过设置断点,一行一行的追究逻辑
  3. 可以同时开始两个debug,并且输入不同的参数,从而确定,不同寄存器存的值
  4. 对着汇编,写c,遇到不通的地方,debug 汇编来进行确定
  5. 遇到不太明白的内存地址,可以把他周围的值都打印出来,看一下
disas 指令 用于反汇编

全名:disassemble

disas [Function]
指定要反汇编的函数。如果指定,反汇编命令将产生整个函数的反汇编输出。
(gdb) disas main


disas [Address]
指定要反汇编的地址。请注意,当仅指定一个地址时,此命令将反汇编包含给定地址的整个函数,包括其上方的指令。
(gdb) disas 0x00000000004008fd

disas [Start],[End]
指定要反汇编的起始地址和结束地址。如果使用这种形式,该命令将不会反汇编整个函数,而只会反汇编起始地址和结束地址之间的指令。例如:
(gdb) disas 0x000000000040068a,0x00000000004006ac

disas [Function],+[Length]
disas [Address],+[Length]
指定从给定地址或函数开始反汇编的字节数,例如:
(gdb) disas main, +10

disas /m [...]
指定此选项后,反汇编命令将显示与反汇编指令相对应的源代码行。
打印函数中变量的值

在gdb命令行中 寄存器前面 % 要打成 $, 如:%rax => $rax

$p [address(指令在内存中的地址)]- 打印变量的值	
不能按照内存地址的那种(如: 0x8(%rax,%rbx,4))来取值
p指令还可以对指令内存中的值,打印需要的形式
如:
$p *(int *)0x10000 (这个可以打印指针的值,如果是直接输入地址,只会打印地址的值)


$x/s [address(变量在内存中的地址)]- 打印ASCII字符串

gdb中使用“x”命令来打印内存的值,格式为“x/nfu addr”。含义为以f格式打印从addr开始的n个长度单元为u的内存值。参数具体含义如下:

a)n:输出单元的个数。

b)f:是输出格式。比如x是以16进制形式输出,o是以8进制形式输出,等等。

c)u:标明一个单元的长度。b是一个byte,h是两个byte(halfword),w是四个byte(word),g是八个byte(giant word)

设置断点调试
b [程序在源代码(c文件)文件中的行号 or *[address]]
如:b 95(行号) or b *0x4f990(address)
程序就会在指定的地方停下来

info break
可以打印所有断点的信息

delete 
可以删除指定的断点(输入断电编号),也可以直接删除所有断点
clear [程序在源代码(c文件)文件中的行号]
可以删除指定行的断点,但该指令不能删除所有断点
disable [num...]
禁用断点,num... 表示可以有多个参数,每个参数都为要禁用断点的编号。如果不给参数就会禁用所有断点
enable 
参数同上,可以启用断点

next 单步到程序源代码的下一行,不进入函数。
nexti 单步一条机器指令,不进入函数。
step 单步到下一个不同的源代码行(包括进入函数)。
stepi 单步一条机器指令。

1.如果想要立即中断当前函数运行并返回,用return,当前函数的剩余语句将不被执行。(return可带参数,可以此随意设定当前函数的返回值)
2.如果想让程序执行到当前函数返回之后停止,用finish,当前函数的剩余语句将
会正常运行。
同时使用 finish 的话,也会打印函数的一些相关信息,例如:可以获取scanf函数的具体!!!
分割窗口

GDB layout 命令_runnerred的博客-CSDN博客

layout:用于分割窗口,可以一边查看代码,一边测试。主要有以下几种用法:
layout src:显示源代码窗口
layout asm:显示汇编窗口
layout regs:显示源代码/汇编和寄存器窗口
layout split:显示源代码和汇编窗口
layout next:显示下一个layout
layout prev:显示上一个layout
Ctrl + L:刷新窗口
Ctrl + x,再按1:单窗口模式,显示一个窗口
Ctrl + x,再按2:双窗口模式,显示两个窗口
Ctrl + x,再按a:回到传统模式,即退出layout,回到执行layout之前的调试窗口。
实验内容

在查看汇编代码的时候,可以通过设置断点,一行一行的追究逻辑

phase_2
   0x0000000000400efc <+0>:	push   %rbp
   0x0000000000400efd <+1>:	push   %rbx
   0x0000000000400efe <+2>:	sub    $0x28,%rsp  # 产生一块40字节大小的临时空间
   0x0000000000400f02 <+6>:	mov    %rsp,%rsi  # 将栈指针赋值给参数寄存器
   0x0000000000400f05 <+9>:	callq  0x40145c <read_six_numbers>
   0x0000000000400f0a <+14>:	cmpl   $0x1,(%rsp)  # 比较1和栈顶元素的大小
   0x0000000000400f0e <+18>:	je     0x400f30 <phase_2+52>  
   0x0000000000400f10 <+20>:	callq  0x40143a <explode_bomb>  # 若不相等则炸
   0x0000000000400f15 <+25>:	jmp    0x400f30 <phase_2+52>
   0x0000000000400f17 <+27>:	mov    -0x4(%rbx),%eax  # 将栈中的上一个元素值赋值
   0x0000000000400f1a <+30>:	add    %eax,%eax  # 栈中的上一个元素值*2后保存
   0x0000000000400f1c <+32>:	cmp    %eax,(%rbx)  # 将上一个元素值的2倍与%rbx对应的值(现元素值)进行比较
   0x0000000000400f1e <+34>:	je     0x400f25 <phase_2+41>
   0x0000000000400f20 <+36>:	callq  0x40143a <explode_bomb>
   0x0000000000400f25 <+41>:	add    $0x4,%rbx   # 将%rbx的+=4
   0x0000000000400f29 <+45>:	cmp    %rbp,%rbx  # 比较是否等于尾指针
   0x0000000000400f2c <+48>:	jne    0x400f17 <phase_2+27>
   0x0000000000400f2e <+50>:	jmp    0x400f3c <phase_2+64>
   0x0000000000400f30 <+52>:	lea    0x4(%rsp),%rbx  # 将栈指针加4的赋值
   0x0000000000400f35 <+57>:	lea    0x18(%rsp),%rbp  # 将栈的尾指针赋值
   0x0000000000400f3a <+62>:	jmp    0x400f17 <phase_2+27>
   0x0000000000400f3c <+64>:	add    $0x28,%rsp
   0x0000000000400f40 <+68>:	pop    %rbx
   0x0000000000400f41 <+69>:	pop    %rbp
   0x0000000000400f42 <+70>:	retq 

首先函数将栈指针传入参数寄存器,考虑到紧挨的read_six_number函数。猜测栈指针作为参数用于保存数字,而另一参数%rdi(上文提到)给出输入字符串地址。因此,函数read_six_number函数用于将输入的字符串转换为6个数字。 得知,本轮需要输入6个数字作为密钥。

接下来从<+14>语句中得知,第一个输入的数字是1。


<+41>(偏移量++)
<+57>(尾指针的赋值)
<+45> (偏移量等于尾值)
判断出,这函数当中存在一个循环。循环中%rbx依次保存栈中的所有元素的地址。而又由<+27>-<+32>语句中可知,栈中元素满足这样的排列:栈中每一元素是它上一元素的两倍 ,即需输入一个首项为1,公比为2,项数为6的等比数列。

phase_3

0x8(%rsp)是第一个数字,要小于7

0x0000000000400f43 <+0>:	sub    $0x18,%rsp
   0x0000000000400f47 <+4>:	lea    0xc(%rsp),%rcx
   0x0000000000400f4c <+9>:	lea    0x8(%rsp),%rdx
   0x0000000000400f51 <+14>:	mov    $0x4025cf,%esi
   0x0000000000400f56 <+19>:	mov    $0x0,%eax
   0x0000000000400f5b <+24>:	callq  0x400bf0 <__isoc99_sscanf@plt> // 等同于scanf("%d %d", &xx, &xx);
   0x0000000000400f60 <+29>:	cmp    $0x1,%eax // 至少输入两个数
   0x0000000000400f63 <+32>:	jg     0x400f6a <phase_3+39>
   0x0000000000400f65 <+34>:	callq  0x40143a <explode_bomb>
   0x0000000000400f6a <+39>:	cmpl   $0x7,0x8(%rsp) // 0x8(%rsp)是输入的第一个数字,要小于7
   0x0000000000400f6f <+44>:	ja     0x400fad <phase_3+106>
   0x0000000000400f71 <+46>:	mov    0x8(%rsp),%eax 
   0x0000000000400f75 <+50>:	jmpq   *0x402470(,%rax,8) 
   // 根据第一个数据来进行跳转赋值,从而判断第二个数字的值,利用了跳转表(0x402470是标号数组的首地址),从<+57> - <+118> 都是根据第一个数据的值来进行跳转
   0x0000000000400f7c <+57>:	mov    $0xcf,%eax
   0x0000000000400f81 <+62>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f83 <+64>:	mov    $0x2c3,%eax
   0x0000000000400f88 <+69>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f8a <+71>:	mov    $0x100,%eax
   0x0000000000400f8f <+76>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f91 <+78>:	mov    $0x185,%eax
   0x0000000000400f96 <+83>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f98 <+85>:	mov    $0xce,%eax
   0x0000000000400f9d <+90>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f9f <+92>:	mov    $0x2aa,%eax
   0x0000000000400fa4 <+97>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400fa6 <+99>:	mov    $0x147,%eax
   0x0000000000400fab <+104>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400fad <+106>:	callq  0x40143a <explode_bomb>
   0x0000000000400fb2 <+111>:	mov    $0x0,%eax
   0x0000000000400fb7 <+116>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400fb9 <+118>:	mov    $0x137,%eax
   
   0x0000000000400fbe <+123>:	cmp    0xc(%rsp),%eax // 0xc(%rsp)是输入的第二个数字,判断是否和跳转表得到的值相等
   0x0000000000400fc2 <+127>:	je     0x400fc9 <phase_3+134>
   0x0000000000400fc4 <+129>:	callq  0x40143a <explode_bomb>
   0x0000000000400fc9 <+134>:	add    $0x18,%rsp
   0x0000000000400fcd <+138>:	retq   

正确答案有:(1, 311),(5, 206) …

程序的整体逻辑,输入两个数字,然后判断第一个数是否小于7,如果小于7,再根据第一个数 x 进入跳转表,从而判断第二个数的值

phase_4

phase_4函数

   0x000000000040100c <+0>:	sub    $0x18,%rsp
   0x0000000000401010 <+4>:	lea    0xc(%rsp),%rcx
   0x0000000000401015 <+9>:	lea    0x8(%rsp),%rdx
   0x000000000040101a <+14>:	mov    $0x4025cf,%esi // 获取scanf的参数
   0x000000000040101f <+19>:	mov    $0x0,%eax 
   0x0000000000401024 <+24>:	callq  0x400bf0 <__isoc99_sscanf@plt> // scanf("%d %d", &xx, &xx);
   0x0000000000401029 <+29>:	cmp    $0x2,%eax
   0x000000000040102c <+32>:	jne    0x401035 <phase_4+41>
   0x000000000040102e <+34>:	cmpl   $0xe,0x8(%rsp) // 第一个参数要小于14
   0x0000000000401033 <+39>:	jbe    0x40103a <phase_4+46>
   0x0000000000401035 <+41>:	callq  0x40143a <explode_bomb>
   
   // 以下为对于第一个参数的处理
   0x000000000040103a <+46>:	mov    $0xe,%edx
   0x000000000040103f <+51>:	mov    $0x0,%esi
   0x0000000000401044 <+56>:	mov    0x8(%rsp),%edi
   0x0000000000401048 <+60>:	callq  0x400fce <func4>
   0x000000000040104d <+65>:	test   %eax,%eax // func4的返回值要为 0
   0x000000000040104f <+67>:	jne    0x401058 <phase_4+76>
   
   0x0000000000401051 <+69>:	cmpl   $0x0,0xc(%rsp) // 第二个参数要等于 0 
   0x0000000000401056 <+74>:	je     0x40105d <phase_4+81>
   0x0000000000401058 <+76>:	callq  0x40143a <explode_bomb>
   0x000000000040105d <+81>:	add    $0x18,%rsp
   0x0000000000401061 <+85>:	retq   

func4函数

   // 初始传入参数%edx=14,%esi=0
   0x0000000000400fce <+0>:	sub    $0x8,%rsp
   0x0000000000400fd2 <+4>:	mov    %edx,%eax
   0x0000000000400fd4 <+6>:	sub    %esi,%eax
   0x0000000000400fd6 <+8>:	mov    %eax,%ecx 
   0x0000000000400fd8 <+10>:	shr    $0x1f,%ecx // 逻辑右移31位,得到%ecx符号位
   0x0000000000400fdb <+13>:	add    %ecx,%eax
   0x0000000000400fdd <+15>:	sar    %eax
   0x0000000000400fdf <+17>:	lea    (%rax,%rsi,1),%ecx
   0x0000000000400fe2 <+20>:	cmp    %edi,%ecx
   0x0000000000400fe4 <+22>:	jle    0x400ff2 <func4+36>
   0x0000000000400fe6 <+24>:	lea    -0x1(%rcx),%edx
   0x0000000000400fe9 <+27>:	callq  0x400fce <func4>
   0x0000000000400fee <+32>:	add    %eax,%eax // return 值
   0x0000000000400ff0 <+34>:	jmp    0x401007 <func4+57>
   0x0000000000400ff2 <+36>:	mov    $0x0,%eax
   0x0000000000400ff7 <+41>:	cmp    %edi,%ecx
   0x0000000000400ff9 <+43>:	jge    0x401007 <func4+57>
   0x0000000000400ffb <+45>:	lea    0x1(%rcx),%esi
   0x0000000000400ffe <+48>:	callq  0x400fce <func4>
   0x0000000000401003 <+53>:	lea    0x1(%rax,%rax,1),%eax // return 值
   0x0000000000401007 <+57>:	add    $0x8,%rsp
   0x000000000040100b <+61>:	retq

转为对应的c代码

// 初始传入参数 edx = 14, esi = 0, edi = (我们自己输入的第一个数字)
int func4(int edx, int esi, int edi) {
	int eax = edx - esi;
    int ecx = eax;
    ecx = (ecx >> 31) & 1;
    eax = (eax + ecx) / 2;
    ecx = eax + esi;	
    if (edi <= ecx) {
		if (edi >= ecx) return 0;
        else {
           return 2 * func4(edx, ecx + 1, edi) + 1;
        }
    }
    else {
       return 2 * func4(ecx - 1, esi, edi);
    }
    return eax;
}

根据func4函数不难发现,当我们输入的第一个参数为 7 时能返回 0;

根据phase_4代码的分析,所以我们正确答案可以为:(7, 0)

phase_5
 0x0000000000401062 <+0>:	push   %rbx
   0x0000000000401063 <+1>:	sub    $0x20,%rsp
   // 以上都是在保存原来的寄存器变量
   0x0000000000401067 <+5>:	mov    %rdi,%rbx // 传入第一个参数 rdi
   0x000000000040106a <+8>:	mov    %fs:0x28,%rax // fs为段寄存器
   0x0000000000401073 <+17>:	mov    %rax,0x18(%rsp) 
   0x0000000000401078 <+22>:	xor    %eax,%eax
   0x000000000040107a <+24>:	callq  0x40131b <string_length>
   0x000000000040107f <+29>:	cmp    $0x6,%eax // 读入的字符串长度要为6
   0x0000000000401082 <+32>:	je     0x4010d2 <phase_5+112>
   0x0000000000401084 <+34>:	callq  0x40143a <explode_bomb>
   0x0000000000401089 <+39>:	jmp    0x4010d2 <phase_5+112>
   
   /*
   这里是解密的关键部分, 转换为 c 语言形式如下:
   char user[6] // (这个是我们输入的字符串)
   char ss[] = ”maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?“;
   char res[6]; int idx = 0;
   for (int i = 0; i < 6; i++) {
	  int si = user[i] & 0xf;
	  res[idx++] = ss[si];
   }
   
   // res == "flyers";
   if (strcmp(res, "flyers") == 0) return; 
   else explode_bomb();
   */
   0x000000000040108b <+41>:	movzbl (%rbx,%rax,1),%ecx 
   0x000000000040108f <+45>:	mov    %cl,(%rsp) 
   0x0000000000401092 <+48>:	mov    (%rsp),%rdx
   0x0000000000401096 <+52>:	and    $0xf,%edx
   0x0000000000401099 <+55>:	movzbl 0x4024b0(%rdx),%edx 
   0x00000000004010a0 <+62>:	mov    %dl,0x10(%rsp,%rax,1)
   0x00000000004010a4 <+66>:	add    $0x1,%rax
   0x00000000004010a8 <+70>:	cmp    $0x6,%rax
   0x00000000004010ac <+74>:	jne    0x40108b <phase_5+41>
   
   
   0x00000000004010ae <+76>:	movb   $0x0,0x16(%rsp)
   0x00000000004010b3 <+81>:	mov    $0x40245e,%esi // [0x40245e] "flyers"
   0x00000000004010b8 <+86>:	lea    0x10(%rsp),%rdi
   0x00000000004010bd <+91>:	callq  0x401338 <strings_not_equal>
   0x00000000004010c2 <+96>:	test   %eax,%eax
   0x00000000004010c4 <+98>:	je     0x4010d9 <phase_5+119>
   
   0x00000000004010c6 <+100>:	callq  0x40143a <explode_bomb>
   0x00000000004010cb <+105>:	nopl   0x0(%rax,%rax,1)
   0x00000000004010d0 <+110>:	jmp    0x4010d9 <phase_5+119>
   0x00000000004010d2 <+112>:	mov    $0x0,%eax
   0x00000000004010d7 <+117>:	jmp    0x40108b <phase_5+41>
   0x00000000004010d9 <+119>:	mov    0x18(%rsp),%rax
   0x00000000004010de <+124>:	xor    %fs:0x28,%rax
   0x00000000004010e7 <+133>:	je     0x4010ee <phase_5+140>
   0x00000000004010e9 <+135>:	callq  0x400b30 <__stack_chk_fail@plt>
   0x00000000004010ee <+140>:	add    $0x20,%rsp
   0x00000000004010f2 <+144>:	pop    %rbx
   0x00000000004010f3 <+145>:	retq   

请添加图片描述
正确答案:ionefg

phase_6
   0x00000000004010f4 <+0>:	push   %r14
   0x00000000004010f6 <+2>:	push   %r13
   0x00000000004010f8 <+4>:	push   %r12
   0x00000000004010fa <+6>:	push   %rbp
   0x00000000004010fb <+7>:	push   %rbx
   0x00000000004010fc <+8>:	sub    $0x50,%rsp
   0x0000000000401100 <+12>:	mov    %rsp,%r13
   0x0000000000401103 <+15>:	mov    %rsp,%rsi
   0x0000000000401106 <+18>:	callq  0x40145c <read_six_numbers> 
   // scanf("%d .. %d"), 输入六个int类型的数
   0x000000000040110b <+23>:	mov    %rsp,%r14
   
   /*
   <+26>~<+93>
    // 要求每个数不同,并且 <= 6;
    int user[6];
    for (int r13 = 0, r12d = 0;; r13++) {
        if (user[r13] <= 6) {
            r12d++;
            if (r12d == 6) break;
            for (int ebx = r12d; ebx <= 5; ebx++) {
                int eax = user[ebx];
                if (eax == user[r13]) explode_bomb();
            }
        }
        else explode_bomb();
    }
   */
   0x000000000040110e <+26>:	mov    $0x0,%r12d
   0x0000000000401114 <+32>:	mov    %r13,%rbp
   0x0000000000401117 <+35>:	mov    0x0(%r13),%eax
   0x000000000040111b <+39>:	sub    $0x1,%eax
   0x000000000040111e <+42>:	cmp    $0x5,%eax
   0x0000000000401121 <+45>:	jbe    0x401128 <phase_6+52>
   0x0000000000401123 <+47>:	callq  0x40143a <explode_bomb>
   0x0000000000401128 <+52>:	add    $0x1,%r12d
   0x000000000040112c <+56>:	cmp    $0x6,%r12d // < 6
   0x0000000000401130 <+60>:	je     0x401153 <phase_6+95>
   0x0000000000401132 <+62>:	mov    %r12d,%ebx // 第二重for循环
   0x0000000000401135 <+65>:	movslq %ebx,%rax 
   0x0000000000401138 <+68>:	mov    (%rsp,%rax,4),%eax
   0x000000000040113b <+71>:	cmp    %eax,0x0(%rbp)
   0x000000000040113e <+74>:	jne    0x401145 <phase_6+81>
   0x0000000000401140 <+76>:	callq  0x40143a <explode_bomb>
   0x0000000000401145 <+81>:	add    $0x1,%ebx
   0x0000000000401148 <+84>:	cmp    $0x5,%ebx
   0x000000000040114b <+87>:	jle    0x401135 <phase_6+65>
   0x000000000040114d <+89>:	add    $0x4,%r13
   0x0000000000401151 <+93>:	jmp    0x401114 <phase_6+32>
   
   /*
   <+95>~<121>
    for (int i = 0; i < 6; i++) {
		user[i] = 7 - user[i];
	}
   */
   0x0000000000401153 <+95>:	lea    0x18(%rsp),%rsi
   0x0000000000401158 <+100>:	mov    %r14,%rax // 从第一个数据开始
   0x000000000040115b <+103>:	mov    $0x7,%ecx 
   0x0000000000401160 <+108>:	mov    %ecx,%edx
   0x0000000000401162 <+110>:	sub    (%rax),%edx
   0x0000000000401164 <+112>:	mov    %edx,(%rax)
   0x0000000000401166 <+114>:	add    $0x4,%rax
   0x000000000040116a <+118>:	cmp    %rsi,%rax
   0x000000000040116d <+121>:	jne    0x401160 <phase_6+108>
   
   /*
    // 这一段是对原有链表按照user[]作为索引进行重新排序
    struct node {
        int val;
        int number;
        node* nxt;
    };
    
    node* head;
    // link_init_val: {{0x14c, 1}->{0xa8, 2}->{0x39c, 3}->{0x2b3, 4}->{0x1dd, 5}->{0x1bb, 6}};
    
    node* rsp20[6]; int rsp20_idx = 0;
    
    for (int rsi = 0; rsi < 6; rsi++) {
        if(user[rsi] <= 1) {
			rsp20[rsp20_idx++] = head;
        }
        else {
            node* rdx = head;
            int eax = 1;
            while (eax < user[rsi]) { 
                rdx = rdx->nxt;
                eax++;
            }
            rsp20[rsp20_idx++] = rdx;
        } // 注意这里的rdx是一个指针
    }
   */
   0x000000000040116f <+123>:	mov    $0x0,%esi
   0x0000000000401174 <+128>:	jmp    0x401197 <phase_6+163>
   0x0000000000401176 <+130>:	mov    0x8(%rdx),%rdx // rdx = rdx->nxt;
   0x000000000040117a <+134>:	add    $0x1,%eax
   0x000000000040117d <+137>:	cmp    %ecx,%eax
   0x000000000040117f <+139>:	jne    0x401176 <phase_6+130>
   0x0000000000401181 <+141>:	jmp    0x401188 <phase_6+148>
   0x0000000000401183 <+143>:	mov    $0x6032d0,%edx // 把0x6032d0这个常数放入%rdx
   0x0000000000401188 <+148>:	mov    %rdx,0x20(%rsp,%rsi,2) 
   0x000000000040118d <+153>:	add    $0x4,%rsi
   0x0000000000401191 <+157>:	cmp    $0x18,%rsi
   0x0000000000401195 <+161>:	je     0x4011ab <phase_6+183>
   0x0000000000401197 <+163>:	mov    (%rsp,%rsi,1),%ecx
   0x000000000040119a <+166>:	cmp    $0x1,%ecx
   0x000000000040119d <+169>:	jle    0x401183 <phase_6+143>
   0x000000000040119f <+171>:	mov    $0x1,%eax
   0x00000000004011a4 <+176>:	mov    $0x6032d0,%edx
   0x00000000004011a9 <+181>:	jmp    0x401176 <phase_6+130>
   
   /*
    for (int i = 0; i < 5; i++) {
        rsp20[i]->nxt = rsp20[i + 1];
    }
    rsp20[6 - 1]->nxt = 0;
   */
   0x00000000004011ab <+183>:	mov    0x20(%rsp),%rbx // rsp20[0]
   0x00000000004011b0 <+188>:	lea    0x28(%rsp),%rax // rsp20[1]
   0x00000000004011b5 <+193>:	lea    0x50(%rsp),%rsi // 尾指针
   0x00000000004011ba <+198>:	mov    %rbx,%rcx
   0x00000000004011bd <+201>:	mov    (%rax),%rdx
   0x00000000004011c0 <+204>:	mov    %rdx,0x8(%rcx)
   0x00000000004011c4 <+208>:	add    $0x8,%rax
   0x00000000004011c8 <+212>:	cmp    %rsi,%rax
   0x00000000004011cb <+215>:	je     0x4011d2 <phase_6+222>
   0x00000000004011cd <+217>:	mov    %rdx,%rcx
   0x00000000004011d0 <+220>:	jmp    0x4011bd <phase_6+201>
   0x00000000004011d2 <+222>:	movq   $0x0,0x8(%rdx)
   
   /*
   	for (int i = 0; i < 6; i++) {
   		if(rsp20[i]->val > rsp[i + 1]->val) continue;
   		else explode_bomb();
   	}
   */
   0x00000000004011da <+230>:	mov    $0x5,%ebp
   0x00000000004011df <+235>:	mov    0x8(%rbx),%rax
   0x00000000004011e3 <+239>:	mov    (%rax),%eax
   0x00000000004011e5 <+241>:	cmp    %eax,(%rbx)
   0x00000000004011e7 <+243>:	jge    0x4011ee <phase_6+250>
   0x00000000004011e9 <+245>:	callq  0x40143a <explode_bomb>
   0x00000000004011ee <+250>:	mov    0x8(%rbx),%rbx
   0x00000000004011f2 <+254>:	sub    $0x1,%ebp
   0x00000000004011f5 <+257>:	jne    0x4011df <phase_6+235>


   0x00000000004011f7 <+259>:	add    $0x50,%rsp
   0x00000000004011fb <+263>:	pop    %rbx
   0x00000000004011fc <+264>:	pop    %rbp
   0x00000000004011fd <+265>:	pop    %r12
   0x00000000004011ff <+267>:	pop    %r13
   0x0000000000401201 <+269>:	pop    %r14
   0x0000000000401203 <+271>:	retq   

正确答案:4 3 2 1 6 5

secret_bomb

或许你会觉得到这就结束了,但在bomb.c末尾还有这样一句话

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

进入secret_bomb

phase_defused函数

   0x00000000004015c4 <+0>:		sub    $0x78,%rsp
   0x00000000004015c8 <+4>:		mov    %fs:0x28,%rax
   0x00000000004015d1 <+13>:	mov    %rax,0x68(%rsp)
   0x00000000004015d6 <+18>:	xor    %eax,%eax
   0x00000000004015d8 <+20>:	cmpl   $0x6,0x202181(%rip)        # 0x603760 <num_input_strings>
   0x00000000004015df <+27>:	jne    0x40163f <phase_defused+123>
   0x00000000004015e1 <+29>:	lea    0x10(%rsp),%r8
   0x00000000004015e6 <+34>:	lea    0xc(%rsp),%rcx
   0x00000000004015eb <+39>:	lea    0x8(%rsp),%rdx
   0x00000000004015f0 <+44>:	mov    $0x402619,%esi // [0x402619]: "%d %d %s"
   0x00000000004015f5 <+49>:	mov    $0x603870,%edi // [0x603870]: "7 0"  不难发现这个是phase_4的答案,所以还要在phase_4输入后面加一个字符串
   0x00000000004015fa <+54>:	callq  0x400bf0 <__isoc99_sscanf@plt> 
   // scanf("%d %d %s");
   0x00000000004015ff <+59>:	cmp    $0x3,%eax
   0x0000000000401602 <+62>:	jne    0x401635 <phase_defused+113>
   0x0000000000401604 <+64>:	mov    $0x402622,%esi // [0x402622]: "DrEvil" 这个就是进入secret_phase的key
   0x0000000000401609 <+69>:	lea    0x10(%rsp),%rdi 
   0x000000000040160e <+74>:	callq  0x401338 <strings_not_equal>
   0x0000000000401613 <+79>:	test   %eax,%eax
   0x0000000000401615 <+81>:	jne    0x401635 <phase_defused+113>
   0x0000000000401617 <+83>:	mov    $0x4024f8,%edi
   0x000000000040161c <+88>:	callq  0x400b10 <puts@plt>
   0x0000000000401621 <+93>:	mov    $0x402520,%edi
   0x0000000000401626 <+98>:	callq  0x400b10 <puts@plt>
   0x000000000040162b <+103>:	mov    $0x0,%eax
   0x0000000000401630 <+108>:	callq  0x401242 <secret_phase>
   0x0000000000401635 <+113>:	mov    $0x402558,%edi
   0x000000000040163a <+118>:	callq  0x400b10 <puts@plt>
   0x000000000040163f <+123>:	mov    0x68(%rsp),%rax
   0x0000000000401644 <+128>:	xor    %fs:0x28,%rax
   0x000000000040164d <+137>:	je     0x401654 <phase_defused+144>
   0x000000000040164f <+139>:	callq  0x400b30 <__stack_chk_fail@plt>
   0x0000000000401654 <+144>:	add    $0x78,%rsp
   0x0000000000401658 <+148>:	retq   

进入办法:在phase_4的数据后面(7,0) + “DrEvil”

secret_bomb函数

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

fun7函数

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

secret_phase 转换为 c 的完整代码

struct node {
    long long val,
    node* lson, rson;
};
node* root;
long long user; // (这个是我们输入的值)
int fun7(node* rdi) {
    if (!rdi) return -1;
    if (rdi->val <= user) {
        if (rdi->val == user) return 0;
        return 2 * fun7(rdi->rson) + 1;
    }
    else {
        return 2 * fun7(rdi->lson);
    }
}

void secret_phase() {
    char s[]; read_line(s);
    user = strtol(s);
    if (user - 1 <= 0x3e8) {
        int res = fun7(root);
        if (res == 2) return;
        else explode_bomb();
    }
    else explode_bomb();
}

二叉树节点情况 ($0x6030f0 为根节点地址)

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

0x603110 <n21>:	0x0000000000000008	0x0000000000603190
0x603120 <n21+16>:	0x0000000000603150	0x0000000000000000
	
0x603130 <n22>:	0x0000000000000032	0x0000000000603170
0x603140 <n22+16>:	0x00000000006031b0	0x0000000000000000

0x603150 <n32>:	0x0000000000000016	0x0000000000603270
0x603160 <n32+16>:	0x0000000000603230	0x0000000000000000

0x603170 <n33>:	0x000000000000002d	0x00000000006031d0
0x603180 <n33+16>:	0x0000000000603290	0x0000000000000000

0x603190 <n31>:	0x0000000000000006	0x00000000006031f0
0x6031a0 <n31+16>:	0x0000000000603250	0x0000000000000000

0x6031b0 <n34>:	0x000000000000006b	0x0000000000603210
0x6031c0 <n34+16>:	0x00000000006032b0	0x0000000000000000
0x6031d0 <n45>:	0x0000000000000028	0x0000000000000000
0x6031e0 <n45+16>:	0x0000000000000000	0x0000000000000000
0x6031f0 <n41>:	0x0000000000000001	0x0000000000000000
0x603200 <n41+16>:	0x0000000000000000	0x0000000000000000
0x603210 <n47>:	0x0000000000000063	0x0000000000000000
0x603220 <n47+16>:	0x0000000000000000	0x0000000000000000
0x603230 <n44>:	0x0000000000000023	0x0000000000000000
0x603240 <n44+16>:	0x0000000000000000	0x0000000000000000
0x603250 <n42>:	0x0000000000000007	0x0000000000000000
0x603260 <n42+16>:	0x0000000000000000	0x0000000000000000

0x603270 <n43>:	0x0000000000000014	0x0000000000000000
0x603280 <n43+16>:	0x0000000000000000	0x0000000000000000

0x603290 <n46>:	0x000000000000002f	0x0000000000000000
0x6032a0 <n46+16>:	0x0000000000000000	0x0000000000000000
0x6032b0 <n48>:	0x00000000000003e9	0x0000000000000000
0x6032c0 <n48+16>:	0x0000000000000000	0x0000000000000000

二叉树部分节点图示

0x24
0x8
0x32
0x6
0x16

我们只需要从根节点开始按照 左 -> 右 的方式走到 0x16 即可

正确答案: 22…

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值