ICS bomblab

发布于 2019-09-11  2018 次阅读


实验目标

拆弹,熟悉汇编语言,学习gdb调试。

实验方法

gdb查看内存,手动调试汇编代码,了解代码原理并找到密码。

实验结果

6关+隐藏关全部拆弹,引爆炸弹1次,SCORE 70.

密码:

Brownie, you are doing a heck of a job.
0 1 1 2 3 5
1 868
132 4 DrEvil
nnlccc
3 6 2 4 1 5
6

解题过程

phase_1

0000000000400ef0 <phase_1>:
  400ef0:   48 83 ec 08             sub    $0x8,%rsp
  400ef4:   be 80 24 40 00          mov    $0x402480,%esi
  400ef9:   e8 c0 03 00 00          callq  4012be <strings_not_equal>
  400efe:   85 c0                   test   %eax,%eax
  400f00:   74 05                   je     400f07 <phase_1+0x17>
  400f02:   e8 1d 06 00 00          callq  401524 <explode_bomb>
  400f07:   48 83 c4 08             add    $0x8,%rsp
  400f0b:   c3                      retq  

查看0x402480内存,得到字符串:

Brownie, you are doing a heck of a job.\0 ...

strings_not_equal()函数比较%edi(密码)和%esi字符串是否相等,相等则返回0。

故把密码设为上述字符串即可通关。

phase_2

0000000000400f0c <phase_2>:
  400f0c:   55                      push   %rbp
  400f0d:   53                      push   %rbx
  400f0e:   48 83 ec 28             sub    $0x28,%rsp
  400f12:   48 89 e6                mov    %rsp,%rsi
  400f15:   e8 40 06 00 00          callq  40155a <read_six_numbers>

read_six_numbers()读入6个int类型的数,并把数放入栈(%rsp).

  400f1a:   83 3c 24 00             cmpl   $0x0,(%rsp)
  400f1e:   75 07                   jne    400f27 <phase_2+0x1b>
  400f20:   83 7c 24 04 01          cmpl   $0x1,0x4(%rsp)
  400f25:   74 21                   je     400f48 <phase_2+0x3c>
  400f27:   e8 f8 05 00 00          callq  401524 <explode_bomb>

此处需确保第一个数和第二个数为0和1.

  400f2c:   eb 1a                   jmp    400f48 <phase_2+0x3c>
  400f2e:   8b 43 f8                mov    -0x8(%rbx),%eax
  400f31:   03 43 fc                add    -0x4(%rbx),%eax
  400f34:   39 03                   cmp    %eax,(%rbx)
  400f36:   74 05                   je     400f3d <phase_2+0x31>
  400f38:   e8 e7 05 00 00          callq  401524 <explode_bomb>
  400f3d:   48 83 c3 04             add    $0x4,%rbx
  400f41:   48 39 eb                cmp    %rbp,%rbx
  400f44:   75 e8                   jne    400f2e <phase_2+0x22>
  400f46:   eb 0c                   jmp    400f54 <phase_2+0x48>
  400f48:   48 8d 5c 24 08          lea    0x8(%rsp),%rbx
  400f4d:   48 8d 6c 24 18          lea    0x18(%rsp),%rbp
  400f52:   eb da                   jmp    400f2e <phase_2+0x22>

此处程序进行一个循环,从第3个数开始,依次判断前一个数是否等于后两个数之和(cmp %eax, (%rbx))。

故答案为:

0 1 1 2 3 5

phase_3

0000000000400f5b <phase_3>:
  400f5b:   48 83 ec 18             sub    $0x18,%rsp
  400f5f:   48 8d 4c 24 08          lea    0x8(%rsp),%rcx
  400f64:   48 8d 54 24 0c          lea    0xc(%rsp),%rdx
  400f69:   be ad 27 40 00          mov    $0x4027ad,%esi
  400f6e:   b8 00 00 00 00          mov    $0x0,%eax
  400f73:   e8 b8 fc ff ff          callq  400c30 <__isoc99_sscanf@plt>
  400f78:   83 f8 01                cmp    $0x1,%eax
  400f7b:   7f 05                   jg     400f82 <phase_3+0x27>
  400f7d:   e8 a2 05 00 00          callq  401524 <explode_bomb>
  400f82:   83 7c 24 0c 07          cmpl   $0x7,0xc(%rsp)
  400f87:   77 3c                   ja     400fc5 <phase_3+0x6a>
  400f89:   8b 44 24 0c             mov    0xc(%rsp),%eax
  400f8d:   ff 24 c5 e0 24 40 00    jmpq   *0x4024e0(,%rax,8)

读入两个数,注意最后一条指令jmpq, 依据%rax的值(第一个参数)进行条件跳转,此处随意选取第一个参数为1.

  400fd1:   b8 64 03 00 00          mov    $0x364,%eax
  400fd6:   3b 44 24 08             cmp    0x8(%rsp),%eax
  400fda:   74 05                   je     400fe1 <phase_3+0x86>
  400fdc:   e8 43 05 00 00          callq  401524 <explode_bomb>
  400fe1:   48 83 c4 18             add    $0x18,%rsp
  400fe5:   c3                      retq   

跳转到此处可知第二个参数为0x364=868.

phase_4

000000000040101e <phase_4>:
  40101e:   48 83 ec 18             sub    $0x18,%rsp
  401022:   48 8d 4c 24 0c          lea    0xc(%rsp),%rcx
  401027:   48 8d 54 24 08          lea    0x8(%rsp),%rdx
  40102c:   be ad 27 40 00          mov    $0x4027ad,%esi
  401031:   b8 00 00 00 00          mov    $0x0,%eax
  401036:   e8 f5 fb ff ff          callq  400c30 <__isoc99_sscanf@plt>
  40103b:   83 f8 02                cmp    $0x2,%eax
  40103e:   75 0c                   jne    40104c <phase_4+0x2e>
  401040:   8b 44 24 0c             mov    0xc(%rsp),%eax
  401044:   83 e8 02                sub    $0x2,%eax
  401047:   83 f8 02                cmp    $0x2,%eax
  40104a:   76 05                   jbe    401051 <phase_4+0x33>
  40104c:   e8 d3 04 00 00          callq  401524 <explode_bomb>

读入两个参数,需保证第二个参数的值>=4.

  401051:   8b 74 24 0c             mov    0xc(%rsp),%esi
  401055:   bf 07 00 00 00          mov    $0x7,%edi
  40105a:   e8 87 ff ff ff          callq  400fe6 <func4>
  40105f:   3b 44 24 08             cmp    0x8(%rsp),%eax
  401063:   74 05                   je     40106a <phase_4+0x4c>
  401065:   e8 ba 04 00 00          callq  401524 <explode_bomb>
  40106a:   48 83 c4 18             add    $0x18,%rsp
  40106e:   c3                      retq   

调用func4(),这是一个递归函数,设传入的两个参数为a,b,分析可知func(a,b)=b+func(a-1,b)+func(a-2,b),取b=4,返回值就是f(7, 4).当然此处没必要分析此函数,直接gdb运行并查看返回值即可。

第一个参数即为递归函数的返回值,此处为132.

phase_5

000000000040106f <phase_5>:
  40106f:   53                      push   %rbx
  401070:   48 89 fb                mov    %rdi,%rbx
  401073:   e8 29 02 00 00          callq  4012a1 <string_length>
  401078:   83 f8 06                cmp    $0x6,%eax
  40107b:   74 05                   je     401082 <phase_5+0x13>
  40107d:   e8 a2 04 00 00          callq  401524 <explode_bomb>

读入一个6字符字符串。

  401082:   b8 00 00 00 00          mov    $0x0,%eax
  401087:   ba 00 00 00 00          mov    $0x0,%edx
  40108c:   0f b6 0c 03             movzbl (%rbx,%rax,1),%ecx
  401090:   83 e1 0f                and    $0xf,%ecx
  401093:   03 14 8d 20 25 40 00    add    0x402520(,%rcx,4),%edx
  40109a:   48 83 c0 01             add    $0x1,%rax
  40109e:   48 83 f8 06             cmp    $0x6,%rax
  4010a2:   75 e8                   jne    40108c <phase_5+0x1d>
  4010a4:   83 fa 2c                cmp    $0x2c,%edx
  4010a7:   74 05                   je     4010ae <phase_5+0x3f>
  4010a9:   e8 76 04 00 00          callq  401524 <explode_bomb>
  4010ae:   5b                      pop    %rbx
  4010af:   90                      nop
  4010b0:   c3                      retq 

查看0x402520内存,得到一个映射表:

2,a,6,1,c,0x10,9,3,4,7,e,5,b,8,f,d

依次枚举每一个字符,取末4位(&0xf)通过上述映射表得到一个数,如此得到6个数,相加之和需等于0x2c=44.

此处答案为nnlccc(15+15+11+1+1+1).

phase_6

00000000004010b1 <phase_6>:
  4010b1:   41 55                   push   %r13
  4010b3:   41 54                   push   %r12
  4010b5:   55                      push   %rbp
  4010b6:   53                      push   %rbx
  4010b7:   48 83 ec 58             sub    $0x58,%rsp
  4010bb:   48 8d 74 24 30          lea    0x30(%rsp),%rsi
  4010c0:   e8 95 04 00 00          callq  40155a <read_six_numbers>
  4010c5:   4c 8d 6c 24 30          lea    0x30(%rsp),%r13
  4010ca:   41 bc 00 00 00 00       mov    $0x0,%r12d
  4010d0:   4c 89 ed                mov    %r13,%rbp
  4010d3:   41 8b 45 00             mov    0x0(%r13),%eax
  4010d7:   83 e8 01                sub    $0x1,%eax
  4010da:   83 f8 05                cmp    $0x5,%eax
  4010dd:   76 05                   jbe    4010e4 <phase_6+0x33>
  4010df:   e8 40 04 00 00          callq  401524 <explode_bomb>
  4010e4:   41 83 c4 01             add    $0x1,%r12d
  4010e8:   41 83 fc 06             cmp    $0x6,%r12d
  4010ec:   75 07                   jne    4010f5 <phase_6+0x44>
  4010ee:   be 00 00 00 00          mov    $0x0,%esi
  4010f3:   eb 42                   jmp    401137 <phase_6+0x86>
  4010f5:   44 89 e3                mov    %r12d,%ebx
  4010f8:   48 63 c3                movslq %ebx,%rax
  4010fb:   8b 44 84 30             mov    0x30(%rsp,%rax,4),%eax
  4010ff:   39 45 00                cmp    %eax,0x0(%rbp)
  401102:   75 05                   jne    401109 <phase_6+0x58>
  401104:   e8 1b 04 00 00          callq  401524 <explode_bomb>
  401109:   83 c3 01                add    $0x1,%ebx
  40110c:   83 fb 05                cmp    $0x5,%ebx
  40110f:   7e e7                   jle    4010f8 <phase_6+0x47>
  401111:   49 83 c5 04             add    $0x4,%r13
  401115:   eb b9                   jmp    4010d0 <phase_6+0x1f>

这一段读入6个整数,要求每个数互不相等且>=1,<=6,故输入为1~6的排列,不妨设为a~f.

  401117:   48 8b 52 08             mov    0x8(%rdx),%rdx
  40111b:   83 c0 01                add    $0x1,%eax
  40111e:   39 c8                   cmp    %ecx,%eax
  401120:   75 f5                   jne    401117 <phase_6+0x66>
  401122:   eb 05                   jmp    401129 <phase_6+0x78>
  401124:   ba f0 42 60 00          mov    $0x6042f0,%edx
  401129:   48 89 14 74             mov    %rdx,(%rsp,%rsi,2)
  40112d:   48 83 c6 04             add    $0x4,%rsi
  401131:   48 83 fe 18             cmp    $0x18,%rsi
  401135:   74 15                   je     40114c <phase_6+0x9b>
  401137:   8b 4c 34 30             mov    0x30(%rsp,%rsi,1),%ecx
  40113b:   83 f9 01                cmp    $0x1,%ecx
  40113e:   7e e4                   jle    401124 <phase_6+0x73>
  401140:   b8 01 00 00 00          mov    $0x1,%eax
  401145:   ba f0 42 60 00          mov    $0x6042f0,%edx
  40114a:   eb cb                   jmp    401117 <phase_6+0x66>

对6个数分别分配了6个8字节变量,不妨设为a'~f',第i个数的值为0x6042f0 + (i-1)*0x10 (地址).

查看0x6042f0内存:

address value
0x6042f0 0x0100000380
0x0000604300
0x604300 0x020000028d
0x0000604310
0x604310 0x03000000ea
0x0000604320
0x604320 0x0400000372
0x0000604330
0x604330 0x05000003cd
0x0000604340
0x604340 0x06000000eb
0x0000000000

此处易知每个新变量对应块(16位)的第一个值为一般值,第二个值(后8位)为指向下一个地址的指针。

  40114c:   48 8b 1c 24             mov    (%rsp),%rbx
  401150:   48 8d 44 24 08          lea    0x8(%rsp),%rax
  401155:   48 8d 74 24 30          lea    0x30(%rsp),%rsi
  40115a:   48 89 d9                mov    %rbx,%rcx
  40115d:   48 8b 10                mov    (%rax),%rdx
  401160:   48 89 51 08             mov    %rdx,0x8(%rcx)
  401164:   48 83 c0 08             add    $0x8,%rax
  401168:   48 39 f0                cmp    %rsi,%rax
  40116b:   74 05                   je     401172 <phase_6+0xc1>
  40116d:   48 89 d1                mov    %rdx,%rcx
  401170:   eb eb                   jmp    40115d <phase_6+0xac>
  401172:   48 c7 42 08 00 00 00    movq   $0x0,0x8(%rdx)

按照a'~f'的值依次调整每个块的指针为哪个块,如a'的指针初始指向b'(0x604300),可更改为c'(0x604310)。

  40117a:   bd 05 00 00 00          mov    $0x5,%ebp
  40117f:   48 8b 43 08             mov    0x8(%rbx),%rax
  401183:   8b 00                   mov    (%rax),%eax
  401185:   39 03                   cmp    %eax,(%rbx)
  401187:   7e 05                   jle    40118e <phase_6+0xdd>
  401189:   e8 96 03 00 00          callq  401524 <explode_bomb>
  40118e:   48 8b 5b 08             mov    0x8(%rbx),%rbx
  401192:   83 ed 01                sub    $0x1,%ebp
  401195:   75 e8                   jne    40117f <phase_6+0xce>
  401197:   48 83 c4 58             add    $0x58,%rsp
  40119b:   5b                      pop    %rbx
  40119c:   5d                      pop    %rbp
  40119d:   41 5c                   pop    %r12
  40119f:   41 5d                   pop    %r13
  4011a1:   c3                      retq  

从a'开始依次判断前一个块的值(取前32位比较)是否小于等于后一个块,所有都小于等于才能通关。

将a=1,2,...,6对应块的值重新排序如下:0x00ea, 0x00eb, 0x028d, 0x0372, 0x0380, 0x03cd,这样只需将a'设为0x00ea对应块3,再将a'的指针设为0x00eb对应块6,...。

密码为3 6 2 4 1 5.

secret_phase

进入方法:

搜索secret_phase,在phase_defused()函数可找到入口:

00000000004016c2 <phase_defused>:
  4016c2:   48 83 ec 68             sub    $0x68,%rsp
  4016c6:   bf 01 00 00 00          mov    $0x1,%edi
  4016cb:   e8 90 fd ff ff          callq  401460 <send_msg>
  4016d0:   83 3d c5 30 20 00 06    cmpl   $0x6,0x2030c5(%rip)        # 60479c <num_input_strings>
  4016d7:   75 6d                   jne    401746 <phase_defused+0x84>

可看出第6关破解后才能进入。

  4016d9:   4c 8d 44 24 10          lea    0x10(%rsp),%r8
  4016de:   48 8d 4c 24 08          lea    0x8(%rsp),%rcx
  4016e3:   48 8d 54 24 0c          lea    0xc(%rsp),%rdx
  4016e8:   be f7 27 40 00          mov    $0x4027f7,%esi
  4016ed:   bf b0 48 60 00          mov    $0x6048b0,%edi
  4016f2:   b8 00 00 00 00          mov    $0x0,%eax
  4016f7:   e8 34 f5 ff ff          callq  400c30 <__isoc99_sscanf@plt>
  4016fc:   83 f8 03                cmp    $0x3,%eax
  4016ff:   75 31                   jne    401732 <phase_defused+0x70>
  401701:   be 00 28 40 00          mov    $0x402800,%esi
  401706:   48 8d 7c 24 10          lea    0x10(%rsp),%rdi
  40170b:   e8 ae fb ff ff          callq  4012be <strings_not_equal>
  401710:   85 c0                   test   %eax,%eax
  401712:   75 1e                   jne    401732 <phase_defused+0x70>

前部分读入一个串,查看内存知读入两个整数一个字符串,结合之前的密码知为读取phase_4的密码对应行并检查最后一个字符串其是否与答案字符串(“DrEvil")相等。

调整密码后进入隐藏关:

00000000004011e0 <secret_phase>:
  4011e0:   53                      push   %rbx
  4011e1:   e8 b6 03 00 00          callq  40159c <read_line>
  4011e6:   ba 0a 00 00 00          mov    $0xa,%edx
  4011eb:   be 00 00 00 00          mov    $0x0,%esi
  4011f0:   48 89 c7                mov    %rax,%rdi
  4011f3:   e8 08 fa ff ff          callq  400c00 <strtol@plt>
  4011f8:   48 89 c3                mov    %rax,%rbx
  4011fb:   8d 40 ff                lea    -0x1(%rax),%eax
  4011fe:   3d e8 03 00 00          cmp    $0x3e8,%eax
  401203:   76 05                   jbe    40120a <secret_phase+0x2a>
  401205:   e8 1a 03 00 00          callq  401524 <explode_bomb>

read_line()读入一行字符,strtol()函数将一行字符串转换为10进制数并存入%rax,要求%rax<=1001.

  40120a:   89 de                   mov    %ebx,%esi
  40120c:   bf 10 41 60 00          mov    $0x604110,%edi
  401211:   e8 8c ff ff ff          callq  4011a2 <fun7>
  401216:   85 c0                   test   %eax,%eax
  401218:   74 05                   je     40121f <secret_phase+0x3f>
  40121a:   e8 05 03 00 00          callq  401524 <explode_bomb>
  40121f:   bf a8 24 40 00          mov    $0x4024a8,%edi
  401224:   e8 17 f9 ff ff          callq  400b40 <puts@plt>
  401229:   e8 94 04 00 00          callq  4016c2 <phase_defused>
  40122e:   5b                      pop    %rbx
  40122f:   c3                      retq   

进入函数fun7(),若返回值为0则通关。

00000000004011a2 <fun7>:
  4011a2:   48 83 ec 08             sub    $0x8,%rsp
  4011a6:   48 85 ff                test   %rdi,%rdi
  4011a9:   74 2b                   je     4011d6 <fun7+0x34>
  4011ab:   8b 17                   mov    (%rdi),%edx
  4011ad:   39 f2                   cmp    %esi,%edx
  4011af:   7e 0d                   jle    4011be <fun7+0x1c>
  4011b1:   48 8b 7f 08             mov    0x8(%rdi),%rdi
  4011b5:   e8 e8 ff ff ff          callq  4011a2 <fun7>
  4011ba:   01 c0                   add    %eax,%eax
  4011bc:   eb 1d                   jmp    4011db <fun7+0x39>
  4011be:   b8 00 00 00 00          mov    $0x0,%eax
  4011c3:   39 f2                   cmp    %esi,%edx
  4011c5:   74 14                   je     4011db <fun7+0x39>
  4011c7:   48 8b 7f 10             mov    0x10(%rdi),%rdi
  4011cb:   e8 d2 ff ff ff          callq  4011a2 <fun7>
  4011d0:   8d 44 00 01             lea    0x1(%rax,%rax,1),%eax
  4011d4:   eb 05                   jmp    4011db <fun7+0x39>
  4011d6:   b8 ff ff ff ff          mov    $0xffffffff,%eax
  4011db:   48 83 c4 08             add    $0x8,%rsp
  4011df:   c3                      retq   

func7()为递归转移,有些复杂,大致意思是不断比较你输入的值x和一个值y,若x>y则增大y,若x<y则减小y,y的增大还和内存有关。经过一段分析,取x=6可以得到返回值0.

得失总结

锻炼了不依赖gdb肉眼调试程序的能力;学习到了gdb的许多新用法;对汇编语言更加了解了。


WRYYYYYYYYY!