我不是病毒2.0

大概是萌新唯一有能力复现的题了吧,太菜了呜呜呜呜呜,打比赛的时候做 flappy-bird 做了一天真的逆不动,看来得 allin 一下安卓了。

对着 官方wp大爹战队的wp 复现的。

[原创]Python逆向——Pyinstaller逆向-软件逆向-看雪论坛-安全社区|安全招聘|bbs.pediy.com (kanxue.com)

题目是个 exe 文件,查壳发现是 pyinstaller 打包的程序

1681463374447

pyinstxtractor.py 解包,但是注意附件的 python 环境是 3.10 的,所以本地的 python 环境也得是 3.10,否则解包出来的 PYZ-00.pyz_extracted 文件夹是空的。PYZ-00.pyz_extracted非常重要,一般一个稍微大一点的项目都会分成多个py文件,甚至会依赖其他模块,这些被依赖的文件解析后都会放入PYZ-00.pyz_extracted中,可以说这里放的是核心代码。

1681463922514

可以看到 pyinstxtractor.py 中有检测 MAGIC_NUMBER 也就是检测了 python 版本。

我们修复一下 main.pycMAGIC_NUMBER, 然后用 dis 模块还原出 main.pyc 的字节码

1
2
3
4
5
6
import dis
import marshal

with open('main.pyc', 'rb') as f:
f.seek(16)
dis.dis(marshal.load(f))

1681465185389

发现 main 函数首先 importsign 模块,然后又 LOADsign模块的 main 函数,那么关键代码应该就在 sign 模块里。

或者直接找在线解密也能得出:

1681466451925

但是本题是带 -key 参数的 python 逆向。

由于 pyinstaller是个开源的包,这也给我们逆向提供了便利。在官方给出的用法中,有给出-key这个参数,说是可以将文件 pyc 进行一定的压缩加密,以防止被逆向。Pyinstaller这个库本身的打包原理大概就是先将 py 编译成 pyc ,然后部分压缩成 pyz ,程序再通过对pycpyz的调用。

可以看到 sign.py 模块就被加密了

1681465989022

我们可以在解包的文件夹中找到 crypto_keyarchive 两个模块。反编译一下发现是用了 AESpyc 文件进行了加密,我们把这两个 pyc 文件反编译出来,然后写脚本对文件进行解密

1681465832379

1681466033023

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import tinyaes
import zlib

CRYPT_BLOCK_SIZE = 16

# 从crypt_key.pyc获取key,也可自行反编译获取
key = bytes('HelloHiHowAreYou', 'utf-8')

inf = open('sign.pyc.encrypted', 'rb') # 打开加密文件
outf = open('sign.pyc', 'wb') # 输出文件

# 按加密块大小进行读取
iv = inf.read(CRYPT_BLOCK_SIZE)

cipher = tinyaes.AES(key, iv)

# 解密
plaintext = zlib.decompress(cipher.CTR_xcrypt_buffer(inf.read()))

# 补pyc头(最后自己补也行)

outf.write(b'\x6F\x0D\x0D\x0A\x00\x00\x00\x00\x70\x79\x69\x30\x10\x01\x00\x00')

# 写入解密数据
outf.write(plaintext)
import tinyaes
import zlib

CRYPT_BLOCK_SIZE = 16

# 从crypt_key.pyc获取key,也可自行反编译获取
key = bytes('HelloHiHowAreYou', 'utf-8')

inf = open('sign.pyc.encrypted', 'rb') # 打开加密文件
outf = open('sign.pyc', 'wb') # 输出文件

# 按加密块大小进行读取
iv = inf.read(CRYPT_BLOCK_SIZE)

cipher = tinyaes.AES(key, iv)

# 解密
plaintext = zlib.decompress(cipher.CTR_xcrypt_buffer(inf.read()))

# 补pyc头(最后自己补也行)

outf.write(b'\x6F\x0D\x0D\x0A\x00\x00\x00\x00\x70\x79\x69\x30\x10\x01\x00\x00')

# 写入解密数据
outf.write(plaintext)

inf.close()
outf.close()

得到 sign.pyc 文件,再放到在线反编译网站里反编译成 sign.py

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
37
38
39
40
41
42
43
#!/usr/bin/env python
# visit https://tool.lu/pyc/ for more information
# Version: Python 3.10

import hashlib as 沈阳
import base64 as 杭州
import ctypes as 蚌埠

def main():
蚌埠.windll.kernel32.VirtualAlloc.restype = 蚌埠.c_void_p
福建 = input('%e6%82%a8%e7%9a%84%e8%be%93%e5%85%a5%ef%bc%9a')
天津 = '9K98jTmDKCXlg9E2kepX4nAi8H0DB57IU57ybV37xjrw2zutw+KnxkoYur3IZzi2ep5tDC6jimCJ7fDpgQ5F3fJu4wHA0LVq9FALbjXN6nMy57KrU8DEloh+Cji3ED3eEl5YWAyb8ktBoyoOkL1c9ASWUPBniHmD7RSqWcNkykt/USjhft9+aV930Jl5VjD6qcXyZTfjnY5MH3u22O9NBEXLj3Y9N5VjEgF2cFJ+Tq7jj92iIlEkNvx8Jl+eH5/hipsonKLTnoLGXs4a0tTQX/uXQOTMBbtd70x04w1Pa0fp+vA9tCw+DXvXj0xmX8c5HMybhpPrwQYDonx7xtS+vRIj/OmU7GxkHOOqYdsGmGdTjTAUEBvZtinOxuR7mZ0r9k+c9da0W93TWm5+2LKNR6OJjmILaJn0lq4foYcfD5+JITDsOD6Vg01yLRG1B4A6OxJ7Rr/DBUabSu2fYf1c4sTFvWgfMV8il6QfJiNMGkVLey1cBPSobenMo+TQC1Ql0//9M4P01sOiwuuVKLvTyDEv6dKO//muVL9S2gq/aZUBWkjj/I5rUJ6Mlt4+jsngmuke9plAjw22fUgz+8uSzn40dhKXfBX/BOCnlwWsMGAefAfoz/XAsoVSG2ioLFmlcYe/WBgaUJEoRUSyv73yiEOTVwIK6EPnDlwRgZZHx2toLu8udpEZ0aKGkex5sn7P8Jf9AbD4/EiQU+FdoJSxGorPSZGvrc4='
北京 = 沈阳.md5('%e4%ba%91%e5%8d%97'.encode('utf-8')).hexdigest()
重庆 = 杭州.b64decode(天津)
河南 = b''
北京_len = len(北京)
广州 = list(range(256))
j = 0
for i in range(256):
j = (j + 广州[i] + ord(北京[i % 北京_len])) % 256
广州[i] = 广州[j]
广州[j] = 广州[i]
山东 = 陕西 = 0
for 河北 in 重庆:
山东 = (山东 + 1) % 256
陕西 = (陕西 + 广州[山东]) % 256
广州[山东] = 广州[陕西]
广州[陕西] = 广州[山东]
河南 += bytes([
河北 ^ 广州[(广州[山东] + 广州[陕西]) % 256]])
四川 = 蚌埠.create_string_buffer(福建.encode())
黑龙江 = 蚌埠.windll.kernel32.VirtualAlloc(蚌埠.c_int(0), 蚌埠.c_int(len(河南)), 蚌埠.c_int(12288), 蚌埠.c_int(64))
蚌埠.windll.kernel32.RtlMoveMemory(蚌埠.c_void_p(黑龙江), (蚌埠.c_ubyte * len(河南)).from_buffer(bytearray(河南)), 蚌埠.c_size_t(len(河南)))
辽宁 = 蚌埠.windll.kernel32.CreateThread(蚌埠.c_int(0), 蚌埠.c_int(0), 蚌埠.c_void_p(黑龙江), 蚌埠.byref(四川), 蚌埠.c_int(0), 蚌埠.pointer(蚌埠.c_int(0)))
蚌埠.windll.kernel32.WaitForSingleObject(蚌埠.c_int(辽宁), 蚌埠.c_int(-1))
if 四川.raw == b'%db%1b%00Dy\\C%cc%90_%ca.%b0%b7m%ab%11%9b^h%90%1bl%19%01%0c%eduP6%0c0%7f%c5E-L%b0%fb%ba%f6%9f%00':
print('%e6%98%af%e7%9a%84%ef%bc%81%e4%bd%a0%e5%be%97%e5%88%b0%e4%ba%86%ef%bc%81')
return None
None('%e4%b8%8d%ef%bc%8c%e5%86%8d%e5%b0%9d%e8%af%95%e6%9b%b4%e5%a4%9a%e3%80%82 %ef%bc%88%e7%ac%91%e8%84%b8%e7%ac%a6%e5%8f%b7%ef%bc%89')

if __name__ == '__main__':
main()
return None

程序把变量都变成了中文名,同时把可见字符都进行了 url 编码降低代码的可读性,但是 RC4 特征还是非常明显的,整个流程大概是先输入了 flag, 然后用 RC4 加密得到一段 shellcode ,最后开一段虚拟内存把这段 shellcode 加载进去对 flag 进行加密,最后返回的结果与 b'%db%1b%00Dy\\C%cc%90_%ca.%b0%b7m%ab%11%9b^h%90%1bl%19%01%0c%eduP6%0c0%7f%c5E-L%b0%fb%ba%f6%9f%00'进行比对。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from Crypto.Cipher import ARC4
import base64
import hashlib

cipher = '9K98jTmDKCXlg9E2kepX4nAi8H0DB57IU57ybV37xjrw2zutw+KnxkoYur3IZzi2ep5tDC6jimCJ7fDpgQ5F3fJu4wHA0LVq9FALbjXN6nMy57KrU8DEloh+Cji3ED3eEl5YWAyb8ktBoyoOkL1c9ASWUPBniHmD7RSqWcNkykt/USjhft9+aV930Jl5VjD6qcXyZTfjnY5MH3u22O9NBEXLj3Y9N5VjEgF2cFJ+Tq7jj92iIlEkNvx8Jl+eH5/hipsonKLTnoLGXs4a0tTQX/uXQOTMBbtd70x04w1Pa0fp+vA9tCw+DXvXj0xmX8c5HMybhpPrwQYDonx7xtS+vRIj/OmU7GxkHOOqYdsGmGdTjTAUEBvZtinOxuR7mZ0r9k+c9da0W93TWm5+2LKNR6OJjmILaJn0lq4foYcfD5+JITDsOD6Vg01yLRG1B4A6OxJ7Rr/DBUabSu2fYf1c4sTFvWgfMV8il6QfJiNMGkVLey1cBPSobenMo+TQC1Ql0//9M4P01sOiwuuVKLvTyDEv6dKO//muVL9S2gq/aZUBWkjj/I5rUJ6Mlt4+jsngmuke9plAjw22fUgz+8uSzn40dhKXfBX/BOCnlwWsMGAefAfoz/XAsoVSG2ioLFmlcYe/WBgaUJEoRUSyv73yiEOTVwIK6EPnDlwRgZZHx2toLu8udpEZ0aKGkex5sn7P8Jf9AbD4/EiQU+FdoJSxGorPSZGvrc4='

cipher = bytes(cipher.encode('utf-8'))

arr = base64.b64decode(cipher)

key = hashlib.md5('云南'.encode('utf-8')).hexdigest()

key = bytes(key.encode('utf-8'))

cipher = ARC4.new(key)

p = cipher.decrypt(bytes(arr))

print(list(p))