使用 ZwUnmapViewOfSection 卸载并替换内存镜像

 

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));
	// 获取源程序的ImageBase
	PIMAGE_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;
	// 修正 ImageBase
	WriteProcessMemory(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;
	}
	// 如果能除尽,直接返回offset
	return 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_lfanew

	return 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);
	}
	// 修改 ImageBase
	pOptionHeader->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);

	// 拷贝 newSectionHeader
	memcpy(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



---------------------
作者:hambaga
来源:CSDN
原文:https://blog.csdn.net/kwansy/article/details/107326264
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值