初探免杀小基础
基础部分
Cobalt Strike基础部分
大部分自行了解,只记部分
参考:https://cloud.tencent.com/developer/article/1595548
Beacon
Beacon是CS在目标主机上运行的payload,在隐蔽的隧道上为我们提供服务,以此长期控制主机
在实际渗透中,我们可以将其嵌入到可执行文件、Word文档或者利用主机漏洞传递Beacon
Beacon功能包括:
- 使用HTTP/DNS检查是否有待执行任务
- 连接多个C2域名
- 分段传输后自动迁移
- 与CS集成,通过社工、主机漏洞以及会话等方式来传递Beacon
Beacon的工作原理
Beacon上线后会主动向我们设置好的Listener发送请求信息
而Team Server控制器接到请求后会检查是否有待执行任务,如果有就会将任务下发到Beacon
Stager
很多攻击框架都是使用分段的shellcode,以防止shellcode过长,覆盖到了上一函数栈帧的数据,导致引发异常
而分段shellcode当中一个重要的东西就是stager
stager是一段很精短的代码,它可以连接下载真正的payload并将其注入内存。我们使用stager就可以解决shellcode过长的问题。
Stager原理
Stager的工作流程如下:
- 申请内存
- 复制Stager到内存中
- 创建线程运行Stager
- Stager再次申请一块内存
- Stager下载加密后的payload,将其写入申请的内存中
- Stager将执行流程传递给加密的payload
- 加密payload自解密为反射的DLL(Reflective DLL)
- Reflective DLL申请一个块内存
- Reflective DLL初始化自身到新的内存中
- Reflective DLL调用payload的入口点函数
- 上线成功
shellcode加载器
加载器要实现的基本功能:
- 开辟内存
- 将shellcode放入内存中
- 使得内存中的shellcode被运行(CPU执行、回调函数执行)
内存操作相关函数
VirtualAlloc
Windows API中用于分配/申请、保留或者提交内存区域的函数
定义如下:
1 | LPVOID VirtualAlloc( |
lpAddress
([in, optional] LPVOID
)
含义:指定希望分配的内存起始地址。
取值:
NULL
(推荐):由系统自动选择合适的地址。- 非空地址:尝试在指定地址分配内存,但系统可能拒绝(需满足对齐要求且地址未被占用)。
注意:除非必要,否则应传
NULL
,避免与系统冲突。dwSize
([in] SIZE_T
)含义:请求分配的内存大小(字节数)。
行为:
- 系统会向上取整到页大小(通常为 4KB)。
- 例如请求 1 字节,实际分配 4KB。
flAllocationType
([in] DWORD
)含义:内存分配类型,常用值为:
MEM_COMMIT
:为指定地址空间分配物理存储(立即占用内存)。MEM_RESERVE
:保留地址空间,但不分配物理内存(直到后续提交)。- 组合使用:
MEM_COMMIT | MEM_RESERVE
同时保留并提交内存。
示例:
1 | // 分配并立即使用 1MB 内存 |
flProtect
([in] DWORD
)
- 含义:内存保护属性,常用值为:
PAGE_READONLY
:只读访问。PAGE_READWRITE
:读写访问。PAGE_EXECUTE_READ
:可执行且可读。PAGE_EXECUTE_READWRITE
:可执行、可读、可写。
- 注意:
- 若访问权限冲突(如写入只读内存),会触发异常(需通过
try-except
处理)。 - 数据段通常设为
PAGE_READWRITE
,代码段设为PAGE_EXECUTE_READ
。
- 若访问权限冲突(如写入只读内存),会触发异常(需通过
使用示例:分配可读写内存
1 | LPVOID pMemory = VirtualAlloc( |
相关的其他函数
API | 分配粒度 | 适用场景 | 备注 |
---|---|---|---|
VirtualAlloc | 页(通常 4KB) | 大块连续内存、虚拟地址管理 | 传统 API,兼容性好 |
VirtualAlloc2 | 页 | 高级内存特性(大页面、NUMA) | 仅 Windows 10+ |
VirtualAllocEx | 页 | 跨进程内存分配 | 需目标进程句柄 |
HeapAlloc | 任意大小 | 频繁分配小块内存 | 基于进程堆,高效 |
MapViewOfFile | 任意大小 | 文件映射、共享内存 | 适合大文件或进程间通信 |
LocalAlloc | 任意大小 | 兼容旧代码 | 基于进程堆 |
MmMapIoSpace | 任意大小 | 内核模式设备映射 | 仅驱动开发可用 |
Memcpy
C 标准库中的一个函数,用于将内存块的内容从一个位置复制到另一个位置
1 | void *memcpy( |
dest
(目标地址)- 类型:
void*
(可接受任意类型的指针) - 含义:指向复制操作的目标内存区域。
- 注意:
- 需确保目标内存区域有足够空间(至少
n
字节),否则会导致内存溢出。 - 若目标区域与源区域重叠,可能导致未定义行为(推荐使用
memmove
处理重叠情况)。
- 需确保目标内存区域有足够空间(至少
- 类型:
src
(源地址)- 类型:
const void*
(只读,防止修改源数据) - 含义:指向要复制的源内存区域。
- 注意:源地址必须有效且可读。
- 类型:
n
(复制字节数)- 类型:
size_t
(无符号整数,通常为unsigned long
) - 含义:指定要从源地址复制到目标地址的字节数。
- 注意:
- 复制的是二进制数据,不关心数据类型(如
int
会被按字节拆分复制)。 - 若
n
过大,可能导致越界访问;若过小,数据会被截断。
- 复制的是二进制数据,不关心数据类型(如
- 类型:
执行 shellcode函数
CreateThread
CreateThread
是 Windows API 中用于创建线程的函数,其参数功能和使用细节如下
1 | HANDLE CreateThread( |
lpThreadAttributes : 一个指向 SECURITY_ATTRIBUTES 结构的指针,用于指定新线程的安全性。可以为 NULL ,表示使用默认的线程安全性。
dwStackSize : 指定新线程的初始堆栈大小。可以为 0,表示使用默认堆栈大小。
lpStartAddress : 指向线程函数的指针,即新线程的入口点。线程函数是一个 LPTHREAD_START_ROUTINE 类型的函数指针,通常是线程的主要执行体。
lpParameter : 传递给线程函数的参数。这是一个指向任意类型的指针,可以用来传递数据给线程 函数。
dwCreationFlags : 指定新线程的创建标志。常用的标志包括:
0 : 默认标志,表示线程立即可执行。
CREATE_SUSPENDED (0x00000004) : 创建后线程处于挂起状态,需要调用 ResumeThread 才能执行
lpThreadId : 一个指向 DWORD 的指针,用于接收新线程的标识符(线程 ID)。如果不需要线程 ID,可以传递 NULL 。
简单加载器编写
不做任何处理
1 |
|
加载文件的(shellcode分离)
1 |
|
简单shellcode加密
SGN加密
参考: