reverse50-handcrafted pyc

0x01 题设

HitCon2016中最简单的逆向题,题目只给出了一份源码:

1
2
3
4
5
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import marshal, zlib, base64
exec(marshal.loads(zlib.decompress(base64.b64decode('eJyNVktv00AQXm/eL0igiaFA01IO4cIVCUGFBBJwqRAckLhEIQmtRfPwI0QIeio/hRO/hJ/CiStH2M/prj07diGRP43Hs9+MZ2fWMxbnP6mux+oK9xVMHPFViLdCTB0xkeKDFEFfTIU4E8KZq8dCvB4UlN3hGEsdddXU9QTLv1eFiGKGM4cKUgsFCNLFH7dFrS9poayFYmIZm1b0gyqxMOwJaU3r6xs9sW1ooakXuRv+un7Q0sIlLVzOCZq/XtsK2oTSYaZlStogXi1HV0iazoN2CV2HZeXqRQ54TlJRb7FUlKyUatISsdzo+P7UU1Gb1POdMruckepGwk9tIXQTftz2yBaT5JQovWvpSa6poJPuqgao+b9l5Aj/R+mLQIP4f6Q8Vb3g/5TB/TJxWGdZr9EQrmn99fwKtTvAZGU7wzS7GNpZpDm2JgCrr8wrmPoo54UqGampFIeS9ojXjc4E2yI06bq/4DRoUAc0nVnng4k6p7Ks0+j/S8z9V+NZ5dhmrJUM/y7JTJeRtnJ2TSYJvsFq3CQt/vnfqmQXt5KlpuRcIvDAmhnn2E0t9BJ3SvB/SfLWhuOWNiNVZ+h28g4wlwUp00w95si43rZ3r6+fUIEdgOZbQAsyFRRvBR6dla8KCzRdslar7WS+a5HFb39peIAmG7uZTHVm17Czxju4m6bayz8e7J40DzqM0jr0bmv9PmPvk6y5z57HU8wdTDHeiUJvBMAM4+0CpoAZ4BPgJeAYEAHmgAUgAHiAj4AVAGORtwd4AVgC3gEmgBBwCPgMWANOAQ8AbwBHgHuAp4D3gLuARwoGmNUizF/j4yDC5BWM1kNvvlxFA8xikRrBxHIUhutFMBlgQoshhPphGAXe/OggKqqb2cibxwuEXjUcQjccxi5eFRL1fDSbKrUhy2CMb2aLyepkegDWsBwPlrVC0/kLHmeCBQ=='))))

0x02 write-up

这道最简单的题搞了我快一个下午,必须要写点什么…

可以将这段base64解码再解压缩后的数据流打印出来看下是什么东西,发现大部分为乱码有少量的python关键字,结合题目标题猜测是pyc文件,也就是compiled后的py字节码文件,于是找来几个工具来disassemble/decompile byte-code,比如uncompyle2, pycdc, pychrysanthemum…总之我就用过这几个tools。

f = open("crack.pyc", 'w')
f.write(zlib.decompress(base64.b64decode(XXXXX)))

先用运行uncompyle2时,遇到“Unknown Magic Number”错误,根据这篇文章的提示,填充pyc文件头格式的八个Bytes:

1
2
3
4
5
6
$ python
>>> import py_compile
>>> py_compile.compile('crack.py')
$ xxd -l 16 crack.pyc
$ head -c 8 crack.pyc > newcrack.pyc
$ cat crack.pyc >> newcrack.pyc

这次再运行uncompyle2脚本,还是报错,是在反编译时遇到了问题:

1
Syntax error at or near 'ROT_TWO' token at offset 36

不过已经显示出了反汇编所有代码,用pychrysanthemum这个图形化界面Disassembler看的更舒服些:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
+ co_consts
108: <type 'int'>
97: <type 'int'>
67: <type 'int'>
32: <type 'int'>
101: <type 'int'>
109: <type 'int'>
121: <type 'int'>
80: <type 'int'>
104: <type 'int'>
116: <type 'int'>
110: <type 'int'>
111: <type 'int'>
114: <type 'int'>
105: <type 'int'>
118: <type 'int'>
117: <type 'int'>
99: <type 'int'>
33: <type 'int'>
73: <type 'int'>
112: <type 'int'>
98: <type 'int'>
100: <type 'int'>
115: <type 'int'>
78: <type 'int'>
123: <type 'int'>
119: <type 'int'>
125: <type 'int'>
58: <type 'int'>
87: <type 'int'>
103: <type 'int'>
46: <type 'int'>
68: <type 'int'>
102: <type 'int'>
41: <type 'int'>
61: <type 'int'>

将这些值chr一下,发现并没有什么规律= =
之后,再查看下反汇编代码发现某些指令规律(大多为LOAD_GLOBAL、LOAD_CONST与CALL_FUNCTION循环),同时又查了下ROT_TWO()和BINARY操作:

ROT_TWO: swaps the two top-most stack items(From docs.python.org)

Binary operations remove the top of the stack(TOS) and the second top-most stack item(TOS1) from the stack. They perform the opreation, and put the result back on the stack.

BINARY_ADD Implements TOS = TOS1 + TOS

猜测这些指令的目的是用来对可能是多个字符串的每个字符进行混淆操作再一一进行比较。

后续我再具体用dis模块处理,分析下这个字节码文件所包含的各种信息。
于是我先把所有的反汇编的所有代码做下处理,保留下所有包含LOAD_CONST指令的代码行,再提取出每条指令调取的字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import sys
import os
import re

file = open("disass")

while 1:
line = file.readline()
if not line:
break
if "LOAD_CONST" in line:
PAT = re.compile(r"(\d+): <type 'int'>")
pat = PAT.search(line)
if pat != None:
print pat.group(1)
#python solve.py > solve

得到下列字符串:

llaC em yP aht notriv lauhcamni !eac Ini npreterP tohty ntybdocese!!!ctihN{noy woc uoc naipmoa eldnur yP nnohttyb doceni euoy rb ria}!napwssro :dorWp gnssadrow…elP esa yrtaga .ni oD tonurbf etecro)= .

在中间部位可以发现8个有意思的字符:
ctihN{no

如果以每四个字符进行翻转,便可以得到:
hitcon{N
于是可以写一个脚本处理下:

1
2
3
4
5
6
7
8
string = "ctihN{noy woc uoc naipmoa eldnur yP nnohttyb doceni euoy rb ria}!napwssro :dorWp gnssadrow...elP  esa yrtaga .ni oD tonurbf etecro)= "
i = 0
s = ""
while i < len(string):
sub = string[i:i+4]
s = s+sub[::-1]
i += 4
print s

得到

hitcon{Now you can compile arund Py honnbyttcod ineyoue br }airpan!rsswd: opWrosng rdas..woPle.se ry aagatin. Do unot fbrcete=)or

当时以为这就是Flag= =
发现后边是乱码,但是可以猜测出来:

hitcon{Now you can compile and run Python bytecode in your brain.}

未完待续…