PE文件(四)FileBuffer-ImageBuffer

文件执行的总过程

当文件从硬盘中读入虚拟内存(FileBuffer)中时,文件数据会被原封不动的复制一份到虚拟内存中,然后进行拉伸对齐。此时虚拟内存中文件数据叫做文件印象或者内存印象,即ImageBuffer。此时ImageBuffer中的文件的数据满足文件运行的条件,但文件仍然不能真正的运行。只有当操作系统将文件真正的装入物理内存中, CPU进行分配执行后,文件才算真正运行了

SizeOfRawData与Misc.VirtualSize

SizeOfRawData表示此节在硬盘上经过文件对齐后的大小,Misc.VirtualSize表示此节在内存中对齐前的大小,即文件数据在内存中真正分配的大小

当我们初始化一个全局变量,如数组int arr[1000] = {0}时,编译器将编译好的.exe文件存放在硬盘上,这1000个int类型的0会存放在某一个节中,并分配1000个0的空间。并且这个空间大小在硬盘中和在内存中是一致的。由于SizeOfRawData的大小加上了文件对齐时多出来的空间,所以SizeOfRawData一般大于或等于Misc.VirtualSize

但是当我们只声明不初始化该数组时,文件在硬盘上的数据对此数组有声明但是不会分配这1000的空间的。所以SizeOfRawData在计算时不会算上这未初始化的空间。但是当文件真正加载到物理内存中时,操作系统会分配这1000的空间,所以Misc.VirtualSize在计算时会加上未初始化的空间的。在这种情况下Misc.VirtualSize可能大于SizeOfRawData

手动模拟FileBuffer到ImageBuffer过程

1.获取FileBuffer:先在硬盘上找一个可执行文件,将该文件的数据复制到缓冲区中,即FileBuffer中

2.申请ImageBuffer:根据SizeOfImage即文件加载到4GB虚拟内存中的大小,使用malloc开辟另一块缓冲区ImageBufferb并初始化,用来存放文件虚拟内存中的数据

3.复制PE头字段:由于经过文件对齐后的所有头和节表的数据加载到ImageBuffer的过程中是不会改变的,所以可以通过sizeofheads获取这些数据的内存大小,直接从FileBuffer中复制到ImageBuffer中

4.复制所有的节:通过第一个节对应节表中的PointerToRawData的值确定该节在FileBuffer的起始地址,然后通过SizeOfRawData的值确定该节在FileBuffer中需要复制的数据的大小,再通过VirtualAddress再加上ImageBase得到此节在ImageBuffer中的起始地址,最后将FileBuffer对应的数据在ImageBuffer中的起始地址位置开始复制,完成第一个节的复制。剩下的节如此循环赋值即可。

选择SizeOfRawData来确定需要复制的节的大小的原因:在极端情况下,当节中存在足够大未初始化的数据时,按照Misc.VirtualSize值将FileBuffer中的数据复制到ImageBuffer中,很可能会把FileBuffer中下一个节的数据也复制过去,这样就会造成复制错误。所以直接用SizeOfRawData就可以了。在这两个选择中更好的做法是比较SizeOfRawData和VirtualSize,选择较小值。但实际上这两种选择哪一个都可以

内存偏移地址与文件偏移地址换算

在此之前我们应该知道DOS头的起始地址和imagebase没有关系,现在假设有一个文件在4GB虚拟内存中起始位置0x500000,有一个数据,其地址为0x501234。我们要求出该地址对应的硬盘内存地址:

1.先算出此数据虚拟内存地址相对于文件在虚拟内存中的起始地址的偏移量

2.通过这个偏移量和每一个节的VirtualAddress做循环比较,当此偏移量大于某一个节的VirtualAddress并且小于此VirtualAddress + Misc.VirtualSize,就说明这个内存地址就在这个节中

3.用此偏移量-此节的VirtualAddress得到这个数据的虚拟内存地址相对于所在节的偏移量

4.找该数据虚拟内存地址所在节对应节表中PointerToRawData,通过PointerToRawData + 内存地址相对于所在节的偏移量来得到此内存地址在硬盘上时相对于文件的偏移量

举例:现在我们要找0x501234对应的文件偏移是多少?

1.0x501234 - 0x500000 = 0x1234

2.因为0x1000 < 0x1234 < 0x1000 + Misc.VirtualSize,所以0x501234在可执行文件的第一个节中

3.0x1234 - 0x1000 = 0x234获取该数据相对于该节的偏移量

由于第一个节的PointerToRawData为0x400,且假设FileBuffer的起始地址为0(相对与文件在真实内存起始地址),则0x501234对应的文件偏移地址为0x400 + 0x234 = 0x634

RVA TO FOA

接下来我们代码实现RVATOFOA的转换

DWORD RvaToFoa(DWORD dwRva, char * szBuffer)
{
	PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)szBuffer;
	PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(szBuffer + pDos->e_lfanew);
	PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNt);
	if (dwRva < pSectionHeader[0].VirtualAddress)//判断是否落在的头部
	{
		//PE头的文件内存和虚拟内存一致	
		return dwRva;
	}
	for (size_t i = 0; i < pNt->FileHeader.NumberOfSections; i++)
	{
		//判断是否落在了某一个区段内
		//区段头:Start = VirtualAddress
		//区段大小:Size
		//区段尾:end = VirtualAddress + Size 
		if (dwRva >= pSectionHeader[i].VirtualAddress && dwRva <= pSectionHeader[i].VirtualAddress + pSectionHeader[i].Misc.VirtualSize)
		{
			//返回文件偏移(FOA)
			//dwRva RVA
			//当前地址所在区段的内存偏移:pSectionHeader[i].VirtualAddress 
			//当前地址到所在区段基址的偏移:dwRva  - VirtualAddress
			//所在区段距离文件头的真实长度:PointerToRawData
			//当前内存地址在文件中的真实地址(FOA):PointerToRawData + dwRva  - VirtualAddress
			return dwRva - pSectionHeader[i].VirtualAddress + pSectionHeader[i].PointerToRawData;
		}
	}
}

作业

FileBuffer转ImageBuffer

1.根据Sizeofimage分配ImageBuffer空间

2.复制头sizeofheader获取文件对齐后所有头和节表大小

3.利用循环,PointtoRaw获取节在FileBuffer中地址,利用SizeofRawData获取要复制的数据大小

#include<stdio.h>
#include<Windows.h>
#include<string.h>
DWORD ReadPEFileSize(const char* lpszFile) //将一个文件的硬盘内存数据读取到自定义的文件内存缓冲区
{
	FILE* pFile = NULL;
	pFile = fopen(lpszFile, "rb");
	DWORD FileSize = 0;
	if (!pFile)
	{
		printf("无法打开EXE文件");
		return 0;
	}
	fseek(pFile, 0, SEEK_END);
	FileSize = ftell(pFile);
	return FileSize;
}
char* ReadPEFile(const char* lpszFile)
{
	FILE* pFile = NULL;
	pFile = fopen(lpszFile, "rb");
	DWORD FileSize = ReadPEFileSize(lpszFile);
	fseek(pFile, 0, SEEK_SET);
	char* pFileBuffer = NULL;
	pFileBuffer = (char*)malloc(sizeof(char) * FileSize);
	if (!pFileBuffer)
	{
		printf("分配空间失败");
		fclose(pFile);
		return 0;
	}
	size_t i = fread(pFileBuffer, FileSize, 1, pFile);
	if (!i)
	{
		printf("读取数据失败!");
		free(pFileBuffer);
		fclose(pFile);
		return 0;
	}
	IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)pFileBuffer;
	IMAGE_NT_HEADERS* pNTHeader = (IMAGE_NT_HEADERS*)((char*)pFileBuffer + pDosHeader->e_lfanew); //(char*)pFileBuffer只有此处转换为了char*类型,之后加减是为char的大小为一个单位进行加减
	if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
	{
		printf("不是有效MZ标志,结束\n");
		free(pFileBuffer);
		fclose(pFile);
		return 0;
	}
	if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
	{
		printf("不是有效的PE标志,打印结束\n");
		free(pFileBuffer);
		pFileBuffer = NULL;
		return 0;
	}
	fclose(pFile);
	return pFileBuffer;
}
char* FileBufferToImageBuffer(const char* lpszFile) //文件转内存并返回内存地址
{
	char* pFileBuffer = ReadPEFile(lpszFile); //获取自定义文件内存缓冲区指针
	if (pFileBuffer == NULL)
	{
		printf("缓冲区指针无效\n");
		return FALSE;
	}
	IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)pFileBuffer; //获取自定义文件内存缓冲区DOS头指针
	IMAGE_FILE_HEADER* pFileHeader = (IMAGE_FILE_HEADER*)((char*)pDosHeader + pDosHeader->e_lfanew + 4);//获取标准PE头指针
	IMAGE_OPTIONAL_HEADER* pOptionHeader = (IMAGE_OPTIONAL_HEADER*)((char*)pFileHeader + 20);//获取可选PE头指针
	IMAGE_SECTION_HEADER* pSecHeader = (IMAGE_SECTION_HEADER*)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取文件节表指针
	char* pSecBuffer = (char*)pDosHeader + pSecHeader->PointerToRawData; //获取第一个节的指针
	char* ImageBuffer = (char*)malloc(pOptionHeader->SizeOfImage); //开辟自定义的文件的虚拟内存大小
	if (ImageBuffer == NULL)
	{
		printf("申请虚拟内存失败\n");
		return FALSE;
	}
	memset(ImageBuffer, 0, pOptionHeader->SizeOfImage); //初始化虚拟内存
	memcpy(ImageBuffer, pDosHeader, pOptionHeader->SizeOfHeaders); //将文件内存头字段数据复制到虚拟内存
	IMAGE_DOS_HEADER* iDosHeader = (IMAGE_DOS_HEADER*)ImageBuffer; //获取自定义虚拟内存内存缓冲区DOS头指针
	IMAGE_FILE_HEADER* iFileHeader = (IMAGE_FILE_HEADER*)((char*)iDosHeader + iDosHeader->e_lfanew + 4);//获取标准PE头指针
	IMAGE_OPTIONAL_HEADER* iOptionHeader = (IMAGE_OPTIONAL_HEADER*)((char*)iFileHeader + 20);//获取可选PE头指针
	IMAGE_SECTION_HEADER* iSecHeader = (IMAGE_SECTION_HEADER*)((char*)iOptionHeader + iFileHeader->SizeOfOptionalHeader); // 获取虚拟内存节表指针
	char* iSecBuffer = (char*)iDosHeader + iSecHeader->VirtualAddress; //保存第一个节的指针
	for (int i = 0; i < pFileHeader->NumberOfSections; i++)
	{
		memcpy(iSecBuffer, pSecBuffer, pSecHeader->SizeOfRawData);
		iSecHeader++;
		iSecBuffer = (char*)iDosHeader + iSecHeader->VirtualAddress;
		pSecHeader++;
		pSecBuffer = (char*)pDosHeader + pSecHeader->PointerToRawData;
	}
	free(pFileBuffer);
	return ImageBuffer;
}
int main(int argc, char* argv[])
{
	const char* lpszFile = "C:\\Users\\扶摇\\Desktop\\Notepad.exe";
	DWORD FileSize = ReadPEFileSize(lpszFile);
	char* ImageBuffer = FileBufferToImageBuffer(lpszFile);
	return 0;
}

ImageBuffer转NewBuffer

1.找到最后一个节的起始位置加上其对齐大小就是所需要的NewBuffer内存大小

2.复制所有的头到NewBuffer

#include<stdio.h>
#include<Windows.h>
#include<string.h>
DWORD ReadPEFileSize(const char* lpszFile) //将一个文件的硬盘内存数据读取到自定义的文件内存缓冲区
{
	FILE* pFile = NULL;
	pFile = fopen(lpszFile, "rb");
	DWORD FileSize = 0;
	if (!pFile)
	{
		printf("无法打开EXE文件");
		return 0;
	}
	fseek(pFile, 0, SEEK_END);
	FileSize = ftell(pFile);
	return FileSize;
}
char* ReadPEFile(const char* lpszFile)
{
	FILE* pFile = NULL;
	pFile = fopen(lpszFile, "rb");
	DWORD FileSize = ReadPEFileSize(lpszFile);
	fseek(pFile, 0, SEEK_SET);
	char* pFileBuffer = NULL;
	pFileBuffer = (char*)malloc(sizeof(char) * FileSize);
	if (!pFileBuffer)
	{
		printf("分配空间失败");
		fclose(pFile);
		return 0;
	}
	size_t i = fread(pFileBuffer, FileSize, 1, pFile);
	if (!i)
	{
		printf("读取数据失败!");
		free(pFileBuffer);
		fclose(pFile);
		return 0;
	}
	IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)pFileBuffer;
	IMAGE_NT_HEADERS* pNTHeader = (IMAGE_NT_HEADERS*)((char*)pFileBuffer + pDosHeader->e_lfanew); //(char*)pFileBuffer只有此处转换为了char*类型,之后加减是为char的大小为一个单位进行加减
	if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
	{
		printf("不是有效MZ标志,结束\n");
		free(pFileBuffer);
		fclose(pFile);
		return 0;
	}
	if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
	{
		printf("不是有效的PE标志,打印结束\n");
		free(pFileBuffer);
		pFileBuffer = NULL;
		return 0;
	}
	fclose(pFile);
	return pFileBuffer;
}
char* FileBufferToImageBuffer(const char* lpszFile) //文件转内存并返回内存地址
{
	char* pFileBuffer = ReadPEFile(lpszFile); //获取自定义文件内存缓冲区指针
	if (pFileBuffer == NULL)
	{
		printf("缓冲区指针无效\n");
		return FALSE;
	}
	IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)pFileBuffer; //获取自定义文件内存缓冲区DOS头指针
	IMAGE_FILE_HEADER* pFileHeader = (IMAGE_FILE_HEADER*)((char*)pDosHeader + pDosHeader->e_lfanew + 4);//获取标准PE头指针
	IMAGE_OPTIONAL_HEADER* pOptionHeader = (IMAGE_OPTIONAL_HEADER*)((char*)pFileHeader + 20);//获取可选PE头指针
	IMAGE_SECTION_HEADER* pSecHeader = (IMAGE_SECTION_HEADER*)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取文件节表指针
	char* pSecBuffer = (char*)pDosHeader + pSecHeader->PointerToRawData; //获取第一个节的指针
	char* ImageBuffer = (char*)malloc(pOptionHeader->SizeOfImage); //开辟自定义的文件的虚拟内存大小
	if (ImageBuffer == NULL)
	{
		printf("申请虚拟内存失败\n");
		return FALSE;
	}
	memset(ImageBuffer, 0, pOptionHeader->SizeOfImage); //初始化虚拟内存
	memcpy(ImageBuffer, pDosHeader, pOptionHeader->SizeOfHeaders); //将文件内存头字段数据复制到虚拟内存
	IMAGE_DOS_HEADER* iDosHeader = (IMAGE_DOS_HEADER*)ImageBuffer; //获取自定义虚拟内存内存缓冲区DOS头指针
	IMAGE_FILE_HEADER* iFileHeader = (IMAGE_FILE_HEADER*)((char*)iDosHeader + iDosHeader->e_lfanew + 4);//获取标准PE头指针
	IMAGE_OPTIONAL_HEADER* iOptionHeader = (IMAGE_OPTIONAL_HEADER*)((char*)iFileHeader + 20);//获取可选PE头指针
	IMAGE_SECTION_HEADER* iSecHeader = (IMAGE_SECTION_HEADER*)((char*)iOptionHeader + iFileHeader->SizeOfOptionalHeader); // 获取虚拟内存节表指针
	char* iSecBuffer = (char*)iDosHeader + iSecHeader->VirtualAddress; //保存第一个节的指针
	for (int i = 0; i < pFileHeader->NumberOfSections; i++)
	{
		memcpy(iSecBuffer, pSecBuffer, pSecHeader->SizeOfRawData);
		iSecHeader++;
		iSecBuffer = (char*)iDosHeader + iSecHeader->VirtualAddress;
		pSecHeader++;
		pSecBuffer = (char*)pDosHeader + pSecHeader->PointerToRawData;
	}
	free(pFileBuffer);
	return ImageBuffer;
}
char* ImageBufferToNewBuffer(char* ImageBuffer,DWORD NewFileSize) //内存转文件并返回内存地址
{
	if (ImageBuffer == NULL)
	{
		printf("缓冲区指针无效\n");
		return FALSE;
	}
	IMAGE_DOS_HEADER* iDosHeader = (IMAGE_DOS_HEADER*)ImageBuffer; //获取自定义文件内存缓冲区DOS头指针
	IMAGE_FILE_HEADER* iFileHeader = (IMAGE_FILE_HEADER*)((char*)iDosHeader + iDosHeader->e_lfanew + 4);//获取标准PE头指针
	IMAGE_OPTIONAL_HEADER* iOptionHeader = (IMAGE_OPTIONAL_HEADER*)((char*)iFileHeader + 20);//获取可选PE头指针
	IMAGE_SECTION_HEADER* iSecHeader = (IMAGE_SECTION_HEADER*)((char*)iOptionHeader + iFileHeader->SizeOfOptionalHeader); // 获取文件节表指针
	char* iSecBuffer = (char*)iDosHeader + iSecHeader->VirtualAddress; //获取第一个节的指针
	char* NewBuffer = (char*)malloc(NewFileSize);
	memset(NewBuffer, 0, NewFileSize);
	memcpy(NewBuffer, iDosHeader, iOptionHeader->SizeOfHeaders);
	IMAGE_DOS_HEADER* NewDosHeader = (IMAGE_DOS_HEADER*)NewDosHeader; //获取自定义虚拟内存内存缓冲区DOS头指针
	IMAGE_FILE_HEADER* NewFileHeader = (IMAGE_FILE_HEADER*)((char*)NewDosHeader + NewDosHeader->e_lfanew + 4);//获取可选PE头指针
	IMAGE_SECTION_HEADER* NewSecHeader = (IMAGE_SECTION_HEADER*)((char*)NewFileHeader + 20 + NewFileHeader->SizeOfOptionalHeader); // 获取虚拟内存节表指针
	char* NewSecBuffer = (char*)NewDosHeader + NewSecHeader->PointerToRawData; //保存第一个节的指针
	for (int i = 0; i < iFileHeader->NumberOfSections; i++)
	{
		memcpy(NewSecBuffer, iSecBuffer, iSecHeader->SizeOfRawData);
		iSecHeader++;
		iSecBuffer = (char*)iDosHeader + iSecHeader->VirtualAddress;
		NewSecHeader++;
		NewSecBuffer = (char*)NewDosHeader + NewSecHeader->PointerToRawData;
	}
    free ImageBuffer;
	return NewBuffer;
}

int main(int argc, char* argv[])
{
	const char* lpszFile = "C:\\Users\\扶摇\\Desktop\\Notepad.exe";
	DWORD FileSize = ReadPEFileSize(lpszFile);
	char* ImageBuffer = FileBufferToImageBuffer(lpszFile);
	char* NewBuffer = ImageBufferToNewBuffer(ImageBuffer, FileSize);
	return 0;
}

RVA TO FOA

已知一个在文件在虚拟内存中相对DOS头的偏移地址,将此地址转化成文件在硬盘时的绝对地址。

由于FileBuffer到ImageBuffer中,节中会有数据被初始化,这里目前来说无法解决,所以就假设节中没有未被初始化的数据,或者拉伸后未被初始化数据不影响地址转化

#include<stdio.h>
#include<Windows.h>
#include<string.h>
//本程序均建立在SecHeader->SizeOfRawData >= SecHeader->Misc.VirtualSize的前提下
DWORD ReadPEFileSize(const char* lpszFile) //将一个文件的硬盘内存数据读取到自定义的文件内存缓冲区
{
	FILE* pFile = NULL;
	pFile = fopen(lpszFile, "rb");
	DWORD FileSize = 0;
	if (!pFile)
	{
		printf("无法打开EXE文件");
		return 0;
	}
	fseek(pFile, 0, SEEK_END);
	FileSize = ftell(pFile);
	return FileSize;
}
char* ReadPEFile(const char* lpszFile)
{
	FILE* pFile = NULL;
	pFile = fopen(lpszFile, "rb");
	DWORD FileSize = ReadPEFileSize(lpszFile);
	fseek(pFile, 0, SEEK_SET);
	char* pFileBuffer = NULL;
	pFileBuffer = (char*)malloc(sizeof(char) * FileSize);
	if (!pFileBuffer)
	{
		printf("分配空间失败");
		fclose(pFile);
		return 0;
	}
	size_t i = fread(pFileBuffer, FileSize, 1, pFile);
	if (!i)
	{
		printf("读取数据失败!");
		free(pFileBuffer);
		fclose(pFile);
		return 0;
	}
	IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)pFileBuffer;
	IMAGE_NT_HEADERS* pNTHeader = (IMAGE_NT_HEADERS*)((char*)pFileBuffer + pDosHeader->e_lfanew); //(char*)pFileBuffer只有此处转换为了char*类型,之后加减是为char的大小为一个单位进行加减
	if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
	{
		printf("不是有效MZ标志,结束\n");
		free(pFileBuffer);
		fclose(pFile);
		return 0;
	}
	if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
	{
		printf("不是有效的PE标志,打印结束\n");
		free(pFileBuffer);
		pFileBuffer = NULL;
		return 0;
	}
	fclose(pFile);
	return pFileBuffer;
}
int Alige(int mes, int aligement)
{
	return mes <= aligement ? aligement : mes + aligement - mes % aligement;
}
char* FileBufferToImageBuffer(const char* lpszFile) //文件转内存并返回内存地址
{
	char* pFileBuffer = ReadPEFile(lpszFile); //获取自定义文件内存缓冲区指针
	if (pFileBuffer == NULL)
	{
		printf("缓冲区指针无效\n");
		return FALSE;
	}
	IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)pFileBuffer; //获取自定义文件内存缓冲区DOS头指针
	IMAGE_FILE_HEADER* pFileHeader = (IMAGE_FILE_HEADER*)((char*)pDosHeader + pDosHeader->e_lfanew + 4);//获取标准PE头指针
	IMAGE_OPTIONAL_HEADER* pOptionHeader = (IMAGE_OPTIONAL_HEADER*)((char*)pFileHeader + 20);//获取可选PE头指针
	IMAGE_SECTION_HEADER* pSecHeader = (IMAGE_SECTION_HEADER*)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取文件节表指针
	char* pSecBuffer = (char*)pDosHeader + pSecHeader->PointerToRawData; //获取第一个节的指针
	char* ImageBuffer = (char*)malloc(pOptionHeader->SizeOfImage); //开辟自定义的文件的虚拟内存大小
	if (ImageBuffer == NULL)
	{
		printf("申请虚拟内存失败\n");
		return FALSE;
	}
	memset(ImageBuffer, 0, pOptionHeader->SizeOfImage); //初始化虚拟内存
	memcpy(ImageBuffer, pDosHeader, pOptionHeader->SizeOfHeaders); //将文件内存头字段数据复制到虚拟内存
	IMAGE_DOS_HEADER* iDosHeader = (IMAGE_DOS_HEADER*)ImageBuffer; //获取自定义虚拟内存内存缓冲区DOS头指针
	IMAGE_FILE_HEADER* iFileHeader = (IMAGE_FILE_HEADER*)((char*)iDosHeader + iDosHeader->e_lfanew + 4);//获取标准PE头指针
	IMAGE_OPTIONAL_HEADER* iOptionHeader = (IMAGE_OPTIONAL_HEADER*)((char*)iFileHeader + 20);//获取可选PE头指针
	IMAGE_SECTION_HEADER* iSecHeader = (IMAGE_SECTION_HEADER*)((char*)iOptionHeader + iFileHeader->SizeOfOptionalHeader); // 获取虚拟内存节表指针
	char* iSecBuffer = (char*)iDosHeader + iSecHeader->VirtualAddress; //保存第一个节的指针
	for (int i = 0; i < pFileHeader->NumberOfSections; i++)
	{
		memcpy(iSecBuffer, pSecBuffer, pSecHeader->SizeOfRawData);
		iSecHeader++;
		iSecBuffer = (char*)iDosHeader + iSecHeader->VirtualAddress;
		pSecHeader++;
		pSecBuffer = (char*)pDosHeader + pSecHeader->PointerToRawData;
	}
	return ImageBuffer;
}
DWORD RVAtoFOA(char* ImageBuffer, char* FileBuffer,  DWORD RVA) //该函数用于将内存偏移地址转换为文件绝对地址
{
	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)FileBuffer; //获取自定义文件内存缓冲区DOS头指针
	PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((char*)pDosHeader + pDosHeader->e_lfanew + 4);//获取标准PE头指针
	PIMAGE_OPTIONAL_HEADER pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)pFileHeader + 20);//获取可选PE头指针
	PIMAGE_SECTION_HEADER pSecHeader = (PIMAGE_SECTION_HEADER)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取文件节表指针
	char* pSecBuffer = (char*)pDosHeader + pSecHeader->PointerToRawData; //获取第一个节的指针
	PIMAGE_DOS_HEADER iDosHeader = (PIMAGE_DOS_HEADER)ImageBuffer; //获取虚拟内存中缓冲区DOS头指针
	PIMAGE_NT_HEADERS iNTHeader = (PIMAGE_NT_HEADERS)((char*)iDosHeader + iDosHeader->e_lfanew); //获取NT头指针
	PIMAGE_FILE_HEADER iFileHeader = (PIMAGE_FILE_HEADER)((char*)iDosHeader + iDosHeader->e_lfanew + 4);//获取标准PE头指针
	PIMAGE_OPTIONAL_HEADER iOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)iFileHeader + 20);//获取可选PE头指针
	PIMAGE_SECTION_HEADER iSecHeader = (PIMAGE_SECTION_HEADER)((char*)iOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取虚拟内存节表指针
	PIMAGE_SECTION_HEADER  iSecHeaderEnd = iSecHeader + pFileHeader->NumberOfSections - 1;// 使节表指针指向最后一个节表
	char* iSecBuffer1 = (char*)iDosHeader + iSecHeader->VirtualAddress; //保存第一个节的指针
	//以下开始虚拟内存节地址转换硬盘内存节地址
	DWORD SecEndAlige = Alige(iSecHeaderEnd->SizeOfRawData, iOptionHeader->SectionAlignment);
	DWORD SecMin = (DWORD)iDosHeader + iSecHeader->VirtualAddress; //节起始位置
	DWORD LastSecPoint = (DWORD)iDosHeader + iSecHeaderEnd->VirtualAddress + iSecHeaderEnd->SizeOfRawData + SecEndAlige;//最后一个节末尾
	RVA = RVA + (DWORD)iDosHeader;
	while (1)
	{
		if (RVA < (DWORD)iDosHeader)
		{
			printf("输入地址过小,请重新输入\n");
			scanf("%d", &RVA);
		}
		else if (RVA > LastSecPoint)
		{
			printf("输入地址过大,请重新输入\n");
			scanf("%d", &RVA);
		}
		else if (RVA <= SecMin && RVA >= (DWORD)iDosHeader)
		{
			printf("RVA to FOA: %d\n", RVA - (DWORD)iDosHeader);
			return RVA - (DWORD)iDosHeader + (DWORD)pDosHeader;
		}
		else
		{
			printf("输入地址正确,开始转换地址\n");
			DWORD offset1 = RVA - (DWORD)iDosHeader; //获取转换地址在虚拟存中相对于DOS头的偏移量
			DWORD nNumber = 0; //用于获取转换地址所在第几个节
			for (int i = 0; i < iFileHeader->NumberOfSections - 1; i++)
			{
				if (offset1 >= (iSecHeader + i)->VirtualAddress && offset1 <= (iSecHeader + i + 1)->VirtualAddress)
				{
					break;
				}
				else if (offset1 >= iSecHeaderEnd->VirtualAddress && offset1 <= LastSecPoint)
				{
					nNumber = iFileHeader->NumberOfSections - 1;
					break;
				}
				else
				{
					nNumber++;
				}
			}
			pSecHeader += nNumber;
			iSecHeader += nNumber;
			DWORD offset2 = offset1 - iSecHeader->VirtualAddress; //获取转换地址在内存中相对于目标节的偏移量
			DWORD pSecBufferPoint = pSecHeader->PointerToRawData + offset2;//获取转换地址在硬盘中相对于dos头的偏移量
			printf("RVA to FOA: %d\n", pSecBufferPoint + (DWORD)pDosHeader);
			return pSecBufferPoint + (DWORD)pDosHeader;
		}
	}
}
int main(int argc, char* argv[])
{
	const char* lpszFile = "C:\\Users\\扶摇\\Desktop\\Notepad.exe";
	char* FileBuffer = ReadPEFile(lpszFile);
	char* ImageBuffer = FileBufferToImageBuffer(lpszFile);
	RVAtoFOA(FileBuffer, ImageBuffer, 500);
	return 0;
}

FOA转RVA

已知一个文件硬盘上绝对地址,将其转换为该文件在虚拟内存状态下的目标地址相对DOS头的偏移地址

#include<stdio.h>
#include<Windows.h>
#include<string.h>
//本程序均建立在SecHeader->SizeOfRawData >= SecHeader->Misc.VirtualSize的前提下
DWORD ReadPEFileSize(const char* lpszFile) //将一个文件的硬盘内存数据读取到自定义的文件内存缓冲区
{
	FILE* pFile = NULL;
	pFile = fopen(lpszFile, "rb");
	DWORD FileSize = 0;
	if (!pFile)
	{
		printf("无法打开EXE文件");
		return 0;
	}
	fseek(pFile, 0, SEEK_END);
	FileSize = ftell(pFile);
	return FileSize;
}
char* ReadPEFile(const char* lpszFile)
{
	FILE* pFile = NULL;
	pFile = fopen(lpszFile, "rb");
	DWORD FileSize = ReadPEFileSize(lpszFile);
	fseek(pFile, 0, SEEK_SET);
	char* pFileBuffer = NULL;
	pFileBuffer = (char*)malloc(sizeof(char) * FileSize);
	if (!pFileBuffer)
	{
		printf("分配空间失败");
		fclose(pFile);
		return 0;
	}
	size_t i = fread(pFileBuffer, FileSize, 1, pFile);
	if (!i)
	{
		printf("读取数据失败!");
		free(pFileBuffer);
		fclose(pFile);
		return 0;
	}
	IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)pFileBuffer;
	IMAGE_NT_HEADERS* pNTHeader = (IMAGE_NT_HEADERS*)((char*)pFileBuffer + pDosHeader->e_lfanew); //(char*)pFileBuffer只有此处转换为了char*类型,之后加减是为char的大小为一个单位进行加减
	if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
	{
		printf("不是有效MZ标志,结束\n");
		free(pFileBuffer);
		fclose(pFile);
		return 0;
	}
	if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
	{
		printf("不是有效的PE标志,打印结束\n");
		free(pFileBuffer);
		pFileBuffer = NULL;
		return 0;
	}
	fclose(pFile);
	return pFileBuffer;
}
int Alige(int mes, int aligement)
{
	return mes <= aligement ? aligement : mes + aligement - mes % aligement;
}
char* FileBufferToImageBuffer(const char* lpszFile) //文件转内存并返回内存地址
{
	char* pFileBuffer = ReadPEFile(lpszFile); //获取自定义文件内存缓冲区指针
	if (pFileBuffer == NULL)
	{
		printf("缓冲区指针无效\n");
		return FALSE;
	}
	IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)pFileBuffer; //获取自定义文件内存缓冲区DOS头指针
	IMAGE_FILE_HEADER* pFileHeader = (IMAGE_FILE_HEADER*)((char*)pDosHeader + pDosHeader->e_lfanew + 4);//获取标准PE头指针
	IMAGE_OPTIONAL_HEADER* pOptionHeader = (IMAGE_OPTIONAL_HEADER*)((char*)pFileHeader + 20);//获取可选PE头指针
	IMAGE_SECTION_HEADER* pSecHeader = (IMAGE_SECTION_HEADER*)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取文件节表指针
	char* pSecBuffer = (char*)pDosHeader + pSecHeader->PointerToRawData; //获取第一个节的指针
	char* ImageBuffer = (char*)malloc(pOptionHeader->SizeOfImage); //开辟自定义的文件的虚拟内存大小
	if (ImageBuffer == NULL)
	{
		printf("申请虚拟内存失败\n");
		return FALSE;
	}
	memset(ImageBuffer, 0, pOptionHeader->SizeOfImage); //初始化虚拟内存
	memcpy(ImageBuffer, pDosHeader, pOptionHeader->SizeOfHeaders); //将文件内存头字段数据复制到虚拟内存
	IMAGE_DOS_HEADER* iDosHeader = (IMAGE_DOS_HEADER*)ImageBuffer; //获取自定义虚拟内存内存缓冲区DOS头指针
	IMAGE_FILE_HEADER* iFileHeader = (IMAGE_FILE_HEADER*)((char*)iDosHeader + iDosHeader->e_lfanew + 4);//获取标准PE头指针
	IMAGE_OPTIONAL_HEADER* iOptionHeader = (IMAGE_OPTIONAL_HEADER*)((char*)iFileHeader + 20);//获取可选PE头指针
	IMAGE_SECTION_HEADER* iSecHeader = (IMAGE_SECTION_HEADER*)((char*)iOptionHeader + iFileHeader->SizeOfOptionalHeader); // 获取虚拟内存节表指针
	char* iSecBuffer = (char*)iDosHeader + iSecHeader->VirtualAddress; //保存第一个节的指针
	for (int i = 0; i < pFileHeader->NumberOfSections; i++)
	{
		memcpy(iSecBuffer, pSecBuffer, pSecHeader->SizeOfRawData);
		iSecHeader++;
		iSecBuffer = (char*)iDosHeader + iSecHeader->VirtualAddress;
		pSecHeader++;
		pSecBuffer = (char*)pDosHeader + pSecHeader->PointerToRawData;
	}
	return ImageBuffer;
}
DWORD FOAtoRVA(char* FileBuffer, char* ImageBuffer, DWORD FOA) //该函数用于将文件绝对地址转换为内存偏移地址
{
	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)FileBuffer; //获取自定义文件内存缓冲区DOS头指针
	PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((char*)pDosHeader + pDosHeader->e_lfanew + 4);//获取标准PE头指针
	PIMAGE_OPTIONAL_HEADER pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)pFileHeader + 20);//获取可选PE头指针
	PIMAGE_SECTION_HEADER pSecHeader = (PIMAGE_SECTION_HEADER)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取文件节表指针
	PIMAGE_SECTION_HEADER  pSecHeaderEnd = pSecHeader + pFileHeader->NumberOfSections - 1;// 获取文件最后一个节的节表指针
	char* pSecBuffer = (char*)pDosHeader + pSecHeader->PointerToRawData; //获取文件第一个节的指针
	PIMAGE_DOS_HEADER iDosHeader = (PIMAGE_DOS_HEADER)ImageBuffer; //获取虚拟内存中缓冲区DOS头指针
	PIMAGE_NT_HEADERS iNTHeader = (PIMAGE_NT_HEADERS)((char*)iDosHeader + iDosHeader->e_lfanew); //获取NT头指针
	PIMAGE_FILE_HEADER iFileHeader = (PIMAGE_FILE_HEADER)((char*)iDosHeader + iDosHeader->e_lfanew + 4);//获取标准PE头指针
	PIMAGE_OPTIONAL_HEADER iOptionHeader = (PIMAGE_OPTIONAL_HEADER)((char*)iFileHeader + 20);//获取可选PE头指针
	PIMAGE_SECTION_HEADER iSecHeader = (PIMAGE_SECTION_HEADER)((char*)iOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取虚拟内存节表指针
	PIMAGE_SECTION_HEADER  iSecHeaderEnd = iSecHeader + pFileHeader->NumberOfSections - 1;// 获取内存最后一个节的节表指针
	char* iSecBuffer1 = (char*)iDosHeader + iSecHeader->VirtualAddress; //保存第一个节的指针
	//以下开始文件绝对内存节地址转换虚拟内存偏移地址
	DWORD SecEndAlige = Alige(iSecHeaderEnd->SizeOfRawData, iOptionHeader->SectionAlignment);
	DWORD SecpMin = (DWORD)pDosHeader + pSecHeader->VirtualAddress; //节起始位置
	DWORD LastSecPoint = (DWORD)iDosHeader + iSecHeaderEnd->VirtualAddress + iSecHeaderEnd->SizeOfRawData + SecEndAlige;//内存最后一个节末尾
	DWORD LastpSecPoint = (DWORD)pDosHeader + pSecHeaderEnd->PointerToRawData + pSecHeaderEnd->SizeOfRawData;//文件最后一个节结尾
	while (1)
	{
		if (FOA < (DWORD)pDosHeader)
		{
			printf("输入地址过小,请重新输入\n");
			scanf("%d", &FOA);
		}
		else if (FOA > LastpSecPoint)
		{
			printf("输入地址过大,请重新输入\n");
			scanf("%d", &FOA);
		}
		else if (FOA <= SecpMin && FOA >= (DWORD)pDosHeader)
		{
			printf("FOA to RVA: %d\n", FOA - (DWORD)pDosHeader);
			return FOA - (DWORD)pDosHeader;
		}
		else
		{
			printf("输入地址正确,开始转换地址\n");
			DWORD offset1 = FOA - (DWORD)pDosHeader; //获取转换地址在硬盘上中相对于DOS头的偏移量
			DWORD nNumber = 0; //用于获取转换地址所在第几个节
			for (int i = 0; i < pFileHeader->NumberOfSections - 1; i++)
			{
				if (offset1 >= (pSecHeader + i)->PointerToRawData && offset1 <= (pSecHeader + i + 1)->PointerToRawData)
				{
					break;
				}
				else if (offset1 >= iSecHeaderEnd->PointerToRawData && offset1 <= LastpSecPoint)
				{
					nNumber = pFileHeader->NumberOfSections - 1;
					break;
				}
				else
				{
					nNumber++;
				}
			}
			iSecHeader += nNumber;
			pSecHeader += nNumber;
			DWORD offset2 = offset1 - pSecHeader->PointerToRawData; //获取转换地址在内存中相对于目标节的偏移量
			DWORD iSecBufferPoint = iSecHeader->VirtualAddress + offset2;//获取转换地址在硬盘中相对于dos头的偏移量
			printf("FOA to RVA: %d\n", iSecBufferPoint);
			return iSecBufferPoint;
		}
	}
}
int main(int argc, char* argv[])
{
	const char* lpszFile = "C:\\Users\\扶摇\\Desktop\\Notepad.exe";
	char* FileBuffer = ReadPEFile(lpszFile);
	char* ImageBuffer = FileBufferToImageBuffer(lpszFile);
	FOAtoRVA(ImageBuffer, FileBuffer, 500);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值