C语言实现如下功能
FileBuffer转ImageBuffer
1.根据Sizeofimage分配ImageBuffer空间
2.复制头 sizeofheader获取文件对齐后所有头和节表大小
3.利用循环,PointtoRaw获取节在FileBuffer中地址,利用SizeofRawData获取要复制的数据大小
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;
}
void FileBufferToImageBufferToNewBuffer(const char* lpszFile)
{
char* pFileBuffer = ReadPEFile(lpszFile); //获取自定义文件内存缓冲区指针
if (pFileBuffer == NULL)
{
printf("缓冲区指针无效\n");
return;
}
DWORD FileSize = ReadPEFileSize(lpszFile);//获取硬盘上文件大小
IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)pFileBuffer; //获取自定义文件内存缓冲区DOS头指针
IMAGE_NT_HEADERS* pNTHeader = (IMAGE_NT_HEADERS*)((char*)pFileBuffer + pDosHeader->e_lfanew); //获取NT头指针
IMAGE_FILE_HEADER* pFileHeader = (IMAGE_FILE_HEADER*)((char*)pNTHeader + 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;
}
memset(ImageBuffer, 0, pOptionHeader->SizeOfImage); //初始化虚拟内存
memcpy(ImageBuffer, pFileBuffer, pOptionHeader->SizeOfHeaders); //将文件内存头字段数据复制到虚拟内存
IMAGE_DOS_HEADER* iDosHeader = (IMAGE_DOS_HEADER*)ImageBuffer; //获取自定义虚拟内存内存缓冲区DOS头指针
IMAGE_OPTIONAL_HEADER* iOptionHeader = (IMAGE_OPTIONAL_HEADER*)((char*)iDosHeader + iDosHeader->e_lfanew + 4 + 20);//获取可选PE头指针
IMAGE_SECTION_HEADER* iSecHeader1 = (IMAGE_SECTION_HEADER*)pSecHeader; // 获取虚拟内存节表指针
IMAGE_SECTION_HEADER* iSecHeader2 = iSecHeader1;后续内存转硬盘用
char* iSecBuffer1 = (char*)iDosHeader + iSecHeader1->VirtualAddress; //保存第一个节的指针
char* iSecBuffer2 = iSecBuffer1; //后续内存转硬盘用
for (int i = 0; i < pFileHeader->NumberOfSections; i++)//循环将文件内存的节表数据复制到虚拟内存中
{
memcpy((char*)iDosHeader + iSecHeader1->VirtualAddress, (char*)pDosHeader + pSecHeader->PointerToRawData, pSecHeader->SizeOfRawData); //复制文件内存节表数据
pSecHeader++; //指向硬盘下一个节表
iSecHeader1++;//指向内存下一个节表
}
pFileBuffer = NULL;
char* NewFileBuffer = (char*)malloc(FileSize); //申请新文件内存,此处开始内存转硬盘
if (NewFileBuffer == NULL)
{
printf("申请虚拟内存失败\n");
return;
}
memset(NewFileBuffer, 0, FileSize);
memcpy(NewFileBuffer, ImageBuffer, iOptionHeader->SizeOfHeaders);
IMAGE_DOS_HEADER* NewDosHeader = (IMAGE_DOS_HEADER*)NewFileBuffer;
IMAGE_SECTION_HEADER* NewSecHeader = (IMAGE_SECTION_HEADER*)iSecHeader2;
for (int i = 0; i < pFileHeader->NumberOfSections; i++)
{
memcpy((char*)NewDosHeader + NewSecHeader->PointerToRawData, (char*)iDosHeader + iSecHeader2->VirtualAddress, iSecHeader2->SizeOfRawData);
iSecHeader2++;
NewSecHeader++;
}
free(ImageBuffer);
free(NewFileBuffer);
}
int main(int argc, char* argv[])
{
const char* lpszFile = "C:\\Windows\\notepad.exe";
FileBufferToImageBufferToNewBuffer(lpszFile);
return 0;
}
2.编写一个函数,将RVA的值转换成FOA
将文件加载到内存时,已知一个数据在内存中的地址,将此地址转化成文件在上时的相对于文件起始地址的文件偏移地址。即将虚拟内存偏移地址转换成文件偏移地址。
由于FileBuffer到ImageBuffer中,节中会有数据被初始化,这里目前来说无法解决,所以就假设节中没有未被初始化的数据,或者拉伸后未被初始化数据不影响地址转化
#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 NULL;
}
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 NULL;
}
size_t i = fread(pFileBuffer, FileSize, 1, pFile);
if (!i)
{
printf("读取数据失败!");
free(pFileBuffer);
fclose(pFile);
return NULL;
}
IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)pFileBuffer;
IMAGE_NT_HEADERS* pNTHeader = (IMAGE_NT_HEADERS*)((char*)pFileBuffer + pDosHeader->e_lfanew);
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
{
printf("不是有效MZ标志,结束\n");
free(pFileBuffer);
pFileBuffer = NULL;
return FALSE;
}
if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
{
printf("不是有效的PE标志,打印结束\n");
free(pFileBuffer);
pFileBuffer = NULL;
return FALSE;
}
fclose(pFile);
return pFileBuffer;
}
void RVAtoFOA(const char* lpszFile)
{
char* pFileBuffer = ReadPEFile(lpszFile); //获取自定义文件内存缓冲区指针
IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)pFileBuffer; //获取自定义文件内存缓冲区DOS头指针
IMAGE_NT_HEADERS* pNTHeader = (IMAGE_NT_HEADERS*)((char*)pFileBuffer + pDosHeader->e_lfanew) ; //获取NT头指针
IMAGE_FILE_HEADER* pFileHeader = (IMAGE_FILE_HEADER*)((char*)pNTHeader + 4);//获取标准PE头指针
IMAGE_OPTIONAL_HEADER* pOptionHeader = (IMAGE_OPTIONAL_HEADER*)((char*)pFileHeader + 20);//获取可选PE头指针
IMAGE_SECTION_HEADER* pSecHeader = (IMAGE_SECTION_HEADER*)((char*)pOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取文件节表指针
IMAGE_SECTION_HEADER* pSecHeader2 = pSecHeader;
char* pSecBuffer = (char*)pDosHeader + pSecHeader->PointerToRawData; //获取第一个节的指针
char* ImageBuffer = (char*)malloc(pOptionHeader->SizeOfImage); //开辟自定义的文件的虚拟内存大小
if (ImageBuffer == NULL)
{
printf("申请虚拟内存失败\n");
exit(0);
}
memset(ImageBuffer, 0, pOptionHeader->SizeOfImage); //初始化虚拟内存
memcpy(ImageBuffer, pFileBuffer, pOptionHeader->SizeOfHeaders); //将文件内存头字段数据复制到虚拟内存
IMAGE_DOS_HEADER* iDosHeader = (IMAGE_DOS_HEADER*)ImageBuffer; //获取自定义虚拟内存内存缓冲区DOS头指针
IMAGE_NT_HEADERS* iNTHeader = (IMAGE_NT_HEADERS*)((char*)ImageBuffer + iDosHeader->e_lfanew); //获取NT头指针
IMAGE_FILE_HEADER* iFileHeader = (IMAGE_FILE_HEADER*)((char*)iNTHeader + 4);//获取标准PE头指针
IMAGE_OPTIONAL_HEADER* iOptionHeader = (IMAGE_OPTIONAL_HEADER*)((char*)iFileHeader + 20);//获取可选PE头指针
IMAGE_SECTION_HEADER* iSecHeader1 = (IMAGE_SECTION_HEADER*)((char*)iOptionHeader + pFileHeader->SizeOfOptionalHeader); // 获取虚拟内存节表指针
IMAGE_SECTION_HEADER* iSecHeader2 = iSecHeader1;
char* iSecBuffer1 = (char*)iDosHeader + iSecHeader1->VirtualAddress; //保存第一个节的指针
char* iSecBuffer2 = iSecBuffer1;
for (int i = 0; i < iFileHeader->NumberOfSections; i++)//循环将文件内存的节表数据复制到虚拟内存中
{
memcpy(iSecBuffer1, pSecBuffer, pSecHeader->SizeOfRawData); //复制文件内存节表数据
pSecHeader++; //指向硬盘下一个节表
iSecHeader1++;//指向内存下一个节表
pSecBuffer = (char*)pDosHeader + pSecHeader->PointerToRawData; //指向硬盘下一个节
iSecBuffer1 = (char*)iDosHeader + iSecHeader1->VirtualAddress; //指向内存下一个节
}//此时文件内存转化为虚拟内存
//以下开始虚拟内存节地址转换硬盘内存节地址
DWORD RVA = 0;
iSecHeader1--;// 使节表指针指向最后一个节表1q
DWORD LastSecBuffer = (DWORD)ImageBuffer + iSecHeader1->VirtualAddress + iSecHeader1->SizeOfRawData;//最后一个节的有效内容末尾
DWORD min = (DWORD)ImageBuffer + iSecHeader2->VirtualAddress;
DWORD max = (DWORD)LastSecBuffer;
printf("请选择你要转换的地址:");
scanf("%d", &RVA);
while(1)
{
if (RVA < min)
{
printf("输入地址过小,请重新输入");
scanf("%d", &RVA);
}
else if (RVA > max)
{
printf("输入地址过大,请重新输入");
scanf("%d", &RVA);
}
else
{
printf("输入地址正确,开始转换地址");
break;
}
}
DWORD offset1 = RVA - (DWORD)ImageBuffer; //获取转换地址在虚拟存中相对于DOS头的偏移量
int nNumber = 0; //用于获取转换地址所在第几个节
while(offset1)
{
if (offset1 >= (DWORD)iSecHeader2->VirtualAddress && offset1 <=(DWORD)iSecHeader2->VirtualAddress + iSecHeader2->Misc.VirtualSize)
{
break;
}
else
{
iSecHeader2++;
nNumber++;
}
}
DWORD offset2 = offset1 - iSecHeader2->VirtualAddress; //获取转换地址相对于目标节的偏移量
pSecHeader2 += nNumber;
char* pSecBufferPoint = (char*)pDosHeader + pSecHeader2->PointerToRawData + offset2;
char* iSecBufferPoint = (char*)iDosHeader + iSecHeader2->VirtualAddress + offset2;
}
int main(int argc, char* argv[])
{
const char* lpszFile = "C:\\Windows\\notepad.exe";
RVAtoFOA(lpszFile);
return 0;
}
上述代码本人经过多种测试能够正确运行,但本人并没有该作业的正确代码,所以无法保证代码绝对正确