2015-08-31

借用UAC完成的提权思路

背景

UAC(User Account Control,用户帐户控制)是微软为提高系统安全而在Windows Vista中引入的新技术,它要求用户在执行可能会影响计算机运行的操作或执行更改影响其他用户的设置的操作之前,提供权限或管理员‌密码。也就是说一旦用户允许启动的应用程序通过UAC验证,那么这个程序也就有了管理员权限。如果我们通过某种方式劫持了通过用户UAC验证的程序,那么相应的我们的程序也就实现了提权的过程。

2015-08-13

python challenge 21-27

继续斗智斗勇中。已经开始谷歌大法好了。。

python challenge 21

invader.zip

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 20下载到的那个压缩包的package.pack文件是个压缩文件,使用了zlib和bz2两种方式压缩。

需要根据上一次解压得到的结果,判断下一次解压用zlib还是bz2,以及判断得到的数据是正向的还是要look backwards。

Zlib压缩的数据开头第一个字节是"\x78",ASCII码是"x".

BZ压缩的数据开头两个字节是"\x42\x7A",ASCII码是"BZ".

def package():
    import bz2
    import zlib

    data = open("files/invader/package.pack", "rb+").read()
    result = []
    while True:
        if data.startswith("x\x9c"):
            data = zlib.decompress(data)
            result.append(" ")
        elif data.startswith("BZ"):
            data = bz2.decompress(data)
            result.append("#")
        elif data.endswith("\x9cx"):
            data = data[::-1]
            result.append("\n")
        elif data.endswith("ZB"):
            data = data[::-1]
            result.append("*")
        else:
            break

    print "".join(result)
>>>
      ###          ###      ########    ########    ##########  ########
    #######      #######    #########   #########   #########   #########
   ##     ##    ##     ##   ##      ##  ##      ##  ##          ##      ##
  ##           ##       ##  ##      ##  ##      ##  ##          ##      ##
  ##           ##       ##  #########   #########   ########    #########
  ##           ##       ##  ########    ########    ########    ######## 
  ##           ##       ##  ##          ##          ##          ##   ## 
   ##     ##    ##     ##   ##          ##          ##          ##    ## 
    #######      #######    ##          ##          #########   ##     ## 
      ###          ###      ##          ##          ##########  ##      ##

其中四种情况的记录标志符“#\n”等这些可以自己设置,最后将其反转标志替换成回车\n,zlib标志替换成空字符,BZ标志替换成#即可打印出答案。

python challenge 22

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

22

查看页面源代码提示:or maybe white.gif would be more bright

将copper.html替换为white.gif显示一张黑色图片,黑的什么都看不到。

google大法说了每帧图上有一个不同的RGB点。

用ps打开,由于是gif动态图只能打开其中一帧,看看RGB全是(0,0,0),鼠标晃晃就找到了那个不同的RGB(8,8,8)。

22-white

然后我么把这张gif拆帧并将RGB(8,8,8)改为白色RGB(255,255,255),这样就能清晰的看到运动轨迹了。

22-white-new

如果上图白点不动的话刷新下页面,或者右键图片审查元素,拿到gif图片链接浏览器直接访问。

代码整理在github

看到运动轨迹后思路就很清楚了,该动画总共133帧,白点围绕100,100左右微小变动,

联想这一关的图片(一个游戏操纵杆),把这些变动的点连接起来。

最后的结果显示通过操纵点的运动一共画出了5个字母的轮廓,所以当白点回归到中心的时候,就是画下一个字母的信号了。

def copper():
    from PIL import Image, ImageDraw, ImageSequence
    org_img = Image.open("img/white.gif")
    new_img = Image.new('RGB', org_img.size, "black")
    new_img_draw = ImageDraw.Draw(new_img)
    x = 0
    y = 0
    for s in ImageSequence.Iterator(org_img):
        left, upper, right, lower = org_img.getbbox()
        dx = left-100
        dy = upper-100
        x += dx
        y += dy
        if dx == dy == 0:
            x += 20
            y += 30
        new_img_draw.point((x, y))

    new_img.save("img/copper.png")

最后生成图片中字母即为level23入口: bonus.html

22-copper

python challenge 23

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

23

看网页源码提示:

it can't find it. this is an undocumented module
va gur snpr bs jung?

va gur snpr bs jung?这段字符串想到了第二关的移位,由于不知道移几位,因此做个枚举

def bonus():
    def shift(str_value, shift_num):
        ret_list = []
        alphas = list(str_value)
        for alpha in alphas:
            temp_ord = ord(alpha) + shift_num
            if 97 <= temp_ord <= 122:
                ret_list.append(chr(temp_ord))
            elif temp_ord > 122:
                ret_list.append(chr(temp_ord - 122 + 96))
            else:
                ret_list.append(" ")
        return "".join(ret_list)

    org_str = "va gur snpr bs jung"
    for i in range(1, 26):
        print ">>%d -> %s" % (i, shift(org_str, i))

看输出信息:

>>1 -> wb hvs toqs ct kvoh
>>2 -> xc iwt uprt du lwpi
>>3 -> yd jxu vqsu ev mxqj
>>4 -> ze kyv wrtv fw nyrk
>>5 -> af lzw xsuw gx ozsl
>>6 -> bg max ytvx hy patm
>>7 -> ch nby zuwy iz qbun
>>8 -> di ocz avxz ja rcvo
>>9 -> ej pda bwya kb sdwp
>>10 -> fk qeb cxzb lc texq
>>11 -> gl rfc dyac md ufyr
>>12 -> hm sgd ezbd ne vgzs
>>13 -> in the face of what
>>14 -> jo uif gbdf pg xibu
>>15 -> kp vjg hceg qh yjcv
>>16 -> lq wkh idfh ri zkdw
>>17 -> mr xli jegi sj alex
...

循环右移13位的时候出现了正常的字符串in the face of what?这意思是this..

再看第一个提示this is an undocumented module,在python导入this模块试试

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

In the face of ambiguity, refuse the temptation to guess.

答案:ambiguity

python challenge 24

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

24

这一关的图片是一张迷宫地图(641, 641),只不过白色部分是迷宫的墙,深色部分是路。

用ps查看像素点,可以看出墙部分的RGB=(255,255,255),路的RGB=(x,0,0)。

根据标题from top to bottom的提示,入口就是图片的右上角,出口就是图片的左下角。

用ps看下右上角的入口点:

24-1

因此最开始通过getpixel(x, 0)[0]=0 找到入口点位置。

同理左下角出口点通过getpixel(x, 640)[0]=0 找到。

代码逻辑:

1.从入口点开始向四个方向找下个坐标点,找到后作为下一个入口点,并将当前像素点标白(255,255,255)

2.如果找到两个以上的下个出口坐标,即到了分岔口,备份之前的坐标队列,以此分岔口作为新的入口点,

3.如果未找到出口,则退回到上个分岔口继续查找。

最后生成的图片可以看到完成的迷宫路径:

24-2

显然这还不是谜底,在仔细观察迷宫的路径,可以发现每个一个像素点,路径上的颜色就会变化,

将这些像素点的R通道数据用二进制写到文件,打开可以发现前两位是PK ,那么这些信息其实是构成了一个ZIP文件。

更改后缀,解压,里面有两个文件,maze.jpg和mybroken.zip,其中maze.jpg图片上的lake就是谜底了。

完整代码:

def ambiguity():
    import Image
    org_img = Image.open("img/maze.png").getdata()
    org_img_size = org_img.size
    new_img = Image.new("RGBA", org_img_size, "black")
    start_pos, end_pos = None, None
    start_pos_count, end_pos_count = 0, 0
    for x in range(org_img_size[0]):
        if org_img.getpixel((x, 0))[0] == 0:
            start_pos = (x, 0)
            start_pos_count += 1
        if org_img.getpixel((x, org_img_size[1]-1))[0] == 0:
            end_pos = (x, org_img_size[1]-1)
            end_pos_count += 1

    if start_pos is None or end_pos is None or start_pos_count != 1 or end_pos_count != 1:
        print "[error] : get start pos or end pos error!"
        return False
    print "[*] start_pos: %s and end_pos: %s" % (start_pos, end_pos)

    path = []
    whole_path = []

    # 右 下 左 上
    dire = [(1, 0), (0, 1), (-1, 0), (0, -1)]
    wall = (255,)*4

    while start_pos != end_pos:
        org_img.putpixel(start_pos, wall)
        flag = 0
        new_pos = start_pos
        for offset in dire:
            try:
                pp = (start_pos[0]+offset[0], start_pos[1]+offset[1])
                if org_img.getpixel(pp) != wall:
                    flag += 1
                    new_pos = pp
            except Exception, e:
                print str(e)
                pass
        # 未找到下个出口
        if flag == 0:
            if not path:
                path = whole_path.pop()
                continue
            start_pos = path[0]
            path = []
        # 分岔口,找到多个像素点,备份之前的坐标队列,以此分岔口作为新的入口点
        elif flag > 1:
            whole_path.append(path)
            path = [start_pos]
            start_pos = new_pos
        # 找到唯一出口,flag = 1
        else:
            path.append(start_pos)
            start_pos = new_pos

    path.append(start_pos)
    whole_path.append(path)

    org_img = Image.open("img/maze.png").getdata()
    data = []
    for path in whole_path:
        for k in path:
            new_img.putpixel(k, wall)
            data.append(org_img.getpixel(k)[0])

    out = open("files/out24.zip", "wb")
    for i in data[1::2]:
        out.write(chr(i))
    out.close()
    new_img.save("img/out24.png")

该题解迷宫逻辑参考PythonChallenge 挑战之路 Level-24

python challenge 25

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

25

谷歌大法好:

这是一幅拼图模样的图片,根据网页源码里的提示can you see the waves?,这一关还要用到wave模块。

把URL后缀改为lake1.wav,lake2.wav,…,lake25会得到25个wav文件。

每个wav文件为10800字节,将三个作为一个像素,可得到一张10800/3=60x60大小的图片。

这25个wav文件对应着刚才的25块拼图,把这25个wav文件的内容依次拼起来,会得到一张300x300大小的图片。

def lake():
    from PIL import Image
    import requests
    import base64
    import StringIO
    import wave

    headers = {
            "Authorization": "Basic %s" % base64.encodestring("butter:fly").replace("\n", "")
            }
    wave_url = 'http://www.pythonchallenge.com/pc/hex/lake%i.wav'
    new_img = Image.new('RGB', (300, 300))
    for i in range(25):
        print "%i/%i" % (i + 1, 25)
        wave_content = requests.get(wave_url % (i + 1), headers=headers).content
        data = wave.open(StringIO.StringIO(wave_content))
        patch = Image.fromstring('RGB', (60, 60), data.readframes(data.getnframes()))
        pos = (60 * (i % 5), 60 * (i / 5))
        new_img.paste(patch, pos)

    new_img.save("img/out25.jpg")

最终得到的图片中单词为最后答案:decent

25-decent

python challenge 26

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

26

看网页源码中的提示:

1.be a man - apologize!
2.you've got his e-mail
3.Hurry up, I'm missing the boat

提示让你先发邮件去道歉,邮箱哪里来,看19关的源码中有段没有用到的记录:

From: leopold.moz@pythonchallenge.com
Subject: what do you mean by "open the attachment?"
Mime-version: 1.0
Content-type: Multipart/mixed; boundary="===============1295515792=="

It is so much easier for you, youngsters.
Maybe my computer is out of order.
I have a real work to do and I must know what's inside!
...

找到邮箱leopold.moz@pythonchallenge.com,要道歉,发送内容是什么?

看23关源码中没有用到的记录:

TODO: do you owe someone an apology? now it is a good time to
tell him that you are sorry. Please show good manners although
it has nothing to do with this level.

现在就可以去给leopold.moz@pythonchallenge.com发送内容为sorry的邮件。

python发邮件模块网上很多,就几行代码,这里我直接用我邮箱发,然后收到回复:

26-sorry

从回复的邮件里得到了一个MD5值:bbb8b499a0eef99b52c7f13f4e78c24b以及它提到的broken.zip文件。这个解压文件在24关中得到,在解压的时候会提示CRC效验失败,也就是说缺少部分数据。但实际上用7z解压时候是可以解压出文件的。

但正常的过程应该是这个MD5值应该就是那个broken.zip的MD5值,需要将原文件的每个字节枚举看得到的MD5值是否为该MD5值,从而得到正确的文件。

def decent():
    import hashlib
    f_content = open("files/mybroken.zip", "rb").read()
    for i in range(len(f_content)):
        for j in range(256):
            new_content = "".join([f_content[:i], chr(j), f_content[i+1:]])
            if hashlib.md5(new_content).hexdigest() == "bbb8b499a0eef99b52c7f13f4e78c24b":
                open("files/mybroken_new.zip", "wb").write(new_content)
                print "offset %d -> %d" % (i, j)
>>> offset 1234 -> 168

最后解压正确的zip文件得到一张图片

26-mybroken

将得到的单词speed和提示Hurry up,I'm missing the boat组合起来得到下关答案speedboat

python challenge 27

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

27

谷歌大法好,要利用调色板这些东西,调色板可参考调色板palette详解

首先查看源码,将zigzag.gif下载下来

27-zigzag

这时候每个像素的上的信息是调色板的索引号,然后就需要用实际的颜色替换索引号得到等价的图片信息。然后将这些数据与原图片信息对比,将不同的坐标点标记颜色,如新建一个黑底图片标白,最后得到一张图片。

27-notword

图片左边是not,右边是word,下边是busy?,中间是钥匙,合起来是not key word。

然后将两个图片不相同的字节放在一起,最后经过颜色替换索引号得到的图片不同的数据得到一个bz压缩文件,解压出来并找到那些关键字里面哪些不是key word打印出,最后得到答案。

def speedboat():
    import Image
    import string
    import bz2
    import keyword
    img = Image.open("img/zigzag.gif")
    img_content = img.tostring()
    img_plt = img.palette.getdata()[1][::3]
    trans = string.maketrans("".join([chr(i) for i in range(256)]), img_plt)
    img_tran = img_content.translate(trans)

    # compare
    diff = ["", ""]
    img = Image.new("1", img.size, 0)
    for i in range(1, len(img_content)):
        if img_content[i] != img_tran[i-1]:
            diff[0] += img_content[i]
            diff[1] += img_tran[i-1]
            img.putpixel(((i-1) % img.size[0], (i-1)/img.size[0]), 1)
    img.save("img/out27.png")
    text = bz2.decompress(diff[0])

    # extract key
    for i in text.split():
        if not keyword.iskeyword(i):
            print i

>>> ../ring/bell.html
    repeat
    switch
    repeat
    ../ring/bell.html
    ../ring/bell.html
    ../ring/bell.html
    ...

得到28关链接为http://www.pythonchallenge.com/pc/ring/bell.htm

账户名:repeat, 密码:switch

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

2015-08-07

python challenge 0-10

最近闲下来了四处看资料,然后找到了这个python challenge网页闯关游戏。
这个游戏需要通过一些提示找出下一关的网页地址,链接:http://www.pythonchallenge.com/
题目出的也是充满想象力,斗智斗勇中。。。
解题代码整理在我的 github

python challenge 0

http://www.pythonchallenge.com/pc/def/0.html
0

# 2的38次幂
def calc():
    return 2 ** 38
>>> 274877906944L

 python challenge 1

http://www.pythonchallenge.com/pc/def/274877906944.html
1
通过图片可知道,所有字符右移两位即可。

def map_():
    first_alpha = chr(ord("m") + 2)
    second_alpha = chr(ord("a") + 2)
    third_alpha = chr(ord("p") + 2)
    return "".join([first_alpha, second_alpha, third_alpha])
>>> ocr

 python challenge 2

http://www.pythonchallenge.com/pc/def/ocr.html
2
查找出现次数最少的字符,先是统计出所有字符出现的次数,然后获取出现次数最早的,要注意按照原来顺序排列。

def ocr():
    stats = {}
    req = requests.get("http://www.pythonchallenge.com/pc/def/ocr.html")
    page_source = req.text
    mess_pattern = re.compile(r"(%%[\s\S]+)-->", re.IGNORECASE)
    mess_content = mess_pattern.findall(page_source)[0]
    # print mess_content

    for character in mess_content:
        if character in stats:
            stats[character] += 1
        else:
            stats[character] = 1
    for key_, value_ in stats.items():
        print "%s -> %d" % (key_, value_)

    # a e i l q u t y
    return "".join(re.findall(r"[a-z]+", mess_content))
>>> u'equality'

 python challenge 3

http://www.pythonchallenge.com/pc/def/equality.html
3
小写字符两边有三个大写字母,格式为aAAAaAAAa
AAAaAAA这种匹配是错误的,要保证两边都是三位大写字母

def equality():
    req = requests.get("http://www.pythonchallenge.com/pc/def/equality.html")
    page_source = req.text
    mess_pattern = re.compile(r"kAewtloYgc[\s\S]+", re.IGNORECASE)
    mess_content = mess_pattern.findall(page_source)[0]
    return "".join(re.findall(r"[a-z]{1}[A-Z]{3}([a-z]{1})[A-Z]{3}[a-z]{1}", mess_content))
>>> u'linkedlist'

 python challenge 4

http://www.pythonchallenge.com/pc/def/linkedlist.php
4
点击图片会有提示,然后更换链接参数继续,大概200多次即可得到答案。
其中正则不能只写"[0-9]+“,中间有陷阱:

There maybe misleading numbers in the text. One example is 82683\. Look only for the next nothing and the next nothing is 63579

完整代码如下:

def linkedlist(times=1, param="123456"):
    try:
        url = "?nothing=".join(["http://www.pythonchallenge.com/pc/def/linkedlist.php", param])
        page_source = requests.get(url).text
        next_param = re.findall("and the next nothing is ([0-9]+)", page_source)[0]
        print "%d -> %s" % (times, next_param)
        times += 1
        linkedlist(times, next_param)
    # 匹配不到跳到异常
    except IndexError:
        print "the right url is : %s" % url
        return page_source
>>> the right url is : http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing=66831
    peak.html

 python challenge 5

http://www.pythonchallenge.com/pc/def/peak.html
5
这关估计卡了不少人,从页面源码只能得到banner.p文件地址,即http://www.pythonchallenge.com/pc/def/banner.p
跟我一样没有用过pickle模块的一般都解不出来,看到文件内容也是没有头绪的,通过上网查了资料才知道如何解题。

# pickle模块的使用
def peak():
    import pickle
    page_source = requests.get("http://www.pythonchallenge.com/pc/def/peak.html").text
    banner_name = re.findall(r"", page_source)[0]
    banner_url = "".join(["http://www.pythonchallenge.com/pc/def/", banner_name])
    banner_content = requests.get(banner_url).text
    # print banner_content
    banner_obj = pickle.loads(banner_content)
    for list_ in banner_obj:
        print "".join([tuple_[0] * tuple_[1] for tuple_ in list_])

>>>
              #####                                                                      ##### 
               ####                                                                       #### 
               ####                                                                       #### 
               ####                                                                       #### 
               ####                                                                       #### 
               ####                                                                       #### 
               ####                                                                       #### 
               ####                                                                       #### 
      ###      ####   ###         ###       #####   ###    #####   ###          ###       #### 
   ###   ##    #### #######     ##  ###      #### #######   #### #######     ###  ###     #### 
  ###     ###  #####    ####   ###   ####    #####    ####  #####    ####   ###     ###   #### 
 ###           ####     ####   ###    ###    ####     ####  ####     ####  ###      ####  #### 
 ###           ####     ####          ###    ####     ####  ####     ####  ###       ###  #### 
####           ####     ####     ##   ###    ####     ####  ####     #### ####       ###  #### 
####           ####     ####   ##########    ####     ####  ####     #### ##############  #### 
####           ####     ####  ###    ####    ####     ####  ####     #### ####            #### 
####           ####     #### ####     ###    ####     ####  ####     #### ####            #### 
 ###           ####     #### ####     ###    ####     ####  ####     ####  ###            #### 
  ###      ##  ####     ####  ###    ####    ####     ####  ####     ####   ###      ##   #### 
   ###    ##   ####     ####   ###########   ####     ####  ####     ####    ###    ##    #### 
      ###     ######    #####    ##    #### ######    ###########    #####      ###      ######

 python challenge 6

http://www.pythonchallenge.com/pc/def/channel.html
6
一张拉链图片,看了源码也没有任何提示,只知道是要捐赠。。。
再仔细看第一行,  ,将html变zip
下载channel.zip文件,链接:http://www.pythonchallenge.com/pc/def/channel.zip
下载完成后解压,和第四题一样,只不过多了文件遍历操作,遍历从任意文件开始都可以。
我这里从os.walk遍历到的文件列表中第一个文件开始遍历。

def channel():
    import os

    root_path = "D:\\downloads\\channel"

    def linked_file(times=1, file_name="123456"):
        try:
            file_path = "\\".join([root_path, file_name])
            file_content = open(file_path, "r+").read()
            next_file_name = re.findall("Next nothing is ([0-9]+)", file_content)[0]
            print "%d -> %s.txt" % (times, next_file_name)
            times += 1
            linked_file(times, "".join([next_file_name, ".txt"]))
        # 匹配不到跳到异常
        except IndexError:
            print "the right link in file : %s" % file_name
            print file_content

    for root, dirs, files in os.walk(root_path):
        for file_ in files:
            linked_file(1, file_)
            break
>>> the right link in file : 46145.txt
    Collect the comments.

坑爹的答案,让我收集注释,那么答案在哪里。。。
再仔细翻解压的文件,发现里边有个readme.txt !!!

welcome to my zipped list.
hint1: start from 90052
hint2: answer is inside the zip

将代码中linkedfile(1, file)改为linked_file(1, "90052.txt”)
最后输出答案一样!!!

难道是我的解压方式不对?!?!
调用zipfile模块,使用ZipInfo类获取comment

def channel():
    import zipfile
    zf = zipfile.ZipFile("D:\\downloads\\channel.zip")
    result = []
    begin_file_name = "90052.txt"
    while True:
        z_info = zf.getinfo(begin_file_name)
        f = zf.open(z_info)
        f_content = f.read()
        next_file_name = re.findall(r"Next nothing is ([0-9]+)", f_content)
        f.close()
        result.append(z_info.comment)
        if next_file_name:
            begin_file_name = "%s.txt" % next_file_name[0]
        else:
            break

    print "".join(result)
>>>
****************************************************************
****************************************************************
**                                                            **
**   OO    OO    XX      YYYY    GG    GG  EEEEEE NN      NN  **
**   OO    OO  XXXXXX   YYYYYY   GG   GG   EEEEEE  NN    NN   **
**   OO    OO XXX  XXX YYY   YY  GG GG     EE       NN  NN    **
**   OOOOOOOO XX    XX YY        GGG       EEEEE     NNNN     **
**   OOOOOOOO XX    XX YY        GGG       EEEEE      NN      **
**   OO    OO XXX  XXX YYY   YY  GG GG     EE         NN      **
**   OO    OO  XXXXXX   YYYYYY   GG   GG   EEEEEE     NN      **
**   OO    OO    XX      YYYY    GG    GG  EEEEEE     NN      **
**                                                            **
****************************************************************
 **************************************************************

将url改为hockey.html,竟然有小关卡!!!
it's in the air. look at the letters
赶紧开开脑洞,空气中是什么,氧气啊!
oxygen :)

 python challenge 7

http://www.pythonchallenge.com/pc/def/oxygen.html
7
这个题需要解析图片,之前没有接触到因此是查资料完成。
1.需要安装PIL库
2.截取黑白部分
3.将RGBA格式的像素每个像素值按照L8位黑白像素的格式转成一个acsii码值
4.将最后获得的答案转换为ASCII码

def oxygen():
    from PIL import Image
    img = Image.open("oxygen.png")
    # left,top,right,bottom
    box = (0, 43, 608, 52)
    belt = img.crop(box)
    # get a sequence object containing pixel values
    pixels = belt.getdata()
    print('mode: %s' % img.mode)
    print('amount of pixel: %d' % len(pixels))
    # print(pixels[0])

    # convert mode RGBA to mode L
    l_belt = belt.convert('L')
    # get a sequence object containing pixel values
    l_pixels = l_belt.getdata()

    result = []
    for i in range(0, 608, 7):
        result.append(chr(l_pixels[i]))

    print ''.join(result)
    
>>> smart guy, you made it. the next level is [105, 110, 116, 101, 103, 114, 105, 116, 121]

最后将[105, 110, 116, 101, 103, 114, 105, 116, 121]转为ascii即为答案:integrity

 python challenge 8

http://www.pythonchallenge.com/pc/def/integrity.html
8
点击中间的蜜蜂弹出要求输入用户名和密码
然后看下源码,找到了用户名密码

un: 'BZh91AY&SYA\xaf\x82\r\x00\x00\x01\x01\x80\x02\xc0\x02\x00 \x00!\x9ah3M\x07<]\xc9\x14\xe1BA\x06\xbe\x084'
pw: 'BZh91AY&SY\x94$|\x0e\x00\x00\x00\x81\x00\x03$ \x00!\x9ah3M\x13<]\xc9\x14\xe1BBP\x91\xf08'

看下加密字符串特征BZ开头,猜测是否为bz2压缩,尝试成功。

def integrity():
    import bz2
    un = 'BZh91AY&SYA\xaf\x82\r\x00\x00\x01\x01\x80\x02\xc0\x02\x00 \x00!\x9ah3M\x07<]\xc9\x14\xe1BA\x06\xbe\x084'
    pw = 'BZh91AY&SY\x94$|\x0e\x00\x00\x00\x81\x00\x03$ \x00!\x9ah3M\x13<]\xc9\x14\xe1BBP\x91\xf08'
    print ": ".join(["username", bz2.decompress(un)])
    print ": ".join(["password", bz2.decompress(pw)])
>>>
    username: huge
    password: file

 python challenge 9

http://www.pythonchallenge.com/pc/return/good.html
9
查看下源码找到最下边注释部分,和8题页面源码部分是不是很相似,应该是绘制图片的RGB值。
替换过去试试看,鼠标移动,发现浮现了一头牛的图案。
下边通过代码将这个图片完整的绘制出来。

def good():
    from PIL import Image, ImageDraw
    im = Image.new('RGB', (500, 500))
    draw = ImageDraw.Draw(im)

    headers = {
        "Authorization": "Basic aHVnZTpmaWxl"
    }
    page_source = requests.get("http://www.pythonchallenge.com/pc/return/good.html", headers=headers).text
    result = re.findall(r"first:\s([\s\S]+)second:\s([\s\S]+)-->", page_source)
    if len(result) > 0 and len(result[0]) == 2:
        first, second = result[0][0], result[0][1]
        first_points = list(eval(first.replace("\n", "")))
        second_points = list(eval(second.replace("\n", "")))
        for i in range(0, len(first_points), 2):
            draw.line(first_points[i:i + 4], fill='white')
        for i in range(0, len(second_points), 2):
            draw.line(second_points[i:i + 4], fill='white')
        im.save('img/09.jpg')

最后打印出来:
9-bull
看到牛先想到单词cow,然后进去试试看,发现有新的提示:
hmm. it's a male.
那就是公牛喽,换成bull,成功。

 python challenge 10 

http://www.pythonchallenge.com/pc/return/bull.html
10
这个题的图片赫然就是我们绘制的那头牛哇,感觉萌萌哒。
看下边len(a[30])=? 要知道这个答案,肯定要得到a这个序列。
查看下页面源码,点开里边的sequence.txt内容为(点击牛也会跳出):

a = [1, 11, 21, 1211, 111221, 

显然,要得到的答案就是这里了,下边找规律吧。

 index    look     say  
 0   .   1 
 1   第0项有 1个1   11 
 2   第1项有 2个1   21 
 3   第2项有 1个2 1个1   1211 
 4   第3项有 1个1 1个2 2个1   111221 
 5    .  . 

规律找到,编写代码如下:

def bull():

    def get_next_item(str_item=""):
        item = []
        if len(str_item) == 0:
            item.append("1")
        elif len(str_item) > 0:
            cur_list = list(str_item)
            same_count = 1
            if len(cur_list) == 1:
                item.append("".join([str(same_count), cur_list[0]]))
            elif len(cur_list) > 1:
                for i in range(len(cur_list)):
                    if i + 1 >= len(cur_list):
                        item.append("".join([str(same_count), cur_list[i]]))
                    elif i + 1 < len(cur_list):
                        if cur_list[i] == cur_list[i+1]:
                            same_count += 1
                        else:
                            item.append("".join([str(same_count), cur_list[i]]))
                            same_count = 1
        return "".join(item)

    index = 0
    cur_item = ""
    while index < 31:
        cur_item = get_next_item(cur_item)
        index += 1
    print "len(a[%d]) = %d" % (index-1, len(cur_item))
    
>>> len(a[30]) = 5808

网上看下别人的答案,找到一种更简单的实现方式,调用groupby函数,一行代码即可实现。

def bull_ex():
    from itertools import groupby
    a = '1'
    for i in range(30):
        a = ''.join(str(len(list(v))) + k for k, v in groupby(a))
    print len(a)
    

Android Root Zap Framework

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