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;
}