001. What about making trans?

Map

关卡地址#

解决方案:#

思路:#

What about making trans?

图中给出了字母的映射关系,通过规律来破解下面这段话:

g fmnc wms bgblr rpylqjyrc gr zw fylb. rfyrq ufyr amknsrcpq ypc dmp. bmgle gr gl zw fylb gq glcddgagclr ylb rfyr’q ufw rfgq rcvr gq qm jmle. sqgle qrpgle.kyicrpylq() gq pcamkkclbcb. lmu ynnjw ml rfc spj.

代码:#

import helper
msg=helper.readFile("../../Data/001/msg.txt")
url='map'

# ^^^^^^^^^^^^^^^^^^^^^^^^Solution 1^^^^^^^^^^^^^^^^^^^^^^^^
def getchar(inchar):
    if inchar >= 'a' and inchar <= 'z':
        tmp=ord(inchar)-ord('a')+2
        return chr(tmp%26+ord('a'))
    else:
        return inchar

def translate(instr):
    return ''.join(map(getchar,instr))

print(translate(msg))
print(translate(url))
# vvvvvvvvvvvvvvvvvvvvvvvvSolution 1vvvvvvvvvvvvvvvvvvvvvvvv


# ^^^^^^^^^^^^^^^^^^^^^^^^Solution 2^^^^^^^^^^^^^^^^^^^^^^^^
import string

table=str.maketrans(string.ascii_lowercase, string.ascii_lowercase[2:]+string.ascii_lowercase[:2])
print(msg.translate(table))
print(url.translate(table))
# vvvvvvvvvvvvvvvvvvvvvvvvSolution 2vvvvvvvvvvvvvvvvvvvvvvvv
$msg=Get-Content (Resolve-Path "../../Data/001/msg.txt").Path
$url='map'

function GetChar ([char]$ch) {
    if($ch -ge 'a' -and $ch -le 'z'){
        # [int][char]'a'=97
        return [char](97+([int]$ch-97+2)%26)
    }
    return $ch
}

function Translate ([string]$instr) {
    [char[]]$rst=New-Object char[] $instr.Length
    for ($i = 0; $i -lt $instr.Length; $i++) {
        $ch=GetChar($instr[$i])   
        $rst[$i]=$ch
    }
    
    return $rst -join ''
}

Translate($msg)
Translate($url)
package main

import(
	"fmt"
	"strings"
)

func (c *Challenge) Challenge001()  {
	msg:=ReadFile("../../Data/001/msg.txt")
	url:="map"

	fmt.Println(translate(msg))
	fmt.Println(translate(url))
}

func getChar(r rune) rune {
	// switch {
	// case r >= 'a' && r <= 'z':
	// 	return 'a' + (r-'a'+2)%26
	// }
	if (r >= 'a' && r <= 'z') {
		return 'a' + (r-'a'+2)%26
	}
	return r
}

func translate(instr string) string {
	return strings.Map(getChar, instr)
}

最终结果: ocr#

下一关地址#

002. ocr

关卡地址#

解决方案:#

思路:#

这一关就不贴图了,直接看图片下提示:

recognize the characters. maybe they are in the book, but MAYBE they are in the page source.

很明显,玄机在源代码中。在源代码中有这样的提示:

find rare characters in the mess below:

所以,从一堆混乱的字符中找到稀有的字符吧!

代码:#

import helper
msg=helper.readFile("../../Data/002/msg.txt")

dic={}

for ch in msg:
    if ch in dic:
        continue
    else:
        dic[ch]=msg.count(ch)

# print(dic)

outstr=[]
for key,value in dic.items():
    if value < 10:
        outstr.append(key)

print(''.join(outstr))
$msg=(Get-Content (Resolve-Path "../../Data/002/msg.txt").Path -Raw).Replace("`r`n","")

# powershell 中 hashtable 遍历的顺序与添加的顺序不一致
# $dic=@{}
$dic=New-Object 'System.Collections.Generic.Dictionary[string,int]'
for ($i = 0; $i -lt $msg.Length; $i++) {
    $ch=$msg[$i]
    if ($dic.ContainsKey($ch)) {
        $dic[$ch]+=1
    } else {
        $dic[$ch]=0
    }  
}

[string]$outstr=""
foreach ($key in $dic.Keys) {
    if ($dic[$key] -le 10) {
        $outstr+=$key
    }
}

Write-Output $outstr
package main

import (
	"fmt"
	"strings"
)

func (c *Challenge) Challenge002()  {
	msg:=ReadFile("../../Data/002/msg.txt")

	// go语言 遍历map时返回值是无序的,相同keys每次构建map时顺序都会变化,构建后多次遍历结果一致。
	dic:=map [rune] int {}
	outstr:=""
	// // 记录字符顺序
	// chars:=""

	// for _,ch := range msg {
	// 	_,exists:=dic[ch]
	// 	if (exists) {
	// 		continue
	// 	} else {
	// 		s:=string(ch)
	// 		chars+=s
	// 		dic[ch]=strings.Count(msg,s)
	// 	}
	// }

	// for _,ch := range chars {
	// 	value,exists:=dic[ch]
	// 	if (exists) {
	// 		if (value < 10) {
	// 			outstr+=string(ch)
	// 		}
	// 	}
	// }

	for _,ch := range msg {
		_,exists:=dic[ch]
		if (exists) {
			continue
		} else {
			s:=string(ch)
			count:=strings.Count(msg,s)
			dic[ch]=count
			if (count < 10) {
				outstr+=s
			}
		}
	}

	fmt.Println(outstr)
}

最终结果: equality#

下一关地址#

003. re

关卡地址#

解决方案:#

思路:#

这一关同样不贴图了,直接看图片下提示:

One small letter, surrounded by EXACTLY three big bodyguards on each of its sides.

和上一关一样,需要从源代码中找到“一个两边完全被三个大写字母包围的小写字母”,这样的序列组成的字符串。

最后得到的是: linkedlist

访问 linkedlist.html 会提示你下一关地址是 linkedlist.php

代码:#

import helper
msg=helper.readFile("../../Data/003/msg.txt")

import re

# pattern=re.compile("[^A-Z][A-Z]{3}([a-z])[A-Z]{3}[^A-Z]")

# matches=pattern.findall(msg)

# print(''.join(matches))

print(''.join(re.findall("[^A-Z][A-Z]{3}([a-z])[A-Z]{3}[^A-Z]",msg)))
$msg=(Get-Content (Resolve-Path "../../Data/003/msg.txt").Path -Raw).Replace("`r`n","")

$pattern=[regex]"[^A-Z][A-Z]{3}([a-z])[A-Z]{3}[^A-Z]"

$outstr=""
$pattern.Matches($msg) | foreach {$outstr += $_.Groups[1].Value}

Write-Output $outstr
package main

import (
	"fmt"
	"regexp"
)

func (c *Challenge) Challenge003() {
	msg:=ReadFile("../../Data/003/msg.txt")

	pattern:=regexp.MustCompile("[^A-Z][A-Z]{3}([a-z])[A-Z]{3}[^A-Z]")

	// 返回一个二维数组
	matches:=pattern.FindAllStringSubmatch(msg,-1)

	outstr:=""
	for _,ch := range matches {
		outstr+=ch[1]
	}

	fmt.Println(outstr)
}

最终结果: linkedlist#

下一关地址#

004. follow the chain

关卡地址#

解决方案:#

思路:#

这一关鼠标移动到图片上指针会变成小手,点击会得到如下信息:

and the next nothing is 44827

同时,url后面多了参数?nothing=12345。回退回关卡地址,查看源代码得到如下提示:

urllib may help. DON’T TRY ALL NOTHINGS, since it will never end. 400 times is more than enough.

结合标题,需要借助urllib库发送400次请求,每次请求都会得到下一次请求的参数,替换参数继续请求,最终得到下一关地址。

PS: 在测试时,遇到这样的提示:Yes. Divide by two and keep going.,已在程序中做判断处理(之前并没有遇到这样的情况,看官方攻略也不像是网站最近更新了,看来是之前比较幸运😝)。

代码:#

url='http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing='
param='12345'

import urllib.request
import re

pattern=re.compile("the next nothing is (/d+)")

for i in range(400):
    # print("request %4d url: %s " %(i,url+param))
    resp=urllib.request.urlopen(url+param).read().decode('utf-8')
    try:
        param=pattern.search(resp).group(1)
    except:
        print("param %s, response: %s" %(param,resp))
        if resp == "Yes. Divide by two and keep going.":
            try:
                tmpParam=str(int(int(param)/2))
                param=tmpParam
                continue
            except:
                break
        break

print('completed')
$url="http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing="
$param="12345"

$pattern=[regex]"the next nothing is (\d+)"

for ($i=0; $i -lt 400; $i++) {
    $resp = (Invoke-WebRequest -Uri $url+$param | Select-Object -ExcludeProperty Content).Content
    $match = $pattern.Match($resp)
    if ($match.Success) {
        $param = $match.Groups[1].Value
    } else {
        [System.Console]::WriteLine($("param: {0}, resp: {1}" -f $param,$resp))
        if ($resp -eq "Yes. Divide by two and keep going.") {
            $param=$param/2
            continue
        }
        break
    }
}

[System.Console]::WriteLine("completed")
package main

import (
	"fmt"
	"net/http"
	"io/ioutil"
	"regexp"
	"strings"
	"strconv"
)

func (c *Challenge) Challenge004() {
	url:="http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing="
	param:="12345"

	pattern:=regexp.MustCompile("the next nothing is (/d+)")

	for i:=0; i<400; i++ {
		resp := httpGet(url+param)
		tmpParam:=pattern.FindStringSubmatch(resp)
		if len(tmpParam) != 2 {
			fmt.Printf("param: %s, response: %s\n",param, resp)
			if strings.Compare(resp,"Yes. Divide by two and keep going.") == 0 {
				intParam,err := strconv.Atoi(param)
				if err == nil {
					param = string(intParam/2)
					continue
				}
			}
			break
		} else {
			param=tmpParam[1]
		}
	}

	fmt.Println("completed")
}

func httpGet(url string) string {
	resp, err := http.Get(url)
	if err == nil {
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if err == nil {
			return string(body)
		}
	}
	return ""
}

最终结果: peak.html#

下一关地址#

007. smarty

关卡地址#

解决方案:#

思路:#

这一关源代码中没有任何提示信息,看来信息都在图片中了。

smarty

图片中间位置有一条灰度图像,灰度图取值范围与ASCII码范围一致,所以可以将灰度值转换为ASCII码。小灰度条的宽度都是7px(除了第一条是5px)。需要注意灰度图与图片右边界有21px空白。

行动起来吧。

经过灰度值转换得到如下信息:

smart guy, you made it. the next level is [105, 110, 116, 101, 103, 114, 105, 116, 121]

将列表中数值转换后就是下一关的地址了。

代码:#

import helper
path="../../Data/007"
helper.ensureDir(path)

import urllib.request
img="http://www.pythonchallenge.com/pc/def/oxygen.png"
(filename, headers)=urllib.request.urlretrieve(img, path+"/oxygen.png")

# python3图像处理库Pillow(friendly PIL fork)
# 文档地址:https://pillow.readthedocs.io/en/5.3.x/
from PIL import Image
im=Image.open(filename)
(w,h)=im.size
px=im.load()

outstr=[]
# 21px是灰度图右边空白的宽度
for i in range(0,w-21,7):
    (r,g,b,a)=px[i,h/2]
    # 灰度图r,g,b三个分量值相等
    outstr.append(chr(r))
im.close()

msg=''.join(outstr)
print(msg)

import re
pattern=re.compile(r'[\d]+')

nextlevel=''.join(map(chr,map(int,pattern.findall(msg))))
print(nextlevel)
$path="../../Data/007"

. .\helper.ps1
New-Dir -Dir $path
$path=$(Resolve-Path $path).Path

$url="http://www.pythonchallenge.com/pc/def/oxygen.png"
$filename=$path+"/oxygen.png"
Invoke-WebRequest -Uri $url -OutFile $filename

[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$img=[System.Drawing.Image]::FromFile($filename)

$msg=""
for ($i=0; $i -lt $img.Width-21; $i+=7) {
    $msg+=[char]$img.GetPixel($i,$img.Height/2).R
}
$img.Dispose()

Write-Output $msg

$outstr=""
$pattern=[regex]"\d+"
$pattern.Matches($msg) | ForEach-Object {$outstr+=[char][int]$_.Value}

Write-Output $outstr
package main

import (
	"fmt"
	"regexp"
	"strconv"
	_ "image/png"
)

func (c *Challenge) Challenge007() {
	path:="../../Data/007"
	EnsureDir(path)

	filename:=path+"/oxygen.png"
	channel:="http://www.pythonchallenge.com/pc/def/oxygen.png"
	Download(channel,filename)

	im:=OpenImage(filename)

	bounds := im.Bounds()

	msg := ""
	y:=(bounds.Max.Y-bounds.Min.Y)/2
	for x:=bounds.Min.X; x<bounds.Max.X-21; x+=7 {
		r,_,_,_ := im.At(x,y).RGBA()
		msg+=string(byte(r))
	}

	fmt.Println(msg)

	pattern:=regexp.MustCompile("/d+")
	matches:=pattern.FindAllString(msg, -1)

	outstr:=""
	for _,match := range matches {
		i,err:=strconv.Atoi(match)
		if err == nil {
			outstr+=string(byte(i))
		}
	}

	fmt.Println(outstr)
}

最终结果: integrity#

下一关地址#

010. what are you looking at?

关卡地址#

解决方案:#

思路:#

图片中的牛说:“你瞅啥?”,然后你说:“瞅你咋地,不光瞅你还打你呢”。(皮一下很开心😝)。然后(点击它)会得到一个sequence.txt文件,内容如下:

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

len(a[30]) = ?

规律是这样的:

  1. 第一个元素: 1
  2. 第二个元素: 11 (表示1个1)
  3. 第三个元素: 21 (表示2个1)
  4. 第四个元素: 1211 (表示1个2,1个1)
  5. 第五个元素: 111221 (表示1个1,1个2,2个1)

可以看出,这是一个简单的字符统计程序

代码:#

def getNext(instr):
    count=0
    curch=instr[0]
    outstr=[]
    for ch in instr:
        if ch != curch:
            outstr.append(str(count)+curch)
            curch=ch
            count=1
        else:
            count+=1
    outstr.append(str(count)+curch)
    return ''.join(outstr)

a=['1']
for i in range(31):
    a.append(getNext(a[i]))
print(len(a[30]))
function GetNext {
    param (
        [string]
        $instr
    )
    
    $count=0
    $curch=$instr[0]
    $outstr=New-Object System.Text.StringBuilder

    for ($i = 0; $i -lt $instr.Length; $i++) {
        $ch=$instr[$i]
        if ($ch -ne $curch) {
            # StringBuilder.Append的值会作为函数的返回值返回,所以要在前面加[void]或$null=
            [void]$outstr.AppendFormat("{0}{1}", $count, $curch)
            $curch=$ch
            $count=1
        } else {
            $count++
        }
    }
    [void]$outstr.AppendFormat("{0}{1}", $count, $curch)

    return $outstr.ToString()
}

$a=New-Object System.Collections.Generic.List[string]
$a.Add("1")
for ($i = 0; $i -lt 31; $i++) {
    $next=GetNext($a[$i])
    $a.Add($next)
}
Write-Output $a[30].Length
package main

import(
	"fmt"
	"strings"
)

func (c *Challenge) Challenge010() {
	a:=[]string {"1"}
	for i := 0; i < 31; i++ {
		a=append(a, getNext(a[i]))
	}
	fmt.Println(len(a[30]))
}

func getNext(instr string) string {
	count:=0
	curch:=instr[0]
	var outstr strings.Builder

	for i := 0; i < len(instr); i++ {
		ch:=instr[i]
		if ch != curch {
			outstr.WriteString(fmt.Sprintf("%d%s",count,string(curch)))
			curch=ch
			count=1
		} else {
			count++
		}
	}
	outstr.WriteString(fmt.Sprintf("%d%s",count,string(curch)))

	return outstr.String()
}

最终结果: 5808#

下一关地址#

017. eat?

关卡地址#

解决方案:#

思路:#

这一关图片中是曲奇饼干,左下角图片是不是有点熟悉?是第四关啦!

查看源代码并没有什么提示。

但是!根据cookie可以联想到什么?对!就是浏览器缓存。

通过浏览器开发者工具查看cookie后,可以得到如下提示:

you+should+have+followed+busynothing…

在第四关,我们请求的是:http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing=

这里需要将nothing替换为busynothing,并且收集cookieinfo的值。

之后得到BZh91AY开头的经过url编码的字符串,是不是又有点熟悉?不熟悉的请回顾第八关

使用python在解码时会有小问题:

OSError: Invalid data stream

在解码前将+替换为%20可以解决该问题。

解码后得到提示:

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

这句话信息量有点大,26号暗示第十五关call his father则暗示第十三关,Mozart的父亲是Leopold(注意L大写)。

给Leopold打电话之后得到:

555-VIOLIN

访问violin.html得到如下提示:

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

替换url之后访问,得到的是一张Leopold的照片,及:

it’s me. what do you want?

额。。。对,and inform him that “the flowers are on their way”

❤️ 如果这篇文章对你有帮助,欢迎赞助支持我继续维护 ❤️

☕ Support me ⚡ 爱发电赞助