The ZwUnmapViewOfSection routine unmaps a view of a section from the virtual address space of a subject process.

NTSTATUS   ZwUnmapViewOfSection(IN HANDLE  ProcessHandle,IN PVOID  BaseAddress    );

这个函数在 wdm.h 里声明,它的功能是卸载进程的内存镜像(Image Buffer),内存镜像是指进程4GB虚拟地址空间中从 ImageBase 开始,长度为 SizeOfImage 的内存。

卸载内存镜像之后,就得到了一个“干净”的4GB空间,然后我们可以用 VirtualAllocEx 往里面填数据,比如换成其他程序的内存镜像。

这样做的意义就是我们可以从内存中启动一个程序,而不用涉及磁盘读写,因为常规的 CreateProcess 必须指定程序路径,而利用这种技术可以避免这一点。

下面给出一个简单的程序,演示如何创建一个挂起的“傀儡进程”,卸载其内存镜像,并替换为另一个程序的内存镜像,然后恢复运行。

该程序在32位XP可以正常运行,但是在64位WIN10 VirtualAllocEx 会返回0x1E7

main.cpp

// 程序功能:创建一个自己的傀儡进程并卸载内存镜像,用另一个程序的imagebuffer替换
// 32位 多字节字符集#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <WINDOWS.H>
#include <STRING.h>
#include <MALLOC.H>
#include "PE.hpp"BOOL EnableDebugPrivilege();int main(int argc, char *argv[])
{// 提权EnableDebugPrivilege();// 读取源文件LPVOID pSrcFileBuffer = NULL;DWORD dwSrcFileSize = FileToMemory("D:\\Program32\\littlegame.exe", &pSrcFileBuffer);if (dwSrcFileSize == 0){printf("读取文件失败\n");return -1;}// 拉伸成内存镜像LPVOID pSrcImageBuffer = NULL;DWORD dwSrcImageBufferSize = FileBufferToImageBuffer(pSrcFileBuffer, &pSrcImageBuffer);// 获取当前进程主模块路径char szCurrentPaths[MAX_PATH] = { 0 };GetModuleFileName(NULL, szCurrentPaths, MAX_PATH);// 以挂起方式创建一个当前进程的傀儡进程,我们只需要它的4GB空间STARTUPINFO si = { 0 };si.cb = sizeof(si);PROCESS_INFORMATION pi;CreateProcess(NULL, szCurrentPaths, NULL, NULL, FALSE, CREATE_SUSPENDED | CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);// 获取新进程主线程上下文CONTEXT context;context.ContextFlags = CONTEXT_FULL;GetThreadContext(pi.hThread, &context);// 获取 ZwUnmapViewOfSection 函数指针HMODULE hModuleNt = LoadLibrary("ntdll.dll");if (hModuleNt == NULL){printf("获取ntdll句柄失败\n");TerminateThread(pi.hThread, 0);return -1;}typedef DWORD(WINAPI *_TZwUnmapViewOfSection)(HANDLE, PVOID);_TZwUnmapViewOfSection pZwUnmapViewOfSection = (_TZwUnmapViewOfSection)GetProcAddress(hModuleNt, "ZwUnmapViewOfSection");if (pZwUnmapViewOfSection == NULL){printf("获取 ZwUnmapViewOfSection 函数指针失败\n");TerminateThread(pi.hThread, 0);return -1;}// 调用 ZwUnmapViewOfSection 卸载新进程内存镜像pZwUnmapViewOfSection(pi.hProcess, GetModuleHandle(NULL));// 获取源程序的ImageBasePIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pSrcImageBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));DWORD dwSrcImageBase = pOptionHeader->ImageBase;// 在傀儡进程的源程序的ImageBase处申请SizeOfImage大小的内存   LPVOID pImageBase = VirtualAllocEx(pi.hProcess, (LPVOID)dwSrcImageBase, dwSrcImageBufferSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);if ((DWORD)pImageBase != dwSrcImageBase){printf("VirtualAllocEx 错误码: 0x%X\n", GetLastError()); // 0x1e7 试图访问无效地址printf("申请到的指针: 0x%X, 期望的地址: 0x%X\n", (DWORD)pImageBase, dwSrcImageBase);TerminateThread(pi.hThread, 0);return -1;}   // 将源程序内存镜像复制到傀儡进程4GB中  if (0 == WriteProcessMemory(pi.hProcess, (LPVOID)dwSrcImageBase, pSrcImageBuffer, dwSrcImageBufferSize, NULL)){printf("写入源程序内存镜像失败\n");TerminateThread(pi.hThread, 0);return -1;}// 修正入口点context.Eax = pOptionHeader->AddressOfEntryPoint + dwSrcImageBase;// 修正 ImageBaseWriteProcessMemory(pi.hProcess, (LPVOID)(context.Ebx + 8), &dwSrcImageBase, 4, NULL);context.ContextFlags = CONTEXT_FULL;SetThreadContext(pi.hThread, &context);// 恢复线程    ResumeThread(pi.hThread);// 脱壳成功printf("脱壳成功,源程序正在运行,敲任意字符退出\n");free(pSrcFileBuffer);free(pSrcImageBuffer);system("pause");return 0;
}// 提权函数:提升为DEBUG权限
BOOL EnableDebugPrivilege()
{HANDLE hToken;BOOL fOk = FALSE;if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)){TOKEN_PRIVILEGES tp;tp.PrivilegeCount = 1;LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);fOk = (GetLastError() == ERROR_SUCCESS);CloseHandle(hToken);}return fOk;
}

PE.hpp

#ifndef PE_HPP_
#define PE_HPP_/********************************************************************************
时间:2020年7月14日
作者:hambaga
说明:重新整理的PE工具函数,仅适用于32位程序
********************************************************************************/#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif // !_CRT_SECURE_NO_WARNINGS#include <stdio.h>
#include <WINDOWS.H>
#include <STRING.h>
#include <MALLOC.H>DWORD FileToMemory(LPCSTR lpszFile, LPVOID *pFileBuffer);
BOOL MemoryToFile(LPVOID pMemBuffer, DWORD dwSize, LPCSTR lpszFile);
BOOL Is32PEFile(LPVOID pFileBuffer, DWORD dwSize);
DWORD FileBufferToImageBuffer(LPVOID pFileBuffer, LPVOID *pImageBuffer);
DWORD ImageBufferToFileBuffer(LPVOID pImageBuffer, LPVOID *pFileBuffer);
DWORD Align(DWORD dwOffset, DWORD dwAlign);
DWORD RvaToFoa(LPVOID pFileBuffer, DWORD dwRva);
DWORD FoaToRva(LPVOID pFileBuffer, DWORD dwFoa);
DWORD MoveNTHeaderAndSectionHeadersToDosStub(LPVOID pFileBuffer);
VOID SetNewImageBase(LPVOID pFileBuffer, DWORD dwNewImageBase);
DWORD WriteEncryptedDataToNewSection(LPVOID pFileBuffer, DWORD dwFileBufferSize, LPVOID *pNewFileBuffer, LPVOID pData, DWORD dwDataSize);
DWORD ReadEncryptedDataFromLastSection(LPVOID pFileBuffer, DWORD dwFileBufferSize, LPVOID *pData);// 读取文件到内存中,返回读取的字节数;读取失败返回0
DWORD FileToMemory(LPCSTR lpszFile, LPVOID *pFileBuffer)
{FILE *pFile = NULL;DWORD dwFileSize = 0;pFile = fopen(lpszFile, "rb");if (pFile == NULL){printf("打开文件失败\n");return 0;}fseek(pFile, 0, SEEK_END);dwFileSize = ftell(pFile);fseek(pFile, 0, SEEK_SET);*pFileBuffer = malloc(dwFileSize);if (*pFileBuffer == NULL){printf("分配内存失败\n");fclose(pFile);return 0;}DWORD dwRead = fread(*pFileBuffer, 1, dwFileSize, pFile);fclose(pFile);if (dwRead != dwFileSize){free(*pFileBuffer);return 0;}return dwRead;
}// 验证是否是合法的32位PE文件
BOOL Is32PEFile(LPVOID pFileBuffer, DWORD dwSize)
{PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);if (*((PWORD)pDosHeader) != IMAGE_DOS_SIGNATURE){printf("不是有效的MZ标志\n");return FALSE;}if (pNTHeader->Signature != IMAGE_NT_SIGNATURE){printf("不是有效的PE标记\n");return FALSE;}return TRUE;
}// 将 FileBuffer 拉伸成 ImageBuffer 并写入到新的缓冲区
// 返回 ImageBuffer 的大小;失败返回0
DWORD FileBufferToImageBuffer(LPVOID pFileBuffer, LPVOID *pImageBuffer)
{PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);*pImageBuffer = malloc(pOptionHeader->SizeOfImage);if (*pImageBuffer == NULL){printf("分配内存失败\n");return 0;}memset(*pImageBuffer, 0, pOptionHeader->SizeOfImage);// 复制DOS头+PE头+可选PE头+节表+文件对齐memcpy(*pImageBuffer, pFileBuffer, pOptionHeader->SizeOfHeaders);// 遍历节表,复制所有节    for (int i = 0; i < pPEHeader->NumberOfSections; i++){memcpy((LPVOID)((DWORD)(*pImageBuffer) + pSectionHeader[i].VirtualAddress), \(LPVOID)((DWORD)pFileBuffer + pSectionHeader[i].PointerToRawData), \pSectionHeader[i].SizeOfRawData);}return pOptionHeader->SizeOfImage;
}// 将 ImageBuffer 变成文件对齐的 FileBuffer 写入新的缓冲区
// 返回复制的大小,失败返回0
DWORD ImageBufferToFileBuffer(LPVOID pImageBuffer, LPVOID *pFileBuffer)
{PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);// 最后一个节表PIMAGE_SECTION_HEADER pLastSectionHeader = pSectionHeader + pPEHeader->NumberOfSections - 1;// 计算要复制的字节// 这一步有BUG,当最后一个节后面还有数据时(多见于控制台程序),丢失数据DWORD dwFileBufferSize = pLastSectionHeader->PointerToRawData + pLastSectionHeader->SizeOfRawData;*pFileBuffer = malloc(dwFileBufferSize);if (*pFileBuffer == NULL){printf("分配内存失败\n");return 0;}memset(*pFileBuffer, 0, dwFileBufferSize);// 复制DOS头+PE头+可选PE头+节表+文件对齐memcpy(*pFileBuffer, pImageBuffer, pOptionHeader->SizeOfHeaders);// 遍历节表,复制文件对齐后的节  for (int i = 0; i < pPEHeader->NumberOfSections; i++){memcpy((LPVOID)((DWORD)(*pFileBuffer) + pSectionHeader[i].PointerToRawData), \(LPVOID)((DWORD)pImageBuffer + pSectionHeader[i].VirtualAddress), \pSectionHeader[i].SizeOfRawData);}return dwFileBufferSize;
}// 内存数据写入文件
BOOL MemoryToFile(LPVOID pMemBuffer, DWORD dwSize, LPCSTR lpszFile)
{FILE *fp = NULL;fp = fopen(lpszFile, "wb+");if (fp == NULL){printf("打开文件失败\n");return FALSE;}DWORD dwWritten = fwrite(pMemBuffer, 1, dwSize, fp);if (dwWritten != dwSize){printf("写入了 %d 字节,不等于 %d\n", dwWritten, dwSize);fclose(fp);return FALSE;}fclose(fp);return TRUE;
}// 计算对齐的函数,如偏移为900,对齐为1000h,返回1000h
DWORD Align(DWORD dwOffset, DWORD dwAlign)
{// 如果偏移小于对齐,向上取整if (dwOffset <= dwAlign) return dwAlign;// 如果偏移大于对齐且不能除尽,向上取整if (dwOffset % dwAlign){return (dwOffset / dwAlign + 1) * dwAlign;}// 如果能除尽,直接返回offsetreturn dwOffset;
}// RVA 转 FOA
DWORD RvaToFoa(LPVOID pFileBuffer, DWORD dwRva)
{PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)(pDosHeader->e_lfanew + (DWORD)pFileBuffer + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);// RVA在文件头中或者文件对齐==内存对齐时,RVA==FOA  错!第一句是对的,第二句是错的if (dwRva < pOptionHeader->SizeOfHeaders){return dwRva;}// 遍历节表,确定偏移属于哪一个节    for (int i = 0; i < pPEHeader->NumberOfSections; i++){if (dwRva >= pSectionHeader[i].VirtualAddress && \dwRva < pSectionHeader[i].VirtualAddress + pSectionHeader[i].Misc.VirtualSize){int offset = dwRva - pSectionHeader[i].VirtualAddress;return pSectionHeader[i].PointerToRawData + offset;}}printf("找不到RVA %x 对应的 FOA,转换失败\n", dwRva);return 0;
}// FOA 转 RVA
DWORD FoaToRva(LPVOID pFileBuffer, DWORD dwFoa)
{PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)(pDosHeader->e_lfanew + (DWORD)pFileBuffer + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);// RVA在文件头中或者文件对齐==内存对齐时,RVA==FOA  错!第一句是对的,第二句是错的if (dwFoa < pOptionHeader->SizeOfHeaders){return dwFoa;}// 遍历节表,确定偏移属于哪一个节    for (int i = 0; i < pPEHeader->NumberOfSections; i++){if (dwFoa >= pSectionHeader[i].PointerToRawData && \dwFoa < pSectionHeader[i].PointerToRawData + pSectionHeader[i].SizeOfRawData){int offset = dwFoa - pSectionHeader[i].PointerToRawData;return pSectionHeader[i].VirtualAddress + offset;}}printf("找不到FOA %x 对应的 RVA,转换失败\n", dwFoa);return 0;
}// 移动NT头和节表到DOS STUB,该函数在新增节时节表空间不足的情况下调用;返回地址减小值
DWORD MoveNTHeaderAndSectionHeadersToDosStub(LPVOID pFileBuffer)
{PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);LPVOID pDst = (LPVOID)((DWORD)pDosHeader + sizeof(IMAGE_DOS_HEADER)); // NT头插入点DWORD dwRet = (DWORD)pNTHeader - (DWORD)pDst; // 返回地址减小的值DWORD dwSize = 4 + sizeof(IMAGE_FILE_HEADER) + pPEHeader->SizeOfOptionalHeader + \sizeof(IMAGE_SECTION_HEADER) * pPEHeader->NumberOfSections; // 移动的字节数LPVOID pSrc = malloc(dwSize);if (pSrc == NULL){printf("分配内存失败\n");return 0;}memcpy(pSrc, (LPVOID)pNTHeader, dwSize); // 保存要复制的数据memset((LPVOID)pNTHeader, 0, dwSize); // 清空原数据memcpy(pDst, pSrc, dwSize); // 移动数据free(pSrc);pDosHeader->e_lfanew = sizeof(IMAGE_DOS_HEADER); // 更新 e_lfanewreturn dwRet;
}// 修改 ImageBase 并修复重定位表
VOID SetNewImageBase(LPVOID pFileBuffer, DWORD dwNewImageBase)
{PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)(pDosHeader->e_lfanew + (DWORD)pDosHeader + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);PIMAGE_BASE_RELOCATION pRelocationTable = (PIMAGE_BASE_RELOCATION)((DWORD)pFileBuffer + \RvaToFoa(pFileBuffer, pOptionHeader->DataDirectory[5].VirtualAddress));DWORD dwImageBaseDelta = dwNewImageBase - pOptionHeader->ImageBase; // 新旧ImageBase 的差值    // 重定位表的 VirtualAddress + 低12位偏移 = RVA// RVA + ImageBase 这个内存里存储了一个“指针”// 要修改的是这个“指针”的值,要让这个“指针”加上两个ImageBase的差值while (pRelocationTable->VirtualAddress || pRelocationTable->SizeOfBlock){size_t n = (pRelocationTable->SizeOfBlock - 8) / 2; // 可能需要修改的地址数量(高4位==0011才要修改)PWORD pOffset = (PWORD)((DWORD)pRelocationTable + 8); // 2字节偏移的数组for (size_t i = 0; i < n; i++){// 高4位等于0011才需要重定位if ((pOffset[i] & 0xF000) == 0x3000){// 计算需要重定位的数据的RVA地址DWORD dwRva = pRelocationTable->VirtualAddress + (pOffset[i] & 0x0FFF);// 计算在文件中的偏移DWORD dwFoa = RvaToFoa(pFileBuffer, dwRva);// 计算在文件中的地址PDWORD pData = (PDWORD)((DWORD)pFileBuffer + dwFoa);// 重定位,即修正写死的地址             *pData += dwImageBaseDelta;}}pRelocationTable = (PIMAGE_BASE_RELOCATION)((DWORD)pRelocationTable + pRelocationTable->SizeOfBlock);}// 修改 ImageBasepOptionHeader->ImageBase = dwNewImageBase;
}// 将数据添加到新增节中
// 返回新缓冲区的大小,失败返回0
// 这个数据节前N个字节是一串十进制字符串,表示数据大小,以NULL结束
DWORD WriteEncryptedDataToNewSection(LPVOID pFileBuffer, DWORD dwFileBufferSize, LPVOID *pNewFileBuffer, LPVOID pData, DWORD dwDataSize)
{// 复制一份 pFileBuffer,不要修改原来的数据LPVOID pFileBuffer3 = malloc(dwFileBufferSize);memcpy(pFileBuffer3, pFileBuffer, dwFileBufferSize);pFileBuffer = pFileBuffer3;pFileBuffer3 = NULL;PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);PWORD pNumberOfSections = &(pPEHeader->NumberOfSections); // 节的数量PIMAGE_SECTION_HEADER pLastSectionHeader = pSectionHeader + *pNumberOfSections - 1; // 最后一个节表PIMAGE_SECTION_HEADER pNewSectionHeader = pSectionHeader + *pNumberOfSections; // 新节表插入点DWORD newFileBufferSize = 0; // 新文件的大小// 判断最后一个节表后面是否有空闲的80字节if (80 > (DWORD)pFileBuffer + pOptionHeader->SizeOfHeaders - (DWORD)pNewSectionHeader){printf("没有足够的80字节插入新节表\n");free(pFileBuffer);return 0;}// 判断空闲的80字节是否全为0,如果不是,则把整个NT头往上挪覆盖dos stub以空出空间插入节表for (int i = 0; i < 80; i++){if (((PBYTE)pNewSectionHeader)[i] != 0){DWORD dwRet = MoveNTHeaderAndSectionHeadersToDosStub(pFileBuffer);printf("节表空间不足,NT头和节表向低地址移动了 %d 字节\n", dwRet);if (dwRet < 80){printf("移动后仍没有足够的80字节空间插入新节表\n");free(pFileBuffer);return 0;}// 更新指针pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);pNumberOfSections = &(pPEHeader->NumberOfSections); // 节的数量pLastSectionHeader = pSectionHeader + *pNumberOfSections - 1; // 最后一个节表pNewSectionHeader = pSectionHeader + *pNumberOfSections; // 新节表插入点break;}}// 创建数据大小标记char sSizeFlag[100] = { 0 };sprintf(sSizeFlag, "%d", dwDataSize);DWORD dwFlagLen = strlen(sSizeFlag) + 1; // 拷贝长度包括NULL// 定义一个 IMAGE_SECTION_HEADER 结构,计算里面的属性IMAGE_SECTION_HEADER newSectionHeader;memcpy(newSectionHeader.Name, ".encsrc", 8);newSectionHeader.Misc.VirtualSize = Align(dwDataSize + dwFlagLen, pOptionHeader->SectionAlignment);newSectionHeader.VirtualAddress = pLastSectionHeader->VirtualAddress + \Align(pLastSectionHeader->Misc.VirtualSize, pOptionHeader->SectionAlignment);newSectionHeader.SizeOfRawData = Align(dwDataSize + dwFlagLen, pOptionHeader->FileAlignment);newSectionHeader.PointerToRawData = pLastSectionHeader->PointerToRawData + pLastSectionHeader->SizeOfRawData;newSectionHeader.PointerToRelocations = 0;newSectionHeader.PointerToLinenumbers = 0;newSectionHeader.NumberOfRelocations = 0;newSectionHeader.NumberOfLinenumbers = 0;newSectionHeader.Characteristics = 0xC0000040;// pNewFileBuffer 分配内存,把 pFileBuffer 复制过去,后面的修改都在 pNewFileBuffer 进行  *pNewFileBuffer = malloc(dwFileBufferSize + newSectionHeader.SizeOfRawData);memcpy(*pNewFileBuffer, pFileBuffer, dwFileBufferSize);memset((LPVOID)((DWORD)*pNewFileBuffer + dwFileBufferSize), 0, newSectionHeader.SizeOfRawData); // 新增节数据清0// 更新指针,指向新内存    pDosHeader = (PIMAGE_DOS_HEADER)*pNewFileBuffer;pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);pNumberOfSections = &(pPEHeader->NumberOfSections);pLastSectionHeader = pSectionHeader + *pNumberOfSections - 1;pNewSectionHeader = pSectionHeader + *pNumberOfSections;// 节的数量+1,SizeOfImage是内存中拉伸后的大小*pNumberOfSections += 1;pOptionHeader->SizeOfImage += Align(newSectionHeader.Misc.VirtualSize, pOptionHeader->SectionAlignment);// 拷贝 newSectionHeadermemcpy(pNewSectionHeader, &newSectionHeader, sizeof(newSectionHeader));// 拷贝数据到新增节LPVOID pNewSec = (LPVOID)((DWORD)*pNewFileBuffer + (DWORD)(pSectionHeader[*pNumberOfSections - 1].PointerToRawData));memcpy(pNewSec, sSizeFlag, dwFlagLen);memcpy((LPVOID)((PBYTE)pNewSec + dwFlagLen), pData, dwDataSize);//printf("插入成功\n");free(pFileBuffer);return dwFileBufferSize + newSectionHeader.SizeOfRawData;
}// 从最后一个节里读取数据,返回数据大小
// 最后一个节名字必须是.encsrc,开头N个字节必须是十进制数字符串,NULL结尾,表示后面的数据字节数
// pNewFileBuffer 输出数据内容,不包含开头大小标记
// 数据通过 pData 返回,与pFileBuffer 共享一块内存
DWORD ReadEncryptedDataFromLastSection(LPVOID pFileBuffer, DWORD dwFileBufferSize, LPVOID *pData)
{PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);PWORD pNumberOfSections = &(pPEHeader->NumberOfSections); // 节的数量PIMAGE_SECTION_HEADER pLastSectionHeader = pSectionHeader + *pNumberOfSections - 1; // 最后一个节表if (memcmp(".encsrc", pLastSectionHeader->Name, 8) != 0){printf("不是有效的加密程序\n");int i = 0;for (; i < 8; i++){printf("%c", pLastSectionHeader->Name[i]);}puts("");return 0;}LPVOID pLastSection = (LPVOID)(pLastSectionHeader->PointerToRawData + (PBYTE)pFileBuffer);DWORD dwDataSize = -1;sscanf((char *)pLastSection, "%d", &dwDataSize);*pData = (PBYTE)pLastSection + strlen((char *)pLastSection) + 1;return dwDataSize;
}#endif

使用 ZwUnmapViewOfSection 卸载并替换内存镜像相关推荐

  1. 自动替换 Kubernetes 镜像

    来源:云原生指北 作者:Addo Zhang 最近萌生了个想法,维护一个后网络友好的仓库镜像,在 Pod 创建时将镜像仓库切换到自维护的仓库,从自维护的仓库拉取镜像. 前几天体验了极狐Gitlab 的 ...

  2. 替换RubyGems 镜像

    替换RubyGems 镜像 更换gem的源: [root@tvm-rpm ~]# gem sources --remove http://rubygems.org/ [root@tvm-rpm ~]# ...

  3. windows没有磁盘_Windows硬盘和内存镜像取证

    Windows硬盘和内存镜像取证 在Windows系统上,有winhex等神器,可以对本地硬盘和物理内存完成镜像取证等工作,但如何对远程的Windows系统的硬盘和物理内存数据进行集中的镜像取证呢?除 ...

  4. jvm 内存镜像_镜像镜像–使用反射在运行时查看JVM内部

    jvm 内存镜像 开发人员:Takipi会告诉您何时新代码在生产中中断– 了解更多 我们都习惯于在我们的日常工作中直接或通过利用反射的框架来运用反射. 它是Java和Scala编程的主要方面,它使我们 ...

  5. 获取计算机内存镜像文件,计算机取证中的内存镜像获取的研究与实现

    摘要: 随着计算机科学技术的迅猛发展和网络普及,以计算机信息系统为工具和j巳罪对象的各式新型j巳罪案件频繁发生,造成的巨大危害也越来越大.怎么可以最大程度地获取计算机j巳罪相关的计算机证据,将犯罪人员 ...

  6. 制作Linux内存镜像+制作对应的volatility profile

    制作Linux内存镜像+制作对应的volatility profile 文章目录 制作Linux内存镜像+制作对应的volatility profile 制作Linux内存镜像 lmg安装 制作vol ...

  7. linux内存镜像技术,GitHub - oscomp/proj61-linux-address-range-memory-mirroring: Linux 内核内存镜像功能增强。...

    proj61-linux-address-range-memory-mirroring 项目描述 高端的 X86 和 ARM 服务器芯片支持局部内存镜像技术(Address Range Memory ...

  8. 安装威锋替换的镜像源

    转自: http://bbs.weiphone.com/read-htm-tid-826313.html 安装威锋替换的镜像源 ==================================== ...

  9. linux 内存镜像提取命令,内存镜像获取工具推荐

    Windows系统: moonsols,官方网站:http://www.moonsols.com/,有免费版和收费版,支持获取内存镜像,文件转换等功能,目前支持到Windows 10. moonsol ...

最新文章

  1. 被操纵的BCE与去中心化的BCH
  2. altium designer 去掉原理图右下角的标签栏
  3. 自定义元类控制类的实例化行为
  4. C语言中的关键字,变量的定义,变量的命名规则,交换两个变量的值,驼峰命名法【 C语言变量名命名法则】
  5. Linux C: 内嵌汇编语法
  6. 实例32:python
  7. 爬虫requests高阶篇详细教程
  8. JSTL表达式的理解和使用
  9. 同花顺黄金分割线及斐波那契数列分析网格(主图公式)
  10. .so文件的基本理解,使用。
  11. 2023中兴笔试复盘
  12. 自己怎么制作GIF表情包 QQ动态图如何制作
  13. 游戏HTML翻翻乐,大班益智游戏翻翻乐优秀教案
  14. mysql implode_PHP implode() 函数
  15. SyncToy 2.1
  16. PTA 单链表(流浪狗收养所)
  17. 数据分析——人力资源
  18. 用hutool工具类导出excel
  19. 电信和互联网用户个人信息保护规定_你的孩子的个人信息可能被窃取,保护规定来了...
  20. Java练手项目2:基于Java的坦克大战游戏(学习Java必备!!!)

热门文章

  1. 哈工大计算机系女生的宿舍,哈工大的硕博士们,这次被学校折腾惨了!
  2. 成功解决ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() o
  3. Crawler之Scrapy:数据挖掘必备的scrapy框架之最完整爬取网页内容攻略
  4. Worker Service in ASP .NET Core
  5. 云时代架构--阅读笔记03
  6. python中strip(),lstrip(),rstrip()函数的讲解
  7. CODEVS1490 [CTSC2008]网络管理
  8. pat1049. Counting Ones (30)
  9. thinkphp 创建子应用
  10. sql当等INSERT之后获取主键值