2015-08-10

python challenge 11-20

接上边继续斗智斗勇中。越来越觉得智商不够用了。。

python challenge 11

http://www.pythonchallenge.com/pc/return/5808.html

11

题目只有一张模糊的图片,源码也没什么提示,看页面title为odd even.

猜测这张模糊的图片是否是将两张图片合成的,因此将其奇偶坐标分离开.

分离坐标有四种情况,(odd, odd),(odd, even),(even, odd),(even, even).

我这里取双奇偶坐标(odd, odd)和(even, even).

在PIL库中,使用load函数的效果比使用putpixel和getpixel更高效,因此选择使用load函数.

def odd_even():
    from PIL import Image
    im = Image.open("img/python-challenge-11.jpg")
    w, h = im.size

    imgs = [Image.new(im.mode, (w / 2, h / 2)) for i in xrange(2)]
    imgs_load = [img.load() for img in imgs]
    org = im.load()

    for i in xrange(w):
        for j in xrange(h):
            if (i + j) % 2 == 0:
                org_pos = (i, j)
                new_pos = (i / 2, j / 2)
                imgs_load[i % 2][new_pos] = org[org_pos]

    [imgs[i].save('img/python-challenge-11-%d.jpg' % i) for i in xrange(2)]

最后生成的两张图片是一样的

11-evil

显示的单词evil即为答案。

python challenge 12

http://www.pythonchallenge.com/pc/return/evil.html

12

一张没有什么异常的图片,查看下源码,注意看图片名字,evil1.jpg,这里应该尝试下evil2.jpg了。

evil2.jpg -> not jpg –gfx

evil3.jpg -> no more evils…

evil4.jpg -> Bert is evil! go back!(只在ie中可看到)

根据这些提示,先将evil2.jpg改为evil2.gfx,得到这个文件

GFX文件格式:GFX是一套跨平台的图形生成包,是一种可用于交互的图形格式,详情百度下。

到这里就没有思路了,看了下提示才知道,原来原图那人把扑克牌分成5堆,要把那个gfx文件用分牌的方式分成5个文件。

gfx的第一个字节给第一个文件,第二个字节给第二个文件,第三个字节给第三个文件,第四个字节给第四个文件,第五个字节给第五个文件,第六个字节给第一个文件……

def evil():
    f = open('files/evil2.gfx', 'rb+')
    content = f.read()
    f.close()

    for i in xrange(5):
        f = open('files/evil2-%d.jpg' % i, 'wb+')
        f.write(content[i::5])
        f.close()

最后得到5张图片连接起来即为结果:disproportional

12-evil2

python challenge 13

http://www.pythonchallenge.com/pc/return/disproportional.html

13

这个页面显示了一个电话按键,并且标题为call him,很明显是要拨打一个电话号码。

图片最下边提示phone that evil,再回头看那个evil4.jpg的提示,Bert is evil! go back!

所以是要打电话给Bert,怎么打,不可能用自己手机打吧!然后在页面到处点点,一不小心点到了5就进去了。

进入页面:http://www.pythonchallenge.com/pc/phonebook.php

这是什么鬼,止步于此,开始度娘。。。

xmlrpc是使用http协议做为传输协议的rpc机制,使用xml文本的方式传输命令和数据blablabla…

反正就是要用到python的xmlrpclib模块来连接这个php页面,然后查看其方法,其中有个名为phone方法就是答案。

def disproportional():
    import xmlrpclib
    xml_rpc = xmlrpclib.ServerProxy("http://www.pythonchallenge.com/pc/phonebook.php")
    print xml_rpc.system.listMethods()
    print xml_rpc.system.methodHelp('phone')
    print xml_rpc.phone('Bert')
>>> ['phone', 'system.listMethods', 'system.methodHelp', 'system.methodSignature', 'system.multicall', 'system.getCapabilities']
    Returns the phone of a person
    555-ITALY

最后输入555-ITALY.html不行。。改为ITALY.html提示SMALL letters。。好吧。。italy.html

python challenge 14

http://www.pythonchallenge.com/pc/return/italy.html

14

所有提示信息:

根据网页标题的提示:walk around
网页源码的提示:100*100 = (100+99+99+98) + (...
下边那张图wire.png点开标题为10000*1

先验证序列公式

# 100*100 = (100+99+99+98) + (...
items = [i + i-1 + i-1 + i-2 for i in xrange(100, 1, -2)]
items_value = reduce(lambda x, y: x+y, items)
print "%d %d" % (len(items), items_value)
>>> 50 10000

可知该序列公式是正确的。

10000x1 = 100x100是否要重新组图,按照这个思路把10000个像素重组

from PIL import Image
org_img = Image.open('img/wire.png')
org_data = list(org_img.getdata())
res_img = Image.new(org_img.mode, (100, 100))
res_data = res_img.load()
org_index = 0
for i in xrange(100):
    for j in xrange(100):
        res_data[j, i] = org_data[org_index]
        org_index += 1
res_img.save("img/wire-0.png")

最开始是按照先行后列的方式重组发现图片是反的,然后按照先列后行的方式重组,得到一个图片。

14-bit

万分欣喜,进入bit.html页面就傻了:you took the wrong curve.

真是个悲伤的故事。。。

下边就完全没有任何头绪,只要又要借助度娘了,最后得到的解题思路是:

将10000x1的那张图片按照序列化的格式进行重新生成100x100的图片,生成的逻辑是:

按照图片面包的方向,从右边开始,外向内绕圈圈,

先向一个方向走100个像素,然后转90度,走99个像素,再转90度,再走99个像素,再转90度,再走98个像素,这样就完成了一个圈。

def italy():
    from PIL import Image
    # 100*100 = (100 + 99 + 99 + 98)+(...
    items = [[i, i-1, i-1, i-2] for i in xrange(100, 1, -2)]

    org_img = Image.open('img/wire.png')
    org_data = list(org_img.getdata())
    new_img = Image.new(org_img.mode, (100, 100))
    new_data = new_img.load()

    index = 0
    x = y = 0
    for item in items:
        for j in xrange(item[0]):
            new_data[x, y] = org_data[index]
            x += 1
            index += 1
        x -= 1
        for j in xrange(item[1]):
            new_data[x, y] = org_data[index]
            y += 1
            index += 1
        y -= 1
        for j in xrange(item[2]):
            new_data[x, y] = org_data[index]
            x -= 1
            index += 1
        x += 1
        for j in xrange(item[3]):
            new_data[x, y] = org_data[index]
            y -= 1
            index += 1
        y += 1

    new_img.save('img/wire-cat.png')

最后得到了一张图片

14-cat

然后输入cat.html进入页面,出现了两只猫的图片,并有一段说明:

and its name is uzi. you’ll hear from him later.

替换为uzi.html进入下一题,uzi不是小狗吗 = =!

python challenge 15

http://www.pythonchallenge.com/pc/return/uzi.html
15

先找到所有有用的提示:

  • 图片信息 1**6年1月26日 右下角2月份有29天 说明是闰年 且2月29日为周日
  • 页面标题 whom 猜测应该是找人
  • 网页源码
    he ain’t the youngest, he is the second # 应该是找到一个年龄列表中第二年轻的
    buy flowers for tomorrow # 结合1月26日应该是说明天即1月27日有事件发生

  • 结合以上信息
    应该是找到1**6年中的所有闰年,且2月29日为周一的时间中第二个年轻的年份,
    然后加上1月27日得到一个事件时间,这个时间和一个人有关系。

# run in python 3.0+
def uzi():
    from datetime import datetime

    def get_week_day(year_, mouth_, day_):
        return datetime(year_, mouth_, day_).strftime("%w")

    leap_years = [i for i in range(1006, 1997, 10) if (i % 4 == 0 and i % 100 != 0) or i % 400 == 0]
    # print (leap_years)
    match_year = [y for y in leap_years if get_week_day(y, 2, 29) == "0"]
    print (match_year)
    print ("%d-01-17" % match_year[-2])
>>> [1176, 1356, 1576, 1756, 1976]
    1756-01-17

由于python2.7版本中the datetime strftime() methods require year >= 1900,

因此该代码需要运行在python3.0+

最后百度关键词1756年1月27日,著名的音乐大师沃尔弗于格·莫扎特(mozart),诞生于奥地利查尔茨堡。

诞生于,诞生啊。。其实最初我以为送花是第二天有人挂掉了去拜祭。。面壁思过中。。。

python challenge 16

http://www.pythonchallenge.com/pc/return/mozart.html

16

页面标题提示:let me get this straight

观察图片将粉色的线条对齐排成一列,即将每行像素点循环左移知道粉色点位于最左边。

其中图片模式为索引像素模式,用ps查看得到粉色Idx=195,对应RGB=(255, 0, 255)。

def mozart():
    from PIL import Image
    org_img = Image.open('img/mozart.gif')
    org_size = org_img.size
    org_data = list(org_img.getdata())

    # new_img = Image.new(org_img.mode, org_size)
    new_img = org_img.copy()
    new_data = new_img.load()

    for y in range(org_size[1]):
        line_pixels = org_data[org_size[0] * y: org_size[0] * (y + 1)]
        pink_index = line_pixels.index(195)
        for x, pixel in enumerate(line_pixels[pink_index:] + line_pixels[:pink_index]):
            new_data[x, y] = pixel

    new_img.save("img/romance.gif")

最后生成答案图片

16-romance

python challenge 17

http://www.pythonchallenge.com/pc/return/romance.html

17

做这个题请自觉开启自虐模式,不要问我为什么,我只想静静。

页面标题:eat? 看下源代码图片名字为cookies.jpg

cookies点心?cookie?打开chrome控制台输入alert(document.cookie)

17-alert

you should have followed busynothing…

结合图片左下角linkedlist页面的图片,想到之前遍历时候找的每个页面,这个应该是遍历找每个页面的cookie信息了。

初步验证,先进入linkedlist的第一个节点:

http://www.pythonchallenge.com/pc/def/linkedlist.php?busynothing=12345

页面内容:

If you came here from level 4 - go back!
You should follow the obvious chain...

and the next busynothing is 44827

大概意思是说这个界面如果你是从第四关跳进去的话请返回,显示的关键字会是nothing而非busynothing。

因此一定要从第一个节点进入,不要从linkedlist.php点击图片进入,否则获取不到cookie信息。

弹出的cookie信息为:info=B

第二个节点:http://www.pythonchallenge.com/pc/def/linkedlist.php?busynothing=44827

弹出的cookie信息为:info=Z

是不是有点眼熟,回头找找第8关的BZ2压缩。。

之后是写代码之路,其中cookie模块参考:urllib2模块、cookielib模块

遍历完cookie信息后得到字符串:

BZh91AY%26SY%94%3A%E2I%00%00%21%19%80P%81%11%00%AFg%9E%A0+%00hE%3DM%B5%23%D0%D4%D1%E2%8D%06%A9%FA%26S%D4%D3%21%A1%EAi7h%9B%9A%2B%BF%60%22%C5WX%E1%ADL%80%E8V%3C%C6%A8%DBH%2632%18%A8x%01%08%21%8DS%0B%C8%AF%96KO%CA2%B0%F1%BD%1Du%A0%86%05%92s%B0%92%C4Bc%F1w%24S%85%09%09C%AE%24

看看格式应该是转义过的,调用urllib转义函数得到结果:

BZh91AY&SY\x94:\xe2I\x00\x00!\x19\x80P\x81\x11\x00\xafg\x9e\xa0 \x00hE=M\xb5#\xd0\xd4\xd1\xe2\x8d\x06\xa9\xfa&S\xd4\xd3!\xa1\xeai7h\x9b\x9a+\xbf`"\xc5WX\xe1\xadL\x80\xe8V<\xc6\xa8\xdbH&32\x18\xa8x\x01\x08!\x8dS\x0b\xc8\xaf\x96KO\xca2\xb0\xf1\xbd\x1du\xa0\x86\x05\x92s\xb0\x92\xc4Bc\xf1w$S\x85\t\tC\xae$\x90

这个看着就很熟悉了,调用bz2进行解压

is it the 26th already? call his father and inform him that "the flowers are on their way". he'll understand.

这个结果看的也是格外的忧伤,竟然还有这么多的坑。。看看26th说的是莫扎特,查下莫扎特的老爹为Leopold。

试试Leopold.html,404啊,404!!!

再仔细看,call his father,打电话,电话!!!

调用13关的代码Bert改为Leopold返回555-VIOLIN

参考13关去数字改小写地址为violin.html,竟然还有,呵呵哒。。。

no! i mean yes! but ../stuff/violin.php.

再改将上级目录改为/stuff/violin.php

即http://www.pythonchallenge.com/pc/stuff/violin.php

看到图片我以为终于完了,仔细一看,编号呢,没编号肯定还是个坑啊,我内心几乎是崩溃的。。

17-violin

看下页面提示标题:it’s me. what do you want?

返回页面也没有什么其他信息,答案在哪里?!?!?

想想这个题主题是cookie,回头再看上边的解压出来的提示信息,后半句还没有用到。

inform him that “the flowers are on their way”

翻译是告诉他鲜花已经来路上,暗示要给他携带信息。

也就是说要带上info=“the flowers are on their way"消息去访问这个页面!

cj._cookies.values()[0]['/']['info'].value = 'the+flowers+are+on+their+way'
violin_source = opener.open('http://www.pythonchallenge.com/pc/stuff/violin.php').read()
print violin_source

打印出来的页面:

<html>
<head>
  <title>it's me. what do you want?</title>
  <link rel="stylesheet" type="text/css" href="../style.css">
</head>
<body>
    <br><br>
    <center><font color="gold">
    <img src="leopold.jpg" border="0"/>
<br><br>
oh well, don't you dare to forget the balloons.</font>
</body>
</html>

终于出结果了,balloons,感觉已经可以开始google大法了QAQ。。

完整逻辑代码,其中略掉level13的代码:

def romance():
    import urllib2
    import cookielib
    import urllib

    cj = cookielib.CookieJar()
    handler = urllib2.HTTPCookieProcessor(cj)
    opener = urllib2.build_opener(handler)

    base_url = "http://www.pythonchallenge.com/pc/def/linkedlist.php"
    param = "12345"
    times = 1
    info = []

    while True:
        try:
            url = "?busynothing=".join([base_url, param])
            page_source = opener.open(url).read()
            param = re.findall("and the next busynothing is ([0-9]+)", page_source)[0]
            ck = cj._cookies.values()[0]['/']['info'].value
            info.append(ck)
            print "%d -> %s -> %s" % (times, param, ck)
            times += 1
        # 匹配不到跳到异常
        except IndexError:
            print "the end is : %d" % times
            break

    message = "".join(info)
    print message
    # 转义
    message = urllib.unquote_plus(message)
    result = message.decode("bz2")
    print result

    # ---level 13 code ---#

    # url = "?busynothing=".join([base_url, param])
    # page_source = opener.open(url).read()
    cj._cookies.values()[0]['/']['info'].value = 'the+flowers+are+on+their+way'
    violin_source = opener.open('http://www.pythonchallenge.com/pc/stuff/violin.php').read()
    print violin_source

python challenge 18

http://www.pythonchallenge.com/pc/return/balloons.html

18

页面标题:can you tell the difference?

明显的两张图片亮度不同,英文brightness.

进入该页面后再看源码提示:maybe consider deltas.gz

将该文件下载下来,解压后看到delta.txt里是分为两栏的16进制数据。

18-deltas

仔细对比两边内容,发现有部分重复的,也就是通过比较两栏内容,来分发字节。

左边特有的,右边特有的,公共部分,分别生成三张图片。

这里需要用到difflib模块来进行数据处理。

def balloons():
    import difflib
    f_delta = open('files/delta.txt', 'r+')
    deltas = f_delta.read().split('\n')
    f_delta.close()

    left_data = []
    right_data = []

    for line in deltas:
        left_data.append(line[:53]+'\n')
        right_data.append(line[56:]+'\n')

    diff = difflib.Differ()
    cmp_result = list(diff.compare(left_data, right_data))

    left_pic = open('img/18-left-diff.png', 'wb')
    right_pic = open('img/18-right-diff.png', 'wb')
    common_pic = open('img/18-common.png', 'wb')

    for line in cmp_result:
        bytes = [(chr(int(h, 16))) for h in line[2:].split()]
        if line.startswith('-'):
            map(left_pic.write, bytes)
        elif line.startswith('+'):
            map(right_pic.write, bytes)
        elif line.startswith(' '):
            map(common_pic.write, bytes)

    right_pic.close()
    left_pic.close()
    common_pic.close()

最后生成的三张图片

18-bin

进入http://www.pythonchallenge.com/pc/hex/bin.html

用户名:butter 密码:fly

python challenge 19

http://www.pythonchallenge.com/pc/hex/bin.html

19

看网页源码,base64加密一段音频数据indian.wav

def hex_bin():
    import base64
    headers = {
        "Authorization": "Basic YnV0dGVyOmZseQ=="
    }
    page_source = requests.get("http://www.pythonchallenge.com/pc/hex/bin.html", headers=headers).text
    wav_data = re.findall(r"base64([\s\S]+?)--", page_source)[0].strip("\n")
    indian = open("files/indian.wav", "wb")
    indian.write(base64.b64decode(wav_data))
    indian.close()

打开生成的音频文件,有个怪怪的声音:sorry..

打开sorry界面显示:what are you apologizing for

再回头看看图片,大陆和海洋的颜色是反转的,尝试将音频每一帧反转

def hex_bin():
    import base64
    import wave
    headers = {
        "Authorization": "Basic YnV0dGVyOmZseQ=="
    }
    page_source = requests.get("http://www.pythonchallenge.com/pc/hex/bin.html", headers=headers).text
    wav_data = re.findall(r"base64([\s\S]+?)--", page_source)[0].strip("\n")
    indian = open("files/indian.wav", "wb")
    indian.write(base64.b64decode(wav_data))
    indian.close()

    indian = wave.open("files/indian.wav", "rb")
    reverse = wave.open("files/indian-reverse.wav", "wb")
    reverse.setnchannels(1)
    reverse.setframerate(indian.getframerate())
    reverse.setsampwidth(indian.getsampwidth())
    for i in range(indian.getnframes()):
        reverse.writeframes(indian.readframes(1)[::-1])
    indian.close()
    reverse.close()

播放下听听,You are a idiot ha ha ha ha ha ha …..

进入idiot.html显示:Now you should apologize,Continue to the next level进入下一关。

python challenge 20

http://www.pythonchallenge.com/pc/hex/idiot2.html

20

页面标题:go away!

图片下边:but inspecting it carefully is allowed.

大概意思是围栏那边的是私有财产,请离开私有财产,虽然不允许进入,但可以观察。

没有一点思路,谷歌大法好,抓下图片包看下请求头信息:

20-range

这里的Content-Range的值是: bytes 0-30202/2123456789,这看上去与所谓的“断点续传”十分相似。

从这个信息知道原始图片大小有2123456789个字节,但是目前的“unreal.jpg” 只有前面的30202 个字节,这就说明看到的“unreal.jpg”并不完整。

那就先要把“unreal.jpg”下载完全才行。

要指定请求的字节范围,需要在请求中加入一个叫Range的头,基本形式是“Range: bytes=1000-2345”。

headers = {
    "Authorization": "Basic YnV0dGVyOmZseQ==",
    "Range": "bytes=30203-"
}
img = requests.get("http://www.pythonchallenge.com/pc/hex/unreal.jpg", headers=headers)
print img.text
>>> u"Why don't you respect my privacy?\n"

服务器返回了有意义的信息,因此写个遍历将所有信息打印出来。

def idiot2():
    pic_url = "http://www.pythonchallenge.com/pc/hex/unreal.jpg"
    next_range = 30203
    end_range = 2123456789
    while next_range <= end_range:
        headers = {
            "Authorization": "Basic YnV0dGVyOmZseQ==",
            "Range": "bytes=%d-" % next_range
        }
        img = requests.get(pic_url, headers=headers)
        if img.status_code == 206:
            print "range : %d -> %s" % (next_range, img.text)
            content_range = img.headers.get("Content-Range")
            next_range = re.findall(r"bytes.+-(\d+)/", content_range)[0]
            next_range = int(next_range) + 1
        else:
            break
>>>
    range : 30203 -> Why don't you respect my privacy?
    range : 30237 -> we can go on in this way for really long time.
    range : 30284 -> stop this!
    range : 30295 -> invader! invader!
    range : 30313 -> ok, invader. you are inside now.
    Process finished with exit code 0

信息中反复提到的入侵者invader,尝试更换下网址为invader.html。

页面显示:Yes! that’s you! 这提示,然并卵。。。

从前边入侵不行,那就从后边入侵试试?

将代码中next_range改为2123456789打印出来为:

esrever ni emankcin wen ruoy si drowssap eht

将其反过来

end_text = "esrever ni emankcin wen ruoy si drowssap eht"
result = [text_[::-1] for text_ in end_text.split()[::-1]]
print " ".join(result)
>>> the password is your new nickname in reverse

通过读取Content-Range再入侵前一个得到信息:

range : 2123456743 -> and it is hiding at 1152983631.

通过这条信息得之其隐藏在1152983631,入侵之后得到pk开头的数据即zip包。

将其下载下来,解压需要密码,密码看前边的提示信息:

the password is your new nickname in reverse

新绰号即为入侵者invader,反过来为redavni。

解压过后生成两个文件,package.pack和readme.txt,打开readme文件内容为:

Yes! This is really level 21 in here.
And yes, After you solve it, you'll be in level 22!

Now for the level:

* We used to play this game when we were kids
* When I had no idea what to do, I looked backwards.

已经进入了level 21.

完整代码:

def idiot2():
    pic_url = "http://www.pythonchallenge.com/pc/hex/unreal.jpg"
    next_range = 30203
    end_range = 2123456789
    while next_range <= end_range:
        headers = {
            "Authorization": "Basic YnV0dGVyOmZseQ==",
            "Range": "bytes=%d-" % next_range
        }
        img = requests.get(pic_url, headers=headers)
        if img.status_code == 206:
            print "range : %d -> %s" % (next_range, img.text)
            content_range = img.headers.get("Content-Range")
            next_range = re.findall(r"bytes.+-(\d+)/", content_range)[0]
            next_range = int(next_range) + 1
        else:
            break

    # reverse
    cur_range = 2123456789
    end_range = 2123456789
    is_download = False
    while cur_range <= end_range:
        headers = {
            "Authorization": "Basic YnV0dGVyOmZseQ==",
            "Range": "bytes=%d-" % cur_range
        }
        img = requests.get(pic_url, headers=headers)
        if img.status_code == 206:
            if is_download is True:
                open("files/invader.zip", "wb").write(img.content)
                break

            print "range : %d -> %s" % (cur_range, img.text)
            content_range = img.headers.get("Content-Range")
            cur_range = re.findall(r"bytes (\d+)-", content_range)[0]
            cur_range = int(cur_range) - 1

            img_text = img.text
            result = [text_[::-1] for text_ in img_text.split()[::-1]]
            print " ".join(result)
        elif img.status_code == 416:
            cur_range = img_text.split()[-1].strip(".")
            cur_range = int(cur_range)
            is_download = True

没有评论:

发表评论

Android Root Zap Framework

‎ 1. Warning 请遵守GPL开源协议, 请遵守法律法规, 本项目仅供学习和交流, 请勿用于非法用途! 道路千万条, 安全第一条, 行车不规范, 亲人两行泪. 2. Android Root Zap Frame...