Student Festival Puzzle Solved in 33 Lines

贵系今年学生节海报谜题:

Part 1

wget "http://www.net9.org/StudentFestival/1001/OnlinePoster.jpg"  # 原链接已失效,可用上面图片代替
exiftool OnlinePoster.jpg | grep Text | tail -n 1 | sed 's/,.*//g;s/ //g;s/^.*://g' > zeroone.txt
echo "ibase=2;obase=10000;$(cat zeroone.txt)" | bc | tr -d '\\\n' | xxd -r -p > bin
file bin
tar xjvf bin
file 1001/*
chmod +x 1001/key
objdump -d 1001/key > key.s     # RTFSC

第一行下载图片..=.=

第二行从 jpg 图片的 exif 信息中中提取 Text Layer, 也即图片中的 01 串, 将其写入 zeroone.txt 文件中.

第三行利用 bc 将 01 串转为 16 进制字符串, 再利用 xxd 转为二进制写入到文件 bin 中.

这里 bc 的效率好低.. 求更好的能一行写出来的方法.. python 是快了很多, 不过要写好几行:

import itertools, sys
s = open('zeroone.txt').readline().strip()
l = itertools.izip_longest(*([iter(s)] * 8))
l = [int(''.join(x), 2) for x in l]
[sys.stdout.write(chr(k)) for k in l]

依云同学指出还可以这样写:

python2 -c 'while True: import sys; sys.stdout.write(chr(int(sys.stdin.read(8).strip() or sys.exit(), 2)))' < zeroone.txt > bin

wjf 学长表示:

python2 -c 'import binascii; print binascii.unhexlify(hex(int(open("zeroone.txt").readline().strip(), 2))[2:-1].encode())' > bin

第四行查看 bin 的类型, 发现是 bz2, 第五行解压之.

第六行查看解压出的文件的类型, 发现一个是 32bit stripped ELF, 一个是 zip. zip 是带密码的, 于是密码要从 ELF 中获取了.

第七行加上执行权限, 第八行 dump 出来汇编, 然后开始艰苦的 RTFSC + gdb.

Part 2

perl -pne 's/\xcd\x80/\x90\x90/g' < 1001/key > 1001/key.modified
python2 << EOF > keys.txt
for n in (t for t in xrange(987654) if t % 223 == 0):
    if str(n).zfill(10)[-3] == str(n).zfill(10)[-5]:
        if n / 651 % 100 == n / 651 / 100 % 100 + 1:
            print n
EOF

直接运行这个 ELF, 会询问密码, 输错就喵的一下退出了. 一尝试 gdb 就会发现进入了死循环, 调试不能, 非常坑爹.

读汇编发现程序里通过 26 号系统调用ptrace 判断了自己是否被 gdb, 看到这里就觉得这程序只能是宋教授写的了..

于是第 1 行用 perl 把系统调用的指令强行换成了两条 nop, 得到新的可执行程序去 gdb. 配合着汇编看, 大致可以搞清楚程序的正常流程: 读入一个数字, 如果其满足一定数学条件就输出 "I don't know the secret.", 否则喵你一下. 接下来 6 行用 python 枚举出了所有满足它的条件的数, 共有 5 个.

但是这些数无论怎么 yy, 都不是那个 zip 的密码...

然而宋教授表示 "显然应该让 ELF 吐密码啊".. 再仔细看看汇编, 发现有一段可疑的函数从来没有被运行到过, 其中还调用了 putchar. 于是猜想在什么特定条件下会进入这段函数, 后来 Blahgeek 发现其实是直接强行调它就好了..

 1 gdb ./1001/key <<< "set pagination off
 2 b __libc_start_main
 3 r
 4 set \$eip=0x8048623
 5 b *0x8048634
 6 c
 7 set \$ebx=$(tail -n 1 keys.txt)
 8 b *0x8048649
 9 disp \$ecx
10 $(for i in {1..14}; do echo "c"; done)
11 q" | egrep -o 'ecx = [0-9]+' | sed 's/.*= //g' > asciipass.txt
12 while read line; do
13   ascii "\d$line" | grep -o 'prints as `.' | sed 's/.*`//g'
14 done < asciipass.txt | paste -sd '' > realpass.txt

用 gdb 进入 ELF, 强行设了 eip 寄存器以调用可疑代码, 将调用参数设为了 keys.txt 中的最后一个数 (其实是教授的生日), 然后会输出一个长度为 14 的字符串作为密码.

Part 3

unzip -oP $(cat realpass.txt) 1001/box
sed 's/^  \([^ ][^ ]\) .* \(..\)$/\1\2/; s/ //g;' UrbanMuller.txt | paste -sd '' > rawbf.txt
perl -pe 's/(.)/chr (ord($1) - 1)/gie' rawbf.txt > final.bf
chromium $(pybf -i final.bf)

解压后得到 UrbanMuller.txt, 文件名是 brainfuck 语言的作者, 打开发现里面长的也比较像一个 brainfuck 程序.

仔细观察发现把所有字符的 ASCII 码减 1, 就会得到一个合法 bf 程序. 再用 pybf 运行一下, 就输出了最终答案.

答案是个 link, 访问它就好了..

广告..

今年的两个精彩视频:

开场 DV: http://video.sina.com.cn/v/b/122479361-2128472472.html

计科 20 万万没想到: http://v.youku.com/v_show/id_XNjUxNjY0OTIw.html

Comments