1.远程内存注入模块

1.支持64位和32位,需要在工程编译的时候选好对应编译模式(禁用优化)。

2.此代码中使用的是CreateRemoteThread函数创建的入口线程,你可以替换成别的获取线程方法,比如说APC线程,线程劫持等技术,获取线程执行入口代码。

3.path_dll是要注入的模块绝对地址可以自行更改。

4.此项目的导入表修复的时候是把dll提前在自己进程内修复后再一次性写入目标进程的,所以目标进程没有你所需要的DLL的话,就会出错,你要把需要的DLL导入进目标进程,再修复。

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


#define path_dll R"(C:\Users\qingfeng\source\repos\RemoteHook\x64\Release\TestDll.dll)"
#define GET_HEADER_DICTIONARY(module, idx)        &(module)->headers->OptionalHeader.DataDirectory[idx]

#ifdef _WIN64
#define POINTER_TYPE ULONGLONG
#else
#define POINTER_TYPE DWORD
#endif

//获取文件的在内存的地址和大小
int GetBuffAddrAndSizeofFile(char* FilePath, _Out_  size_t* buff, _Out_ DWORD* FileSize, _Out_ DWORD* FileSize1);

//修复重定位表
void PerformBaseRelocation(char* buff, size_t Value);

//重建IAT表
BOOL RebuildImportTable(char* buff);

//拉伸文件
size_t StretchFile(size_t pFileBuff, DWORD FileSize);


//内存注入
void MemoryInject(DWORD dwPID)
{
    //1.把dll文件加载到内存
    size_t pFileBuf_dll = 0;//dll在内存中的地址
    DWORD FileSize_dll = 0;//dll的内存大小
    DWORD FileSize_dll_1 = 0;//dll的文件大小
    if (GetBuffAddrAndSizeofFile((char*)path_dll, &pFileBuf_dll, &FileSize_dll, &FileSize_dll_1) == 0)
    {
        return;
    }

    //2.拉伸dll
    size_t NewBuff = StretchFile(pFileBuf_dll, FileSize_dll);

    //释放pFileBuf_dll
    delete[](char*)pFileBuf_dll;
    //获取PE头和NT头
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)NewBuff;
    PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(NewBuff + pDosHeader->e_lfanew);

    //3.0打开目标进程
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, dwPID);

    //3.1在目标进程申请内存
    LPBYTE lpbPeAddress = (LPBYTE)VirtualAllocEx(hProcess, NULL, FileSize_dll, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if (NULL == lpbPeAddress)
    {
        printf("在目标进程申请内存失败!");
        return;
    }

    //4.0 修复重定位表
    DWORD locationDelta = (SIZE_T)((DWORD)lpbPeAddress - pNtHeader->OptionalHeader.ImageBase);
    if (0 != locationDelta)
    {
        PerformBaseRelocation((char*)NewBuff, locationDelta);
    }

    //4.1 修复IAT表
    if (!RebuildImportTable((char*)NewBuff))
    {
        printf("修复失败!");
        return;
    }

    //把修复后的dll数据拷贝到目标进程内存
    BOOL res = WriteProcessMemory(hProcess, lpbPeAddress, (LPCVOID)NewBuff, FileSize_dll, 0);
    if (0 == res)
    {
        printf("在目标进程写入失败!");
        VirtualFreeEx(hProcess, lpbPeAddress, FileSize_dll, MEM_RELEASE);
        CloseHandle(hProcess);
        return;
    }

    //5.找到dll在目标进程的入口
    size_t EnterPointer = (size_t)lpbPeAddress + pNtHeader->OptionalHeader.AddressOfEntryPoint;

    //dll入口参数结构体
    typedef struct MyStruct
    {
        HMODULE hModule;
        size_t  ul_reason_for_call;
        LPVOID lpReserved;
    }ParamCall;

    ParamCall paramcall;
    paramcall.hModule = (HMODULE)lpbPeAddress;//DLL模块在进程虚拟空间中的起始地址
    paramcall.ul_reason_for_call = DLL_PROCESS_ATTACH;
    paramcall.lpReserved = 0;


    //在目标进程申请内存
    LPBYTE shlcodeaddr = (LPBYTE)VirtualAllocEx(hProcess, NULL, 0x40, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    

    //6.构造32 shellcode
#ifndef _WIN64
    //E8 XXXXXXXX = 要跳转的地址 - E8指令所在的地址 - 5
    size_t jmp_addr = EnterPointer - ((size_t)shlcodeaddr + 15) - 5;
    char shellcode[] = {
        0x68,
        ((DWORD)paramcall.lpReserved) & 0xFF, (((DWORD)paramcall.lpReserved) & 0xFF00) >> 8,(((DWORD)paramcall.lpReserved) & 0xFF0000) >> 16,((DWORD)paramcall.lpReserved) >> 24,
        0x68,
        (paramcall.ul_reason_for_call) & 0xFF, ((paramcall.ul_reason_for_call) & 0xFF00) >> 8,((paramcall.ul_reason_for_call) & 0xFF0000) >> 16,(paramcall.ul_reason_for_call) >> 24,
        //0x6A,0x0,0x6A,0x1,
        0x68,
        ((DWORD)paramcall.hModule) & 0xFF, (((DWORD)paramcall.hModule) & 0xFF00) >> 8,(((DWORD)paramcall.hModule) & 0xFF0000) >> 16,((DWORD)paramcall.hModule) >> 24,
        0xE8,
        (jmp_addr) & 0xFF, ((jmp_addr) & 0xFF00) >> 8,((jmp_addr) & 0xFF0000) >> 16,(jmp_addr) >> 24,
        0xC3

    };
#else
    //要跳转的地址
    size_t jmp_addr = EnterPointer;
    /*
    *  48:83EC 28                  | sub rsp,28                                      |
    *  49:B8 7856341278563412      | mov r8,1234567812345678                         |
       48:BA 8877665544332211      | mov rdx,1122334455667788                        | rdx:EntryPoint
       48:B9 8877665544332211      | mov rcx,1122334455667788                        |
       FF15 07000000               | call qword ptr ds:[$+5]                |
       48:83C4 28                  | add rsp,28                                      |
       C3                          | ret                                             |
       1122334455667788(jmp_addr)

    */

    char shellcode[] = {
        0x48 ,0x83 ,0xEC ,0x28,
        0x49,0xB8,
        ((size_t)paramcall.lpReserved) & 0xFF, (((size_t)paramcall.lpReserved) & 0xFF00) >> 8,(((size_t)paramcall.lpReserved) & 0xFF0000) >> 16,(((size_t)paramcall.lpReserved) & 0xFF000000) >> 24,
        (((size_t)paramcall.lpReserved) & 0xFF00000000) >> 32,(((size_t)paramcall.lpReserved) & 0xFF0000000000) >> 40,(((size_t)paramcall.lpReserved) & 0xFF000000000000) >> 48,(((size_t)paramcall.lpReserved) & 0xFF00000000000000) >> 56,
        0x48, 0xBA,
        ((size_t)paramcall.ul_reason_for_call) & 0xFF, (((size_t)paramcall.ul_reason_for_call) & 0xFF00) >> 8,(((size_t)paramcall.ul_reason_for_call) & 0xFF0000) >> 16,(((size_t)paramcall.ul_reason_for_call) & 0xFF000000) >> 24,
        (((size_t)paramcall.ul_reason_for_call) & 0xFF00000000) >> 32,(((size_t)paramcall.ul_reason_for_call) & 0xFF0000000000) >> 40,(((size_t)paramcall.ul_reason_for_call) & 0xFF000000000000) >> 48,(((size_t)paramcall.ul_reason_for_call) & 0xFF00000000000000) >> 56,
        0x48, 0xB9,
        ((size_t)paramcall.hModule) & 0xFF, (((size_t)paramcall.hModule) & 0xFF00) >> 8,(((size_t)paramcall.hModule) & 0xFF0000) >> 16,(((size_t)paramcall.hModule) & 0xFF000000) >> 24,
        (((size_t)paramcall.hModule) & 0xFF00000000) >> 32,(((size_t)paramcall.hModule) & 0xFF0000000000) >> 40,(((size_t)paramcall.hModule) & 0xFF000000000000) >> 48,(((size_t)paramcall.hModule) & 0xFF00000000000000) >> 56,
        0xFF,0x15, 0x05,00,00,00,
        0x48 ,0x83 , 0xC4 ,0x28,
        0xc3,
        ((size_t)jmp_addr) & 0xFF, (((size_t)jmp_addr) & 0xFF00) >> 8,(((size_t)jmp_addr) & 0xFF0000) >> 16,(((size_t)jmp_addr) & 0xFF000000) >> 24,
        (((size_t)jmp_addr) & 0xFF00000000) >> 32,(((size_t)jmp_addr) & 0xFF0000000000) >> 40,(((size_t)jmp_addr) & 0xFF000000000000) >> 48,(((size_t)jmp_addr) & 0xFF00000000000000) >> 56
    };

#endif

    if (!WriteProcessMemory(hProcess, shlcodeaddr, (LPCVOID)shellcode, sizeof(shellcode), NULL))
    {
        MessageBoxA(NULL, "Write trueAddress failed", "", MB_OK);
        return;
    }

    //7.开启远程线程
    DWORD threadId;
    HANDLE hThead;
    hThead = CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)shlcodeaddr, 0, 0, &threadId);
    WaitForSingleObject(hThead, -1);
    CloseHandle(hThead);

    //释放NewBuff
    delete[](char*)NewBuff;
}

int main()
{
    DWORD pid = 0;
    printf("请输入进程ID: ");
    scanf_s("%d", &pid);
    MemoryInject(pid);
    std::cout << "Hello World!\n";
}

//修复重定位表
void PerformBaseRelocation(char* buff, size_t Value)
{
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)buff;
    PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(buff + pDosHeader->e_lfanew);


    PVOID optionalHeader = (PVOID)((size_t)pNtHeader + 24);
    //获取目录表头指针
    PIMAGE_DATA_DIRECTORY pDataDirectory = {};

    if (!(pNtHeader->FileHeader.Characteristics & 0x100)) {
        pDataDirectory = ((PIMAGE_OPTIONAL_HEADER64)optionalHeader)->DataDirectory;
    }
    else
    {
        pDataDirectory = ((PIMAGE_OPTIONAL_HEADER32)optionalHeader)->DataDirectory;
    }


   
    if (pDataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size > 0)
    {
        PIMAGE_BASE_RELOCATION relocation = (PIMAGE_BASE_RELOCATION)((size_t)buff + pDataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
        while (relocation->VirtualAddress > 0)
        {
            BYTE* dest = (PBYTE)((size_t)buff + relocation->VirtualAddress);
            WORD* relInfo = (PWORD)((size_t)relocation + sizeof(IMAGE_BASE_RELOCATION));
            for (int i = 0; i < ((relocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2); ++i, ++relInfo)
            {
                size_t* patchAddrHL;
                int type, offset;

                //the upper 4 bits define the type of relocation
                type = *relInfo >> 12;
                //the lower 12 bits define the offset
                offset = *relInfo & 0xFFF;

                switch (type)
                {
                case IMAGE_REL_BASED_ABSOLUTE:
                    //skip relocation
                    break;
                case IMAGE_REL_BASED_HIGHLOW:
                case IMAGE_REL_BASED_DIR64:
                    //change comlete 32 bit address
                    patchAddrHL = (size_t*)(dest + offset);
                    *patchAddrHL += Value;
                    break;
                default:
                    break;
                }
            }

            //advance to next relocation block
            relocation = PIMAGE_BASE_RELOCATION((size_t)relocation + relocation->SizeOfBlock);
        }
    }

}

//重建IAT表
BOOL RebuildImportTable(char* buff)
{
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)buff;
    PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(buff + pDosHeader->e_lfanew);
    int result = 1;


    PVOID optionalHeader = (PVOID)((size_t)pNtHeader + 24);
    //获取目录表头指针



    PIMAGE_DATA_DIRECTORY pDataDirectory = pNtHeader->OptionalHeader.DataDirectory;




    if (pDataDirectory[1].Size > 0)
    {
        //获取导入表地址
        PIMAGE_IMPORT_DESCRIPTOR ImportAddr = PIMAGE_IMPORT_DESCRIPTOR(pDataDirectory[1].VirtualAddress + (DWORD64)buff);

        for (; !IsBadReadPtr(ImportAddr, sizeof(PIMAGE_IMPORT_DESCRIPTOR)) && ImportAddr->Name; ++ImportAddr)
        {
            POINTER_TYPE* thunkRef;
            FARPROC* funcRef;

            HMODULE hModule = LoadLibraryA((LPCSTR)(buff + ImportAddr->Name));

            if (ImportAddr->OriginalFirstThunk)
            {
                thunkRef = (POINTER_TYPE*)(buff + ImportAddr->OriginalFirstThunk);
                funcRef = (FARPROC*)(buff + ImportAddr->FirstThunk);
            }
            else
            {
                //no hint table
                thunkRef = (POINTER_TYPE*)(buff + ImportAddr->FirstThunk);
                funcRef = (FARPROC*)(buff + ImportAddr->FirstThunk);
            }

            for (; *thunkRef; ++thunkRef, ++funcRef)
            {

                if (IMAGE_SNAP_BY_ORDINAL(*thunkRef))
                {
                    *funcRef = GetProcAddress(hModule, (LPCSTR)IMAGE_ORDINAL(*thunkRef));
                }
                else
                {
                    PIMAGE_IMPORT_BY_NAME pFuncName = (PIMAGE_IMPORT_BY_NAME)(*thunkRef + (DWORD64)buff);
                    *funcRef = GetProcAddress(hModule, (LPCSTR)&pFuncName->Name);
                }
                if (*funcRef == 0)
                {
                    result = 0;
                    break;
                }
                
  
            }
        }
    }
    return result;
}

//获取文件的在内存的地址和大小
int GetBuffAddrAndSizeofFile(char* FilePath, _Out_ size_t* buff, _Out_ DWORD* FileSize, _Out_ DWORD* FileSize1)
{
    //1.1 获取文件句柄
    HANDLE hFile = CreateFileA(
        FilePath,
        GENERIC_READ,
        0,
        NULL,
        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

    //1.2 获取文件大小
    DWORD dwFileSize = GetFileSize(hFile, NULL);
    CHAR* pFileBuf = new CHAR[dwFileSize];
    memset(pFileBuf, 0, dwFileSize);

    //1.3 将文件读取到内存
    DWORD ReadSize = 0;
    ReadFile(hFile, pFileBuf, dwFileSize, &ReadSize, NULL);

    //1.4 关闭句柄
    CloseHandle(hFile);


    //2.1 判断是否为PE文件
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuf;
    if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
    {
        printf("NO MZ header\n");
        return 0;
    }
    PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(pFileBuf + pDosHeader->e_lfanew);
    if (pNtHeader->Signature != IMAGE_NT_SIGNATURE)
    {
        printf("NO PE header\n");
        return 0;
    }

    //2.2 把文件的在内存中的地址和大小分别赋值给参数二和参数三
    *buff = (size_t)pFileBuf;
    *FileSize = pNtHeader->OptionalHeader.SizeOfImage;
    *FileSize1 = dwFileSize;
    return 1;
}

//拉伸文件
size_t StretchFile(size_t pFileBuff, DWORD FileSize)
{
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuff;
    PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(pFileBuff + pDosHeader->e_lfanew);

    //1.1 根据内存大小 申请内存
    char* NewFileBuff = new char[FileSize];
    if (NewFileBuff == NULL)
    {
        printf("内存申请失败!");
        return 0;
    }
    memset(NewFileBuff, 0, FileSize);

    //1.2 拉伸文件
     // 拷贝DOS头 + DOS STUB + PE头到headers地址处
    memcpy(NewFileBuff, pDosHeader, pNtHeader->OptionalHeader.SizeOfHeaders);

    // 从dll文件内容中拷贝每个section(节)的数据到新的内存区域
    PIMAGE_OPTIONAL_HEADER32 OptionalHeader = (PIMAGE_OPTIONAL_HEADER32)(pFileBuff + pDosHeader->e_lfanew + 4 + IMAGE_SIZEOF_FILE_HEADER);
    PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((size_t)OptionalHeader + pNtHeader->FileHeader.SizeOfOptionalHeader);

    for (int i = 0; i < pNtHeader->FileHeader.NumberOfSections; i++, pSectionHeader++)
    {
        char* x = (char*)NewFileBuff + pSectionHeader->VirtualAddress;
        char* y = (char*)pFileBuff + pSectionHeader->PointerToRawData;
        memcpy(x, y, pSectionHeader->SizeOfRawData);
    }

    return (size_t)NewFileBuff;
}