我不是病毒2.0
大概是萌新唯一有能力复现的题了吧,太菜了呜呜呜呜呜,打比赛的时候做 flappy-bird 做了一天真的逆不动,看来得 allin 一下安卓了。
对着 官方wp 和 大爹战队的wp 复现的。
[原创]Python逆向——Pyinstaller逆向-软件逆向-看雪论坛-安全社区|安全招聘|bbs.pediy.com (kanxue.com)
题目是个 exe 文件,查壳发现是 pyinstaller
打包的程序
用 pyinstxtractor.py
解包,但是注意附件的 python 环境是 3.10 的,所以本地的 python 环境也得是 3.10,否则解包出来的 PYZ-00.pyz_extracted
文件夹是空的。PYZ-00.pyz_extracted
非常重要,一般一个稍微大一点的项目都会分成多个py
文件,甚至会依赖其他模块,这些被依赖的文件解析后都会放入PYZ-00.pyz_extracted
中,可以说这里放的是核心代码。
可以看到 pyinstxtractor.py
中有检测 MAGIC_NUMBER
也就是检测了 python 版本。
我们修复一下 main.pyc
的 MAGIC_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))
|
发现 main
函数首先 import
了 sign
模块,然后又 LOAD
了 sign
模块的 main
函数,那么关键代码应该就在 sign
模块里。
或者直接找在线解密也能得出:
但是本题是带 -key
参数的 python 逆向。
由于 pyinstaller
是个开源的包,这也给我们逆向提供了便利。在官方给出的用法中,有给出-key这个参数,说是可以将文件 pyc
进行一定的压缩加密,以防止被逆向。Pyinstaller
这个库本身的打包原理大概就是先将 py
编译成 pyc
,然后部分压缩成 pyz
,程序再通过对pyc
和 pyz
的调用。
可以看到 sign.py
模块就被加密了
我们可以在解包的文件夹中找到 crypto_key
和 archive
两个模块。反编译一下发现是用了 AES
对 pyc
文件进行了加密,我们把这两个 pyc
文件反编译出来,然后写脚本对文件进行解密
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
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()))
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
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()))
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
|
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))
|