10.PE文件之合并节

目录

合并节对PE文件中哪些值会有影响?

合并节步骤:

手动合并节

代码合并节


合并节与新增节扩大节有一点区别.合并节如果文件与内存对齐不同需拉伸到内存后进行合并节.

合并节对PE文件中哪些值会有影响?

  • IMAGE_FILE_HEADER→NumberOfSections(当前PE文件节的数量,如果更改需要修正此值).
  • 第一个节得包含其余所有节的属性.
  • 修正第一个节内存中的大小以及文件中的大小.
  • 如果文件对齐与内存对齐相等则不需要拉伸即可完成.如果文件对齐与内存对齐不相同必须拉伸到ImageBuffer来处理否则合并节后程序无法运行.

合并节步骤:

  • 修改第一个节的内存大小以及文件大小,公式如下:
  • MAX = 最后一个节内存中的大小 > 最后一个节文件中的大小 ? 最后一个节内存中的大小 : 最后一个节文件中的大小.
  • DWORD dwMax = pSec[pFil->NumberOfSections - 1].Misc.VirtualSize > pSec[pFil->NumberOfSections - 1].SizeOfRawData ? pSec[pFil->NumberOfSections - 1].Misc.VirtualSize : pSec[pFil->NumberOfSections - 1].SizeOfRawData;
  • 第一个节内存中的大小 = 文件中的大小 = 最后一个节内存中的起始位置 + MAX - SizeOfHeader(内存对齐后的大小).
  • pSec->Misc.VirtualSize = pSec->SizeOfRawData = pSec[pFil->NumberOfSections - 1].VirtualAddress + dwMax - Align(pOpo->SectionAlignment, pOpo->SizeOfHeaders);
  • 修正第一个节的属性.
  • 修改IMAGE_FILE_HEADER→NumberOfSections为1.
  • 抹除其余节数据

手动合并节

通过WinHex工具查看PE文件默认属性

测试文件内存与文件对齐一致(1000h)

1).修改第一个节的内存大小以及文件大小

MAX = 最后一个节内存中的大小 > 最后一个节文件中的大小 ? 最后一个节内存中的大小 : 最后一个节文件中的大小 = 0x8000

第一个节内存中的大小 = 文件中的大小 = 最后一个节内存中的起始位置 + MAX - SizeOfHeader(内存对齐后的大小) = 0x2b000 + 0x8000 - 0x1000 = 0x32000

2).修正第一个节的属性

IMAGE_SECTION_HEADER[0] -> Characteristics = 0x60000020

IMAGE_SECTION_HEADER[1] -> Characteristics = 0x40000040

IMAGE_SECTION_HEADER[2] -> Characteristics = 0xC0000040

IMAGE_SECTION_HEADER[3] -> Characteristics = 0x40000040

所有属性进行OR(或运算)得出结果 = 0xE0000060

3.修改IMAGE_FILE_HEADER→NumberOfSections为1

4.抹除其余节数据

存为文件,通过PE工具查看其数据

程序是否可以正常运行就知道合并节成功与否.

代码合并节

读取文件代码

PVOID FileToMem(IN PCHAR szFilePath, OUT LPDWORD dwFileSize)
{
	//打开文件
	FILE* pFile = fopen(szFilePath, "rb");
	if (!pFile)
	{
		printf("FileToMem fopen Fail \r\n");
		return NULL;
	}
 
	//获取文件长度
	fseek(pFile, 0, SEEK_END);			//SEEK_END文件结尾
	DWORD Size = ftell(pFile);
	fseek(pFile, 0, SEEK_SET);			//SEEK_SET文件开头
 
	//申请存储文件数据缓冲区
	PCHAR pFileBuffer = (PCHAR)malloc(Size);
	if (!pFileBuffer)
	{
		printf("FileToMem malloc Fail \r\n");
		fclose(pFile);
		return NULL;
	}
 
	//读取文件数据
	fread(pFileBuffer, Size, 1, pFile);
 
	//判断是否为可执行文件
	if (*(PSHORT)pFileBuffer != IMAGE_DOS_SIGNATURE)
	{
		printf("Error: MZ \r\n");
		fclose(pFile);
		free(pFileBuffer);
		return NULL;
	}
 
	if (*(PDWORD)(pFileBuffer + *(PDWORD)(pFileBuffer + 0x3C)) != IMAGE_NT_SIGNATURE)
	{
		printf("Error: PE \r\n");
		fclose(pFile);
		free(pFileBuffer);
		return NULL;
	}
 
	if (dwFileSize)
	{
		*dwFileSize = Size;
	}
 
	fclose(pFile);
 
	return pFileBuffer;
}

输出文件代码

VOID MemToFile(IN PCHAR szFilePath, IN PVOID pFileBuffer, IN DWORD dwFileSize)
{
	//打开文件
	FILE* pFile = fopen(szFilePath, "wb");
	if (!pFile)
	{
		printf("MemToFile fopen Fail \r\n");
		return;
	}
 
	//输出文件
	fwrite(pFileBuffer, dwFileSize, 1, pFile);
 
	fclose(pFile);
}

FileBuffer -> ImageBuffer

VOID FileBufferToImageBuffer(IN PVOID pFileBuffer, OUT PVOID* pImageBuffer)
{
	//定位PE结构
	PIMAGE_DOS_HEADER			pDos = (PIMAGE_DOS_HEADER)pFileBuffer;
	PIMAGE_NT_HEADERS			pNth = (PIMAGE_NT_HEADERS)((PUCHAR)pFileBuffer + pDos->e_lfanew);
	PIMAGE_FILE_HEADER			pFil = (PIMAGE_FILE_HEADER)((PUCHAR)pNth + 4);
	PIMAGE_OPTIONAL_HEADER		pOpo = (PIMAGE_OPTIONAL_HEADER)((PUCHAR)pFil + IMAGE_SIZEOF_FILE_HEADER);
	PIMAGE_SECTION_HEADER		pSec = (PIMAGE_SECTION_HEADER)((PUCHAR)pOpo + pFil->SizeOfOptionalHeader);
 
	//通过IMAGE_OPTIONAL_HEADER.SizeOfOptionalHeader确定分配内存缓存大小
	DWORD dwImageSize = pOpo->SizeOfImage;
	PUCHAR pTemp = (PUCHAR)malloc(dwImageSize);
	if (!pTemp)
	{
		printf("FileBufferToImageBuffer malloc Fail \r\n");
		*pImageBuffer = NULL;
		return ;
	}
	memset(pTemp, 0, dwImageSize);
	
	//拷贝头+节表数据
	memcpy(pTemp, pFileBuffer, pOpo->SizeOfHeaders);
 
	//拷贝节区数据
	for (size_t i = 0; i < pFil->NumberOfSections; i++)
	{
		memcpy(
			pTemp + pSec[i].VirtualAddress,					//内存镜像基址 + 内存节区偏移
			(PUCHAR)pFileBuffer + pSec[i].PointerToRawData,	//文件镜像基址 + 文件节区偏移
			pSec[i].SizeOfRawData						//拷贝大小
		);
 
	}
 
	*pImageBuffer = pTemp;
}

ImageBuffer -> FileBuffer

DWORD ImageBufferToFileBuffer(IN PVOID pImageBuffer, OUT PVOID* pFileBuffer)
{
	//定位PE结构
	PIMAGE_DOS_HEADER			pDos = (PIMAGE_DOS_HEADER)pImageBuffer;
	PIMAGE_NT_HEADERS			pNth = (PIMAGE_NT_HEADERS)((PUCHAR)pImageBuffer + pDos->e_lfanew);
	PIMAGE_FILE_HEADER			pFil = (PIMAGE_FILE_HEADER)((PUCHAR)pNth + 4);
	PIMAGE_OPTIONAL_HEADER		pOpo = (PIMAGE_OPTIONAL_HEADER)((PUCHAR)pFil + IMAGE_SIZEOF_FILE_HEADER);
	PIMAGE_SECTION_HEADER		pSec = (PIMAGE_SECTION_HEADER)((PUCHAR)pOpo + pFil->SizeOfOptionalHeader);
 
	//遍历获取内存镜像对应文件镜像大小
	DWORD dwFileSize = pOpo->SizeOfHeaders;
	for (size_t i = 0; i < pFil->NumberOfSections; i++)
	{
		dwFileSize += pSec[i].SizeOfRawData;
	}
	//也可通过最后一个节区的文件偏移+文件大小获取
 
	//分配文件缓存
	PUCHAR pTemp = (PUCHAR)malloc(dwFileSize);
	if (!pTemp)
	{
		printf("ImageBufferToFileBuffer malloc Fail \r\n");
		*pFileBuffer = NULL;
		return NULL;
	}
	memset(pTemp, 0, dwFileSize);
 
	//拷贝头+节表数据
	memcpy(pTemp, pImageBuffer, pOpo->SizeOfHeaders);
 
	//拷贝节区数据
	for (size_t i = 0; i < pFil->NumberOfSections; i++)
	{
		memcpy(
			pTemp + pSec[i].PointerToRawData,					//文件镜像基址 + 文件节区偏移
			(PUCHAR)pImageBuffer + pSec[i].VirtualAddress,		//内存镜像基址 + 内存节区偏移
			pSec[i].SizeOfRawData							//拷贝大小
		);
 
	}
 
	*pFileBuffer = pTemp;
 
	return dwFileSize;	
}

合并节代码

PVOID MergeSection2(PCHAR pBuffer, LPDWORD pNewFileSize)
{
	//定位结构
	PIMAGE_DOS_HEADER        pDos = (PIMAGE_DOS_HEADER)pBuffer;
	PIMAGE_NT_HEADERS        pNth = (PIMAGE_NT_HEADERS)(pBuffer + pDos->e_lfanew);
	PIMAGE_FILE_HEADER		 pFil = (PIMAGE_FILE_HEADER)((PUCHAR)pNth + 4);
	PIMAGE_OPTIONAL_HEADER   pOpo = (PIMAGE_OPTIONAL_HEADER)((PUCHAR)pFil + IMAGE_SIZEOF_FILE_HEADER);
	PIMAGE_SECTION_HEADER    pSec = (PIMAGE_SECTION_HEADER)((PUCHAR)pOpo + pFil->SizeOfOptionalHeader);


	//修改最后一个节的属性
	DWORD dwMax = pSec[pFil->NumberOfSections - 1].Misc.VirtualSize > pSec[pFil->NumberOfSections - 1].SizeOfRawData ? pSec[pFil->NumberOfSections - 1].Misc.VirtualSize : pSec[pFil->NumberOfSections - 1].SizeOfRawData;

	pSec->Misc.VirtualSize = pSec->SizeOfRawData = pSec[pFil->NumberOfSections - 1].VirtualAddress + dwMax - Align(pOpo->SectionAlignment, pOpo->SizeOfHeaders);

	for (size_t i = 1; i < pFil->NumberOfSections; i++)
	{
		pSec->Characteristics |= pSec[i].Characteristics;
	}

	//抹除其他节表数据
	memset(pSec + 1, 0, IMAGE_SIZEOF_SECTION_HEADER * (pFil->NumberOfSections - 1));

	//修正NumberOfSections
	pFil->NumberOfSections = 1;

	return NULL;
}

测试代码

int main()
{
	//读取文件二进制数据
	DWORD dwFileSize = 0;
	PCHAR pFileBuffer = FileToMem(FILE_PATH_IN, &dwFileSize);

	PCHAR pImageBuffer = NULL;
	FileBufferToImageBuffer(pFileBuffer, &pImageBuffer);

	MergeSection2(pImageBuffer, 0);

	PUCHAR pNewBuffer = NULL;
	dwFileSize = ImageBufferToFileBuffer(pImageBuffer, &pNewBuffer);

	MemToFile(FILE_PATH_OUT, pNewBuffer, dwFileSize);

	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值