准备工作

在终端中输入objdump -d bomb > bomb.asm,将bomb反汇编并保存到文件bomb.asm中,相较于在gdb中输入disassemble命令更便于阅读。

080489e4 <main>:80489e4:   55                      push   %ebp80489e5: 89 e5                   mov    %esp,%ebp80489e7:    53                      push   %ebx80489e8: 83 e4 f0                and    $0xfffffff0,%esp80489eb: 83 ec 10                sub    $0x10,%esp80489ee:   8b 45 08                mov    0x8(%ebp),%eax80489f1:   8b 5d 0c                mov    0xc(%ebp),%ebx80489f4:   83 f8 01                cmp    $0x1,%eax80489f7:    75 0c                   jne    8048a05 <main+0x21>80489f9:   a1 a4 c3 04 08          mov    0x804c3a4,%eax80489fe:   a3 d0 c3 04 08          mov    %eax,0x804c3d08048a03:   eb 74                   jmp    8048a79 <main+0x95>8048a05:   83 f8 02                cmp    $0x2,%eax8048a08:    75 49                   jne    8048a53 <main+0x6f>8048a0a:   c7 44 24 04 68 a0 04    movl   $0x804a068,0x4(%esp)8048a11: 08 8048a12: 8b 43 04                mov    0x4(%ebx),%eax8048a15:   89 04 24                mov    %eax,(%esp)8048a18:  e8 63 fe ff ff          call   8048880 <fopen@plt>8048a1d:   a3 d0 c3 04 08          mov    %eax,0x804c3d08048a22:   85 c0                   test   %eax,%eax8048a24:    75 53                   jne    8048a79 <main+0x95>8048a26:   8b 43 04                mov    0x4(%ebx),%eax8048a29:   89 44 24 0c             mov    %eax,0xc(%esp)8048a2d:   8b 03                   mov    (%ebx),%eax8048a2f:  89 44 24 08             mov    %eax,0x8(%esp)8048a33:   c7 44 24 04 6a a0 04    movl   $0x804a06a,0x4(%esp)8048a3a: 08 8048a3b: c7 04 24 01 00 00 00    movl   $0x1,(%esp)8048a42:  e8 59 fe ff ff          call   80488a0 <__printf_chk@plt>8048a47:    c7 04 24 08 00 00 00    movl   $0x8,(%esp)8048a4e:  e8 ed fd ff ff          call   8048840 <exit@plt>8048a53:    8b 03                   mov    (%ebx),%eax8048a55:  89 44 24 08             mov    %eax,0x8(%esp)8048a59:   c7 44 24 04 87 a0 04    movl   $0x804a087,0x4(%esp)8048a60: 08 8048a61: c7 04 24 01 00 00 00    movl   $0x1,(%esp)8048a68:  e8 33 fe ff ff          call   80488a0 <__printf_chk@plt>8048a6d:    c7 04 24 08 00 00 00    movl   $0x8,(%esp)8048a74:  e8 c7 fd ff ff          call   8048840 <exit@plt>8048a79:    e8 bd 05 00 00          call   804903b <initialize_bomb>8048a7e:  c7 04 24 ec a0 04 08    movl   $0x804a0ec,(%esp)8048a85:    e8 76 fd ff ff          call   8048800 <puts@plt>8048a8a:    c7 04 24 28 a1 04 08    movl   $0x804a128,(%esp)8048a91:    e8 6a fd ff ff          call   8048800 <puts@plt>8048a96:    e8 62 06 00 00          call   80490fd <read_line>8048a9b:    89 04 24                mov    %eax,(%esp)8048a9e:  e8 ad 00 00 00          call   8048b50 <phase_1>8048aa3:  e8 b3 07 00 00          call   804925b <phase_defused>8048aa8:    c7 04 24 54 a1 04 08    movl   $0x804a154,(%esp)8048aaf:    e8 4c fd ff ff          call   8048800 <puts@plt>8048ab4:    e8 44 06 00 00          call   80490fd <read_line>8048ab9:    89 04 24                mov    %eax,(%esp)8048abc:  e8 b3 00 00 00          call   8048b74 <phase_2>8048ac1:  e8 95 07 00 00          call   804925b <phase_defused>8048ac6:    c7 04 24 a1 a0 04 08    movl   $0x804a0a1,(%esp)8048acd:    e8 2e fd ff ff          call   8048800 <puts@plt>8048ad2:    e8 26 06 00 00          call   80490fd <read_line>8048ad7:    89 04 24                mov    %eax,(%esp)8048ada:  e8 e5 00 00 00          call   8048bc4 <phase_3>8048adf:  e8 77 07 00 00          call   804925b <phase_defused>8048ae4:    c7 04 24 bf a0 04 08    movl   $0x804a0bf,(%esp)8048aeb:    e8 10 fd ff ff          call   8048800 <puts@plt>8048af0:    e8 08 06 00 00          call   80490fd <read_line>8048af5:    89 04 24                mov    %eax,(%esp)8048af8:  e8 e2 01 00 00          call   8048cdf <phase_4>8048afd:  e8 59 07 00 00          call   804925b <phase_defused>8048b02:    c7 04 24 80 a1 04 08    movl   $0x804a180,(%esp)8048b09:    e8 f2 fc ff ff          call   8048800 <puts@plt>8048b0e:    e8 ea 05 00 00          call   80490fd <read_line>8048b13:    89 04 24                mov    %eax,(%esp)8048b16:  e8 26 02 00 00          call   8048d41 <phase_5>8048b1b:  e8 3b 07 00 00          call   804925b <phase_defused>8048b20:    c7 04 24 ce a0 04 08    movl   $0x804a0ce,(%esp)8048b27:    e8 d4 fc ff ff          call   8048800 <puts@plt>8048b2c:    e8 cc 05 00 00          call   80490fd <read_line>8048b31:    89 04 24                mov    %eax,(%esp)8048b34:  e8 51 02 00 00          call   8048d8a <phase_6>8048b39:  e8 1d 07 00 00          call   804925b <phase_defused>8048b3e:    b8 00 00 00 00          mov    $0x0,%eax8048b43:    8b 5d fc                mov    -0x4(%ebp),%ebx8048b46:  c9                      leave  8048b47: c3                      ret    8048b48: 90                      nop8048b49: 90                      nop8048b4a: 90                      nop8048b4b: 90                      nop8048b4c: 90                      nop8048b4d: 90                      nop8048b4e: 90                      nop8048b4f: 90                      nop

首先分析main函数,观察到main函数中,read_line函数的意义是读取一行字符串。phase_1~phase_6,read_line之后都会将返回值%eax传给(%esp),也就是read_line读取的字符串的首地址作为接下来执行phase_n的第一个参数。

Phase_1

密钥:I can see Russia from my house!

08048b50 <phase_1>:8048b50:    83 ec 1c                sub    $0x1c,%esp8048b53:   c7 44 24 04 a4 a1 04    movl   $0x804a1a4,0x4(%esp)8048b5a: 08 8048b5b: 8b 44 24 20             mov    0x20(%esp),%eax8048b5f:  89 04 24                mov    %eax,(%esp)8048b62:  e8 5d 04 00 00          call   8048fc4 <strings_not_equal>8048b67:    85 c0                   test   %eax,%eax8048b69:    74 05                   je     8048b70 <phase_1+0x20>8048b6b:    e8 66 05 00 00          call   80490d6 <explode_bomb>8048b70: 83 c4 1c                add    $0x1c,%esp8048b73:   c3                      ret   

分析

阅读phase_1的前5行代码,观察到数据$0x804a1a4和0x20(%esp)对应的read_line读入的字符串首地址作为参数被传入栈帧中,结合第6行的strings_not_equal函数,可以推测,$0x804a1a4处存储的也是一个字符串的首地址,并且将要与此前读入的字符串进行比较。阅读strings_not_equal的汇编代码,得知该函数比较两个字符串是否相等,若相等则返回0,保存在%eax中。再看第7、8、9行,可知若%eax为0,则跳过第9行至<phase_1+0x20>处,否则执行第9行。第9行的指令进入explode_bomb函数,执行此行命令将会引爆炸弹,所以必须令程序在第8行时进行跳转。结合以上信息,得知,我们必须令输入的字符串与$0x804a1a4处的字符串相等,才能通过phase_1。

通过gdb中的print命令,打印出$0x804a1a4处的字符串,出现了如图所示的字符串“I can see Russia from my house!”,这便是phase_1的通关密钥。运行bomb,输入该字符串,成功通过phase_1。

Phase_2

密钥:0 1 1 2 3 5

08048b74 <phase_2>:8048b74:    56                      push   %esi8048b75: 53                      push   %ebx8048b76: 83 ec 34                sub    $0x34,%esp8048b79:   8d 44 24 18             lea    0x18(%esp),%eax8048b7d:  89 44 24 04             mov    %eax,0x4(%esp)8048b81:   8b 44 24 40             mov    0x40(%esp),%eax8048b85:  89 04 24                mov    %eax,(%esp)8048b88:  e8 7e 06 00 00          call   804920b <read_six_numbers>8048b8d: 83 7c 24 18 00          cmpl   $0x0,0x18(%esp)8048b92:  75 07                   jne    8048b9b <phase_2+0x27>8048b94:    83 7c 24 1c 01          cmpl   $0x1,0x1c(%esp)8048b99:  74 05                   je     8048ba0 <phase_2+0x2c>8048b9b:    e8 36 05 00 00          call   80490d6 <explode_bomb>8048ba0: 8d 5c 24 20             lea    0x20(%esp),%ebx8048ba4:  8d 74 24 30             lea    0x30(%esp),%esi8048ba8:  8b 43 f8                mov    -0x8(%ebx),%eax8048bab:  03 43 fc                add    -0x4(%ebx),%eax8048bae:  39 03                   cmp    %eax,(%ebx)8048bb0:  74 05                   je     8048bb7 <phase_2+0x43>8048bb2:    e8 1f 05 00 00          call   80490d6 <explode_bomb>8048bb7: 83 c3 04                add    $0x4,%ebx8048bba:    39 f3                   cmp    %esi,%ebx8048bbc:    75 ea                   jne    8048ba8 <phase_2+0x34>8048bbe:    83 c4 34                add    $0x34,%esp8048bc1:   5b                      pop    %ebx8048bc2: 5e                      pop    %esi8048bc3: c3                      ret    

分析

观察到phase_2调用了read_six_numbers这个函数,根据phase_1的经验,phase_n函数内调用的函数很可能就是突破口。第4~7行配置read_six_numbers的参数,其中根据main函数可知0x40(%esp)中的内容为输入的字符串的首地址,结合阅读read_six_numbers函数的代码,得知该函数读取6个整型数字,并将读取的数字从左到右依次保存至0x18(%esp)~0x2c(%esp)中。若读取的数字不足6个,将会引爆炸弹。然而,并非随意输入6个数字即可。通过第9~13行得知,0x18(%esp)中的内容,即第一个数字必须等于0,而0x1c(%esp)中的内容,即第二个数字必须等于1,否则都将进入explode_bomb函数导致炸弹引爆。而15~24行是一个循环结构,翻译成伪代码为

pos = %edx; end_pos = %esi;
a[] = 读取的数字
while(pos != end_pos)
{if(a[pos] + a[pos+1] != a[pos+2])explode_bomb();pos++;
}

于是,可以得知,要输入的6个数字,就是斐波那契数列的前六个数字:0 1 1 2 3 5。

Phase_3

密钥:0 392 1 -213 2 -47 3 -878 4 0 5 -878

08048bc4 <phase_3>:8048bc4:    83 ec 2c                sub    $0x2c,%esp8048bc7:   8d 44 24 1c             lea    0x1c(%esp),%eax8048bcb:  89 44 24 0c             mov    %eax,0xc(%esp)8048bcf:   8d 44 24 18             lea    0x18(%esp),%eax8048bd3:  89 44 24 08             mov    %eax,0x8(%esp)8048bd7:   c7 44 24 04 c3 a3 04    movl   $0x804a3c3,0x4(%esp)8048bde: 08 8048bdf: 8b 44 24 30             mov    0x30(%esp),%eax8048be3:  89 04 24                mov    %eax,(%esp)8048be6:  e8 85 fc ff ff          call   8048870 <__isoc99_sscanf@plt>8048beb: 83 f8 01                cmp    $0x1,%eax8048bee:    7f 05                   jg     8048bf5 <phase_3+0x31>8048bf0:    e8 e1 04 00 00          call   80490d6 <explode_bomb>8048bf5: 83 7c 24 18 07          cmpl   $0x7,0x18(%esp)8048bfa:  77 66                   ja     8048c62 <phase_3+0x9e>8048bfc:    8b 44 24 18             mov    0x18(%esp),%eax8048c00:  ff 24 85 00 a2 04 08    jmp    *0x804a200(,%eax,4)8048c07:  b8 00 00 00 00          mov    $0x0,%eax8048c0c:    eb 05                   jmp    8048c13 <phase_3+0x4f>8048c0e:    b8 5d 02 00 00          mov    $0x25d,%eax8048c13:  2d a6 00 00 00          sub    $0xa6,%eax8048c18:   eb 05                   jmp    8048c1f <phase_3+0x5b>8048c1a:    b8 00 00 00 00          mov    $0x0,%eax8048c1f:    05 3f 03 00 00          add    $0x33f,%eax8048c24:  eb 05                   jmp    8048c2b <phase_3+0x67>8048c26:    b8 00 00 00 00          mov    $0x0,%eax8048c2b:    2d 6e 03 00 00          sub    $0x36e,%eax8048c30:  eb 05                   jmp    8048c37 <phase_3+0x73>8048c32:    b8 00 00 00 00          mov    $0x0,%eax8048c37:    05 6e 03 00 00          add    $0x36e,%eax8048c3c:  eb 05                   jmp    8048c43 <phase_3+0x7f>8048c3e:    b8 00 00 00 00          mov    $0x0,%eax8048c43:    2d 6e 03 00 00          sub    $0x36e,%eax8048c48:  eb 05                   jmp    8048c4f <phase_3+0x8b>8048c4a:    b8 00 00 00 00          mov    $0x0,%eax8048c4f:    05 6e 03 00 00          add    $0x36e,%eax8048c54:  eb 05                   jmp    8048c5b <phase_3+0x97>8048c56:    b8 00 00 00 00          mov    $0x0,%eax8048c5b:    2d 6e 03 00 00          sub    $0x36e,%eax8048c60:  eb 0a                   jmp    8048c6c <phase_3+0xa8>8048c62:    e8 6f 04 00 00          call   80490d6 <explode_bomb>8048c67: b8 00 00 00 00          mov    $0x0,%eax8048c6c:    83 7c 24 18 05          cmpl   $0x5,0x18(%esp)8048c71:  7f 06                   jg     8048c79 <phase_3+0xb5>8048c73:    3b 44 24 1c             cmp    0x1c(%esp),%eax8048c77:  74 05                   je     8048c7e <phase_3+0xba>8048c79:    e8 58 04 00 00          call   80490d6 <explode_bomb>8048c7e: 83 c4 2c                add    $0x2c,%esp8048c81:   c3                      ret    

分析

第2~9行是为scanf函数配置参数以及执行scanf函数的过程,用print查看$0x804a3c3中的内容,发现是“%d %d”,说明scanf读取两个整型数,即密钥由两个整型数构成。第13~14行,0x18(%esp)中是读取的第一个数,ja 指令意味着无符号数的大小比较,即第一个数的取值范围在[0, 7]中。第15~16行是变址寻址的地址跳转,%eax中是我们输入的第一个数,跳转的地址是*(0x804a200+(%eax)*4)。这里以输入的第一个数是0为例,跳转的地址为*0x804a200,查看该地址中的内容,发现是0x8048c0e,于是跳转至该处的指令上。指令不停执行,直到倒数第6~7行,我们发现,该处指令对第一个数的范围做了新的限制,即将第一个数的范围缩小到[0, 5]。倒数第5行,将%eax与输入的第二个数进行比较,若不相等则引爆炸弹。%eax是根据此前的操作得来的,这时利用print命令查看%eax的内容,发现为392,即密钥为0 392。但由于第一个数可取0,1,2,3,4,5,故依次测试其余5个数作为第一个数,可得到不同第一个数对应的第二个数。所有可能的结果为0 392 或 1 -213 或 2 -47 或 3 -878 或 4 0 或5 -878。

Phase_4

密钥:40 2 60 3 80 4

08048c82 <func4>:8048c82:  83 ec 1c                sub    $0x1c,%esp8048c85:   89 5c 24 10             mov    %ebx,0x10(%esp)8048c89:  89 74 24 14             mov    %esi,0x14(%esp)8048c8d:  89 7c 24 18             mov    %edi,0x18(%esp)8048c91:  8b 74 24 20             mov    0x20(%esp),%esi8048c95:  8b 5c 24 24             mov    0x24(%esp),%ebx8048c99:  85 f6                   test   %esi,%esi8048c9b:    7e 2b                   jle    8048cc8 <func4+0x46>8048c9d:  83 fe 01                cmp    $0x1,%esi8048ca0:    74 2b                   je     8048ccd <func4+0x4b>8048ca2:  89 5c 24 04             mov    %ebx,0x4(%esp)8048ca6:   8d 46 ff                lea    -0x1(%esi),%eax8048ca9:  89 04 24                mov    %eax,(%esp)8048cac:  e8 d1 ff ff ff          call   8048c82 <func4>8048cb1:    8d 3c 18                lea    (%eax,%ebx,1),%edi8048cb4:   89 5c 24 04             mov    %ebx,0x4(%esp)8048cb8:   83 ee 02                sub    $0x2,%esi8048cbb:    89 34 24                mov    %esi,(%esp)8048cbe:  e8 bf ff ff ff          call   8048c82 <func4>8048cc3:    8d 1c 07                lea    (%edi,%eax,1),%ebx8048cc6:   eb 05                   jmp    8048ccd <func4+0x4b>8048cc8:  bb 00 00 00 00          mov    $0x0,%ebx8048ccd:    89 d8                   mov    %ebx,%eax8048ccf:    8b 5c 24 10             mov    0x10(%esp),%ebx8048cd3:  8b 74 24 14             mov    0x14(%esp),%esi8048cd7:  8b 7c 24 18             mov    0x18(%esp),%edi8048cdb:  83 c4 1c                add    $0x1c,%esp8048cde:   c3                      ret    08048cdf <phase_4>:8048cdf:    83 ec 2c                sub    $0x2c,%esp8048ce2:   8d 44 24 18             lea    0x18(%esp),%eax8048ce6:  89 44 24 0c             mov    %eax,0xc(%esp)8048cea:   8d 44 24 1c             lea    0x1c(%esp),%eax8048cee:  89 44 24 08             mov    %eax,0x8(%esp)8048cf2:   c7 44 24 04 c3 a3 04    movl   $0x804a3c3,0x4(%esp)8048cf9: 08 8048cfa: 8b 44 24 30             mov    0x30(%esp),%eax8048cfe:  89 04 24                mov    %eax,(%esp)8048d01:  e8 6a fb ff ff          call   8048870 <__isoc99_sscanf@plt>8048d06: 83 f8 02                cmp    $0x2,%eax8048d09:    75 0e                   jne    8048d19 <phase_4+0x3a>8048d0b:    8b 44 24 18             mov    0x18(%esp),%eax8048d0f:  83 f8 01                cmp    $0x1,%eax8048d12:    7e 05                   jle    8048d19 <phase_4+0x3a>8048d14:    83 f8 04                cmp    $0x4,%eax8048d17:    7e 05                   jle    8048d1e <phase_4+0x3f>8048d19:    e8 b8 03 00 00          call   80490d6 <explode_bomb>8048d1e: 8b 44 24 18             mov    0x18(%esp),%eax8048d22:  89 44 24 04             mov    %eax,0x4(%esp)8048d26:   c7 04 24 06 00 00 00    movl   $0x6,(%esp)8048d2d:  e8 50 ff ff ff          call   8048c82 <func4>8048d32:    3b 44 24 1c             cmp    0x1c(%esp),%eax8048d36:  74 05                   je     8048d3d <phase_4+0x5e>8048d38:    e8 99 03 00 00          call   80490d6 <explode_bomb>8048d3d: 83 c4 2c                add    $0x2c,%esp8048d40:   c3                      ret    

分析

第2~9行是为scanf函数配置参数以及执行scanf函数的过程,用print查看$0x804a3c3中的内容,发现是“%d %d”,说明scanf读取两个整型数,即密钥由两个整型数构成,这也可以通过第10~11行,scanf的返回值不为2时则引爆炸弹看出。第12~16行,0x18(%esp)中的内容是输入的第二个数(与phase_3不同是因为写入的位置调换了),可以看出第二个数的取值范围为(1, 4]。第18~20行,为func4函数配置参数。接下来我们进入func4函数,func4是一个递归函数,为了便于解释,在此将func4函数翻译成c语言。func4函数翻译成c语言为

int func4(int k, int n)
{if(k <= 0) return 0;if(k == 1) return n;return func4(k-1, n) + func4(k-2, n) + n;
}

由于第二个数的取值范围为(1, 4],故第二个数可取2,3,4,这里以2为例。代码中传入的参数为k = 6, n = 2(即以2为例的第二个数),经过计算得到func4(6, 2)的结果为40。接着观察到第21~22行,需要比较func4的结果是否与0x1c(%esp),即输入的第一个数相等,若不相等则引爆炸弹。于是,便可得知第一个数即是func(6, 第二个数)的值,当第二个数为2时,第一个数为40,故密钥为40 2。当第二个数取3/4时,还可以生成密钥60 3和80 4。

Phase_5

密钥:`adejn abdejl abejmn adefin adefjm adekln adijln aefjkn aeflmn aegjln bdefln beijln cdejln defgjn defijl dehjmn deijkn deilmn dfjlmn efhjln efijmn ejklmn 更多

08048d41 <phase_5>:8048d41:    53                      push   %ebx8048d42: 83 ec 18                sub    $0x18,%esp8048d45:   8b 5c 24 20             mov    0x20(%esp),%ebx8048d49:  89 1c 24                mov    %ebx,(%esp)8048d4c:  e8 5a 02 00 00          call   8048fab <string_length>8048d51:    83 f8 06                cmp    $0x6,%eax8048d54:    74 05                   je     8048d5b <phase_5+0x1a>8048d56:    e8 7b 03 00 00          call   80490d6 <explode_bomb>8048d5b: ba 00 00 00 00          mov    $0x0,%edx8048d60:    b8 00 00 00 00          mov    $0x0,%eax8048d65:    0f be 0c 03             movsbl (%ebx,%eax,1),%ecx8048d69:   83 e1 0f                and    $0xf,%ecx8048d6c:    03 14 8d 20 a2 04 08    add    0x804a220(,%ecx,4),%edx8048d73:  83 c0 01                add    $0x1,%eax8048d76:    83 f8 06                cmp    $0x6,%eax8048d79:    75 ea                   jne    8048d65 <phase_5+0x24>8048d7b:    83 fa 45                cmp    $0x45,%edx8048d7e:   74 05                   je     8048d85 <phase_5+0x44>8048d80:    e8 51 03 00 00          call   80490d6 <explode_bomb>8048d85: 83 c4 18                add    $0x18,%esp8048d88:   5b                      pop    %ebx8048d89: c3                      ret    

分析

由第3~6行可知,程序将输入的字符串作为参数执行了string_length函数,若输入的字符串长度不为6,则引爆炸弹。接下来是一个循环结构,遍历输入的字符串,将当前遍历的字节与0xf相与得到一个数,这个数将作为首地址在0x804a220处的整型数组的下标,并将访问到的整型数组中的数进行累加,若和为0x45,则该字符串即是密钥。为了便于解释,将汇编代码翻译成c代码如下:

char s[6] = 输入的字符串
int a[16] = 首地址在0x804a220处的数组,由于和0xf相与,故下标最大值为15,数组长度为16
int sum = 0; //累加和
for(int i = 0; i < 6; ++i)
{int pos = (int)s[i] & 0xf;sum += a[pos];
}
if(sum != 0x45)explode_bomb();

于是,我们要找到密钥,就要先找到总和为0x45的6个数的下标。为了方便找到这样的6个数的集合,可以编写程序,得到这16个数中,共有22组这样的6个数的集合。这22组数在数组中的下标如下:

0 1 4 5 10 14

1 2 4 5 10 12

1 2 5 10 13 14

1 4 5 6 9 14

1 4 5 6 10 13

1 4 5 11 12 14

1 4 9 10 12 14

1 5 6 10 11 14

1 5 6 12 13 14

1 5 7 10 12 14

2 4 5 6 12 14

2 5 9 10 12 14

3 4 5 10 12 14

4 5 6 7 10 14

4 5 6 9 10 12

4 5 8 10 13 14

4 5 9 10 11 14

4 5 9 12 13 14

4 6 10 12 13 14

5 6 8 10 12 14

5 6 9 10 13 14

5 10 11 12 13 14

那么,现在的问题变成了如何构造满足条件的字符串了。因为下标的范围为[0, 15],ASCII码为0~15的字符都是控制字符,是不可打印的字符,所以不能够直接输入[0, 15]对应的字符组成的字符串。考虑到输出英文字母比较直观,且字母’a’和’A’的ASCII码分别为1100001和1000001,后4位是0001,故可以将下标加上’a’-1或’A’-1,将得到的字符组成字符串即为密钥。如下标为5时,其二进制为0101,加上’a’-1为1100101,和0xf(1111)相与后仍为0101。

通过这种方法,更换下标的加数或调换下标的顺序,可以产生大量的密钥。

Phase_6

密钥:5 3 2 4 6 1

08048d8a <phase_6>:8048d8a:    56                      push   %esi8048d8b: 53                      push   %ebx8048d8c: 83 ec 44                sub    $0x44,%esp8048d8f:   8d 44 24 10             lea    0x10(%esp),%eax8048d93:  89 44 24 04             mov    %eax,0x4(%esp)8048d97:   8b 44 24 50             mov    0x50(%esp),%eax8048d9b:  89 04 24                mov    %eax,(%esp)8048d9e:  e8 68 04 00 00          call   804920b <read_six_numbers>8048da3: be 00 00 00 00          mov    $0x0,%esi8048da8:    8b 44 b4 10             mov    0x10(%esp,%esi,4),%eax8048dac:   83 e8 01                sub    $0x1,%eax8048daf:    83 f8 05                cmp    $0x5,%eax8048db2:    76 05                   jbe    8048db9 <phase_6+0x2f>8048db4:    e8 1d 03 00 00          call   80490d6 <explode_bomb>8048db9: 83 c6 01                add    $0x1,%esi8048dbc:    83 fe 06                cmp    $0x6,%esi8048dbf:    74 33                   je     8048df4 <phase_6+0x6a>8048dc1:    89 f3                   mov    %esi,%ebx8048dc3:    8b 44 9c 10             mov    0x10(%esp,%ebx,4),%eax8048dc7:   39 44 b4 0c             cmp    %eax,0xc(%esp,%esi,4)8048dcb:    75 05                   jne    8048dd2 <phase_6+0x48>8048dcd:    e8 04 03 00 00          call   80490d6 <explode_bomb>8048dd2: 83 c3 01                add    $0x1,%ebx8048dd5:    83 fb 05                cmp    $0x5,%ebx8048dd8:    7e e9                   jle    8048dc3 <phase_6+0x39>8048dda:    eb cc                   jmp    8048da8 <phase_6+0x1e>8048ddc:    8b 52 08                mov    0x8(%edx),%edx8048ddf:   83 c0 01                add    $0x1,%eax8048de2:    39 c8                   cmp    %ecx,%eax8048de4:    75 f6                   jne    8048ddc <phase_6+0x52>8048de6:    89 54 b4 28             mov    %edx,0x28(%esp,%esi,4)8048dea:   83 c3 01                add    $0x1,%ebx8048ded:    83 fb 06                cmp    $0x6,%ebx8048df0:    75 07                   jne    8048df9 <phase_6+0x6f>8048df2:    eb 1c                   jmp    8048e10 <phase_6+0x86>8048df4:    bb 00 00 00 00          mov    $0x0,%ebx8048df9:    89 de                   mov    %ebx,%esi8048dfb:    8b 4c 9c 10             mov    0x10(%esp,%ebx,4),%ecx8048dff:   b8 01 00 00 00          mov    $0x1,%eax8048e04:    ba 3c c1 04 08          mov    $0x804c13c,%edx8048e09:  83 f9 01                cmp    $0x1,%ecx8048e0c:    7f ce                   jg     8048ddc <phase_6+0x52>8048e0e:    eb d6                   jmp    8048de6 <phase_6+0x5c>8048e10:    8b 5c 24 28             mov    0x28(%esp),%ebx8048e14:  8b 44 24 2c             mov    0x2c(%esp),%eax8048e18:  89 43 08                mov    %eax,0x8(%ebx)8048e1b:   8b 54 24 30             mov    0x30(%esp),%edx8048e1f:  89 50 08                mov    %edx,0x8(%eax)8048e22:   8b 44 24 34             mov    0x34(%esp),%eax8048e26:  89 42 08                mov    %eax,0x8(%edx)8048e29:   8b 54 24 38             mov    0x38(%esp),%edx8048e2d:  89 50 08                mov    %edx,0x8(%eax)8048e30:   8b 44 24 3c             mov    0x3c(%esp),%eax8048e34:  89 42 08                mov    %eax,0x8(%edx)8048e37:   c7 40 08 00 00 00 00    movl   $0x0,0x8(%eax)8048e3e:   be 05 00 00 00          mov    $0x5,%esi8048e43:    8b 43 08                mov    0x8(%ebx),%eax8048e46:   8b 10                   mov    (%eax),%edx8048e48:  39 13                   cmp    %edx,(%ebx)8048e4a:  7d 05                   jge    8048e51 <phase_6+0xc7>8048e4c:    e8 85 02 00 00          call   80490d6 <explode_bomb>8048e51: 8b 5b 08                mov    0x8(%ebx),%ebx8048e54:   83 ee 01                sub    $0x1,%esi8048e57:    75 ea                   jne    8048e43 <phase_6+0xb9>8048e59:    83 c4 44                add    $0x44,%esp8048e5c:   5b                      pop    %ebx8048e5d: 5e                      pop    %esi8048e5e: c3                      ret    

分析

第2~5行读取6个整型数,由6~11行可知,读入的6个数的范围都必须在区间[1, 6]中。第12~22行的作用是判断读入的6个数是否两两互不相同,若否则会引爆炸弹。接下来的部分是将存储在0x804c13c处的内容存储到栈帧中,其中0x804c13c处存储的数据类型是node,node是一个由两个int(一个记录数值,一个记录node编号)、一个node*构成的结构体(实际上,node是一个链表),共占12个字节。存储node的方式是:按照读入的6个数(假定为a[i]),将编号为a[i]的node存储到第i个位置。然后从栈帧中的存储的第一个node开始,每个node与其后一个node中的数值进行比较,若后大于前,则会引爆炸弹。

综合该函数的行为,可以发现,函数要求将6个node在栈帧中根据数值从大到小排列,而输入的6个数,即是从大到小排列的node的编号。

以下是6个node的信息:

(gdb) x 0x804c13c

0x804c13c <node1>:     0x00000036

0x804c140 <node1+4>: 0x00000001

0x804c144 <node1+8>: 0x0804c148

0x804c148 <node2>:     0x000000c1

0x804c14c <node2+4>: 0x00000002

0x804c150 <node2+8>: 0x0804c154

0x804c154 <node3>:     0x00000273

0x804c158 <node3+4>: 0x00000003

0x804c15c <node3+8>: 0x0804c160

0x804c160 <node4>:     0x00000089

0x804c164 <node4+4>: 0x00000004

0x804c168 <node4+8>: 0x0804c16c

0x804c16c <node5>:     0x000003d7

0x804c170 <node5+4>: 0x00000005

0x804c174 <node5+8>: 0x0804c178

0x804c178 <node6>:     0x0000006c

0x804c17c <node6+4>: 0x00000006

0x804c180 <node6+8>: 0x00000000

可见,6个node根据数值从大到小排列的编号为5 3 2 4 6 1,即为密钥。

Secret_phase

密钥:107

0804925b <phase_defused>:804925b:  81 ec 8c 00 00 00       sub    $0x8c,%esp8049261:   65 a1 14 00 00 00       mov    %gs:0x14,%eax8049267:    89 44 24 7c             mov    %eax,0x7c(%esp)804926b:  31 c0                   xor    %eax,%eax804926d:    83 3d cc c3 04 08 06    cmpl   $0x6,0x804c3cc8049274:   75 72                   jne    80492e8 <phase_defused+0x8d>8049276:  8d 44 24 2c             lea    0x2c(%esp),%eax804927a:  89 44 24 10             mov    %eax,0x10(%esp)804927e:  8d 44 24 28             lea    0x28(%esp),%eax8049282:  89 44 24 0c             mov    %eax,0xc(%esp)8049286:   8d 44 24 24             lea    0x24(%esp),%eax804928a:  89 44 24 08             mov    %eax,0x8(%esp)804928e:   c7 44 24 04 c9 a3 04    movl   $0x804a3c9,0x4(%esp)8049295: 08 8049296: c7 04 24 d0 c4 04 08    movl   $0x804c4d0,(%esp)804929d:    e8 ce f5 ff ff          call   8048870 <__isoc99_sscanf@plt>80492a2: 83 f8 03                cmp    $0x3,%eax80492a5:    75 35                   jne    80492dc <phase_defused+0x81>80492a7:  c7 44 24 04 d2 a3 04    movl   $0x804a3d2,0x4(%esp)80492ae: 08 80492af: 8d 44 24 2c             lea    0x2c(%esp),%eax80492b3:  89 04 24                mov    %eax,(%esp)80492b6:  e8 09 fd ff ff          call   8048fc4 <strings_not_equal>80492bb:    85 c0                   test   %eax,%eax80492bd:    75 1d                   jne    80492dc <phase_defused+0x81>80492bf:  c7 04 24 98 a2 04 08    movl   $0x804a298,(%esp)80492c6:    e8 35 f5 ff ff          call   8048800 <puts@plt>80492cb:    c7 04 24 c0 a2 04 08    movl   $0x804a2c0,(%esp)80492d2:    e8 29 f5 ff ff          call   8048800 <puts@plt>80492d7:    e8 d4 fb ff ff          call   8048eb0 <secret_phase>80492dc: c7 04 24 f8 a2 04 08    movl   $0x804a2f8,(%esp)80492e3:    e8 18 f5 ff ff          call   8048800 <puts@plt>80492e8:    8b 44 24 7c             mov    0x7c(%esp),%eax80492ec:  65 33 05 14 00 00 00    xor    %gs:0x14,%eax80492f3:    74 05                   je     80492fa <phase_defused+0x9f>80492f5:  e8 d6 f4 ff ff          call   80487d0 <__stack_chk_fail@plt>80492fa:    81 c4 8c 00 00 00       add    $0x8c,%esp8049300:   c3                      ret    8049301: 90                      nop8049302: 90                      nop8049303: 90                      nop8049304: 90                      nop8049305: 90                      nop8049306: 90                      nop8049307: 90                      nop8049308: 90                      nop8049309: 90                      nop804930a: 90                      nop804930b: 90                      nop804930c: 90                      nop804930d: 90                      nop804930e: 90                      nop804930f: 90                      nop08048e5f <fun7>:8048e5f:   53                      push   %ebx8048e60: 83 ec 18                sub    $0x18,%esp8048e63:   8b 54 24 20             mov    0x20(%esp),%edx8048e67:  8b 4c 24 24             mov    0x24(%esp),%ecx8048e6b:  85 d2                   test   %edx,%edx8048e6d:    74 37                   je     8048ea6 <fun7+0x47>8048e6f:   8b 1a                   mov    (%edx),%ebx8048e71:  39 cb                   cmp    %ecx,%ebx8048e73:    7e 13                   jle    8048e88 <fun7+0x29>8048e75:   89 4c 24 04             mov    %ecx,0x4(%esp)8048e79:   8b 42 04                mov    0x4(%edx),%eax8048e7c:   89 04 24                mov    %eax,(%esp)8048e7f:  e8 db ff ff ff          call   8048e5f <fun7>8048e84: 01 c0                   add    %eax,%eax8048e86:    eb 23                   jmp    8048eab <fun7+0x4c>8048e88:   b8 00 00 00 00          mov    $0x0,%eax8048e8d:    39 cb                   cmp    %ecx,%ebx8048e8f:    74 1a                   je     8048eab <fun7+0x4c>8048e91:   89 4c 24 04             mov    %ecx,0x4(%esp)8048e95:   8b 42 08                mov    0x8(%edx),%eax8048e98:   89 04 24                mov    %eax,(%esp)8048e9b:  e8 bf ff ff ff          call   8048e5f <fun7>8048ea0: 8d 44 00 01             lea    0x1(%eax,%eax,1),%eax8048ea4:    eb 05                   jmp    8048eab <fun7+0x4c>8048ea6:   b8 ff ff ff ff          mov    $0xffffffff,%eax8048eab: 83 c4 18                add    $0x18,%esp8048eae:   5b                      pop    %ebx8048eaf: c3                      ret    08048eb0 <secret_phase>:8048eb0:   53                      push   %ebx8048eb1: 83 ec 18                sub    $0x18,%esp8048eb4:   e8 44 02 00 00          call   80490fd <read_line>8048eb9:    c7 44 24 08 0a 00 00    movl   $0xa,0x8(%esp)8048ec0:   00 8048ec1: c7 44 24 04 00 00 00    movl   $0x0,0x4(%esp)8048ec8:   00 8048ec9: 89 04 24                mov    %eax,(%esp)8048ecc:  e8 0f fa ff ff          call   80488e0 <strtol@plt>8048ed1:  89 c3                   mov    %eax,%ebx8048ed3:    8d 40 ff                lea    -0x1(%eax),%eax8048ed6:  3d e8 03 00 00          cmp    $0x3e8,%eax8048edb:  76 05                   jbe    8048ee2 <secret_phase+0x32>8048edd:   e8 f4 01 00 00          call   80490d6 <explode_bomb>8048ee2: 89 5c 24 04             mov    %ebx,0x4(%esp)8048ee6:   c7 04 24 88 c0 04 08    movl   $0x804c088,(%esp)8048eed:    e8 6d ff ff ff          call   8048e5f <fun7>8048ef2: 83 f8 03                cmp    $0x3,%eax8048ef5:    74 05                   je     8048efc <secret_phase+0x4c>8048ef7:   e8 da 01 00 00          call   80490d6 <explode_bomb>8048efc: c7 04 24 c4 a1 04 08    movl   $0x804a1c4,(%esp)8048f03:    e8 f8 f8 ff ff          call   8048800 <puts@plt>8048f08:    e8 4e 03 00 00          call   804925b <phase_defused>8048f0d:    83 c4 18                add    $0x18,%esp8048f10:   5b                      pop    %ebx8048f11: c3                      ret    8048f12: 90                      nop8048f13: 90                      nop8048f14: 90                      nop8048f15: 90                      nop8048f16: 90                      nop8048f17: 90                      nop8048f18: 90                      nop8048f19: 90                      nop8048f1a: 90                      nop8048f1b: 90                      nop8048f1c: 90                      nop8048f1d: 90                      nop8048f1e: 90                      nop8048f1f: 90                      nop

分析

在阅读汇编代码时,发现phase_6之下还有一个secret_phase,直接输入6个密钥会发现直接退出了运行,而不会进入这个秘密关卡,于是我启用搜索,看看是什么函数调用了这个secret_phase。

在代码中搜索secret_phase,会发现phase_defused函数调用了secret_phase,而这个phase_defused函数是每个phase通过之后会执行的函数,于是我联想到是否需要在某个phase输入特定的信息才会进入这个secret_phase。阅读phase_defused的汇编代码,会发现出现了几个内存的地址,查看这些地址的内容,会发现这的确是secret_phase的入口。

其中,DrEvil出现在0x804a3d2中,而phase_defused调用了strings_not_equal函数来比较DrEvil和一个应该是我们输入的字符串的地方,这说明,我们开启secret_phase需要输入的信息是DrEvil!

80492a7: c7 44 24 04 d2 a3 04     movl   $0x 804a3d2,0x4(%esp)80492ae:      0880492af:      8d 44 24 2c           lea    0x2c(%esp),%eax80492b3:      89 04 24                    mov    %eax,(%esp)80492b6:      e8 09 fd ff ff            call   8048fc4 <strings_not_equal>80492bb:      85 c0                      test   %eax,%eax80492bd:      75 1d                      jne    80492dc <phase_defused+0x81>

那么这个DrEvil该什么时候输入呢?

804929d: e8 ce f5 ff ff            call   8048870 <__isoc99_sscanf@plt>
80492a2:      83 f8 03             cmp    $0x3,%eax
80492a5: 75 35                     jne    80492dc <phase_defused+0x81>

通过以上这3行可以看到,包括DrEvil在内,输入的参数的数量需要是3,且分别为%d %d %s否则将会跳过secret_phase的调用。可以发现,只有phase_4的密钥加上DrEvil满足这个条件。经测试,在phase_4的密钥后加上DrEvil后,在phase_6通过后,果然出现了secret_phase。

进入secret_phase,函数先读取我们输入的密钥,然后调用了c语言的strtol函数,这个函数将字符串转为长整型数,其中参数0xa意味着将其转为十进制。接下来函数会比较经转换后的数自减1后与0x3e8(十进制为1000)的无符号数大小,这意味着我们输入的数必须在[1, 1001]中。接着,函数会调用一个递归函数func7,参数为0x804c088和我们输入的数,func7的伪代码如下

int func7(int* k, int n)
{if(k <= 0) return -1;int kn = *k;if(kn == n) return 0;else if(kn < n) return 2*func7(k+2, n) + 1;else return 2*func7(k+1, n);
}

又由以下两个指令知,func7(0x804c088, 我们输入的数)的返回值必须为3,否则将引爆炸弹。

8048ef2: 83 f8 03              cmp    $0x3,%eax
8048ef5: 74 05                 je     8048efc <secret_phase+0x4c>

观察内存中地址为0x804c088的内容,是一棵二叉树:第一个地址存储数据,第二、三个地址存储下子结点的地址。于是我们只要分析func7的行为,并根据0x804c088中的内容,即可确定要输入的数。

首先,函数要求func7最终返回3。要构造一个3,根据func7的特性,可以这样构造:2*(2*0+1)+1,这意味着,最后一次要返回0,倒数第二次(或第二次)要返回2*func7(k+2, n) + 1,倒数第三次(或第一次)要返回2*func7(k+2, n) + 1。或者说,要返回的值是二叉树查找时比较的次数。要满足这样的关系,假如我们把第1~3次比较的数设为k1、k2、k3,那么,我们输入的数n要满足的条件是n < k1且n < k2且n == k3。要找到k3,查找0x804c088处的内存即可,经查找,为0x6b,转换为十进制为107,即为密钥。

(gdb) x/x 0x804c088

0x804c088 <n1>:   0x00000024

0x804c08c <n1+4>:      0x0804c094

0x804c090 <n1+8>:      0x0804c0a0

0x804c094 <n21>: 0x00000008

0x804c098 <n21+4>:    0x0804c0c4

0x804c09c <n21+8>:    0x0804c0ac

0x804c0a0 <n22>: 0x00000032

0x804c0a4 <n22+4>:    0x0804c0b8

0x804c0a8 <n22+8>:    0x0804c0d0

0x804c0ac <n32>: 0x00000016

0x804c0b0 <n32+4>:    0x0804c118

0x804c0b4 <n32+8>:    0x0804c100

0x804c0b8 <n33>: 0x0000002d

0x804c0bc <n33+4>:    0x0804c0dc

0x804c0c0 <n33+8>:    0x0804c124

0x804c0c4 <n31>: 0x00000006

0x804c0c8 <n31+4>:    0x0804c0e8

0x804c0cc <n31+8>:     0x0804c10c

0x804c0d0 <n34>: 0x0000006b

0x804c0d4 <n34+4>:    0x0804c0f4

0x804c0d8 <n34+8>:    0x0804c130

0x804c0dc <n45>: 0x00000028

0x804c0e0 <n45+4>:    0x00000000

0x804c0e4 <n45+8>:    0x00000000

0x804c0e8 <n41>: 0x00000001

0x804c0ec <n41+4>:     0x00000000

0x804c0f0 <n41+8>:     0x00000000

0x804c0f4 <n47>:  0x00000063

0x804c0f8 <n47+4>:     0x00000000

0x804c0fc <n47+8>:     0x00000000

0x804c100 <n44>: 0x00000023

0x804c104 <n44+4>:    0x00000000

0x804c108 <n44+8>:    0x00000000

0x804c10c <n42>: 0x00000007

0x804c110 <n42+4>:    0x00000000

0x804c114 <n42+8>:    0x00000000

0x804c118 <n43>: 0x00000014

0x804c11c <n43+4>:     0x00000000

0x804c120 <n43+8>:    0x00000000

0x804c124 <n46>: 0x0000002f

0x804c128 <n46+4>:    0x00000000

0x804c12c <n46+8>:    0x00000000

0x804c130 <n48>: 0x000003e9

0x804c134 <n48+4>:    0x00000000

0x804c138 <n48+8>:    0x00000000

《深入理解计算机系统》Lab3 Bomblab相关推荐

  1. 《深入理解计算机系统》--BombLab学习笔记(含隐藏阶段)

    前言 在皓哥的鼓励下,磕磕绊绊断断续续终于做完了BombLab,这个实验确实很有趣而且对我帮助很大,做完也非常的有成就感(HGNB)

  2. 《深入理解计算机系统》实验二Bomb Lab下载和官方文档机翻

    前言 <深入理解计算机系统>官网:http://csapp.cs.cmu.edu/3e/labs.html 该篇文章是实验二Bomb Lab的Writeup机翻. 原文:http://cs ...

  3. 《深入理解计算机系统》读书随笔-位操作

    最近开始读<深入理解计算机系统>这本书.对于书中提到的从程序员的角度解读计算机系统这一说法非常感兴趣,所以决定好好读一读.从开始接触计算机编程就是站在一个高级语言的层次,虽然对编译原理,操 ...

  4. 【组队学习】【32期】深入理解计算机系统

    深入理解计算机系统 航路开辟者:李岳昆.易远哲 领航员:初晓宇 航海士:叶前坤.沈豪 基本信息 开源内容:https://github.com/datawhalechina/team-learning ...

  5. 深入理解计算机系统 -资料整理 高清中文版_在所不辞的博客-CSDN博客_深入理解计算机系统第四版pdf

    深入理解计算机系统 -资料整理 高清中文版_在所不辞的博客-CSDN博客_深入理解计算机系统第四版pdf

  6. 深入理解计算机系统(3)

    深入理解计算机系统(3) 本文我们主要讲关于数据的的表示方式:原码,反码和补码. 本文在写作过程中,参考了园中的这篇文章<原码,反码,补码详解>,特此声明. 一原码 计算机中是使用二进制来 ...

  7. 《深入理解计算机系统-程序结构》读书笔记

    1.计算机系统漫游 计算机系统是由硬件和系统软件组成的,他们共同工作来运行应用程序.在<深入理解计算机系统>一书中将会学到很多实践的技巧.例如:了解编译器是如何实现过程调用的.避免缓冲区溢 ...

  8. csapp 、sicp 、深入理解计算机系统、 计算机程序的构造和解释

    CSAPP 第一版的英文版 深入理解计算机系统第一版中文版  这个是csdn账号  这里上传文件大小在10M以内  这个pdf是19+M的 深入理解计算机系统第二版的中文版下载 第一版英文版的介绍原书 ...

  9. 《深入理解计算机系统》第七章读书笔记

    <深入理解计算机系统>第七章读书笔记 第七章:连接 连接 1.连接:将各种代码和数据部分收集起来并组合成为一个单一文件的过程.这个文件可被加载或拷贝到存储器并执行. 2.连接可以执行于编译 ...

  10. 深入理解计算机系统:网络编程 上

    一直以来对计算机网络比较感兴趣,但是无奈大学计算机网络的学习非常表面,已经忘得差不多了.毕业后读了一些网络方面的书,对网络知识的冰山一角有了一些感悟. 随着网络方面的书越读越多,不懂的地方也越来越多. ...

最新文章

  1. HTML5新特性总结
  2. 安卓手机也能跑YOLOv5了!
  3. 基于SSM实现保健院管理系统
  4. qemu 对虚机的地址空间管理
  5. FireDAC 中文字段过滤问题
  6. 电脑老是弹出vrvedp_m_出现三个可疑进程vrvedp_m.exe vrvrf_c.exe vrvsafec.exe
  7. 理解zookeeper选举机制
  8. Swift中文教程(一)基础数据类型
  9. 【two pointers 细节题】cf1041dD. Glider
  10. 关于Java String对象创建问题解惑
  11. 解决apache的the requested operation has failed
  12. CSS hack 初学小结
  13. STC学习:温度与光照传感器
  14. 报告:黑马11月就业薪资出炉!哪个学科更好就业?
  15. 北京大学王悦博士给学生的话
  16. 计算机ram特点,RAM有什么特点
  17. Python开发 之 Python3打包(windows/linux)详解
  18. 去《挪威的森林》之后
  19. ROS 问题(topic types do not match、topic datatype/md5sum not match、msg xxx have changed. rerun cmake)
  20. 【小技巧】PDF 转 图片 虚拟打印

热门文章

  1. s8 android z,三星Galaxy S8领衔:2017年十佳Android智能手机
  2. 三剑客----awk
  3. 文件分片上传阿里云OSS
  4. DEV05 GBase 8a MPP Cluster 数据库性能优化
  5. phpredis使用zadd批量添加到集合
  6. python代码手机壁纸_python爬虫高清壁纸小白实战代码
  7. 一篇文章基础HTML总结
  8. Linux系统创建vlan教程,linux vlan配置详解
  9. 学习期间的感悟和个人写的一段歌词
  10. Blender插件开发:bpy.utils模块