0x01 phase_1

1
2
3
4
5
6
7
8
9
0000000000400e70 <phase_1>:
400e70: 48 83 ec 08 sub $0x8,%rsp ;开辟8个字节的栈
400e74: be f8 1a 40 00 mov $0x401af8,%esi ;给寄存器%esi存入立即数0x401af8(传入参数,应该是运行时字符串的地址)
400e79: e8 bf 03 00 00 call 40123d <strings_not_equal> ;调用地址在0x40123d的函数<strings_not_equal>
400e7e: 85 c0 test %eax,%eax ;检查返回值%eax里是否为0
400e80: 74 05 je 400e87 <phase_1+0x17> ;若为0则跳转到0x400e87
400e82: e8 b6 07 00 00 call 40163d <explode_bomb> ;不为0则继续调用函数<explode_bomb>
400e87: 48 83 c4 08 add $0x8,%rsp ;清空8个字节的栈
400e8b: c3 ret ;函数返回

分析完很容易发现,在调用检查字符串是否相等的函数时,寄存器%esi传入了一个立即数地址,应该是要用来比较的字符串,在gdb调试时打印这个字符串。

image-20240422224132204

1
Science isn't about why, it's about why not?

成功打印出字符串。

0x02 phase_2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
0000000000400e8c <phase_2>:
400e8c: 48 89 5c 24 e0 mov %rbx,-0x20(%rsp) ;被调用者保护寄存器%rbx的值入栈(8个字节)
400e91: 48 89 6c 24 e8 mov %rbp,-0x18(%rsp) ;被调用者保护寄存器%rbp的值入栈(8个字节)
400e96: 4c 89 64 24 f0 mov %r12,-0x10(%rsp) ;被调用者保护寄存器%r12的值入栈(8个字节)
400e9b: 4c 89 6c 24 f8 mov %r13,-0x8(%rsp) ;被调用者保护寄存器%r13的值入栈(8个字节)
400ea0: 48 83 ec 48 sub $0x48,%rsp ;栈向下开辟72个字节
400ea4: 48 89 e6 mov %rsp,%rsi ;将栈指针的值存放入%rsi(第二个传入参数)中
400ea7: e8 97 08 00 00 call 401743 <read_six_numbers> ;调用<read_six_numbers>,返回的6个数字存在%rsp上偏移24bytes内
400eac: 48 89 e5 mov %rsp,%rbp ;更新栈底%rbp
400eaf: 4c 8d 6c 24 0c lea 0xc(%rsp),%r13 ;%r13 = %rsp + 0xc
400eb4: 41 bc 00 00 00 00 mov $0x0,%r12d ;%r12d清零
400eba: 48 89 eb mov %rbp,%rbx ;%rbx存栈底指针,循环在此开始
400ebd: 8b 45 0c mov 0xc(%rbp),%eax ;%eax = *(%rbp + 0xc)
400ec0: 39 45 00 cmp %eax,0x0(%rbp) ;比较%rbp上的值和%eax
400ec3: 74 05 je 400eca <phase_2+0x3e> ;如果相等则跳转
400ec5: e8 73 07 00 00 call 40163d <explode_bomb> ;否则bomb
400eca: 44 03 23 add (%rbx),%r12d ;%r12d += *(%rbx)
400ecd: 48 83 c5 04 add $0x4,%rbp ;栈底指针%rbp向上偏移4个字节
400ed1: 4c 39 ed cmp %r13,%rbp ;%rbp 不等于 %rsp + 0xc 则循环,从400eba开始
400ed4: 75 e4 jne 400eba <phase_2+0x2e>
400ed6: 45 85 e4 test %r12d,%r12d ;检测%r12d是否为0,不为0则跳转到400ee0
400ed9: 75 05 jne 400ee0 <phase_2+0x54>
400edb: e8 5d 07 00 00 call 40163d <explode_bomb>
400ee0: 48 8b 5c 24 28 mov 0x28(%rsp),%rbx ;下面是被保存的寄存器的出栈
400ee5: 48 8b 6c 24 30 mov 0x30(%rsp),%rbp
400eea: 4c 8b 64 24 38 mov 0x38(%rsp),%r12
400eef: 4c 8b 6c 24 40 mov 0x40(%rsp),%r13
400ef4: 48 83 c4 48 add $0x48,%rsp ;例行的清空栈帧和返回
400ef8: c3 ret

这个是函数phase_2的大体分析,其中函数,我为了了解读取的6个数字存放在了哪里,于是进入分析了一下,以下贴分析后的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
0000000000401743 <read_six_numbers>:
401743: 48 83 ec 18 sub $0x18,%rsp ;开辟24个字节的栈
401747: 48 89 f2 mov %rsi,%rdx ;%rdx = %rsi(第3个参数)
40174a: 48 8d 4e 04 lea 0x4(%rsi),%rcx ;%rcx = %rsi + 0x4 (第4个参数)
40174e: 48 8d 46 14 lea 0x14(%rsi),%rax ;%rax = %rsi + 0x14
401752: 48 89 44 24 08 mov %rax,0x8(%rsp) ;将%rax的值存放在栈指针向上偏移8个字节的位置(参数构造顶第8个)
401757: 48 8d 46 10 lea 0x10(%rsi),%rax ;%rax = %rsi + 0x10
40175b: 48 89 04 24 mov %rax,(%rsp) ;将%rax的值存放在栈指针位置(参数构造顶第7个参数)
40175f: 4c 8d 4e 0c lea 0xc(%rsi),%r9 ;%r9 = %rsi + 0xc (第5个参数)
401763: 4c 8d 46 08 lea 0x8(%rsi),%r8 ;%r8 = %rsi + 0x8 (第6个参数)
401767: be b2 1e 40 00 mov $0x401eb2,%esi ;将立即数0x401eb2存入%esi,经过调试后发现是6个%d,scanf函数的表达式
40176c: b8 00 00 00 00 mov $0x0,%eax ;%eax清零
401771: e8 3a f3 ff ff call 400ab0 <__isoc99_sscanf@plt>
;__isoc99_sscanf函数的返回值是读取的数字个数这里传入的六个参数(phase_2栈上的地址,这里用%rsi的偏移量来表示,实际上是phase_2栈帧的参数构造顶),以4个寄存器2个在栈上的方式,传入scanf函数。

401776: 83 f8 05 cmp $0x5,%eax ;如果读取的个数大于5个(这里需要读6个)
401779: 7f 05 jg 401780 <read_six_numbers+0x3d> ;则跳出程序正常返回
40177b: e8 bd fe ff ff call 40163d <explode_bomb> ;否则调用<explode_bomb>,程序中止
401780: 48 83 c4 18 add $0x18,%rsp ;清空<read_six_numbers>的栈
401784: c3 ret ;返回

很容易发现,这6个数字,分别通过%rdx,%rcx,%r8,%r9和两个栈上的参数构造区的栈帧,来读取6个数字。这6个参数是指针,分别是函数phase_2的栈指针%rsp向上偏移的24的bytes,每4个字节一次偏移。

核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
400eac:	48 89 e5             	mov    %rsp,%rbp                   ;更新栈底%rbp
400eaf: 4c 8d 6c 24 0c lea 0xc(%rsp),%r13 ;%r13 = %rsp + 0xc
400eb4: 41 bc 00 00 00 00 mov $0x0,%r12d ;%r12d清零
400eba: 48 89 eb mov %rbp,%rbx ;%rbx存栈底指针,循环在此开始
400ebd: 8b 45 0c mov 0xc(%rbp),%eax ;%eax = *(%rbp + 0xc)
400ec0: 39 45 00 cmp %eax,0x0(%rbp) ;比较%rbp上的和%eax
400ec3: 74 05 je 400eca <phase_2+0x3e> ;如果相等则跳转
400ec5: e8 73 07 00 00 call 40163d <explode_bomb> ;否则bomb
400eca: 44 03 23 add (%rbx),%r12d ;%r12d += *(%rbx)
400ecd: 48 83 c5 04 add $0x4,%rbp ;栈底指针%rbp向上偏移4个字节
400ed1: 4c 39 ed cmp %r13,%rbp ;%rbp 不等于 %rsp + 0xc 则循环,从400eba开始
400ed4: 75 e4 jne 400eba <phase_2+0x2e>
400ed6: 45 85 e4 test %r12d,%r12d ;检测%r12d是否为0,不为0则跳转到400ee0(正常进行)
400ed9: 75 05 jne 400ee0 <phase_2+0x54>
400edb: e8 5d 07 00 00 call 40163d <explode_bomb>

回过来分析phase_2,%r13存放着第4个参数的地址,%r12是每次循环的*(%rbx)的求和,%rbx每次循环都会向上偏移4字节,实际上就是从读取的第1个数字按顺序取址,然后放到%rbx中,直到偏移到0xc(偏移2次到第三个参数,第三次跳出循环),其中每次把读取的数字和地址偏移0xc后的数字比较。

简单点说就是,把第1个数字和第4个数字比,第2个和第5个比,第3个和第6个比,全部相同,且数字不全为0,就能通过测试。

1
1 2 3 1 2 3

0x03 phase_3

这是类似switch的一个跳转表的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
0000000000400ef9 <phase_3>:
400ef9: 48 83 ec 18 sub $0x18,%rsp ;开辟24个字节的栈
400efd: 48 8d 4c 24 08 lea 0x8(%rsp),%rcx ;%rcx = %rsp + 0x8
400f02: 48 8d 54 24 0c lea 0xc(%rsp),%rdx ;%rdx = %rsp + 0xc
400f07: be be 1e 40 00 mov $0x401ebe,%esi ;键入某个字符串,调试后发现是%d %d,应该是两个输入
400f0c: b8 00 00 00 00 mov $0x0,%eax ;清零%eax
400f11: e8 9a fb ff ff call 400ab0 <__isoc99_sscanf@plt> ;输入两个数字,存在%rsp分别偏移0x8和0xc上
400f16: 83 f8 01 cmp $0x1,%eax ;读取大于一个数字,即两个数字时跳转,正常运行
400f19: 7f 05 jg 400f20 <phase_3+0x27>
400f1b: e8 1d 07 00 00 call 40163d <explode_bomb>
400f20: 83 7c 24 0c 07 cmpl $0x7,0xc(%rsp) ;检测第1个数字是否小于等于7
400f25: 77 3c ja 400f63 <phase_3+0x6a>
400f27: 8b 44 24 0c mov 0xc(%rsp),%eax ;第1个数字赋值给%eax
400f2b: ff 24 c5 60 1b 40 00 jmp *0x401b60(,%rax,8) ;调试发现*0x401b60是地址0xf00f32,即下一条指令的地址
400f32: b8 17 02 00 00 mov $0x217,%eax ;根据%rax的值实现8字节一次的偏移,其实就是switch跳转
400f37: eb 3b jmp 400f74 <phase_3+0x7b> ;下面是类似跳转表的东西
400f39: b8 d6 00 00 00 mov $0xd6,%eax
400f3e: eb 34 jmp 400f74 <phase_3+0x7b>
400f40: b8 53 01 00 00 mov $0x153,%eax
400f45: eb 2d jmp 400f74 <phase_3+0x7b>
400f47: b8 77 00 00 00 mov $0x77,%eax
400f4c: eb 26 jmp 400f74 <phase_3+0x7b>
400f4e: b8 60 01 00 00 mov $0x160,%eax
400f53: eb 1f jmp 400f74 <phase_3+0x7b>
400f55: b8 97 03 00 00 mov $0x397,%eax
400f5a: eb 18 jmp 400f74 <phase_3+0x7b>
400f5c: b8 9c 01 00 00 mov $0x19c,%eax
400f61: eb 11 jmp 400f74 <phase_3+0x7b>
400f63: e8 d5 06 00 00 call 40163d <explode_bomb>
400f68: b8 00 00 00 00 mov $0x0,%eax
400f6d: eb 05 jmp 400f74 <phase_3+0x7b>
400f6f: b8 9e 03 00 00 mov $0x39e,%eax
400f74: 3b 44 24 08 cmp 0x8(%rsp),%eax ;给%eax赋值后跳转到这里,与第2个数字比较
400f78: 74 05 je 400f7f <phase_3+0x86> ;相等就正常退出函数
400f7a: e8 be 06 00 00 call 40163d <explode_bomb>
400f7f: 48 83 c4 18 add $0x18,%rsp
400f83: c3 ret

我这里用最简单的一种组合

1
0 535

0x04 phase_4

该函数是一个函数调用的嵌套,读取一个数字然后检测返回是否是55

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
0000000000400fc1 <phase_4>:
400fc1: 48 83 ec 18 sub $0x18,%rsp ;惯例开辟24字节的栈
400fc5: 48 8d 54 24 0c lea 0xc(%rsp),%rdx ;第1个参数存在%rsp+0xc上
400fca: be c1 1e 40 00 mov $0x401ec1,%esi ;%esi惯例是 %d ,用来读取一个数字
400fcf: b8 00 00 00 00 mov $0x0,%eax ;%eax初始化
400fd4: e8 d7 fa ff ff call 400ab0 <__isoc99_sscanf@plt>
400fd9: 83 f8 01 cmp $0x1,%eax
400fdc: 75 07 jne 400fe5 <phase_4+0x24>
400fde: 83 7c 24 0c 00 cmpl $0x0,0xc(%rsp) ;读取的数字个数应该为1,否则bomb
400fe3: 7f 05 jg 400fea <phase_4+0x29>
400fe5: e8 53 06 00 00 call 40163d <explode_bomb>
400fea: 8b 7c 24 0c mov 0xc(%rsp),%edi ;读取的数字赋值到%edi,作为参数传入<func4>
400fee: e8 91 ff ff ff call 400f84 <func4>
400ff3: 83 f8 37 cmp $0x37,%eax ;返回值等于55则正常退出
400ff6: 74 05 je 400ffd <phase_4+0x3c>
400ff8: e8 40 06 00 00 call 40163d <explode_bomb>
400ffd: 48 83 c4 18 add $0x18,%rsp
401001: c3 ret

这里的核心代码是func4,func4是一个递归调用。分析后能发现,假如传入参数是a,当a大于1时,return func4(a - 1) + func4(a - 2),否则return 1,其实很容易发现这是一个斐波那契数列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
0000000000400f84 <func4>:
400f84: 48 89 5c 24 f0 mov %rbx,-0x10(%rsp)
400f89: 48 89 6c 24 f8 mov %rbp,-0x8(%rsp)
400f8e: 48 83 ec 18 sub $0x18,%rsp ;开栈和寄存器保护
400f92: 89 fb mov %edi,%ebx ;传入参数(假设叫a)赋值到%ebx
400f94: b8 01 00 00 00 mov $0x1,%eax ;返回值赋值1
400f99: 83 ff 01 cmp $0x1,%edi ;如果a小于或者等于1,直接返回
400f9c: 7e 14 jle 400fb2 <func4+0x2e>
400f9e: 8d 7b ff lea -0x1(%rbx),%edi ;否则把a-1然后再次传入func4
400fa1: e8 de ff ff ff call 400f84 <func4>
400fa6: 89 c5 mov %eax,%ebp ;%ebp存func4(a-1)的返回值
400fa8: 8d 7b fe lea -0x2(%rbx),%edi
400fab: e8 d4 ff ff ff call 400f84 <func4> ;a-2传入func4
400fb0: 01 e8 add %ebp,%eax ;%eax = func4(a-1)+func4(a-2)
400fb2: 48 8b 5c 24 08 mov 0x8(%rsp),%rbx
400fb7: 48 8b 6c 24 10 mov 0x10(%rsp),%rbp
400fbc: 48 83 c4 18 add $0x18,%rsp
400fc0: c3 ret ;返回func4(a-1)+func4(a-2)

列出斐波那契数列的前几项

1
1 1 2 3 5 8 13 21 34 55

输入55的索引,从0开始算,就是9,输入9就能正常通过这一关了

0x05 phase_5

第五个函数的核心代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
0000000000401002 <phase_5>:
401002: 48 83 ec 18 sub $0x18,%rsp ;开栈24字节
401006: 48 8d 4c 24 08 lea 0x8(%rsp),%rcx
40100b: 48 8d 54 24 0c lea 0xc(%rsp),%rdx
401010: be be 1e 40 00 mov $0x401ebe,%esi
401015: b8 00 00 00 00 mov $0x0,%eax
40101a: e8 91 fa ff ff call 400ab0 <__isoc99_sscanf@plt> ;读取两个数字,第1个存在%rsp+0xc上,第2个存在%rsp+0x8上
40101f: 83 f8 01 cmp $0x1,%eax
401022: 7f 05 jg 401029 <phase_5+0x27> ;读取2个否则bomb
401024: e8 14 06 00 00 call 40163d <explode_bomb>
401029: 8b 44 24 0c mov 0xc(%rsp),%eax ;%eax存第1个参数
40102d: 83 e0 0f and $0xf,%eax ;%eax取低4位掩码(十进制0到15)
401030: 89 44 24 0c mov %eax,0xc(%rsp) ;存回去
401034: 83 f8 0f cmp $0xf,%eax ;若%eax等于15则bomb(需要不为15)
401037: 74 2c je 401065 <phase_5+0x63>
401039: b9 00 00 00 00 mov $0x0,%ecx
40103e: ba 00 00 00 00 mov $0x0,%edx ;%edx和%ecx初始化,都为0
401043: 83 c2 01 add $0x1,%edx ;循环起始,每次循环%edx += 1
401046: 48 98 cltq ;将%eax扩展到64位放入%rax
401048: 8b 04 85 a0 1b 40 00 mov 0x401ba0(,%rax,4),%eax ;%eax = 0x401ba0 + 4 * %rax
40104f: 01 c1 add %eax,%ecx ;把%eax加到%ecx
401051: 83 f8 0f cmp $0xf,%eax
401054: 75 ed jne 401043 <phase_5+0x41> ;如果%eax不等于15继续循环
401056: 89 44 24 0c mov %eax,0xc(%rsp) ;%eax赋值给第1个参数
40105a: 83 fa 0c cmp $0xc,%edx
40105d: 75 06 jne 401065 <phase_5+0x63> ;%edx要等于12,才能正常运行
40105f: 3b 4c 24 08 cmp 0x8(%rsp),%ecx
401063: 74 05 je 40106a <phase_5+0x68> ;如果第2个参数等于%ecx,则正常结束
401065: e8 d3 05 00 00 call 40163d <explode_bomb>
40106a: 48 83 c4 18 add $0x18,%rsp
40106e: c3 ret

分析发现,第1个参数%eax是0到14间的一个数字,用来取0x401ba0的偏移。取完偏移后,把读出的值存到%eax中,并累加到%ecx中,如果%eax=15则跳出循环。

之后用%eax更新第1个参数,检测循环次数,要为12次,接着检测第2个参数是否和累加值%ecx相等。

可以用gdb调试把0x401ba0所有偏移得到的值打印出来分析。

1
x/16wx 0x401ba0

打印出来的值如下:

1
2
3
4
0x401ba0 <array.3014>:  0x0000000a      0x00000002      0x0000000e      0x00000007
0x401bb0 <array.3014+16>: 0x00000008 0x0000000c 0x0000000f 0x0000000b
0x401bc0 <array.3014+32>: 0x00000000 0x00000004 0x00000001 0x0000000d
0x401bd0 <array.3014+48>: 0x00000003 0x00000009 0x00000006 0x00000005

这些值,可能会在等下的偏移中取到,姑且把他看成是存着0到f的数组,然后偏移量%rax看成是下标索引

1
2
10  2  14  7  8  12  15  11  0  4  1  13  3  9  6  5      上面是值
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 下面是索引%rax

这下逻辑就十分清晰了,我们要输入一个下标索引,根据索引映射到数组的值,再作为下一次映射的下标,我们要做的事情就是让他映射12次(第12次时读到值15),且算出每次映射的值的和。

写出逆向脚本

1
2
3
4
5
6
7
8
9
a = [10,  2 , 14 , 7,  8,  12,  15,  11 ,0  ,4  ,1,  13  ,3 , 9,  6,  5]
last_number = 15
sum = 0
temp = last_number
for i in range(0,12):
sum += temp
temp = a.index(temp)
print(temp,sum)
#7 93

0x06 phase_6

phase_6很容易发现是一个链表。

假设node0是一个链表的节点,三次解引用就是沿着链表向前搜索三次,然后要满足搜索到的节点的值和node0节点的值相等。来具体看看函数phase_6:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
00000000004010d9 <phase_6>:
4010d9: 48 83 ec 08 sub $0x8,%rsp
4010dd: ba 0a 00 00 00 mov $0xa,%edx ;%edx = 10
4010e2: be 00 00 00 00 mov $0x0,%esi ;%esi = 0
4010e7: e8 94 fa ff ff call 400b80 <strtol@plt> ;将字符串转换为长整型数(字符串是读取的)
4010ec: 89 05 8e 16 20 00 mov %eax,0x20168e(%rip) # 602780 <node0> ;%eax存为node0的值(由读取字符串转换而来的值)
4010f2: bf 80 27 60 00 mov $0x602780,%edi ;%edi存为node0的地址
4010f7: e8 73 ff ff ff call 40106f <fun6>
4010fc: 48 8b 40 08 mov 0x8(%rax),%rax ;三次解引用(链表顺序向前搜索)
401100: 48 8b 40 08 mov 0x8(%rax),%rax
401104: 48 8b 40 08 mov 0x8(%rax),%rax
401108: 8b 15 72 16 20 00 mov 0x201672(%rip),%edx # 602780 <node0>
40110e: 39 10 cmp %edx,(%rax) ;比较三次解引用后的值和node0的值,相等则程序正常进行
401110: 74 05 je 401117 <phase_6+0x3e>
401112: e8 26 05 00 00 call 40163d <explode_bomb>
401117: 48 83 c4 08 add $0x8,%rsp
40111b: c3 ret

下面是核心代码func6:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
000000000040106f <fun6>:
40106f: 4c 8b 47 08 mov 0x8(%rdi),%r8 ;%r8 存 node0的8字节偏移的值(应该是下一节点的地址)
401073: 48 c7 47 08 00 00 00 movq $0x0,0x8(%rdi) ;node0指向下一节点的地址段更新为0
40107a: 00
40107b: 48 89 f8 mov %rdi,%rax ;node0的地址存到%rax
40107e: 48 89 f9 mov %rdi,%rcx ;node0的地址存到%rcx
401081: 4d 85 c0 test %r8,%r8
401084: 75 40 jne 4010c6 <fun6+0x57> ;%r8不为0则跳转
401086: 48 89 f8 mov %rdi,%rax ;返回node0的地址
401089: c3 ret
40108a: 48 89 d1 mov %rdx,%rcx
40108d: 48 8b 51 08 mov 0x8(%rcx),%rdx
401091: 48 85 d2 test %rdx,%rdx
401094: 74 09 je 40109f <fun6+0x30>
401096: 39 32 cmp %esi,(%rdx)
401098: 7f f0 jg 40108a <fun6+0x1b>
40109a: 48 89 cf mov %rcx,%rdi
40109d: eb 03 jmp 4010a2 <fun6+0x33>
40109f: 48 89 cf mov %rcx,%rdi
4010a2: 48 39 d7 cmp %rdx,%rdi ;循环在此开始
4010a5: 74 06 je 4010ad <fun6+0x3e> ;相等就跳转
4010a7: 4c 89 47 08 mov %r8,0x8(%rdi)
4010ab: eb 03 jmp 4010b0 <fun6+0x41>
4010ad: 4c 89 c0 mov %r8,%rax ;下一节点的地址存到%rax
4010b0: 49 8b 48 08 mov 0x8(%r8),%rcx ;下下节点的地址存到%rcx
4010b4: 49 89 50 08 mov %rdx,0x8(%r8)
4010b8: 48 85 c9 test %rcx,%rcx
4010bb: 74 1a je 4010d7 <fun6+0x68> ;%rcx为0就结束函数func6(下个节点是尾节点就结束函数)
4010bd: 49 89 c8 mov %rcx,%r8 ;下下个节点的地址存到%r8
4010c0: 48 89 c1 mov %rax,%rcx ;下个节点的地址存到%rcx
4010c3: 48 89 c7 mov %rax,%rdi ;下个节点的地址存到%rdi
4010c6: 48 89 ca mov %rcx,%rdx ;跳转到这
4010c9: 48 85 c9 test %rcx,%rcx
4010cc: 74 d4 je 4010a2 <fun6+0x33> ;%rcx为0则跳转(循环)
4010ce: 41 8b 30 mov (%r8),%esi ;下下个节点的值存到%esi
4010d1: 39 31 cmp %esi,(%rcx) ;下个节点的值和下下节点值比较
4010d3: 7f b8 jg 40108d <fun6+0x1e> ;前个节点大于后个节点就跳转
4010d5: eb cb jmp 4010a2 <fun6+0x33>
4010d7: f3 c3 repz ret

核心代码大概是链表的修改过程,是一个降序排序,把你输入的值存入node0。看不懂也没关系,直接上gdb动调很容易理解是降序排序。
用gdb调试打印出链表节点的值:(your input)→374→826→370→782→488→673→286→600→529,phase_6查找node3与你的输入值比较,其实就是要求你输入的值在排序中是倒数第4大的。对比数据发现,只要your input在600到673之间,就能满足上述条件。

826->782->673->your input ->600 …

0x07 实验结论

学习和掌握了gdb调试工具的使用。

学会了objdump反编译机器码

通过gdb调试工具和objdump分析汇编程序

成功逆出了6步phase