记python逆向

近期遇到不少python逆向,小计一下

一、直接反编译

1、uncompyle6反编译

pip install uncompyle6

uncompyle6 -o test.py test.pyc
这种方法目前反编译不了python3.9级之后的版本

2、https://tool.lu/pyc/

一个在线反编译pyc文件的网站,
该网站支持包括python3.9,python3.10的多个版本的pyc文件
 

3、pycdc工具

安装:
git clone https://github.com/zrax/pycdc.git 
cd pycdc 
cmake .
make 

之后加入环境变量

使用:

pycdc ./

上面的在线反编译网站,其内核也是使用了pycdc工具

二、手撸字节码

有时候出题人不给我们.pyc文件,或者反编译工具不正确,这时候就需要我们直接读取字节码了
import dis
import marshal
f = open("你的pyc文件.pyc","rb").read()
code = marshal.loads(f[16:])    #反序列化#python3版本的需要去除前16个字节,python2版本的需要去除前8个字节
dis.dis(code)    #反编译
print(code)

 

三、python打包的exe/elf文件(无key)

python存在一个名叫Pyinstaller的开源工具包,使用此工具可以把.py文件打包成.exe或.elf文件

1、解包成pyc文件

我们可以使用Extractor工具或者 pyinstxtractor.py 工具进行解包

 python pyinstxtractor.py xx.exe

2、修改模数

一般来说解包后的文件有一个奇怪的问题,主函数的.pyc文件的模数会被修改,我们需要根据文件夹中一定会存在的struct.pyc文件进行修改

3、反编译或直接读取字节码

之后我们就可以重复一、二、的步骤进行解决问题了

 

四、python打包的exe/elf文件

Pyinstaller,py文件使用的时候有一个-key参数,可以对一些pyc文件进行加密,防止被逆向

一般把加密后的pyc文件称为pyz文件

以一个名为qgdjyelf文件为例,介绍一种dump的方式(我见好多大佬解包前要dump一下,我不是很明白原理,明明不dump也能解包啊...这里记录一下)

1、dump

objcopy --dump-section pydata=unwerid.dump qgdjy
0
 

2、将得到的dump文件用Pyinstaller工具解包

注意,我们要使用相同的python进行解包,不然可能会导致.pyz文件夹丢失,切记切记

3、将解包出的.pyc文件的模数转化成struct的模数

4、反编译

pycdc ./qgdjy.pyc
0
我们也可以通过下载pycdas来查看字节码
命令如下
cd pycdc/
cmake .
sudo make install
 

5、解密pyz文件

 
dump文件解包后有一个文件夹
0
里面加密了一些包
0
 
加密的方法与密钥在下图pyc文件中
0
0
archive.pyc就是加密的过程,crypto_key是加密的密钥,而我们需要解密.pyz文件
 
 
以cup.pyc.encrypted文件为例
我们需要对他解密
0
加密代码:
import marshal, struct, sys, zlib, _thread as thread
CRYPT_BLOCK_SIZE = 16
PYZ_TYPE_MODULE = 0
PYZ_TYPE_PKG = 1
PYZ_TYPE_DATA = 2
PYZ_TYPE_NSPKG = 3
 
......
 
#关键加密代码
class Cipher(object):
    __doc__ = '\n    This class is used only to decrypt Python modules.\n    '
 
    def __init__(self):
                #引入密钥
        import pyimod00_crypto_key
        key = pyimod00_crypto_key.key
        if not type(key) is str:
            raise AssertionError
        elif len(key) > CRYPT_BLOCK_SIZE:
            self.key = key[0:CRYPT_BLOCK_SIZE]
        else:
            self.key = key.zfill(CRYPT_BLOCK_SIZE)
        assert len(self.key) == CRYPT_BLOCK_SIZE
        import tinyaes
        self._aesmod = tinyaes
        del sys.modules['tinyaes']
 
        #利用TinyAES进行加密
    def __create_cipher(self, iv):
        return self._aesmod.AES(self.key.encode(), iv)
 
        #提供的解密算法,原义是想让pyz文件正常解压运行,在这里可以被我们利用
    def decrypt(self, data):
        cipher = self._Cipher__create_cipher(data[:CRYPT_BLOCK_SIZE])
        return cipher.CTR_xcrypt_buffer(data[CRYPT_BLOCK_SIZE:])
 
......

根据加密代码,我们可以写出解密代码(其实是抄的...)

解密代码:
import cup
import tinyaes
import zlib

CRYPT_BLOCK_SIZE = 16

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

inf = open('cup.pyc.encrypted', 'rb')  # 打开加密文件
outf = open('cup.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\0\0\0\0\0\0\0\0\0\0\0\0')

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

inf.close()
outf.close()
这样就得到了解密后的pyc包文件
 

五、加花的pyc文件

这个暂时没有遇到,只是听过...以后补
-------------------------------------------------------------------------------------------------------------- 2023/01/13 -------------------------------------------------------------------------------------------------------------- 
可算让我遇到了,nnd.....
HWS冬令营遇到了这种类型的题目,讲一下思路吧
内容有点多,另写了一篇博客
 
 
参考:
posted @ 2022-04-29 23:34  TLSN  阅读(1658)  评论(2编辑  收藏  举报