CSAPP LAB2 BombLab

CSAPP LAB2 BombLab

给一个可执行文件,需要按顺序输入6个字串,每个字串是一个小炸弹的密码。通过反汇编找出每个字串的内容。

考察汇编语言,gdb的使用。

关于GDB的使用

  • 终端下输入gdb进入GDB,使用file <文件名>加载二进制文件,加载后只是读取符号表,并不执行。使用set args <参数>给程序设置参数,可以为空(我遇到了没设置参数,但参数不为空的情况>_<,所以还是清一下参数比较好)。quit命令退出gdb。

  • run命令执行程序,kill命令杀死程序。step <数字i>为执行i条指令,类似单步执行,如果不填参数默认为1,类似vs的F11。nexti为执行一行,不进入函数,类似vs的F10。continue为执行到下一断点,类似vs的F5。finish为跳出函数,类似vs的Shift+F11。

  • disas <参数>为反汇编指令,根据参数不同:

    • 参数为函数名,则反汇编对应函数disas main
    • 参数为一个地址,则反汇编对应地址附近的函数disas 0x400e4e
  • print <参数>为查看指令,根据参数不同可以查看寄存器,内存等:

    • 参数为寄存器名,输出寄存器内容,按十进制输出:print $rax;按十六进制输出:print /x $rax

    • 可以使用GDB的”@”操作符查看连续内存,”@”的左边是第一个内存的地址的值,”@”的右边则你你想查看内存的长度。例如,对于如下代码:int arr[] = {2, 4, 6, 8, 10},可以通过如下命令查看arr前三个单元的数据:

    (gdb) p *arr@3

    $2 = {2, 4, 6}

  • examine /<n/f/u> <addr>命令来查看内存地址中的值:

    • n 表示显示内存的长度,也就是说从当前地址向后显示几个地址的内容。
    • f 表示显示的格式,如果是字符串,则用s,如果是数字,则可以用i。
    • u 表示从当前地址往后请求的字节数,默认是4个bytes。(b单字节,h双字节,w四字节,g八字节)
    • <addr> 表示一个内存地址。

炸弹1

先看main函数,发现可以指定一个文件作为stdin,便于储存每个密码。炸弹是由6个小炸弹串联起来,需要依次解除。那么先看炸弹1。

/* Hmm...  Six phases must be more secure than one phase! */
input = read_line();             /* Get input                   */
phase_1(input);                  /* Run the phase               */
phase_defused();                 /* Drat!  They figured it out!
                      * Let me know how they did it. */
printf("Phase 1 defused. How about the next one?\n");

read_line读入一行文字,作为参数交到phase_1里面,反汇编phase_1:

Dump of assembler code for function phase_1:
   0x0000000000400ee0 <+0>: sub    $0x8,%rsp
   0x0000000000400ee4 <+4>: mov    $0x402400,%esi
   0x0000000000400ee9 <+9>: callq  0x401338 <strings_not_equal>
   0x0000000000400eee <+14>:test   %eax,%eax
   0x0000000000400ef0 <+16>:je     0x400ef7 <phase_1+23>
   0x0000000000400ef2 <+18>:callq  0x40143a <explode_bomb>
   0x0000000000400ef7 <+23>:add    $0x8,%rsp
   0x0000000000400efb <+27>:retq   
End of assembler dump.

首先说明一下,函数开始会给自己分配栈空间,减小栈指针,在结束(8行)又会释放自己的栈空间,增加栈指针。又知道main调用这个函数时传了一个参数,他在1号参数寄存器%rdi中,第三行将0x402400存入寄存器%esi,是2号参数寄存器,在调用strings_not_equal函数。表明传入两个字符串,判断他们相不相等。%eax是返回值寄存器,test+je的组合表明为0则跳转。即字串相等返回0,释放栈,返回main;不等返回1,爆炸。

所以查看比较字串的内容,x/s 0x402400,得到第一题答案:Border relations with Canada have never been better.

炸弹2

与上面同样的思路,反汇编phase_2

Dump of assembler code for function phase_2:
   0x0000000000400efc <+0>:     push   %rbp
   0x0000000000400efd <+1>:     push   %rbx
   0x0000000000400efe <+2>:     sub    $0x28,%rsp
   0x0000000000400f02 <+6>:     mov    %rsp,%rsi
   0x0000000000400f05 <+9>:     callq  0x40145c <read_six_numbers>
   0x0000000000400f0a <+14>:    cmpl   $0x1,(%rsp)
   0x0000000000400f0e <+18>:    je     0x400f30 <phase_2+52>
   0x0000000000400f10 <+20>:    callq  0x40143a <explode_bomb>
   0x0000000000400f15 <+25>:    jmp    0x400f30 <phase_2+52>
   0x0000000000400f17 <+27>:    mov    -0x4(%rbx),%eax
   0x0000000000400f1a <+30>:    add    %eax,%eax
   0x0000000000400f1c <+32>:    cmp    %eax,(%rbx)
   0x0000000000400f1e <+34>:    je     0x400f25 <phase_2+41>
   0x0000000000400f20 <+36>:    callq  0x40143a <explode_bomb>
   0x0000000000400f25 <+41>:    add    $0x4,%rbx
   0x0000000000400f29 <+45>:    cmp    %rbp,%rbx
   0x0000000000400f2c <+48>:    jne    0x400f17 <phase_2+27>
   0x0000000000400f2e <+50>:    jmp    0x400f3c <phase_2+64>
   0x0000000000400f30 <+52>:    lea    0x4(%rsp),%rbx
   0x0000000000400f35 <+57>:    lea    0x18(%rsp),%rbp
   0x0000000000400f3a <+62>:    jmp    0x400f17 <phase_2+27>
   0x0000000000400f3c <+64>:    add    $0x28,%rsp
   0x0000000000400f40 <+68>:    pop    %rbx
   0x0000000000400f41 <+69>:    pop    %rbp
   0x0000000000400f42 <+70>:    retq   
End of assembler dump.

第二三行是保存被调用者保存寄存器,第四行分配栈空间,24‘25’26行同理。%rsi是2号参数寄存器,将栈指针传入。然后调用read_six_numbers函数,表明从输入的字串(一直在一号参数寄存器)读出6个int,放在栈指针开始的6*4=24(0x18)字节空间内。需要反汇编一下read_six_numbers函数,会看到里面调用c99_sscanf函数,第二个参数是个字符串%d %d %d %d %d %d,这里略过。

(%rsp)表示访问栈指针处地址指向内存的元素,就是scanf进来的第一个数。7\8\9行表示判断第一个数是不是1,不是就爆炸。跳到了20行。

其实接下来进入一个循环,20\21\22行是初始化循环。%rbx内存当前地址,%rbp存退出循环的判定条件。到11行,将(%rbx-4)的值载入寄存器%eax,回想初始化的时候将%rbx初始化为栈指针+4,即第二个int的位置,减4就是上一个int。12行将上一个int乘2,13行与当前int比较,不等就爆炸。16行前移%rbx,指向下一个int。17行与终止条件比较,注意初始化时%rbp被初始化为0x18,十进制是24,表示6个int,不是十进制的18!!!

所以看到,第一个数是1,后面每个是前一个的二倍,所以答案是1 2 4 8 16 32。之后恢复被调用者保存的寄存器,释放栈,返回。

炸弹3

反汇编phase_3

Dump of assembler code for function phase_3:
   0x0000000000400f43 <+0>:     sub    $0x18,%rsp
   0x0000000000400f47 <+4>:     lea    0xc(%rsp),%rcx
   0x0000000000400f4c <+9>:     lea    0x8(%rsp),%rdx
   0x0000000000400f51 <+14>:    mov    $0x4025cf,%esi
   0x0000000000400f56 <+19>:    mov    $0x0,%eax
   0x0000000000400f5b <+24>:    callq  0x400bf0 <__isoc99_sscanf@plt>
   0x0000000000400f60 <+29>:    cmp    $0x1,%eax
   0x0000000000400f63 <+32>:    jg     0x400f6a <phase_3+39>
   0x0000000000400f65 <+34>:    callq  0x40143a <explode_bomb>
   0x0000000000400f6a <+39>:    cmpl   $0x7,0x8(%rsp)
   0x0000000000400f6f <+44>:    ja     0x400fad <phase_3+106>
   0x0000000000400f71 <+46>:    mov    0x8(%rsp),%eax
   0x0000000000400f75 <+50>:    jmpq   *0x402470(,%rax,8)
   0x0000000000400f7c <+57>:    mov    $0xcf,%eax
   0x0000000000400f81 <+62>:    jmp    0x400fbe <phase_3+123>
   0x0000000000400f83 <+64>:    mov    $0x2c3,%eax
   0x0000000000400f88 <+69>:    jmp    0x400fbe <phase_3+123>
   0x0000000000400f8a <+71>:    mov    $0x100,%eax
   0x0000000000400f8f <+76>:    jmp    0x400fbe <phase_3+123>
   0x0000000000400f91 <+78>:    mov    $0x185,%eax
   0x0000000000400f96 <+83>:    jmp    0x400fbe <phase_3+123>
   0x0000000000400f98 <+85>:    mov    $0xce,%eax
   0x0000000000400f9d <+90>:    jmp    0x400fbe <phase_3+123>
   0x0000000000400f9f <+92>:    mov    $0x2aa,%eax
   0x0000000000400fa4 <+97>:    jmp    0x400fbe <phase_3+123>
   0x0000000000400fa6 <+99>:    mov    $0x147,%eax
   0x0000000000400fab <+104>:   jmp    0x400fbe <phase_3+123>
   0x0000000000400fad <+106>:   callq  0x40143a <explode_bomb>
   0x0000000000400fb2 <+111>:   mov    $0x0,%eax
   0x0000000000400fb7 <+116>:   jmp    0x400fbe <phase_3+123>
   0x0000000000400fb9 <+118>:   mov    $0x137,%eax
   0x0000000000400fbe <+123>:   cmp    0xc(%rsp),%eax
   0x0000000000400fc2 <+127>:   je     0x400fc9 <phase_3+134>
   0x0000000000400fc4 <+129>:   callq  0x40143a <explode_bomb>
   0x0000000000400fc9 <+134>:   add    $0x18,%rsp
   0x0000000000400fcd <+138>:   retq   
End of assembler dump.

看到7行调用sscanf,得知前面是在为调用做准备。使用x/s 0x4025cf命令查看字串得到%d %d,表明是读入两个int。分别存在(%rdx)=(%rsp+8)(%rcx)=(%rsp+c)处。8\9行判断sscanf的返回值,就是读入的int个数。11行判断第一个参数不能大于7,用jg的无符号数判断,是为了防止负数。

到了14行,jmpq *0x402470(,%rax,8),这是一个switch语句,使用比例变址的基址寻址。*0x402470表示实际的基止是该地址的数据,查看该地址的指向的数据,使用:x/s *0x402470命令,得到0x400f7c <phase_3+57>: "\270", <incomplete sequence \317>,表明从phase_3+57开始。我们看到,这是一系列的case语句,每个将一个值移入%eax,在33行将第二个参数与%eax比较,不等就爆炸。

所以我们得出炸弹3的一个解:0 207

炸弹4

反汇编phase_4,有:

Dump of assembler code for function phase_4:
   0x000000000040100c <+0>:     sub    $0x18,%rsp
   0x0000000000401010 <+4>:     lea    0xc(%rsp),%rcx
   0x0000000000401015 <+9>:     lea    0x8(%rsp),%rdx
   0x000000000040101a <+14>:    mov    $0x4025cf,%esi
   0x000000000040101f <+19>:    mov    $0x0,%eax
   0x0000000000401024 <+24>:    callq  0x400bf0 <__isoc99_sscanf@plt>
   0x0000000000401029 <+29>:    cmp    $0x2,%eax
   0x000000000040102c <+32>:    jne    0x401035 <phase_4+41>
   0x000000000040102e <+34>:    cmpl   $0xe,0x8(%rsp)
   0x0000000000401033 <+39>:    jbe    0x40103a <phase_4+46>
   0x0000000000401035 <+41>:    callq  0x40143a <explode_bomb>
   0x000000000040103a <+46>:    mov    $0xe,%edx
   0x000000000040103f <+51>:    mov    $0x0,%esi
   0x0000000000401044 <+56>:    mov    0x8(%rsp),%edi
   0x0000000000401048 <+60>:    callq  0x400fce <func4>
   0x000000000040104d <+65>:    test   %eax,%eax
   0x000000000040104f <+67>:    jne    0x401058 <phase_4+76>
   0x0000000000401051 <+69>:    cmpl   $0x0,0xc(%rsp)
   0x0000000000401056 <+74>:    je     0x40105d <phase_4+81>
   0x0000000000401058 <+76>:    callq  0x40143a <explode_bomb>
   0x000000000040105d <+81>:    add    $0x18,%rsp
   0x0000000000401061 <+85>:    retq   
End of assembler dump.

同样的套路,知道sscanf读入两个int。第一个必须0<=x<=14。然后调用函数func4,带三个参数,分别是之前驶入的第一个,0,14。先继续往下读,发现func4的返回值必须为0,且在19行,输入的第二个参数要是0。

反汇编func4,发现是个递归函数,真的头大。不过我试着避免递归,发现可以得到答案。

Dump of assembler code for function func4:
   0x0000000000400fce <+0>:     sub    $0x8,%rsp
   0x0000000000400fd2 <+4>:     mov    %edx,%eax
   0x0000000000400fd4 <+6>:     sub    %esi,%eax
   0x0000000000400fd6 <+8>:     mov    %eax,%ecx
   0x0000000000400fd8 <+10>:    shr    $0x1f,%ecx
   0x0000000000400fdb <+13>:    add    %ecx,%eax
   0x0000000000400fdd <+15>:    sar    %eax
   0x0000000000400fdf <+17>:    lea    (%rax,%rsi,1),%ecx
   0x0000000000400fe2 <+20>:    cmp    %edi,%ecx
   0x0000000000400fe4 <+22>:    jle    0x400ff2 <func4+36>
   0x0000000000400fe6 <+24>:    lea    -0x1(%rcx),%edx
   0x0000000000400fe9 <+27>:    callq  0x400fce <func4>
   0x0000000000400fee <+32>:    add    %eax,%eax
   0x0000000000400ff0 <+34>:    jmp    0x401007 <func4+57>
   0x0000000000400ff2 <+36>:    mov    $0x0,%eax
   0x0000000000400ff7 <+41>:    cmp    %edi,%ecx
   0x0000000000400ff9 <+43>:    jge    0x401007 <func4+57>
   0x0000000000400ffb <+45>:    lea    0x1(%rcx),%esi
   0x0000000000400ffe <+48>:    callq  0x400fce <func4>
   0x0000000000401003 <+53>:    lea    0x1(%rax,%rax,1),%eax
   0x0000000000401007 <+57>:    add    $0x8,%rsp
   0x000000000040100b <+61>:    retq   
End of assembler dump.

列出寄存器表:

%eax%ecx%edi%esi%edx
x0
0x0400fce <+0>:sub $0x8,%rsp
0x0400fd2 <+4>:mov %edx,%eax14
0x0400fd4 <+6>:sub %esi,%eax14
0x0400fd6 <+8>:mov %eax,%ecx14
0x0400fd8 <+10>:shr $0x1f,%ecx0
0x0400fdb <+13>:add %ecx,%eax14
0x0400fdd <+15>:sar %eax7
0x0400fdf <+17>:lea (%rax,%rsi,1),%ecx7
0x0400fe2 <+20>:cmp %edi,%ecx
0x0400fe4 <+22>:jle 0x400ff2 \func4+36>x<=7
0x0400fe6 <+24>:lea -0x1(%rcx),%edx
0x0400fe9 <+27>:callq 0x400fce \
0x0400fee <+32>:add %eax,%eax
0x0400ff0 <+34>:jmp 0x401007 \
0x0400ff2 <+36>:mov $0x0,%eax0
0x0400ff7 <+41>:cmp %edi,%ecx
0x0400ff9 <+43>:jge 0x401007 \x>=7
0x0400ffb <+45>:lea 0x1(%rcx),%esi
0x0400ffe <+48>:callq 0x400fce \
0x0401003 <+53>:lea 0x1(%rax,%rax,1),%eax
0x0401007 <+57>:add $0x8,%rsp
0x040100b <+61>:retq

在加粗处,可以发现,只要让x=7,是可以避免递归的。且返回值%eax也是0。所以我们得到了答案,7 0

炸弹5

反汇编phase_5

Dump of assembler code for function phase_5:
   0x0000000000401062 <+0>:     push   %rbx
   0x0000000000401063 <+1>:     sub    $0x20,%rsp
   0x0000000000401067 <+5>:     mov    %rdi,%rbx
   0x000000000040106a <+8>:     mov    %fs:0x28,%rax
   0x0000000000401073 <+17>:    mov    %rax,0x18(%rsp)
   0x0000000000401078 <+22>:    xor    %eax,%eax
   0x000000000040107a <+24>:    callq  0x40131b <string_length>
   0x000000000040107f <+29>:    cmp    $0x6,%eax
   0x0000000000401082 <+32>:    je     0x4010d2 <phase_5+112>
   0x0000000000401084 <+34>:    callq  0x40143a <explode_bomb>
   0x0000000000401089 <+39>:    jmp    0x4010d2 <phase_5+112>
   0x000000000040108b <+41>:    movzbl (%rbx,%rax,1),%ecx
   0x000000000040108f <+45>:    mov    %cl,(%rsp)
   0x0000000000401092 <+48>:    mov    (%rsp),%rdx
   0x0000000000401096 <+52>:    and    $0xf,%edx
   0x0000000000401099 <+55>:    movzbl 0x4024b0(%rdx),%edx
   0x00000000004010a0 <+62>:    mov    %dl,0x10(%rsp,%rax,1)
   0x00000000004010a4 <+66>:    add    $0x1,%rax
   0x00000000004010a8 <+70>:    cmp    $0x6,%rax
   0x00000000004010ac <+74>:    jne    0x40108b <phase_5+41>
   0x00000000004010ae <+76>:    movb   $0x0,0x16(%rsp)
   0x00000000004010b3 <+81>:    mov    $0x40245e,%esi
   0x00000000004010b8 <+86>:    lea    0x10(%rsp),%rdi
   0x00000000004010bd <+91>:    callq  0x401338 <strings_not_equal>
   0x00000000004010c2 <+96>:    test   %eax,%eax
   0x00000000004010c4 <+98>:    je     0x4010d9 <phase_5+119>
   0x00000000004010c6 <+100>:   callq  0x40143a <explode_bomb>
   0x00000000004010cb <+105>:   nopl   0x0(%rax,%rax,1)
   0x00000000004010d0 <+110>:   jmp    0x4010d9 <phase_5+119>
   0x00000000004010d2 <+112>:   mov    $0x0,%eax
   0x00000000004010d7 <+117>:   jmp    0x40108b <phase_5+41>
   0x00000000004010d9 <+119>:   mov    0x18(%rsp),%rax
   0x00000000004010de <+124>:   xor    %fs:0x28,%rax
   0x00000000004010e7 <+133>:   je     0x4010ee <phase_5+140>
   0x00000000004010e9 <+135>:   callq  0x400b30 <__stack_chk_fail@plt>
   0x00000000004010ee <+140>:   add    $0x20,%rsp
   0x00000000004010f2 <+144>:   pop    %rbx
   0x00000000004010f3 <+145>:   retq   
End of assembler dump.

9\10行知道,输入一个字符串,长度为6。13到21行是个循环,先跳过,看22到25行。25行调用strings_not_equal函数,22\23\24行为调用做准备,%rdi%esi分别是一号二号参数,查看一下二号参数的值:x/s 0x40245e,得到flyers。如果返回值0,这两个字串相等,就跳出,否则爆炸。

现在来看循环,19\20行知道,%rax是控制循环的变量。在第四行将输入字串的指针给了%rbx,13行把指向字串的内容给%ecx,14\15\16行倒腾一阵,最终就是取了输入字母的二进制后四位。涉及一些mov指令的填充问题,细节参考csapp。17行采用了一个基址寻址,将输入字母的二进制后四位作为偏移,加到基址0x4024b0上,给%edx。18行将新生成的字串拷到栈区,使用基址比例变址寻址。

所以理清一下这一段,就是输入字串每个字母的二进制后四位作为偏移量,从基址0x4024b0中取字母,构成新的字串,与flyers比较,相等就ok。所以我们看一下x/s 0x4024b0的内容:maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?"。找出flyers的偏移,分别为9 15 14 5 6 7,那么输入的字串只要后四位符合就好,一个答案是ionefg(0x49,0x4f,0x4E,0x45,0x46,0x47)

炸弹6

这个好像是个链表排序,有点麻烦就没细想,参考%%%大佬

秘密关卡

如何发现?看bomb.cphase_6后面并没有输出,但是解除炸弹是有输出的。说明秘密在phase_defused函数里面。

参考大佬

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值