PE新增节

原理

新增节的原理是在最后一个节表后面新增一个节表,然后在ImageBuffer后面新增一块内存;要注意新增节表后必须留有一个节表空间(40字节)的0,否则会出错。有些程序如notepad,最后一个节表后面是一些数据,删除后程序无法运行,对于这种情况必须特殊处理,比如扩大节后再新增节,或者把NT头往上挪,覆盖掉dos stub垃圾数据,当然,方法是多样的。
除了上述工作,还需要修改PE头中节的数量(加1),修改SizeOfImage的大小,增加的值应该是要新增的节的大小内存对齐后的结果。另外,不要忘记设置新增节的属性。

测试了几个32位程序,有的能正常工作,有的不行。出错的程序有个共同点是他们最后一个节表后面并不是0,而是绑定导入表,以32位notepad为例,最后一个节表后面的数据是这样的:
PE新增节_第1张图片
为了解决这个问题,我添加了移动NT头的代码,也就是刚才说的,把NT头移动到DOS STUB中的方式。

移动NT头+新增节的代码

// 移动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;
}



// 新增一个大小为 newSectionSize 的代码节
// dwFileBufferSize 是原来的文件大小
// 返回新缓冲区的大小,失败返回0
DWORD AddSection(LPVOID pFileBuffer, LPVOID *pNewFileBuffer, DWORD dwFileBufferSize, DWORD dwNewSectionSize)
{
	// 复制一份 pFileBuffer,不要修改原来的数据
	LPVOID pFileBuffer2 = malloc(dwFileBufferSize);
	memcpy(pFileBuffer2, pFileBuffer, dwFileBufferSize);
	pFileBuffer = pFileBuffer2;

	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(pFileBuffer2);
		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(pFileBuffer2);
				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;
		}
	}

	// 定义一个 IMAGE_SECTION_HEADER 结构,计算里面的属性
	IMAGE_SECTION_HEADER newSectionHeader;
	memcpy(newSectionHeader.Name, ".newsec", 8);
	newSectionHeader.Misc.VirtualSize = Align(dwNewSectionSize, pOptionHeader->SectionAlignment);
	newSectionHeader.VirtualAddress = pLastSectionHeader->VirtualAddress + \
		Align(pLastSectionHeader->Misc.VirtualSize, pOptionHeader->SectionAlignment);	
	newSectionHeader.SizeOfRawData = Align(dwNewSectionSize, pOptionHeader->FileAlignment);
	newSectionHeader.PointerToRawData = pLastSectionHeader->PointerToRawData + pLastSectionHeader->SizeOfRawData;
	newSectionHeader.PointerToRelocations = 0;
	newSectionHeader.PointerToLinenumbers = 0;
	newSectionHeader.NumberOfRelocations = 0;
	newSectionHeader.NumberOfLinenumbers = 0;
	newSectionHeader.Characteristics = 0x60000020;

	// 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));

	printf("插入成功\n");
	free(pFileBuffer2);
	return dwFileBufferSize + newSectionHeader.SizeOfRawData;
}

完整代码
https://blog.csdn.net/Kwansy/article/details/106234264

你可能感兴趣的:(PE)