本作品采用知识共享署名-非商业性使用-相同方式共享 3.0Unported许可协议进行许可。允许非商业转载,但应注明作者及出处。
作者:xialulee
最初发布于:2012年03月04日,http://blog.sina.com.cn/xialulee
这一个关卡页面的图像显示了很多cookie(一开始我还以为是一堆石头),说明这一关与cookie有关。在图像的左下角有一幅小图,曾经在linkedlist那一关出现过,说明这又是一个linkedlist演伸出来的关卡(之前还有一个读zip注释的)。在Firefox中用shift+ctrl+k打开JavaScript控制台,输入:
alert(document.cookie)
可以看见如下的说明:
cookie说:you should have followedbusynothing。看来这一关,是要一边遍历linkedlist,一边读取cookie。之前是有一个关卡是一边遍历linkedlist,一边读取zip中的注释,那时我用awk+python搞定的,awk遍历linkedlist,python读取注释。
先来看看链表的节点是什么样的。打开http://www.pythonchallenge.com/pc/def/linkedlist.php?busynothing=12345进入第一个节点:
页面中给出了下一个节点的ID:
If you came here from level 4 - go back!
You should follow the obvious chain...
and the next busynothing is 44827
44827即是下一个节点的ID。看看这个页面的cookie是什么,还是打开JavaScript控制台,输入alert(document.cookie),得到:info=B;...接着进入下一个节点http://www.pythonchallenge.com/pc/def/linkedlist.php?busynothing=44827
得到下一个节点的ID:
and the next busynothing is 45439
再用JavaScript控制台,执行代码,得到cookie为:info=Z;...
回想一下,遍历了两个节点,得到两个字符“BZ”,BZ看起来是不是很眼熟呢?原来之前有一个关卡(level8),就是获取用户名和密码的那一个,利用bzip2算法。那时的bzip2数据,就是以BZ开头的。所以,这一关应该是遍历所有的节点,找出每个节点cookie中的字符,将它们拼接起来,用bzip2解压缩。
之前在攻克遍历linkedlist的关卡时,都是用的awk,这一次也不例外。第一次的时候直接利用gawk的网络编程能力来完成的。这一次不用这么麻烦的手法,来个比较轻松一点的,awk调用wget。wget可以输出responseheader的信息,例如:
我们只需要过滤出含有“Set-Cookie”和“and the next busynothing is(d+)”的行,即可得到下一个节点的ID和当前节点包含的数据。于是编写了如下的gawk脚本(在msys中执行,使用的wget来自GnuWin32):
#!/usr/bin/gawk-f
#2012.03.04PM07:53
#level17
#xialulee
BEGIN{
RS="rn";
command="wget-SO-http://huge:file@www.pythonchallenge.com/pc/def/linkedlist.php?busynothing=%d2>&1";
idx=12345;
bzstr[0]=0;
while(((cmd=sprintf(command,idx))|getline)>0){
if(match($0,/^[t]*Set-Cookie:info=([^;]+);/,result)){
bzstr[cnt++]=result[1];
printresult[1];
}
if(match($0,/andthenextbusynothingis([0-9]+)/,result)){
close(cmd);
idx=result[1];
printidx;
}
}
bunzip2="bunzip2";
for(i=0;i<cnt;++i){
ch=bzstr[i];
sub(/+/,"",ch);
if(sub(/^%/,"0x",ch)){
printf("%c",strtonum(ch))|bunzip2;
}else{
printf("%s",ch)|bunzip2;
}
}
close(bunzip2);
}
值得说明的有这样几点。
一、while循环,会不断地从wget输出的数据中读取行,进行分析,但是一旦遇到了含有“and the nextbusynothing is(d+)”的行,就会提取其中的数值保存到变量idx中,作为下一个节点的ID,关闭当前的wget,以新的ID开始一个新的wget进程。如果遍历到了尾部的节点,则idx不会更新,while循环会一直读取这个wget进程stdin的所有输出,然后退出。
二、遍历所有节点得到的数据需要进行相当于Python中的urllib.unquote_plus操作,在上面的gawk脚本中由最后的for循环来完成。同时,将unquote之后的数据通过管道传递给一个bunzip2进程,让其进行解压缩操作。
脚本最后的输出结果为:
“is it the 26th already? call his father and inform him that "theflowers are on their way". he'llunderstand.”这句话看来和Mozart那一个level(level15)有关。同时又说要打电话,说明可能和图像是电话的那一关(level13)有关。给Mozart的老爸打电话试试:
In [5]: s =Server('http://www.pythonchallenge.com/pc/phonebook.php')
In [6]: s.phone('Leopold')
Out[6]: '555-VIOLIN'
用violin构造URL:http://www.pythonchallenge.com/pc/return/violin.html,得到说明:
no! i mean yes! but ../stuff/violin.php.
再进入(..表示上一级目录):http://www.pythonchallenge.com/pc/stuff/violin.php,出现一张大头照,页面的标题为:it'sme. what do you want?
对于这种猜谜型的实在是有点搞不懂,在参考了http://unixwars.com/2007/09/23/python-challenge-level-17-eat/之后,才知道接下来的做法,在Vimperator中输入:
:js << EOF
var xhr = new XMLHttpRequest();
xhr.open("GET","http://www.pythonchallenge.com/pc/stuff/violin.php");
xhr.setRequestHeader("Cookie","info=the+flowers+are+on+their+way");
xhr.onreadystatechange = function(aEvt) {
if (xhr.readyState==4&& xhr.status==200) {
alert(xhr.responseText);
}
}
xhr.send(null);
EOF
弹出:
其中的ballons,就是通关密语。终于过了这一关!