滴水逆向三期实践附:前后使用到的PE相关函数合集 Currency.h

课上海东说尽量把常用函数放进一个头文件中,附带实现.cpp

这里作一个 Currency.h  和 Currency.cpp 函数基本是前面出现过的,包含功能和输入输出说明。

后面的文章也会使用到大部分。

首先是 Currency.h

#pragma once
#include "windows.h"

//函数声明								
//**************************************************************************								
//ReadPEFile:将文件读取到缓冲区								
//参数说明:								
//lpszFile 文件路径								
//pFileBuffer 缓冲区指针								
//返回值说明:								
//读取失败返回0  否则返回实际读取的大小								
//**************************************************************************								
DWORD ReadPEFile(IN LPSTR lpszFile, OUT LPVOID* pFileBuffer);


//**************************************************************************								
//CopyFileBufferToImageBuffer:将文件从FileBuffer复制到ImageBuffer								
//参数说明:								
//pFileBuffer  FileBuffer指针								
//ppImageBuffer ImageBuffer二重指针								
//返回值说明:								
//读取失败返回0  否则返回复制的大小								
//**************************************************************************								
DWORD CopyFileBufferToImageBuffer(IN LPVOID pFileBuffer, OUT LPVOID* ppImageBuffer);


//**************************************************************************								
//CopyImageBufferToFileBuffer:将ImageBuffer中的数据复制到新的缓冲区								
//参数说明:								
//pImageBuffer ImageBuffer指针								
//ppFileBuffer FileBuffer二重指针								
//返回值说明:								
//读取失败返回0  否则返回复制的大小								
//**************************************************************************								
DWORD CopyImageBufferToFileBuffer(IN LPVOID pImageBuffer, OUT LPVOID* ppFileBuffer);


//**************************************************************************								
//MemeryToFile:将内存中的数据复制到文件								
//参数说明:								
//pMemBuffer 内存中数据的指针								
//size 要复制的大小								
//lpszFile 要存储的文件路径								
//返回值说明:								
//读取失败返回0  否则返回复制的大小								
//**************************************************************************								
BOOL MemeryToFile(IN LPVOID pMemBuffer, IN size_t size, OUT LPSTR lpszFile);


//**************************************************************************								
//RVA2FOA:将内存偏移转换为文件偏移								
//参数说明:								
//pFileBuffer FileBuffer指针								
//dwRva RVA的值								
//返回值说明:								
//返回转换后的FOA的值  如果失败返回0								
//**************************************************************************								
DWORD RVA2FOA(IN LPVOID pFileBuffer, IN DWORD dwRva);


//**************************************************************************								
//Align:计算对齐后的值					
//参数说明:								
//x  需要进行对齐的值								
//Alignment 对齐大小						
//返回值说明:								
//返回x进行Alignment值对齐后的值								
//**************************************************************************	
int Align(int x, int Alignment);

//**************************************************************************								
//GetFunctionAddrByName:根据名字找到导出表中的函数地址			
//参数说明:								
//pFileBuffer:FileBuffer指针						
//str: 函数名指针					
//返回值说明:								
//返回导出表中的函数地址								
//**************************************************************************	
LPVOID GetFunctionAddrByName(LPVOID pFileBuffer, char* str);

//**************************************************************************								
//GetFunctionAddrByOrdinals:根据序号找到导出表中的函数地址			
//参数说明:								
//pFileBuffer:FileBuffer指针						
//ord:函数序号				
//返回值说明:								
//返回导出表中的函数地址								
//**************************************************************************	
LPVOID GetFunctionAddrByOrdinals(LPVOID pFileBuffer, DWORD ord);


//输出内存buffer中的头
void PrintHeader(LPVOID pFileBuffer);


//验证FileBuffer和ImageBuffer映射是否正确
bool VerifyFileBufferAndImageBuffer(LPVOID pFileBuffer, LPVOID pImageBuffer);


//测试 内存偏移转换为文件偏移 是否正确
bool TestRvaToFile(LPVOID pFileBuffer, DWORD Offset);


//**************************************************************************								
//AddNewSection:新增节,可能含有头抬升。函数将保存新增节后的文件
//参数说明:								
//pFileBuffer:FileBuffer指针	
//SizeOfFileBuffer:pFileBuffer指向的大小,也即FileBuffer大小
//SizeOfNewSectio:新增节的大小
//pNewFileBuffer:返回指向新FileBuffer的指针
//返回值说明:								
//新FileBuffer的大小,NewFileBuffer的大小
//**************************************************************************	
int AddNewSection(IN LPVOID pFileBuffer, IN int SizeOfFileBuffer, IN DWORD SizeOfNewSection, OUT LPVOID* ppNewFileBuffer);

其次是对应的函数实现, Currency.cpp 内容:

#include "Currency.h"
#include"windows.h"
#include"stdio.h"
//Currency::Currency(){}
//Currency::~Currency(){}
//函数声明								
//**************************************************************************								
//ReadPEFile:将文件读取到缓冲区								
//参数说明:								
//lpszFile 文件路径								
//ppFileBuffer 缓冲区指针								
//返回值说明:								
//读取失败返回0  否则返回实际读取的大小								
//**************************************************************************								
DWORD ReadPEFile(IN LPSTR lpszFile, OUT LPVOID* ppFileBuffer)
{
	FILE *pFile = NULL;
	DWORD fileSize = 0;
	//LPVOID pFileBuffer = NULL;    //LPVOID 即  void *

								  //打开文件	
	pFile = fopen(lpszFile, "rb");
	if (!pFile)
	{
		printf(" 无法打开 EXE 文件! ");
		*ppFileBuffer = NULL;
		return 0;
	}
	//读取文件大小		
	fseek(pFile, 0, SEEK_END);
	fileSize = ftell(pFile);
	fseek(pFile, 0, SEEK_SET);
	
	*ppFileBuffer = malloc(fileSize);  //pFileBuffer指向一段 分配的exe文件这么大的内存
	if (! *ppFileBuffer)
	{
		printf(" 分配空间失败! ");
		fclose(pFile);
		*ppFileBuffer = NULL;
		return 0;
	}
	//将文件数据读取到缓冲区	
	size_t n = fread(*ppFileBuffer, fileSize, 1, pFile);   //size_t 即 unsigned int
	if (!n)
	{
		printf(" 读取数据失败! ");
		free(*ppFileBuffer);
		fclose(pFile);
		*ppFileBuffer = NULL;
		return 0;
	}
	//printf(" 成功! ");
	//关闭文件	
	fclose(pFile);
	return fileSize;		//最后返回指向 已填充exe文件内容 的新开辟空间 的指针
}
//**************************************************************************								
//CopyFileBufferToImageBuffer:将文件从FileBuffer复制到ImageBuffer								
//参数说明:								
//pFileBuffer  FileBuffer指针								
//ppImageBuffer ImageBuffer二重指针								
//返回值说明:								
//读取失败返回0  否则返回复制的大小								
//**************************************************************************								
DWORD CopyFileBufferToImageBuffer(IN LPVOID pFileBuffer, OUT LPVOID* ppImageBuffer)
{
	PIMAGE_DOS_HEADER pDosHeader = NULL;
	PIMAGE_NT_HEADERS pNTHeader = NULL;
	PIMAGE_FILE_HEADER pFileHeader = NULL;
	PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;
	if (!pFileBuffer)
	{
		printf("pFileBuffer空指针\n");
		return 0;
	}
	//Dos头
	pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;	// 强转 DOS_HEADER 结构体指针
	//判断是否是有效的MZ标志
	if (*((PWORD)pDosHeader) != IMAGE_DOS_SIGNATURE)	//pFileBuffer强转 WORD* 后 取指针指向的内容
	{
		printf("不是有效的MZ标志\n");
		return 0;
	}
	//判断是否是有效的PE标志
	if (*((PDWORD)((DWORD)pDosHeader + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)	//基址pFileBuffer + lfanew 为 NTHeader首址
	{
		printf("不是有效的PE标志\n");
		return 0;
	}
	//NT头
	pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
	//PE头
	pFileHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);	//NT头地址 + 4 为 FileHeader 首址
	//可选PE头	
	pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);//SIZEOF_FILE_HEADER为固定值且不存在于PE文件字段中
	//分配ImageBuffer的空间
	*ppImageBuffer = malloc(pOptionalHeader->SizeOfImage);
	if (!*ppImageBuffer)
	{
		printf("分配空间失败\n");
		return 0;
	}
	printf("SizeOfImage:%x\n", pOptionalHeader->SizeOfImage);
	memset(*ppImageBuffer, 0, pOptionalHeader->SizeOfImage);	//初始化全为0
	//拷贝头
	printf("SizeOfHeaders:%x\n", pOptionalHeader->SizeOfHeaders);
	memcpy(*ppImageBuffer, pDosHeader, pOptionalHeader->SizeOfHeaders);
    //循环遍历节表
	pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
	for (int i = 1; i <= pFileHeader->NumberOfSections; i++, pSectionHeader++)
	{
		//将FileBuffer中从 其基址+PointerToRawData 的地方开始,拷贝SizeOfRawData大小的内存,到ImageBuffer 其基址+VirtualAddress 处
		memcpy( (LPVOID)((DWORD)(*ppImageBuffer)+ pSectionHeader->VirtualAddress), (LPVOID)((DWORD)pDosHeader + pSectionHeader->PointerToRawData),pSectionHeader->SizeOfRawData);
	}	//出循环后pSectionHeader指向不存在的节表,多了一节
	pSectionHeader = NULL;  //所以pSectionHeader指针不可再使用
	printf("拷贝完成\n");
	return pOptionalHeader->SizeOfImage;
}
//**************************************************************************								
//CopyImageBufferToFileBuffer:将ImageBuffer中的数据复制到新的缓冲区								
//参数说明:								
//pImageBuffer ImageBuffer指针								
//ppFileBuffer FileBuffer二重指针									
//返回值说明:								
//读取失败返回0  否则返回复制的大小								
//**************************************************************************								
DWORD CopyImageBufferToFileBuffer(IN LPVOID pImageBuffer, OUT LPVOID* ppFileBuffer)
{
	PIMAGE_DOS_HEADER pDosHeader = NULL;
	PIMAGE_NT_HEADERS pNTHeader = NULL;
	PIMAGE_FILE_HEADER pFileHeader = NULL;
	PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;
	if (!pImageBuffer)
	{
		printf("pImageBuffer空指针\n");
		return 0;
	}
	//Dos头
	pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;	// 强转 DOS_HEADER 结构体指针
	//判断是否是有效的MZ标志
	if (*((PWORD)pDosHeader) != IMAGE_DOS_SIGNATURE)	//pFileBuffer强转 WORD* 后 取指针指向的内容
	{
		printf("不是有效的MZ标志\n");
		return 0;
	}
	//判断是否是有效的PE标志
	if (*((PDWORD)((DWORD)pDosHeader + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)	//基址pFileBuffer + lfanew 为 NTHeader首址
	{
		printf("不是有效的PE标志\n");
		return 0;
	}
	//NT头
	pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
	//PE头
	pFileHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);	//NT头地址 + 4 为 FileHeader 首址
	//可选PE头	
	pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);//SIZEOF_FILE_HEADER为固定值且不存在于PE文件字段中
	//循环节表找到最后一节
	pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
	for (int i = 1; i < pFileHeader->NumberOfSections; i++, pSectionHeader++)	//注意这里i从1开始 i < NumberOfSections
	{}	//出循环后pSectionHeader指向最后一个节表																					
	//分配FileBuffer的空间   这里头信息读取是基于ImageBuffer的,所以(最后一节的地址 + 最后一节的长度)相当与文件最末尾,而用 文件最末尾 - 基址 得到原本文件的长度
	DWORD SizeOfFile = (DWORD)pSectionHeader->PointerToRawData + (DWORD)pSectionHeader->SizeOfRawData ;
	printf("大小:%x\n", SizeOfFile);
	*ppFileBuffer = malloc(SizeOfFile);
	printf("SizeOfFile:%x\n", SizeOfFile);
	memset(*ppFileBuffer, 0, SizeOfFile);	//初始化全为0
	//拷贝头
	printf("SizeOfHeaders:%x\n", pOptionalHeader->SizeOfHeaders);
	memcpy(*ppFileBuffer, pImageBuffer, pOptionalHeader->SizeOfHeaders);
	//循环遍历节表
	pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
	for (int i = 1; i <= pFileHeader->NumberOfSections; i++, pSectionHeader++)
	{
		//将ImageBuffer中从 其基址+VirtualAddress 的地方开始,拷贝SizeOfRawData大小的内存,到FileBuffer 其基址+PointerToRawData 处
		printf("%d次", i);
		memcpy((LPVOID)((DWORD)(*ppFileBuffer) + pSectionHeader->PointerToRawData), (LPVOID)((DWORD)pImageBuffer + pSectionHeader->VirtualAddress), pSectionHeader->SizeOfRawData);
	}	//出循环后pSectionHeader指向不存在的节表,多了一节
	pSectionHeader = NULL;  //所以pSectionHeader指针不可再使用
	printf("拷贝完成,大小:%x\n", SizeOfFile);
	return SizeOfFile;
}
//**************************************************************************								
//MemeryToFile:将内存中的数据复制到文件								
//参数说明:								
//pMemBuffer 内存中数据的指针								
//size 要复制的大小								
//lpszFile 要存储的文件路径								
//返回值说明:								
//读取失败返回0  否则返回复制的大小								
//**************************************************************************								
BOOL MemeryToFile(IN LPVOID pMemBuffer, IN size_t size, OUT LPSTR lpszFile)
{
	FILE * fpw = fopen(lpszFile, "wb");
	if (fpw == NULL)
	{
		printf("fpw fopen fail");
		return false;
	}
	if (fwrite(pMemBuffer, 1, size, fpw) == 0)
	{
		printf("fpw fwrite fail");
		return false;
	}
	fclose(fpw);			//关闭写文件流
	fpw = NULL;
	printf("success\n");
	return true;
}
//**************************************************************************								
//RVA2FOA:将内存偏移转换为文件偏移								
//参数说明:								
//pFileBuffer FileBuffer指针								
//dwRva RVA的值								
//返回值说明:								
//返回转换后的FOA的值  如果失败返回0								
//**************************************************************************								
DWORD RVA2FOA(IN LPVOID pFileBuffer, IN DWORD dwRva)
{
	PIMAGE_DOS_HEADER pDosHeader = NULL;
	PIMAGE_FILE_HEADER pFileHeader = NULL;
	PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;
	PIMAGE_SECTION_HEADER pNextSectionHeader = NULL;
	//DOS头
	pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;	// 强转 DOS_HEADER 结构体指针
	//PE头
	pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pFileBuffer + pDosHeader->e_lfanew + 4);	//NT头地址 + 4 为 FileHeader 首址
	//可选PE头	
	pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);//SIZEOF_FILE_HEADER为固定值且不存在于PE文件字段中
	if (dwRva < pOptionalHeader->SizeOfHeaders)	//偏移小于头的大小,内存偏移则为文件偏移
	{
		return dwRva;
	}
    //首个节表
	pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
	//下一个节表
	pNextSectionHeader = pSectionHeader + 1;
	//循环遍历节表
	for (int i = 1; i < pFileHeader->NumberOfSections; i++, pSectionHeader++, pNextSectionHeader++)//注意这里i从1开始 i < NumberOfSections
	{	//注意这里的pSectionHeader已经是加了基址的,不是偏移, 是绝对地址。而dwRva是偏移地址
		if (dwRva >= pSectionHeader->VirtualAddress && dwRva < pNextSectionHeader->VirtualAddress)//大于当前节的内存偏移而小于下一节的内存偏移
		{	//则dwRva属于当前节,则dwRva - VirtualAddress为dwRva基于当前节的偏移。此偏移加上当前节的文件起始偏移地址 则为dwRva在文件中的偏移
			DWORD PointerToRawData = pSectionHeader->PointerToRawData;
			DWORD VirtualAddress = pSectionHeader->VirtualAddress;
			DWORD aa = pSectionHeader->PointerToRawData + dwRva - pSectionHeader->VirtualAddress;
			return pSectionHeader->PointerToRawData + dwRva - pSectionHeader->VirtualAddress ;
		}
	}	//出循环后pSectionHeader指向最后一个节表
	//大于当前节(最后一节)的内存偏移且小于内存映射大小
	if (dwRva >= pSectionHeader->VirtualAddress && dwRva < pOptionalHeader->SizeOfImage)
	{	//同上return
		return pSectionHeader->PointerToRawData + dwRva - pSectionHeader->VirtualAddress;
	}
	else //大于内存映射大小
	{
		printf("dwRva大于内存映射大小\n");
		return -1;
	}
}

//**************************************************************************								
//Align:计算对齐后的值					
//参数说明:								
//x  需要进行对齐的值								
//Alignment 对齐大小						
//返回值说明:								
//返回x进行Alignment值对齐后的值								
//**************************************************************************	
int Align(int x, int Alignment)
{
	if (x%Alignment==0)
	{
		return x;
	}
	else
	{
		return (1 + (x / Alignment)) * Alignment;
	}
}

//**************************************************************************								
//GetFunctionAddrByName:根据名字找到导出表中的函数地址			
//参数说明:								
//pFileBuffer:FileBuffer指针						
//str: 函数名指针					
//返回值说明:								
//返回导出表中的函数地址								
//**************************************************************************	
LPVOID GetFunctionAddrByName(LPVOID pFileBuffer, char* str)
{
	PIMAGE_DOS_HEADER pDosHeader = NULL;
	PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
	PIMAGE_EXPORT_DIRECTORY pExportDirectory = NULL;
	DWORD nameFOA = NULL;
	DWORD AddressOfNamesFOA = NULL;
	DWORD AddressOfNameOrdinalsFOA = NULL;
	DWORD AddressOfFunctionsFOA = NULL;
	WORD Ordinal = NULL;
	char * name = NULL;
	int i = 0;
	//Dos头
	pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;	// 强转 DOS_HEADER 结构体指针
	//可选PE头	  简化后的处理
	pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileBuffer + pDosHeader->e_lfanew + 4 + IMAGE_SIZEOF_FILE_HEADER);
	//导出表
	pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer + RVA2FOA(pFileBuffer, pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress));
	//printf("NumberOfNames:%d\n", pExportDirectory->NumberOfNames);
	//导出表中的AddressOfNames为Rva,将其转换为FOA得到AddressOfNamesFOA
	AddressOfNamesFOA = RVA2FOA(pFileBuffer, pExportDirectory->AddressOfNames);
	for (i = 0; i < pExportDirectory->NumberOfNames; i++)
	{	//AddressOfNamesFOA只是Names表的FOA地址,需加上pFileBuffer构成的绝对地址才能取出其中的值。
		//取出的值即Names地址表第i个name的Rva地址,转成FOA得到name的FOA地址
		nameFOA = RVA2FOA(pFileBuffer, *(PDWORD)((DWORD)pFileBuffer + AddressOfNamesFOA));
		name = (char *)(nameFOA + (DWORD)pFileBuffer);//name的FOA加上pFileBuffer构成绝对地址,该地址才真正指向字符串
		if (!strcmp(str, name))
		{
			break;
		}
		AddressOfNamesFOA += 4;	//往前走4字节,指向Names地址表下一个元素,即下一个name地址
	}
	if (i == pExportDirectory->NumberOfNames)
	{
		printf("找不到名为%s的函数!\n", str);
		return 0;
	}
	AddressOfNameOrdinalsFOA = RVA2FOA(pFileBuffer, pExportDirectory->AddressOfNameOrdinals);	//同Names表找法
	Ordinal = *(PWORD)((DWORD)pFileBuffer + AddressOfNameOrdinalsFOA + i*2);	//因为Ordinal表元素为2字节,绝对地址加上i*2直接取第i个元素
	printf("i:%d,Ordinal:%d\n", i,Ordinal);
	AddressOfFunctionsFOA = RVA2FOA(pFileBuffer, pExportDirectory->AddressOfFunctions);
	return (LPVOID)*(PDWORD)((DWORD)pFileBuffer + AddressOfFunctionsFOA + Ordinal * 4);//同上
}


//**************************************************************************								
//GetFunctionAddrByOrdinals:根据序号找到导出表中的函数地址			
//参数说明:								
//pFileBuffer:FileBuffer指针						
//ord:函数序号				
//返回值说明:								
//返回导出表中的函数地址								
//**************************************************************************	
LPVOID GetFunctionAddrByOrdinals(LPVOID pFileBuffer, DWORD ord)
{
	PIMAGE_DOS_HEADER pDosHeader = NULL;
	PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
	PIMAGE_EXPORT_DIRECTORY pExportDirectory = NULL;
	DWORD nameFOA = NULL;
	DWORD AddressOfNameOrdinalsFOA = NULL;
	DWORD AddressOfFunctionsFOA = NULL;
	WORD Ordinal = NULL;
	int i = 0;
	//Dos头
	pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;	// 强转 DOS_HEADER 结构体指针
	//可选PE头	  简化后的处理
	pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileBuffer + pDosHeader->e_lfanew + 4 + IMAGE_SIZEOF_FILE_HEADER);
	//导出表
	pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer + RVA2FOA(pFileBuffer, pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress));
	if (ord - pExportDirectory->Base >= pExportDirectory->NumberOfNames || ord - pExportDirectory->Base < 0)
	{
		printf("找不到序号为%d的函数!\n", ord);
		return 0;
	}
	AddressOfFunctionsFOA = RVA2FOA(pFileBuffer, pExportDirectory->AddressOfFunctions);//导入表函数表地址转FOA
	return (LPVOID)*(PDWORD)((DWORD)pFileBuffer + AddressOfFunctionsFOA + (ord - pExportDirectory->Base) * 4);//绝对地址 加上(ord-base)*4 直接取第ord-base个元素
}

//测试 内存偏移转换为文件偏移 是否正确
bool TestRvaToFile(LPVOID pFileBuffer ,DWORD Offset)
{
	PIMAGE_DOS_HEADER pDosHeader = NULL;
	PIMAGE_NT_HEADERS pNTHeader = NULL;
	PIMAGE_FILE_HEADER pFileHeader = NULL;
	PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;
	PIMAGE_SECTION_HEADER pNextSectionHeader = NULL;
	//DOS头
	pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
	//NT头
	pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
	//PE头
	pFileHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);	//NT头地址 + 4 为 FileHeader 首址
	//可选PE头	
	pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);//SIZEOF_FILE_HEADER为固定值且不存在于PE文件字段中
	//循环遍历节表
	pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
	for (int i = 1; i <= pFileHeader->NumberOfSections; i++, pSectionHeader++)
	{
		//通过函数由Rva得出的FOA
		DWORD FuncFOA = RVA2FOA(pFileBuffer, Offset + pSectionHeader->VirtualAddress);
		//查节表得出的FOA
		DWORD RealFOA = Offset + pSectionHeader->PointerToRawData;
		if (FuncFOA != RealFOA)
		{
			printf("第%d个节表测试错误!\n", i);
			return false;
		}
	}	//出循环后pSectionHeader指向不存在的节表,多了一节
	pSectionHeader = NULL;  //所以pSectionHeader指针不可再使用
	printf("测试成功!\n");
	return true;
}

//验证FileBuffer和ImageBuffer映射是否正确
bool VerifyFileBufferAndImageBuffer(LPVOID pFileBuffer, LPVOID pImageBuffer)
{
	PIMAGE_DOS_HEADER pDosHeader = NULL;
	PIMAGE_NT_HEADERS pNTHeader = NULL;
	PIMAGE_FILE_HEADER pFileHeader = NULL;
	PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;
	//DOS头
	pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;	// 强转 DOS_HEADER 结构体指针
	//NT头
	pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
	//PE头
	pFileHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);	//NT头地址 + 4 为 FileHeader 首址
	//可选PE头	
	pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);//SIZEOF_FILE_HEADER为固定值且不存在于PE文件字段中
	//头验证
	PBYTE PBFileBuffer = (PBYTE)pFileBuffer;
	PBYTE PBImageBuffer = (PBYTE)pImageBuffer;
	//printf("开始FB:%x   IB:%x\n", FileBuffer, ImageBuffer);
	for (int i = 0; i < pOptionalHeader->SizeOfHeaders; i++)
	{
		if (*PBImageBuffer != *PBFileBuffer)	//pFileBuffer强转 WORD* 后 取指针指向的内容
		{
			printf("头验证失败\n");
			return false;
		}
		PBImageBuffer++;
		PBFileBuffer++;
	}
	//printf("结束FB:%x   IB:%x\n", FileBuffer, ImageBuffer);
	printf("头验证通过\n");
	//循环遍历节表进行节验证
	pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
	for (int i = 1; i <= pFileHeader->NumberOfSections; i++)
	{
		PBFileBuffer = (PBYTE)((DWORD)pFileBuffer + pSectionHeader->PointerToRawData);
		PBImageBuffer = (PBYTE)((DWORD)pImageBuffer + pSectionHeader->VirtualAddress);
		for (int j = 0; j < pSectionHeader->SizeOfRawData; j++)
		{
			if (*PBImageBuffer != *PBFileBuffer)	//pFileBuffer强转 WORD* 后 取指针指向的内容
			{
				printf("第%x节第%x个字节验证失败\n",i,j);
				return false;
			}
			PBImageBuffer++;
			PBFileBuffer++;
		}
		//节表++
		pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER);
	}	//出循环后pSectionHeader指向不存在的节表,多了一节
	pSectionHeader = NULL;  //所以pSectionHeader指针不可再使用
	printf("节验证通过\n");
	return true;
}

//输出内存buffer中的头
void PrintHeader(LPVOID pFileBuffer)
{
	PIMAGE_DOS_HEADER pDosHeader = NULL;
	PIMAGE_NT_HEADERS pNTHeader = NULL;
	PIMAGE_FILE_HEADER pFileHeader = NULL;
	PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;
	printf("************PrintHeader开始************\n");
	if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)	//pFileBuffer强转 WORD* 后 取指针指向的内容
	{
		printf("不是有效的MZ标志\n");
		free(pFileBuffer);
		return;
	}
	pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;	// 强转 DOS_HEADER 结构体指针
													//打印DOS头	
	printf("**********DOS头**********\n");
	printf("MZ标志:%x\n", pDosHeader->e_magic);
	printf("PE偏移:%x\n", pDosHeader->e_lfanew);
	//判断是否是有效的PE标志	
	if (*((PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)	//基址pFileBuffer + lfanew 为 NTHeader首址
	{
		printf("不是有效的PE标志\n");
		free(pFileBuffer);
		return;
	}
	pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
	//打印NT头	
	printf("*********NT HEADERS*********\n");
	printf("NT:%x\n", pNTHeader->Signature);
	pFileHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);	//NT头地址 + 4 为 FileHeader 首址
	printf("********FILE HEADER********\n");
	printf("PE:%x\n", pFileHeader->Machine);
	printf("节的数量:%x\n", pFileHeader->NumberOfSections);
	printf("SizeOfOptionalHeader:%x\n", pFileHeader->SizeOfOptionalHeader);
	//可选PE头	
	pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);//SIZEOF_FILE_HEADER为固定值且不存在于PE文件字段中
	printf("*********OPTIOINAL HEADER********\n");
	printf("Magic:%x\n", pOptionalHeader->Magic);
	//循环遍历节表
	pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
	for (int i = 1; i <= pFileHeader->NumberOfSections; i++)
	{
		char Name[IMAGE_SIZEOF_SHORT_NAME + 1];
		//printf("size:%x\n", sizeof(Name));
		memset(Name, 0, sizeof(Name));
		printf("******第%d个节表******\n", i);
		memcpy(Name, pSectionHeader->Name, IMAGE_SIZEOF_SHORT_NAME);
		printf("Name:%s\n", Name);
		//节表++
		pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER);
	}	//出循环后pSectionHeader指向不存在的节表,多了一节
	pSectionHeader = NULL;  //所以pSectionHeader指针不可再使用
	printf("************PrintHeader结束************\n");
}

//**************************************************************************								
//AddNewSection:新增节,可能含有头抬升。函数将保存新增节后的文件
//参数说明:								
//pFileBuffer:FileBuffer指针	
//SizeOfFileBuffer:pFileBuffer指向的大小,也即FileBuffer大小
//SizeOfNewSectio:新增节的大小
//pNewFileBuffer:返回指向新FileBuffer的指针
//返回值说明:								
//新FileBuffer的大小,NewFileBuffer的大小
//**************************************************************************	
int AddNewSection(IN LPVOID pFileBuffer,IN int SizeOfFileBuffer,IN DWORD SizeOfNewSection, OUT LPVOID* ppNewFileBuffer)
{
	PIMAGE_DOS_HEADER pDosHeader = NULL;
	PIMAGE_NT_HEADERS pNTHeader = NULL;
	PIMAGE_FILE_HEADER pFileHeader = NULL;
	PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;
	PIMAGE_SECTION_HEADER pLastSectionHeader = NULL;	//指向最后一个节表
	PIMAGE_SECTION_HEADER pNewSectionHeader = NULL;		//指向最后一个节表的下一个节表,即不存在的节表作为新开辟的节表
	bool isUplift = false;
	//Dos头
	pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;	// 强转 DOS_HEADER 结构体指针
	//PE头
	pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pFileBuffer + pDosHeader->e_lfanew + 4);	//NT头地址 + 4 为 FileHeader 首址
	//可选PE头
	pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
	//首个节表
	pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
	for (int i = 1; i < pFileHeader->NumberOfSections; i++, pSectionHeader++)	//注意这里i从1开始 i < NumberOfSections
	{
	}	//出循环后pSectionHeader指向最后一个节表				
	pLastSectionHeader = pSectionHeader;
	pNewSectionHeader = pLastSectionHeader + 1;
	//节表结束的位置+两个节表的大小 仍然≤ 头大小,才可继续。插一个留两个为了插入一个新节表后仍有一个节表的位置填充0
	if ((DWORD)pNewSectionHeader + IMAGE_SIZEOF_SECTION_HEADER * 2 <= (DWORD)pFileBuffer + pOptionalHeader->SizeOfHeaders)
	{	//要节表后留出两个节表的空位且全为0,保证其中没有可能使用的数据,才允许插入新节表。
		PBYTE pTemp = (PBYTE)pNewSectionHeader;
		for (int i = 0; i < IMAGE_SIZEOF_SECTION_HEADER * 2; i++, pTemp++)
		{
			if (*pTemp)
			{
				printf("节表插入空位存在数据,需进行头抬升\n");
				isUplift = true;
				break;
			}
		}
	}
	else
	{
		printf("无节表插入空位,需进行头抬升\n");
		isUplift = true;
	}
	//isUplift = true; //头抬升测试
	if (isUplift)
	{
		if ((DWORD)pFileBuffer + sizeof(IMAGE_DOS_HEADER) - (DWORD)pNTHeader >= IMAGE_SIZEOF_SECTION_HEADER * 2)
		{
			printf("可抬升NT头\n");
			//开始拷贝,将NT头拷贝到DOS头结束之后,长度为NT头开始到最后一个节表结束时的长度,即pNewSectionHeader
			memcpy((void*)((DWORD)pFileBuffer + sizeof(IMAGE_DOS_HEADER)), pNTHeader, (DWORD)pNewSectionHeader - (DWORD)pNTHeader);
			//拷贝后重置e_lfanew位置
			//printf(" e_lfanew: %x\n", pDosHeader->e_lfanew);
			pDosHeader->e_lfanew = sizeof(IMAGE_DOS_HEADER);
			//printf("e_lfanew: %x\n", pDosHeader->e_lfanew);
			//抬升后更新所有被抬升的头指针
			//NT头
			pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
			//PE头
			pFileHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);	//NT头地址 + 4 为 FileHeader 首址
			//可选PE头	
			pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);//SIZEOF_FILE_HEADER为固定值且不存在于PE文件字段中
			//首个节表
			pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
			for (int i = 1; i < pFileHeader->NumberOfSections; i++, pSectionHeader++)	//注意这里i从1开始 i < NumberOfSections
			{
			}	//出循环后pSectionHeader指向最后一个节表				
			pLastSectionHeader = pSectionHeader;
			pNewSectionHeader = pLastSectionHeader + 1;
			//验证代码,判断是否是有效的PE标志
			if (*((PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)	//基址pFileBuffer + lfanew 为 NTHeader首址
			{
				printf("抬升后验证失败,不是有效的PE标志\n");
				return 0;
			}
			printf("抬升成功!\n");
			//抬升成功后将最后一个节表后两个节表位置的空间置零
			memset(pNewSectionHeader, 0, IMAGE_SIZEOF_SECTION_HEADER * 2);

		}
		else
		{
			printf("不可抬升NT头,不可插入节表\n");
			free(pFileBuffer);
			return 0;
		}
	}
	//开始构造新节表
	printf("可插入节表\n");
	strcpy((char*)pNewSectionHeader->Name, (char*)".NewSec");
	//printf("Name:%s\n", pNewSectionHeader->Name);
	pNewSectionHeader->Misc.VirtualSize = SizeOfNewSection;
	//节区在内存中的偏移 = 内存中整个PE文件映射的大小
	pNewSectionHeader->VirtualAddress = pOptionalHeader->SizeOfImage;
	//节区在文件对齐中的大小  以VirtualSize内存对齐向上取整
	printf("FileAlignment:%x\n", pOptionalHeader->FileAlignment);
	pNewSectionHeader->SizeOfRawData = Align(SizeOfNewSection, pOptionalHeader->FileAlignment);
	printf("SizeOfRawData:%x\n", pNewSectionHeader->SizeOfRawData);
	//节区在文件中的偏移 = 文件大小  (也可以是最后一个节区所在文件中位置 + 最后一个节区在文件对齐中的大小)二者一致
	pNewSectionHeader->PointerToRawData = SizeOfFileBuffer;//pLastSectionHeader->PointerToRawData + pLastSectionHeader->SizeOfRawData;
	//printf("PointerToRawData:%x\n",pNewSectionHeader->PointerToRawData);
	pNewSectionHeader->PointerToRelocations = 0;
	//printf("PointerToRelocations:%x\n", pNewSectionHeader->PointerToRelocations);
	pNewSectionHeader->PointerToLinenumbers = 0;
	pNewSectionHeader->NumberOfRelocations = 0;
	pNewSectionHeader->NumberOfLinenumbers = 0;
	pNewSectionHeader->Characteristics = 0xe0000060;
	//新节表构造完毕, 修改节的数量
	pFileHeader->NumberOfSections++;
	//修改sizeOfImage的大小
	//节区在内存对齐中的大小  以VirtualSize内存对齐向上取整,对齐后加到SizeOfImage中
	printf("SectionAlignment: %x\nSizeOfImage:%x\n", pOptionalHeader->SectionAlignment, pOptionalHeader->SizeOfImage);
	pOptionalHeader->SizeOfImage += Align(SizeOfNewSection, pOptionalHeader->SectionAlignment);
	printf("SizeOfImage:%x\n", pOptionalHeader->SizeOfImage);
	//printf("Characteristics:%x\n", pNewSectionHeader->Characteristics);
	//开辟节区新空间
	int SizeOfNewFileBuffer = SizeOfFileBuffer + pNewSectionHeader->SizeOfRawData;
	*ppNewFileBuffer = malloc(SizeOfNewFileBuffer);
	//新节区之前的值全部复制,新节表部分已改变
	memcpy(*ppNewFileBuffer, pFileBuffer, SizeOfFileBuffer);
	//新节区全部置零
	memset((void*)((DWORD)*ppNewFileBuffer + pNewSectionHeader->PointerToRawData), 0, pNewSectionHeader->SizeOfRawData);
	return SizeOfNewFileBuffer;
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值