CS免杀之05shellcode和加载器
什么是shellcode
Shellcode 是一种特殊的二进制代码,一般都包含恶意攻击的 payload, 主要功能是在目标系统上执行特定的操作,例如获取系统权限、建立远程访问通道、执行恶意代码等。之所以叫 shellcode 是因为大多数这类二进制代码成功利用后会获取目标系统 shell 的控制权限
shellcode文件通常是二进制文件,可直接由计算机的CPU执行,当然也可采用八进制、十六进制等字符串进行存储 , 但在运行时,这些字符串依旧会被转换为二进制用于执行。实际运用会选择十六进制,因为十六进制和二进制的转换是最高效的,且支持的工具更多。一般也不选择二进制的保存,二进制的数据在传输时可能会出现问题。
一个程序的运行背后发生的事情
打开可执行文件 --> 定位到磁盘二进制文件 --> 将可执行文件读取到内存 --> 系统调度可执行文件的二进制指令 --> CPU执行指令计算出结果
使用 shellcode 和加载器的分离是为了实现隐藏恶意代码的作用,加载器为可执行指令,不直接包含恶意代码,shellcoed 通过字符串形式的十六进制存储恶意代码内容,这样能规避杀软的直接扫描查杀,当然十六进制的恶意代码一样会有特征,这就需要进行进一步的处理才能实现不被直接查杀了。
raw 格式通常指的是未经处理的原始数据文件,CS生成的 Payload 就是以 raw 格式保存的二进制文件。
/* length: 888 bytes */
unsigned char buf[] = "\xfc\x48\x83\xe4\xf0\xe8\xc8\x00\x00...............xe8\x9f\xfd\xff\xff\x31\x30\x2e\x30\x2e\x32\x2e\x31\x35\x00\x3a\xde\x68\xb1";
这个就是c语言中, 定义shellcode的代码, 其中
unsigned char
: 这表示 buf
数组的元素类型是无符号字符(8位)
buf[]
: 这是字符数组的声明,名称为 buf
\xfc....
: 这是字符串字面值, 这是一个16进制
加载器
加载器的作用就是用于将字符串形式的恶意代码处理成可执行的二进制指令,然后加载到内存中并执行,
加载器,需要实现的功能如下
- 申请内存
- 把shellcode加载到这块内存中
- 让这块内存中的shellcode被cpu执行
加载器常用函数
VirtualAlloc函数
VirtualAlloc
是 Windows API 中用于分配/申请、保留或提交内存区域的函数。
函数定义如下:
LPVOID VirtualAlloc(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect
);
参数的含义如下:
-
lpAddress
: 指定分配或保留的内存区域的首选基地址。如果此参数为NULL
,则系统会选择适当的地址。如果指定了地址,系统将尝试在指定的基地址上分配内存。注意,此地址必须是页对齐的。 -
dwSize
: 指定要分配的内存区域的大小,以字节为单位。 -
flAllocationType
: 指定内存的分配类型。可能的值包括:MEM_COMMIT (0x1000)
: 将物理页面分配给内存中的一个或多个页,并将这些页的内容初始化为零。如果同时指定了MEM_RESERVE
,则系统会保留这些页的地址空间而不将它们分配给任何物理页面。MEM_RESERVE (0x2000)
: 保留指定地址空间,不分配物理内存。这样可以阻止其他内存分配函数malloc
和LocalAlloc
等再使用已保留的内存范围,直到它被释放。当使用上面的VirtualAlloc
函数保留了一段地址空间后,接下还你还可以继续多次调用同样的函数提交这段地址空间中的不同页面。MEM_RESET (0x80000)
: 将分配的页面的内容初始化为零。这个标志仅在使用MEM_COMMIT
标志时才有意义。
-
flProtect
: 指定内存保护属性。可能的值包括:PAGE_EXECUTE (0x10)
: 允许页面被执行。PAGE_EXECUTE_READ (0x20)
: 允许页面被执行和读取。PAGE_EXECUTE_READWRITE (0x40)
: 允许页面被执行、读取和写入。可读可写可执行PAGE_EXECUTE_WRITECOPY (0x80)
: 允许页面被执行和写入。页面内容可以被其他进程写入。PAGE_READONLY (0x02)
: 允许页面被读取。PAGE_READWRITE (0x04)
: 允许页面被读取和写入。PAGE_WRITECOPY (0x08)
: 允许页面被写入。页面内容可以被其他进程写入。
memcpy函数
memcpy
是 C 标准库中的一个函数,用于将内存块的内容从一个位置复制到另一个位置
函数定义如下:
void *memcpy(
void *dest,
const void *src,
size_t n
);
dest
: 指向目标内存区域的指针,即复制操作的目标位置。src
: 指向源内存区域的指针,即复制操作的源位置。n
: 要复制的字节数。
现在内存相关的操作, 这些函数都可以实现了, 就差怎么执行shellcode了, 执行shellcode的方式很多, 后面会专门拿出来总结都有哪些, 当下我们使用 创建线程 运行我们的shellcode, 这就需要 CreateThread
CreateThread
CreateThread
是 Windows API 中用于创建新线程的函数。
函数定义如下:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
-
lpThreadAttributes
: 一个指向SECURITY_ATTRIBUTES
结构的指针,用于指定新线程的安全性。可以为NULL
,表示使用默认的线程安全性。 -
dwStackSize
: 指定新线程的初始堆栈大小。可以为0
,表示使用默认堆栈大小。 -
lpStartAddress
: 指向线程函数的指针,即新线程的入口点。线程函数是一个LPTHREAD_START_ROUTINE
类型的函数指针,通常是线程的主要执行体。 -
lpParameter
: 传递给线程函数的参数。这是一个指向任意类型的指针,可以用来传递数据给线程函数。 -
dwCreationFlags
: 指定新线程的创建标志。常用的标志包括:0
: 默认标志,表示线程立即可执行。CREATE_SUSPENDED (0x00000004)
: 创建后线程处于挂起状态,需要调用ResumeThread
才能执行。- ...
-
lpThreadId
: 一个指向 DWORD 的指针,用于接收新线程的标识符(线程 ID)。如果不需要线程 ID,可以传递NULL
。