CSAPP-lab2:Bomb-lab

本文最后更新于:2022年10月4日 晚上

先导知识

  • AT&T格式的x86汇编
  • gdb调试工具的使用

函数参数的压栈顺序:

  • rdi
  • rsi
  • rdx
  • rcx
  • r8
  • r9

命令速记:

  • list,简写为l
    • 输出源代码,默认为十行
    • l n : 输出第n行前后的代码
    • l function : 输出函数function前后的代码
  • break,简写为b
    • b 行号:在某一行设置断点
    • b 函数:在函数入口设置断点
  • run运行程序并在断点处停止
  • next往后执行一行语句
  • backtrace查看函数调用的栈帧
  • p打印变量的值或者地址(print),类似的还有x命令(examine)
  • info locals查看当前栈帧局部变量的值
  • info registers可以查看当前寄存器的值
  • 打印单个寄存器的值
    • i registers eax
    • p $eax
  • display + 变量名 可以每次停下来的时候自动打印变量的值
  • 直接敲回车键:重复上一条命令
  • layout命令可以拆分窗口
    • layout regs显示寄存器的值的独立窗口
    • layout asm显示汇编的独立窗口
    • ctrl+x 1 单窗口模式
    • ctrl+x 2 双窗口模式
    • ctrl+x a回到默认的gdb模式

100个gdb小技巧

准备工作

  • 得到汇编代码
    1
    2
    3
    objdump -d bomb >> bomb.s

    less bomb.s
  • ssh到服务器
  • tmux进行分屏,一边是汇编代码一边是gdb调试

phase_1

  • strings_not_equal有两个参数,第一个是你所输入的字符串的首地址(rdi),第二个是正确的字符串的首地址(rsi)
    • strings_not_equal(input_string,correct_string)
    • 如果两个个字符串是相等的,那么eax作为函数的返回值将会是0
  • 可以使用x/s 0x402400去观察这个地址的值是什么,可以看到是一个字符串
  • 得到答案:Border relations with Canada have never been better.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    0000000000400ee0 <phase_1>:
    400ee0: 48 83 ec 08 sub $0x8,%rsp # 给栈分配空间
    400ee4: be 00 24 40 00 mov $0x402400,%esi # rdi保存第一个参数,rsi保存第二个参数,这里是为strings_not_equal函数准备参数
    400ee9: e8 4a 04 00 00 callq 401338 <strings_not_equal>
    400eee: 85 c0 test %eax,%eax # test a,b 是a与b做与运算的意思
    400ef0: 74 05 je 400ef7 <phase_1+0x17> # je/jz 是当运算结果为0(ZF标志为1)时跳转。test和je一起使用是为了检测eax是不是0
    400ef2: e8 43 05 00 00 callq 40143a <explode_bomb>
    400ef7: 48 83 c4 08 add $0x8,%rsp
    400efb: c3 retq
    strings_not_equal函数的具体实现
    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
    0000000000401338 <strings_not_equal>: # %rdi保存第一个参数 %rsi保存第二个参数 %rdx保存第三个参数
    401338: 41 54 push %r12
    40133a: 55 push %rbp
    40133b: 53 push %rbx
    40133c: 48 89 fb mov %rdi,%rbx #将我们输入的字符串的地址赋给rbx,这里也是下面string_length函数的参数
    40133f: 48 89 f5 mov %rsi,%rbp #将答案的字符串的地址赋给rbp。这里赋值的目的是为了给下面的函数调用腾出寄存器
    401342: e8 d4 ff ff ff callq 40131b <string_length>
    401347: 41 89 c4 mov %eax,%r12d #将string_length函数的返回值赋给r12d
    40134a: 48 89 ef mov %rbp,%rdi #桢指针赋给rdi,也即是答案的字符串的指针赋给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 #将我们输入的字符串的地址赋给eax
    40135f: 84 c0 test %al,%al#检测al的值是不是0
    401361: 74 25 je 401388 <strings_not_equal+0x50>#是0就跳到401388
    401363: 3a 45 00 cmp 0x0(%rbp),%al#将答案字符串第一个字节与al进行比对
    401366: 74 0a je 401372 <strings_not_equal+0x3a>#如果相等就跳到401372
    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#rbx加到下一个字节
    401376: 48 83 c5 01 add $0x1,%rbp#rbp加到下一个字节
    40137a: 0f b6 03 movzbl (%rbx),%eax#把rbx对应内存的值赋给eax
    40137d: 84 c0 test %al,%al#看al是不是00说明到了字符串末尾处
    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

phase_2

  • read_six_numbers(input_string,rsp)
    • 第一个参数由rdi保存,也即是输入的字符串的地址
    • 第二个参数是rsi保存,也即是rsp的地址,本质上strings_not_equal函数是把字符串中的数字存到了rsp上
    • 调用了sscanf函数
  • 读取了六个数字之后需要和正确答案比对,这里使用了循环进行流程控制
    • 比如第一个数字必须是1,然后后面的数字可以陆续推导得出
  • 1 2 4 8 16 32
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
0000000000400efc <phase_2>:
400efc: 55 push %rbp
400efd: 53 push %rbx
400efe: 48 83 ec 28 sub $0x28,%rsp #0x28=40 十个int类型数
400f02: 48 89 e6 mov %rsp,%rsi
400f05: e8 52 05 00 00 callq 40145c <read_six_numbers>#读取六个数字,第一个参数:rdi 第二个:rsp
400f0a: 83 3c 24 01 cmpl $0x1,(%rsp)#比较1和rsp对应内存的值,如果不相同就爆炸(第一个数字必须是1)
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 #把rbx-4地址的值也即是第1个输入的数字赋给eax,eax=1
400f1a: 01 c0 add %eax,%eax #eax=2
400f1c: 39 03 cmp %eax,(%rbx) #和rbx指向的内容进行对比如果不相同就爆炸,因此rbx指向内容必须为2
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 #rbx+4,移动到下一个int数字的地址
400f29: 48 39 eb cmp %rbp,%rbx #和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 #把第二个数字的地址赋给rbx
400f35: 48 8d 6c 24 18 lea 0x18(%rsp),%rbp #rbp是最后一个数字的地址 0x18=16+8=24 24/4=6
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

关于sscanf函数的具体实现:

关于sscanf函数的用法可以在C++ reference上找到,理解该函数的调用和传参可以帮助理解汇编代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* sscanf example */
#include <stdio.h>

int main ()
{
char sentence []="Rudolph is 12 years old";
char str [20];
int i;
//这里有四个参数,分别是sentence:字符串的地址;"%s %*s %d"字符串,str,&i
sscanf (sentence,"%s %*s %d",str,&i);
printf ("%s -> %d\n",str,i);

return 0;
}

汇编代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
000000000040145c <read_six_numbers>: # 参数:rdi(输入的数字字符串) rsi() rdx rcx r8 r9
40145c: 48 83 ec 18 sub $0x18,%rsp #0x18==24byte,也即是六个int类型的数字
401460: 48 89 f2 mov %rsi,%rdx # rsi是第一个数字的地址,赋给rdx
401463: 48 8d 4e 04 lea 0x4(%rsi),%rcx #rcx是第二个数字的地址
401467: 48 8d 46 14 lea 0x14(%rsi),%rax # rax是第5个数字的地址
40146b: 48 89 44 24 08 mov %rax,0x8(%rsp) # rax
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

phase_3

  • 刚进入phase_3这个函数就调用了sscanf函数,因此需要准备参数
    • sscanf(input_string,”%d %d”,rdx,rcx)
    • sscanf函数返回读取的数字的数量,如果不是大于等于2会爆炸
  • 输入的第一个数字和7进行比较,如果大于就爆炸,因此只能大于等于0小于等于7
  • 重点是jmpq *0x402470(,%rax,8)语句,这里可以查看*0x402470的值从而确定要跳转的地方
  • 如果输入的第一个数字是0,那么就会跳转到下面的第一个分支语句,从而确定第二个值
  • 这里有很多个答案,其中一个是0 207
    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
    0000000000400f43 <phase_3>:# 参数顺序:rdi(输入的数字字符串) rsi() rdx rcx r8 r9 
    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 #gdb查看发现是 “%d %d”,第二个参数
    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#eax是输入的第一个数字
    400f75: ff 24 c5 70 24 40 00 jmpq *0x402470(,%rax,8)#8*rax+*0x402470,跳转到下面语句的其中之一
    400f7c: b8 cf 00 00 00 mov $0xcf,%eax#如果输入的第一个数字是0那么就会跳到这里
    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#得到第二个值0xc(%rsp)
    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

phase_4

  • 这个和上一个类似,都是输入两个值判断是不是满足要求
  • 函数内又调用了func4函数,所以是一个递归。func4函数是一个有三个参数的递归函数,如果一直手动分析递归的过程会把自己绕晕,所以执行jle直接跳出递归,最后发现解题确实用不着递归,直接跳出去就好了
  • 第一个参数需要满足<=7又要满足>=7因此就是7
  • 第二个参数需要满足是0
    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
    40
    41
    42
    43
    44
    45
    46
    47
    000000000040100c <phase_4>:# 参数顺序:rdi(输入的字符串) rsi() rdx rcx r8 r9 
    40100c: 48 83 ec 18 sub $0x18,%rsp
    401010: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx #输入的第二个数字,也即是sscanf的第三个参数
    401015: 48 8d 54 24 08 lea 0x8(%rsp),%rdx #输入的第一个数字
    40101a: be cf 25 40 00 mov $0x4025cf,%esi #字符串“%d %d”的首地址
    40101f: b8 00 00 00 00 mov $0x0,%eax
    401024: e8 c7 fb ff ff callq 400bf0 <__isoc99_sscanf@plt>
    401029: 83 f8 02 cmp $0x2,%eax #eax是sscanf函数的返回值,这里必须是2
    40102c: 75 07 jne 401035 <phase_4+0x29>
    40102e: 83 7c 24 08 0e cmpl $0xe,0x8(%rsp)#0xe=14 (rsp+8)<=14
    401033: 76 05 jbe 40103a <phase_4+0x2e>
    401035: e8 00 04 00 00 callq 40143a <explode_bomb>
    40103a: ba 0e 00 00 00 mov $0xe,%edx#edx=14
    40103f: be 00 00 00 00 mov $0x0,%esi#esi=0
    401044: 8b 7c 24 08 mov 0x8(%rsp),%edi#输入的第一个数字=edi,edi<=8
    401048: e8 81 ff ff ff callq 400fce <func4>
    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>
    40105d: 48 83 c4 18 add $0x18,%rsp
    401061: c3 retq

    0000000000400fce <func4>:#edi:输入的第一个数字 esi:0
    400fce: 48 83 ec 08 sub $0x8,%rsp
    400fd2: 89 d0 mov %edx,%eax#eax=14
    400fd4: 29 f0 sub %esi,%eax#eax=eax-esi=14-0=14
    400fd6: 89 c1 mov %eax,%ecx#ecx=14
    400fd8: c1 e9 1f shr $0x1f,%ecx#logic shift right 31 bits ecx=0
    400fdb: 01 c8 add %ecx,%eax#eax=14
    400fdd: d1 f8 sar %eax #mathematical shift right 1 bit,rax=14>>1=7
    400fdf: 8d 0c 30 lea (%rax,%rsi,1),%ecx #ecx=rax+rsi=7+0=7
    400fe2: 39 f9 cmp %edi,%ecx #edi<=ecx
    400fe4: 7e 0c jle 400ff2 <func4+0x24>
    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>
    400ff2: b8 00 00 00 00 mov $0x0,%eax
    400ff7: 39 f9 cmp %edi,%ecx
    400ff9: 7d 0c jge 401007 <func4+0x39>
    400ffb: 8d 71 01 lea 0x1(%rcx),%esi
    400ffe: e8 cb ff ff ff callq 400fce <func4>
    401003: 8d 44 00 01 lea 0x1(%rax,%rax,1),%eax
    401007: 48 83 c4 08 add $0x8,%rsp
    40100b: c3 retq

phase_5

输入一个字符串,长度为6,对于每个字符,取低四位,然后加上0x4024b0进行偏移,这样操作得到的字符串与地址为0x40245e的字符串进行比较(x/s查看可以得到是“flyer”)

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
40
41
42
0000000000401062 <phase_5>:#rdi保存输入的字符串
401062: 53 push %rbx
401063: 48 83 ec 20 sub $0x20,%rsp #2*16=32 bytes
401067: 48 89 fb mov %rdi,%rbx #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)#放在rsp+0x18位置
401078: 31 c0 xor %eax,%eax #eax清零
40107a: e8 9c 02 00 00 callq 40131b <string_length>
40107f: 83 f8 06 cmp $0x6,%eax#这里输入的字符串的长度必须是6
401082: 74 4e je 4010d2 <phase_5+0x70>
401084: e8 b1 03 00 00 callq 40143a <explode_bomb>
401089: eb 47 jmp 4010d2 <phase_5+0x70>
40108b: 0f b6 0c 03 movzbl (%rbx,%rax,1),%ecx#ecx=rbx+rax=rbx,也即是输入的字符串的位置
40108f: 88 0c 24 mov %cl,(%rsp)#cl是ecx的低8位,也即是第一个字符
401092: 48 8b 14 24 mov (%rsp),%rdx#把第一个字符赋给rdx
401096: 83 e2 0f and $0xf,%edx#与运算 f=1111 这里是只取低4
# 使用x/16xb 0x4024b0查看数组内的值,这里相当于把输入的字符取低四位,加上0x4024b0进行偏移得到flyers相同的字符串
401099: 0f b6 92 b0 24 40 00 movzbl 0x4024b0(%rdx),%edx #edx=0x73
4010a0: 88 54 04 10 mov %dl,0x10(%rsp,%rax,1)#dl=0x73 -> (rsp+rax+0x10)
4010a4: 48 83 c0 01 add $0x1,%rax # rax : 0->1
4010a8: 48 83 f8 06 cmp $0x6,%rax
4010ac: 75 dd jne 40108b <phase_5+0x29>
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>
4010cb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
4010d0: eb 07 jmp 4010d9 <phase_5+0x77>
4010d2: b8 00 00 00 00 mov $0x0,%eax#eax置为0
4010d7: eb b2 jmp 40108b <phase_5+0x29>
4010d9: 48 8b 44 24 18 mov 0x18(%rsp),%rax
4010de: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
4010e5: 00 00
4010e7: 74 05 je 4010ee <phase_5+0x8c>
4010e9: e8 42 fa ff ff callq 400b30 <__stack_chk_fail@plt>
4010ee: 48 83 c4 20 add $0x20,%rsp
4010f2: 5b pop %rbx
4010f3: c3 retq

phase_6

拖进ida F5得到C语言代码,下面是我加了注释的版本:

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
int __cdecl phase_6(char *s)
{
signed int v1; // edi
int i; // ebx
signed int v3; // edi
_DWORD *v4; // esi
int j; // ebx
int v6; // esi
signed int v7; // edi
int v8; // eax
int v9; // esi
signed int v10; // edi
int result; // eax
int v12; // [esp+0h] [ebp-58h]
int v13; // [esp+4h] [ebp-54h]
int v14; // [esp+8h] [ebp-50h]
int v15; // [esp+Ch] [ebp-4Ch]
int v16; // [esp+24h] [ebp-34h]
int v17[6]; // [esp+28h] [ebp-30h]
int v18[6]; // [esp+40h] [ebp-18h]

read_six_numbers(s, (int)v18);//读入六个数字
v1 = 0;

do
{
if ( (unsigned int)(v18[v1] - 1) > 5 ) //这六个数字必须都是小于等于6
explode_bomb(v12, v13, v14, v15);
for ( i = v1 + 1; i <= 5; ++i )
{
if ( v18[v1] == v18[i] )//两两不相等,也即是1~6各出现一个
explode_bomb(v12, v13, v14, v15);
}
++v1;
}
while ( v1 <= 5 );
// 六个数字都是小于等于6且互不相等
v3 = 0;
do
{
v4 = &node1;//v4是一个node1结构体的地址
for ( j = 1; j < v18[v3]; ++j )
v4 = (_DWORD *)v4[2];//v4[2]存储的是指向下一个结构体的指针
v17[v3++] = (int)v4;//v17存储排序后的结构体链表的地址
}
while ( v3 <= 5 );

v6 = v17[0];
v16 = v17[0];
v7 = 1;
do
{
v8 = v17[v7];
*(_DWORD *)(v6 + 8) = v8;// +0 +4 +8分别对应:储存的值 序号 下一个结构体的地址,这里是把排序后的结构体链接起来
v6 = v8;
++v7;
}
while ( v7 <= 5 );

*(_DWORD *)(v8 + 8) = 0;//最后一个节点,指向的地址是0
v9 = v16;
v10 = 0;
do
{
result = *(_DWORD *)v9;
if ( *(_DWORD *)v9 < **(_DWORD **)(v9 + 8) )//排序后的结构体链表需要满足:存储的值是从大到小排序
explode_bomb(v12, v13, v14, v15);
v9 = *(_DWORD *)(v9 + 8);
++v10;
}
while ( v10 <= 4 );

return result;
}

观察node结构体的特点,发现node[0]表示存储的值,node[1]表示node编号,node[2] 存储下一个node的地址

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
.data:0804B254 node3           db  2Dh ; -
.data:0804B255 db 1
.data:0804B256 db 0
.data:0804B257 db 0
.data:0804B258 db 3
.data:0804B259 db 0
.data:0804B25A db 0
.data:0804B25B db 0
.data:0804B25C db 48h ; H
.data:0804B25D db 0B2h
.data:0804B25E db 4
.data:0804B25F db 8
.data:0804B260 public node2
.data:0804B260 node2 db 0D5h
.data:0804B261 db 2
.data:0804B262 db 0
.data:0804B263 db 0
.data:0804B264 db 2
.data:0804B265 db 0
.data:0804B266 db 0
.data:0804B267 db 0
.data:0804B268 db 54h ; T
.data:0804B269 db 0B2h
.data:0804B26A db 4
.data:0804B26B db 8

我们输入的数字将会把结构体进行排序,排序结果最终需要满足的要求是,结构体存储的值必须要从大到小排列,因此我们找出所有的node存储的值即可

结构体编号 1 2 3 4 5 6
储存值 0xFD 0x2D5 0x12D 0x3E5 0xD4 0x1B0
排序后编号 5 2 4 1 6 3

4 2 6 3 1 5


CSAPP-lab2:Bomb-lab
http://gls.show/p/44962f68/
作者
郭佳明
发布于
2022年9月4日
许可协议