介绍:这是一个“拆弹”游戏,玩家要在反汇编中找到每一关的拆弹密码,一共有6个phases。 该Lab的下载地址:http://csapp.cs.cmu.edu/3e/labs.html

警告:本文含有剧透内容,想要玩Lab2的慎看!

`` 惯例先看代码:

0000000000401062 <phase_5>:
  401062:   push   %rbx
  401063:   sub    $0x20,%rsp
  401067:   mov    %rdi,%rbx
  40106a:   mov    %fs:0x28,%rax
  401073:   mov    %rax,0x18(%rsp)
  401078:   xor    %eax,%eax
  40107a:   callq  40131b <string_length>
  40107f:   cmp    $0x6,%eax
  401082:   je     4010d2 <phase_5+0x70>
  401084:   callq  40143a <explode_bomb>
  401089:	jmp    4010d2 <phase_5+0x70>
  40108b:   movzbl (%rbx,%rax,1),%ecx
  40108f:   mov    %cl,(%rsp)
  401092:   mov    (%rsp),%rdx
  401096:	and    $0xf,%edx
  401099:   movzbl 0x4024b0(%rdx),%edx
  4010a0:   mov    %dl,0x10(%rsp,%rax,1)
  4010a4:   add    $0x1,%rax
  4010a8:   cmp    $0x6,%rax
  4010ac:   jne    40108b <phase_5+0x29>
  4010ae:   movb   $0x0,0x16(%rsp)
  4010b3:   mov    $0x40245e,%esi
  4010b8:   lea    0x10(%rsp),%rdi
  4010bd:   callq  401338 <strings_not_equal>
  4010c2:   test   %eax,%eax
  4010c4:   je     4010d9 <phase_5+0x77>
  4010c6:   callq  40143a <explode_bomb>
  4010cb:   nopl   0x0(%rax,%rax,1)
  4010d0:   jmp    4010d9 <phase_5+0x77>
  4010d2:   mov    $0x0,%eax
  4010d7:   jmp    40108b <phase_5+0x29>
  4010d9:	mov    0x18(%rsp),%rax
  4010de:	xor    %fs:0x28,%rax
  4010e7:	je     4010ee <phase_5+0x8c>
  4010e9:	callq  400b30 <__stack_chk_fail@plt>
  4010ee:	add    $0x20,%rsp
  4010f2:	pop    %rbx
  4010f3:	retq

这段汇编出现了一堆之前没见过的操作,比如开头的mov %fs:0x28,%rax,经过查询后是用来进行stack-guard check的,详见这个回答。 无视掉那些奇怪的操作以后,转换成伪c语言大约是这样:

rbx=rdi; //input
*(rsp+18)=rax;
eax=string_length(rdi);
if(eax==6)goto 4010d2;
explode_bomb();
goto 4010d2;
40108b:
ecx=*(rbx+rax);
*rsp=cl; //cx低8bit
rdx=*rsp;
edx=edx & 0xf
edx=*(0x4024b0+rdx);
*(rsp+rax+0x10)=dl; //dx低8bit
rax++;
if(rax!=0x6) goto 40108b;
*(rsp+0x16)=0;
if(!strings_not_equal(rsp+0x10,*0x40245e)) goto 4010d9;
explode_bomb();
goto 4010d9;
4010d2:
eax=0;
goto 40108b;
4010d9:
rax=*(rsp+18)

经过简单的分析和整理,可得:

rbx=rdi; //input
rax=string_length(rdi);
assert(rax==6);
rax=0;

for(rax=0;rax!=0x6;rax++){
    *rsp=rbx[rax];
    *(rsp+rax+0x10)=(*("maduiersnfotvbyl"+((*rsp)%16)));
}

*(rsp+0x16)=0;
assert(!strings_not_equal(rsp+0x10,"flyers"));

所以这段程序其实就是读入一个6个字符的字符串,对每个字符%16,然后映射到"maduiersnfotvbyl"这16个字符中用来构成"flyers"。 所以只要把9 15 14 5 6 7加16直到变成一个可以输入的字符即可。 用Python写一个脚本来找:

>>> ["".join([chr(c+i*16) for c in [9,15,14,5,6,7]]) for i in range(0,10)]
['\t\x0f\x0e\x05\x06\x07', '\x19\x1f\x1e\x15\x16\x17', ")/.%&'", '9?>567', 'IONEFG', 'Y_^UVW', 'ionefg', 'y\x7f~uvw', '\x89\x8f\x8e\x85\x86\x87', '\x99\x9f\x
9e\x95\x96\x97']

随便选一个"IONEFG"输进去过关。