CSAPP:BombLab 详细解析

Bomb Lab 来自《深入理解计算机系统》(CSAPP)一书的第三章的配套实验,该实验的目的是通过反汇编可执行程序,来反推出程序执行内容,进而能够正确破解”密码“,解除“炸弹”。

本实验共有6个 phase,对于每个 phase,你需要输入一段字符串,然后让代码中 explode_bomb函数不执行,这样就不会 boom !

准备工作

在拆炸弹之前我们先复习一下第三章的知识

1.关于跳转指令

指令 解释
jz 如果ZF=1,跳转至指定位置
jnz 如果ZF=0,跳转至制定位置
je 与jz类似,但通常在一条cmp指令后使用。如果源操作数与目的操作数相等,则跳转
jne 与jnz类似,但通常在一条cmp指令后使用。如果源操作数与目的操作数不相等,则跳转
jg cmp指令做有符号比较之后,如果目的操作数大于原操作数,跳转
jge cmp指令做有符号比较之后,如果目的操作数大于或等于原操作数,跳转
ja 与jg类似,但使用无符号比较
jae 与jge类似,但使用无符号比较
jl cmp指令做有符号比较之后,如果目的操作数小于原操作数,则跳转
jle cmp指令做有符号比较之后,如果目的操作数小于或等于原操作数,则跳转
jb 与jl类似,但使用无符号比较
jbe 与jle类似,但使用无符号比较
jo 如果前一条指令置位了溢出标志位(OF=1),则跳转
js 如果符号标志位被置位(SF=1),则跳转
jecxz 如果ECX=0,则跳转

2. 关于栈

当某个函数运行时,机器需要分配一定的内存去进行函数内的各种操作,这个过程中分配的那部分栈称为栈帧。下图描述了栈帧的通用结构。

栈帧是一段有界限的内存区间,由最顶端的两个指针界定,寄存器 %ebp 为帧指针(栈底),而寄存器 %esp 为栈指针(也就是说寄存器%ebp保存了所分配内存的最高地址,寄存器%esp保存了所分配内存的最低地址)。

当程序执行时,栈指针(栈顶)可以移动,因此大多数信息的访问都是相对于桢指针的。

 在函数被调用之前,调用者会为调用函数做准备,具体来说就是传参。(传参对应的寄存器分别为:第一个参数%rdi,第二个参数%rsi......)

(c 程序使用栈存放局部变量、函数参数和返回地址)

被调用函数运行时,我们在函数里面是要执行各种操作的,所以我们需要给新栈帧分配一定的内存。也就是:

sub $0x10,%esp

(举个例子,具体分配内存大小按照所需空间分配),将%esp低地址移动16个字节。有了这么多的储存空间,才能支持函数里面的各种操作。

被调用函数运行结束时,恢复原来栈顶的状态。

⚠️这里要注意:函数的返回值一般储存在%eax寄存器中

请把下面这张图刻进 DNA 

3.关于sscanf

sscanf(char *string, char *format, arg1,arg2, arg3....)

其中是%rdi寄存器作为指向string的头指针

同理,%rsi作为format

arg1, arg2, arg3, arg4分别是由寄存器%rdx, %rcx, %r8, %r9存储

如果上面的知识你已经掌握了,那我们来开始拆炸弹吧!

Bomb Lab

首先拿到代码我们先看 README 文件,好吧,什么都没有,我们继续看其他文件。

发现 bomb.c 文件,但是没有头文件,所以不能进行运行和编译。但可以看出该程序要求从命令行或者文件以“行”为单位读入字符串,每行字符串对应一个phase的输入。如果phase执行完毕,会调用phase_defused 函数表明该 phase 成功搞定。

最后剩一个可执行文件,我们通过gdb调试,反汇编bomb文件,尝试得到该可执行文件的汇编代码。

具体操作如下:

objdump -d bomb > bomb.asm

这样就把反汇编得到的汇编代码存在一个文件 bomb.asm 中,便于查看。

然后通过打 breakpoint 的方法,以及查看寄存器和内存里存的值的情况,结合汇编语句,推算出应该输入的语句。

phase_1

在汇编代码中找到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   

阅读代码我们先找到了 explode_bomb,在继续往上看

由于我们不能执行 explode_bomb 函数,所以需要在该函数之前执行跳转指令跳过explode_bomb 函数,那么什么条件可以执行该跳转指令呢?我们在继续往上看

发现 %eax 作为上一个函数的返回值,若 %eax 为0,才可以执行跳转

继续往上推发现了函数 strings_not_equal ,通过阅读代码可以发现这个函数是判断输入的两个字符串是否相等,根据上面我们提到的知识可以知道函数传进去的参数分别在寄存器 %edi 和 %es i中,其中 %edi 是我们输入的字符串

答案出来了,寄存器 %esi 里的值就是本题答案,寄存器 %esi 是被地址在 0x402400 的内容赋值,我们可以通过gdb查看一下该地址内容

先进入bomb(注意此时路径一定要在bomb文件下)

gdb bomb

查看该地址内容

(gdb) x/s 0x402400

出现本题答案

Border relations with Canada have never been better. 

测试结果如下: 

phase_2  (循环)

还是阅读代码找到 explode_bomb 函数,发现有两个

我们先看一下如何避免第一个引爆函数的执行

好与上题同理,这里需让 %rsp 的值为1,后执行跳转指令 je

目标地址 400f30 处是 lea 指令,功能为加载有效地址

两条指令执行后运行时栈的状态为

 设置完栈地址后又执行了跳转指令

  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>

我们来仔细看一下这几条指令

首先 %eax 为 %rsp 指针指向的位置(值为1),%eax = %eax + %eax(此时值为2)

比较 %eax 与% rbx 指针所指向的值,这里为了不引爆炸弹,所以 %rbx 指针指向的值必须等于 %eax(值为2),那么我们可以知道 %rbx 指针指向的位置的值为 2

下一步将 %rbx + 4(其实也就是%rbx指针向上移动4个字节),比较 %rbp 与 %rbx ,相等则跳到结束语句,不相等则继续执行上几条指令

由此我们可以看出这是几条语句是循环指令,当 %rbp 与 %rbx 的值相等,则结束循环,若不相等则仍然执行上述指令

同理可得整个栈的内容为

由此phase_2的功能大概可以清楚了,首先将字符串解析成6个数字的正数数组,其中第一个整数必须为1,后面的每一个元素都是前一个元素的两倍。

即可得到本题答案

1 2 4 8 16 32

phase_3 (switch-case)

这次我们从上往下开始看

进入 phase_3 之后,首先是通过 sscanf 解析字符串,通过 gdb 调试可看出 sscanf 的 format 是%d %d  因此推断输出两个数字

(gdb) x/s 0x4025cf
0x4025cf:	"%d %d"

好下面我们开始找 explode_bomb 函数,发现有四个( 400f6f 行也算进去了)

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>

在找到第一个 explode_bomb 函数后往上看,发现在地址 400f63 处必须跳转

也就等同于输入的第一个值要大于等于 1

下面我们找到第二个函数

  400f6a:	83 7c 24 08 07       	cmpl   $0x7,0x8(%rsp)
  400f6f:	77 3c                	ja     400fad <phase_3+0x6a> #bomb

此时 7 必须大于 0x8(%rsp) --就是输入的第一个值,才不会发生爆炸

我们继续往下看

将输入的第一个值存到 %eax 中,然后执行一个间接跳转,地址为内存地址 0x402470(,%rax,8)处的值(这里%rax是我们输入的第一个值)

下面我们用 gdb 调试一下看看内存地址为 0x402470(,%rax,8) 的值

这里我们能看出,当输入值(%rax)为 0 的时候,跳转到 0x400f7c

同理为 1 的时候,跳到 0x400fb9 ...... 可以得出一个跳转表

index 0 1 2 3 4 5 6 7
address 0x400f7c 0x400fb9 0x400f83 0x400f8a 0x400f91 0x400f98 0x400f9f 0x400fa6
%eax 207 311 707 256 389 206 682 327

为了跳过 400fad 行的炸弹,我们需要完成跳转表跳到  400fbe

这里为跳过 400fc4 行的 explode_bomb 函数,我们输入的第二个值要对应上面的跳转表

所以答案出来了,可以是以下答案的任意一种

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

phase_4 (递归)

这回我们还是从上往下看,发现很多代码与上一个相同

这里的 sscanf 也是输入两个数字,分别在地址0x8(%rsp)、0xc(%rsp) 上

  401029:	83 f8 02             	cmp    $0x2,%eax
  40102c:	75 07                	jne    401035 <phase_4+0x29> #bomb

在 40102c 行我们遇到了第一个炸弹,此时限制条件是输入的第一个值不能为 2

  40102e:	83 7c 24 08 0e       	cmpl   $0xe,0x8(%rsp)
  401033:	76 05                	jbe    40103a <phase_4+0x2e>
  401035:	e8 00 04 00 00       	callq  40143a <explode_bomb>

在这里的为了跳过这个炸弹,限制条件是0x8(%rsp) --输入的第一个值要小于 14(0xe) 

  40103a:	ba 0e 00 00 00       	mov    $0xe,%edx
  40103f:	be 00 00 00 00       	mov    $0x0,%esi
  401044:	8b 7c 24 08          	mov    0x8(%rsp),%edi
  401048:	e8 81 ff ff ff       	callq  400fce <func4>

再解决这两个限制条件之后,将 edx、esi、edi 赋值,将参数传进 func4函数

当执行完 func4 函数之后返回的值在寄存器 %eax 中

  40104d:	85 c0                	test   %eax,%eax
  40104f:	75 07                	jne    401058 <phase_4+0x4c>
  401051:	83 7c 24 0c 00       	cmpl   $0x0,0xc(%rsp)
  401056:	74 05                	je     40105d <phase_4+0x51>
  401058:	e8 dd 03 00 00       	callq  40143a <explode_bomb>

此时为了防止调用 explode_bomb 函数,返回值 %eax 要为 0 

并且,0xc(%rsp) --输入的第二个值 也必须为 0

至此我们知道:

  • 解析字符串为两个数字 x、y

  • x 必须小于等于14

  • 调用 fun​​​​​​​c4,x、0、14 分别为其参数

  • func4 返回值必须为0,y 必须为0

那下面看一下 func4 函数

这个函数最初看着可能有点麻烦,需要整理一下

eax = edx
eax = eax - esi
ecx = eax 
ecx = 0
eax = eax + ecx
eax = eax / 2
ecx = esi + eax
if(edi >= ecx)
{
    return
    eax = 0 //正好是我们想要的结果
    edi <= ecx 
}

else
{
    edx = rcx - 1
    func4()
    eax = eax + eax
    return
}

我们将之前对参数带入发现:

        寄存器 %eax 和 %ecx 存了7

        %ecx 寄存器的值与 %edi 里的值比较 (%edi里是我们输入的第一个数)

这里比较的时候有两种情况

发生跳转时

  400ff2:	b8 00 00 00 00       	mov    $0x0,%eax
  400ff7:	39 f9                	cmp    %edi,%ecx
  400ff9:	7d 0c                	jge    401007 <func4+0x39>

        此时 %edi 大于等于 7

        地址400ff2处把%eax置为0,正好是我们想要的结果

        比较要求 7 大于等于 %edi,后跳转可以顺利退出

我们发现当 %edi = 7 的时候可以满足这些条件顺利退出(这是一个答案,也就是说第一个输入的值可以为 7 )

不发生跳转时

  400fe6:	8d 51 ff             	lea    -0x1(%rcx),%edx
  400fe9:	e8 e0 ff ff ff       	callq  400fce <func4>
  400fee:	01 c0                	add    %eax,%eax
  400ff0:	eb 15                	jmp    401007 <func4+0x39>

这里用到递归,但也不要怕,我们只需要按照上面的指令,对寄存器 %eax 和 %ecx 一顿操作就可以了

至此答案就出来了:我们第一个输入的值可以是 0、1、3、7,第二个输入的值为 0

0 0 / 1 0 / 3 0 / 7 0

phase_5 (循环 字符串 ascii 码)

从上往下看,首先将输入值赋给寄存器 rbx ,第40106a行是金丝雀值,这里不用管,我们可以简单的看成从内存中读取一个数到寄存器 rax 中(忘记的可以回头看看书,这里是为了验证是否发生缓冲区溢出)

  401062:	53                   	push   %rbx
  401063:	48 83 ec 20          	sub    $0x20,%rsp
  401067:	48 89 fb             	mov    %rdi,%rbx
  40106a:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax
  401071:	00 00 
  401073:	48 89 44 24 18       	mov    %rax,0x18(%rsp)
  401078:	31 c0                	xor    %eax,%eax
  40107a:	e8 9c 02 00 00       	callq  40131b <string_length>
  40107f:	83 f8 06             	cmp    $0x6,%eax
  401082:	74 4e                	je     4010d2 <phase_5+0x70>
  401084:	e8 b1 03 00 00       	callq  40143a <explode_bomb>

其次将寄存器 eax 清零,调用 string_length 函数(之前我们看过,这里不再赘述,就是比较字符串长度的函数)

此时为了防止炸弹爆炸,我们知道了输入的字符串长度应该为 6

 4010d2:	b8 00 00 00 00       	mov    $0x0,%eax
 4010d7:	eb b2                	jmp    40108b <phase_5+0x29>

寄存器 eax 清零,继续执行跳转

  40108b:	0f b6 0c 03          	movzbl (%rbx,%rax,1),%ecx
  40108f:	88 0c 24             	mov    %cl,(%rsp)
  401092:	48 8b 14 24          	mov    (%rsp),%rdx
  401096:	83 e2 0f             	and    $0xf,%edx
  401099:	0f b6 92 b0 24 40 00 	movzbl 0x4024b0(%rdx),%edx
  4010a0:	88 54 04 10          	mov    %dl,0x10(%rsp,%rax,1)
  4010a4:	48 83 c0 01          	add    $0x1,%rax
  4010a8:	48 83 f8 06          	cmp    $0x6,%rax
  4010ac:	75 dd                	jne    40108b <phase_5+0x29>

这里我们将汇编代码做一下调整

for(rax = 0,rax != 6,rax ++)
{
    ecx = rbx + rax
	*(rsp) = cl
	rdx = *(rsp)
	edx = edx & 0xf
	edx = 0x4024b0 + rdx
	rsp + rax + 0x10 = dl
}

取输入字符串的字符,然后逐次将每个字符与 0xf “与”操作,得到的值作为 0x4024b0 处字符串的下标。

与 0xf “与运算” 操作意味着能取到 0x4024b0 处字符串的范围是 0-15,通过 gdb 查看 0x4024b0 处字符串:

得到了前16为字符串:maduiersnfotvbyl

通过 for 循环能够生成一个字符串,该字符串由输入的每个字符和 0xf “与运算” 得到的值,作为 maduiersnfotvbyl 的下标,来选择字符

那么生成的字符串是什么呢?我们继续往下看

  4010ae:	c6 44 24 16 00       	movb   $0x0,0x16(%rsp)
  4010b3:	be 5e 24 40 00       	mov    $0x40245e,%esi
  4010b8:	48 8d 7c 24 10       	lea    0x10(%rsp),%rdi
  4010bd:	e8 76 02 00 00       	callq  401338 <strings_not_equal>
  4010c2:	85 c0                	test   %eax,%eax
  4010c4:	74 13                	je     4010d9 <phase_5+0x77>
  4010c6:	e8 6f 03 00 00       	callq  40143a <explode_bomb>

4010ae 行是字符串的结束符,将生成的字符串与 0x40245e 处的字符串比较,为了防止炸弹爆炸,我们生成的字符串就要和 0x40245e 处的字符串相等,那我们查看一下

 所以结果出来了:

1.输入的字符串长度为6

2.输入字符串的每个字符与 0xf “与运算”得到的数值在“maduiersnfotvbyl”中寻找字符,最后得到一段字符串

3.循环得到的字符串应该为“flyers”

我们可以发现“flyers”六个字母对应 “maduiersnfotvbyl” 的下标分别为 9、15、14、5、6、7

查一下 ascii 表,可以找到六个 & 0xf 分别为 9、15、14、5、6、7 的字符

答案为:

ionefg

phase_6 (链表、循环)

代码看着很长,我们拆成六个模块一点一点看

第一个模块就是调用 read_six_numbers 函数

  4010f4:	41 56                	push   %r14
  4010f6:	41 55                	push   %r13
  4010f8:	41 54                	push   %r12
  4010fa:	55                   	push   %rbp
  4010fb:	53                   	push   %rbx
  4010fc:	48 83 ec 50          	sub    $0x50,%rsp
  401100:	49 89 e5             	mov    %rsp,%r13
  401103:	48 89 e6             	mov    %rsp,%rsi
  401106:	e8 51 03 00 00       	callq  40145c <read_six_numbers>
  40110b:	49 89 e6             	mov    %rsp,%r14
  40110e:	41 bc 00 00 00 00    	mov    $0x0,%r12d

不停的赋值,然后读入六个数,得到的栈如下图

 第二个模块就是一个嵌套循环,利用上图的栈,对输入的数进行一些操作,这里就是看指针的变化

  401114:	4c 89 ed             	mov    %r13,%rbp
  401117:	41 8b 45 00          	mov    0x0(%r13),%eax
  40111b:	83 e8 01             	sub    $0x1,%eax
  40111e:	83 f8 05             	cmp    $0x5,%eax
  401121:	76 05                	jbe    401128 <phase_6+0x34>
  401123:	e8 12 03 00 00       	callq  40143a <explode_bomb>
  401128:	41 83 c4 01          	add    $0x1,%r12d
  40112c:	41 83 fc 06          	cmp    $0x6,%r12d
  401130:	74 21                	je     401153 <phase_6+0x5f>
  401132:	44 89 e3             	mov    %r12d,%ebx
  401135:	48 63 c3             	movslq %ebx,%rax
  401138:	8b 04 84             	mov    (%rsp,%rax,4),%eax
  40113b:	39 45 00             	cmp    %eax,0x0(%rbp)
  40113e:	75 05                	jne    401145 <phase_6+0x51>
  401140:	e8 f5 02 00 00       	callq  40143a <explode_bomb>
  401145:	83 c3 01             	add    $0x1,%ebx
  401148:	83 fb 05             	cmp    $0x5,%ebx
  40114b:	7e e8                	jle    401135 <phase_6+0x41>
  40114d:	49 83 c5 04          	add    $0x4,%r13
  401151:	eb c1                	jmp    401114 <phase_6+0x20>
//双层循环

这里大家可能要回去复习一下几种寻址方式

比如(%r13)表示的是 r13 寄存器指向的值而不是 r13 寄存器的值

好,那我们看一下这个嵌套循环是怎么实现的呢?

40111e 行中 eax 寄存器其实是外层循环,循环六次,每次 r12 寄存器分别指向 num1、num2、num3......

内层循环就是拿 r12 寄存器的值和剩下的五个对比,这里为了跳过炸弹,这里告诉我们六个值都是不一样的且都小于等于6

用 c 代码给大家清楚的看一下

for (int r12 = 0; r12 != 6; r12++) {
    rbp = r13;
    eax = *r13;
    eax -= 1;
    if ((eax > 5)		// 最大为6
        explode_bomb();

    for (ebx = r12+1; ebx <= 5; ebx++) {
        rax = ebx;
        eax = *(rsp+rax*4);
        if (*rbp == eax)    //所有值不能相等
            explode_bomb();
        }

    r13 += 4;
}

第三个模块就是一个循环,让每个栈中的 num 的值修改为 “7 - num”

  401153:	48 8d 74 24 18       	lea    0x18(%rsp),%rsi
  401158:	4c 89 f0             	mov    %r14,%rax
  40115b:	b9 07 00 00 00       	mov    $0x7,%ecx
  401160:	89 ca                	mov    %ecx,%edx
  401162:	2b 10                	sub    (%rax),%edx
  401164:	89 10                	mov    %edx,(%rax)
  401166:	48 83 c0 04          	add    $0x4,%rax
  40116a:	48 39 f0             	cmp    %rsi,%rax
  40116d:	75 f1                	jne    401160 <phase_6+0x6c>
//num = 7 - num

这里还是要注意一个寻址方式的问题哦~

第四个模块是本题组重要的环节,真的很复杂,可以多看几遍

  40116f:	be 00 00 00 00       	mov    $0x0,%esi
  401174:	eb 21                	jmp    401197 <phase_6+0xa3>

  401176:	48 8b 52 08          	mov    0x8(%rdx),%rdx
  40117a:	83 c0 01             	add    $0x1,%eax
  40117d:	39 c8                	cmp    %ecx,%eax
  40117f:	75 f5                	jne    401176 <phase_6+0x82>
  401181:	eb 05                	jmp    401188 <phase_6+0x94>
  
  401183:	ba d0 32 60 00       	mov    $0x6032d0,%edx
  401188:	48 89 54 74 20       	mov    %rdx,0x20(%rsp,%rsi,2)
  40118d:	48 83 c6 04          	add    $0x4,%rsi
  401191:	48 83 fe 18          	cmp    $0x18,%rsi
  401195:	74 14                	je     4011ab <phase_6+0xb7>

  401197:	8b 0c 34             	mov    (%rsp,%rsi,1),%ecx
  40119a:	83 f9 01             	cmp    $0x1,%ecx
  40119d:	7e e4                	jle    401183 <phase_6+0x8f>

  40119f:	b8 01 00 00 00       	mov    $0x1,%eax
  4011a4:	ba d0 32 60 00       	mov    $0x6032d0,%edx
  4011a9:	eb cb                	jmp    401176 <phase_6+0x82>
//将六个节点的起始地址按照某顺序放到栈上

对栈里的6个数字,先判断他们是否为1

如果为1,就把0x6032d0放在栈里;如果不为1,就循环操作

对这个地址进行计算之后的地址里的值放在寄存器里,可以得到5个地址

分别是 0x6032e0、0x6032f0、 0x603300,、0x603310、0x603320

分别对应值2、3、4、5、6,我们用 gdb 查看对应的值

然后把他们按照某种顺序放在栈的 %rsp + 0x20 到 %rsp + 0x48 处

第五个模块对每个地址里存的值做了操作,其实就是将链表重新连接

  4011ab:	48 8b 5c 24 20       	mov    0x20(%rsp),%rbx
  4011b0:	48 8d 44 24 28       	lea    0x28(%rsp),%rax
  4011b5:	48 8d 74 24 50       	lea    0x50(%rsp),%rsi
  4011ba:	48 89 d9             	mov    %rbx,%rcx
  4011bd:	48 8b 10             	mov    (%rax),%rdx
  4011c0:	48 89 51 08          	mov    %rdx,0x8(%rcx)
  4011c4:	48 83 c0 08          	add    $0x8,%rax
  4011c8:	48 39 f0             	cmp    %rsi,%rax
  4011cb:	74 05                	je     4011d2 <phase_6+0xde>
  4011cd:	48 89 d1             	mov    %rdx,%rcx
  4011d0:	eb eb                	jmp    4011bd <phase_6+0xc9>

  4011d2:	48 c7 42 08 00 00 00 	movq   $0x0,0x8(%rdx)
  4011d9:	00 
//重新连接链表

第六个模块为了防止炸弹爆炸,我们只能将链表降序连接

  4011da:	bd 05 00 00 00       	mov    $0x5,%ebp
  4011df:	48 8b 43 08          	mov    0x8(%rbx),%rax
  4011e3:	8b 00                	mov    (%rax),%eax
  4011e5:	39 03                	cmp    %eax,(%rbx)
  4011e7:	7d 05                	jge    4011ee <phase_6+0xfa>
  4011e9:	e8 4c 02 00 00       	callq  40143a <explode_bomb>
  4011ee:	48 8b 5b 08          	mov    0x8(%rbx),%rbx
  4011f2:	83 ed 01             	sub    $0x1,%ebp
  4011f5:	75 e8                	jne    4011df <phase_6+0xeb>

好啦答案就出来啦

链表排序,输入的数字为原链表位置,输入的次序为新链表的位置

又因为第二个循环对array每个数字 7 - array[i],因此输入的数字需要将链表重排成为升序

地址 对应值 输入值
0x6032d0 14c 1 6
0x6032e0 a8 2 5
0x6032f0 39c 3 4
0x603300 2b3 4 3
0x603310 1dd 5 2
0x603320 1bb 6 1

原链表的从小到大顺序是 5 6 1 2 3 4, 重排后为 4 3 2 1 6 5

即可得到答案

4 3 2 1 6 5

总结

其实这篇博客算是我写的最久的一篇了(可能下一篇会更久),上学期写项目有压力的时候开始写(结果写完更有压力了bushi)一直到所有比赛结束,我才开始收心写这篇博客。

不过也正是因为如此我做了很多遍这个lab,同时让我对汇编、逆向有了更深层次的了解,希望能给你们带来帮助❤️

猜你喜欢

转载自blog.csdn.net/weixin_52259822/article/details/124219469
今日推荐