#include "stdafx.h"
#include <malloc.h>
//函数声明
//**************************************************************************
//ReadPEFile:将文件读取到缓冲区
//参数说明:
//lpszFile 文件路径
//pFileBuffer 缓冲区指针
//返回值说明:
//读取失败返回0 否则返回实际读取的大小
//**************************************************************************
DWORD ReadPEFile(IN LPSTR lpszFile,OUT LPVOID* pFileBuffer);
//**************************************************************************
//CopyFileBufferToImageBuffer:将文件从FileBuffer复制到ImageBuffer
//参数说明:
//pFileBuffer FileBuffer指针
//pImageBuffer ImageBuffer指针
//返回值说明:
//读取失败返回0 否则返回复制的大小
//**************************************************************************
DWORD CopyFileBufferToImageBuffer(IN LPVOID pFileBuffer,OUT LPVOID* pImageBuffer);
//**************************************************************************
//CopyImageBufferToNewBuffer:将ImageBuffer中的数据复制到新的缓冲区
//参数说明:
//pImageBuffer ImageBuffer指针
//pNewBuffer NewBuffer指针
//返回值说明:
//读取失败返回0 否则返回复制的大小
//**************************************************************************
DWORD CopyImageBufferToNewBuffer(IN LPVOID pImageBuffer,OUT LPVOID* pNewBuffer);
//**************************************************************************
int main(int argc, char* argv[])
{
}
DWORD ReadPEFile(IN LPSTR lpszFile,OUT LPVOID* pFileBuffer)
{
FILE* pFile = NULL;
DWORD fileSize = 0;
LPVOID pTempFileBuffer = NULL;
pFile = fopen(lpszFile,"rb");
if (!pFile)
{
printf("打开文件失败!\r\n");
return 0;
}
//读取文件内容后,获取文件的大小
fseek(pFile,0,SEEK_END);
fileSize = ftell(pFile);
fseek(pFile,0,SEEK_SET);
pTempFileBuffer = malloc(fileSize);//动态分配空间
if (!pTempFileBuffer)
{
printf("内存分配失败!\r\n");
fclose(pFile);
return 0;
}
//根据申请到的内存空间,读取数据
size_t n = fread(pTempFileBuffer,fileSize,1,pFile);
if (!n)
{
printf("读取数据失败!\r\n");
free(pTempFileBuffer); // 释放内存空间
fclose(pFile); // 关闭文件流
return 0;
}
}
DWORD CopyFileBufferToImageBuffer(IN LPVOID pFileBuffer,OUT LPVOID* pImageBuffer)
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
LPVOID pTempImageBuffer = NULL;
if(!pFileBuffer)
{
printf("FileBuffer 获取失败!\r\n");
return 0;
}
//判断是否是有效的MZ标志
if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)
{
printf("无效的MZ标识\r\n");
return 0;
}
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
//判断是否是有效的PE标志
if (*((PDWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
{
printf("无效的PE标记\r\n");
return 0;
}
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader)+4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
//节表的首地址等于可选pe头的首地址加上可选pe头的大小(可选pe头的大小可以在标准pe头中的SizeOfOptionalHeader中查询)
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//开辟一块新的内存空间,存放imagebuffer,开辟的大小是经过拉伸之后的大小,在可选pe头中的sizeofimage中记录拉伸后的大小
LPVOID pTempImageBuffer = malloc(pOptionHeader->SizeOfImage);
if (!pTempImageBuffer)
{
printf("在堆中申请一块内存空间失败\r\n");
return 0;
}
//初始化空间
memset(pTempImageBuffer,0,pOptionHeader->SizeOfImage);
//将FileBuffer复制到ImageBuffer
//因为(DOC+NT+节表没有进行拉伸,所以现将他们之间copy到imagebuffer中,copy的大小为pOptionHeader->SizeOfHeaders)
memcpy(pTempImageBuffer,pDosHeader,pOptionHeader->SizeOfHeaders);
//因为有多个节,所以要循环copy
//定义一个临时节表的指针
PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
//这个循环的作用就是,每次把节在filebuffer的地址(filebuffer+PointerToRawData)
//复制到imagebuffer中的imagebuffer+VirtualAddress中去,
//每次复制的大小是pTempSectionHeader->SizeOfRawData
for (int i=0;i<pPEHeader->NumberOfSections;i++,pTempSectionHeader++)
{
memcpy((void*)((DWORD)pTempImageBuffer + pTempSectionHeader->VirtualAddress),
(void*)((DWORD)pFileBuffer + pTempSectionHeader->PointerToRawData),pTempSectionHeader->SizeOfRawData);
}
//返回数据
*pImageBuffer = pTempImageBuffer;
//将复制好后节的首地址保存到指针pImageBuffer中
pTempImageBuffer = NULL;
//初始化清空临时使用的pTempImageBuffer
return pOptionHeader->SizeOfImage;
}
DWORD CopyImageBufferToNewBuffer(IN LPVOID pImageBuffer,OUT LPVOID* pNewBuffer)
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
LPVOID pTempNewBuffer = NULL;
DWORD sizeOfFile = 0;
DWORD numberOfSection = 0;
if (pImageBuffer == NULL)
{
printf("缓冲区指针无效\r\n");
}
//判断是否是有效的MZ标志
if (*((PWORD)pImageBuffer) != IMAGE_DOS_SIGNATURE)
{
printf("不是有效的MZ头\r\n");
return 0;
}
pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
//判断是否是有效的PE标志
if (*((PDWORD)((DWORD)pImageBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
{
printf("不是有效的PE标志\r\n");
return 0;
}
//NT头地址
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer + pDosHeader->e_lfanew);
//标准PE文件头
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
//可选PE头
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
//第一个节表地址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//计算文件大小
sizeOfFile = pOptionHeader->SizeOfHeaders;
for(DWORD i = 0;i<pPEHeader->NumberOfSections;i++)
{
sizeOfFile += pSectionHeader[i].SizeOfRawData; // pSectionHeader[i]另一种加法
}
//根据SizeOfImage申请新的空间
pTempNewBuffer = malloc(sizeOfFile);
if (!pTempNewBuffer)
{
printf("申请内存空间失败\r\n");
return 0;
}
//初始化新的缓冲区
memset(pTempNewBuffer,0,sizeOfFile);
//根据SizeOfHeaders 先copy头
memcpy(pTempNewBuffer,pDosHeader,pOptionHeader->SizeOfHeaders);
for (int j=0;j<pPEHeader->NumberOfSections;j++,pTempSectionHeader++)
{
memcpy((PDWORD)((DWORD)pTempNewBuffer+pTempSectionHeader->PointerToRawData),
(PDWORD)((DWORD)pImageBuffer+pTempSectionHeader->VirtualAddress),
pTempSectionHeader->SizeOfRawData);
}
*pNewBuffer = pTempNewBuffer;
pTempNewBuffer = NULL;
return sizeOfFile;
}
// test_lc_01.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "string.h"
#include <malloc.h>
#include <windows.h>
// exe->filebuffer 返回值为计算所得文件大小
int ReadPEFile(char* file_path,PVOID* pFileBuffer)
{
FILE* pfile = NULL; // 文件指针
DWORD file_size = 0;
LPVOID pTempFilebuffer = NULL;
// 打开文件
pfile = fopen(file_path,"rb"); // 如果有新的指针,就要进行判断
if(!pfile)
{
printf("打开exe文件失败!\n");//如果分配失败就要关闭文件、释放动态内存、指针指向NULL
return 0;
}
// 读取文件大小
fseek(pfile,0,SEEK_END);
file_size = ftell(pfile);
fseek(pfile,0,SEEK_SET);
// 分配空间
pTempFilebuffer = malloc(file_size); // 如果有新的指针,就要进行判断
if(!pTempFilebuffer)
{
printf("分配空间失败!\n");//如果分配失败就要关闭文件、释放动态内存、指针指向NULL
fclose(pfile);
return 0 ;
}
// 将数据读取到内存中
size_t n = fread(pTempFilebuffer,file_size,1,pfile);
if(!n)
{
printf("数据读取到内存中失败!\n"); //如果分配失败就要关闭文件、释放动态内存、指针指向NULL
fclose(pfile);
free(pTempFilebuffer);
return 0 ;
}
// 关闭文件(已经读取到内存了)
*pFileBuffer = pTempFilebuffer;
pTempFilebuffer = NULL;
fclose(pfile);
return file_size;
}
// filebuffer -> imagebuffer
DWORD CopyFileBufferToImageBuffer(PVOID pFileBuffer,PVOID* pImageBuffer)
{
// 初始化PE头部结构体
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
// 初始化IMAGE_BUFFER指针(temparay)
LPVOID pTempImagebuffer = NULL;
if(!pFileBuffer)
{
printf("(2pimagebuffer阶段)读取到内存的pfilebuffer无效!\n");
return 0 ;
}
// 判断是否是可执行文件
if(*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE) // IMAGE_DOS_SIGNATURE是4字节,将pFileBuffer强制类型转换为4字节指针类型(PWORD)
{
printf("(2pimagebuffer阶段)不含MZ标志,不是exe文件!\n");
return 0;
}
//强制结构体类型转换pDosHeader
pDosHeader = PIMAGE_DOS_HEADER(pFileBuffer);
//判断是否含有PE标志
if(*((PDWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) // 注意指针的加法是:去掉一个*后的类型相加。必须转换为DWORD类型再加减。
{ //相加后的和 强制类型转换为4字节指针类型(PWORD) IMAGE_NT_SIGNATURE 4BYTES
printf("(2pimagebuffer阶段)不是有效的PE标志!\n");
return 0;
}
// 强制结构体类型转换
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader+pPEHeader->SizeOfOptionalHeader);
// 分配动态内存
pTempImagebuffer = malloc(pOptionHeader->SizeOfImage);
if(!pTempImagebuffer)
{
printf("分配动态内存失败!\n");
free(pTempImagebuffer);
return 0;
}
// 初始化动态内存
memset(pTempImagebuffer,0,pOptionHeader->SizeOfImage);
// 拷贝头部
memcpy(pTempImagebuffer,pDosHeader,pOptionHeader->SizeOfHeaders);
// 循环拷贝节表
PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
for(DWORD i = 0;i<pPEHeader->NumberOfSections;i++,pTempSectionHeader++)
{
memcpy((void*)((DWORD)pTempImagebuffer+pTempSectionHeader->VirtualAddress),(void*)((DWORD)pFileBuffer+pTempSectionHeader->PointerToRawData),pTempSectionHeader->SizeOfRawData);
}
// 返回数据
*pImageBuffer = pTempImagebuffer;
pTempImagebuffer = NULL;
return pOptionHeader->SizeOfImage;
}
//imagebuffer->newbuffer
DWORD CopyImageBufferToNewBuffer(PVOID pImageBuffer,PVOID* pNewBuffer)
{
// 初始化PE头部结构体
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
// 初始化NEW_BUFFER指针(temparay)
LPVOID pTempNewbuffer = NULL;
// 判断pImageBuffer是否有效
if(!pImageBuffer)
{
printf("(2pnewbuffer阶段)读取到内存的pimagebuffer无效!\n");
return 0;
}
//判断是不是exe文件
if(*((PWORD)pImageBuffer) != IMAGE_DOS_SIGNATURE)
{
printf("(2pnewbuffer阶段)不含MZ标志,不是exe文件!\n");
return 0;
}
// 强制结构体类型转换
pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
if(*((PDWORD)((DWORD)pImageBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
{
printf("(2pnewbuffer阶段)不是有效的PE标志!\n");
return 0;
}
// 强制结构体类型转换
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+4); // 这里必须强制类型转换
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader+pPEHeader->SizeOfOptionalHeader);
//获取new_buffer的大小
int new_buffer_size = pOptionHeader->SizeOfHeaders;
for(DWORD i = 0;i<pPEHeader->NumberOfSections;i++)
{
new_buffer_size += pSectionHeader[i].SizeOfRawData; // pSectionHeader[i]另一种加法
}
// 分配内存(newbuffer)
pTempNewbuffer = malloc(new_buffer_size);
if(!pTempNewbuffer)
{
printf("(2pnewbuffer阶段)分配Newbuffer失败!\n");
return 0;
}
memset(pTempNewbuffer,0,new_buffer_size);
// 拷贝头部
memcpy(pTempNewbuffer,pDosHeader,pOptionHeader->SizeOfHeaders);
// 循环拷贝节区
PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
for(DWORD j = 0;j<pPEHeader->NumberOfSections;j++,pTempSectionHeader++)
{ //PointerToRawData节区在文件中的偏移,VirtualAddress节区在内存中的偏移地址,SizeOfRawData节在文件中对齐后的尺寸
memcpy((PDWORD)((DWORD)pTempNewbuffer+pTempSectionHeader->PointerToRawData),(PDWORD)((DWORD)pImageBuffer+pTempSectionHeader->VirtualAddress),pTempSectionHeader->SizeOfRawData);
}
//返回数据
*pNewBuffer = pTempNewbuffer; //暂存的数据传给参数后释放
pTempNewbuffer = NULL;
return new_buffer_size; // 返回计算得到的分配内存的大小
}
//newbuffer->存盘
int newbuffer_write2_exe(PVOID NewFileBuffer,DWORD FileSize, char* FilePath)
{
FILE* fp1 = fopen(FilePath,"wb");
if(fp1 != NULL)
{
fwrite(NewFileBuffer,FileSize,1,fp1);
}
fclose(fp1);
return 1;
}
// RVA转换成FOA
DWORD convertRVAtoFOA(DWORD pRVA,PVOID pFileBuffer,PVOID pImageBuffer)
{
// 初始化PE头部结构体
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
// 判断pImageBuffer是否有效
if(!pImageBuffer)
{
printf("(RVA转换成FOA阶段)读取到内存的pimagebuffer无效!\n");
return 0;
}
//判断是不是exe文件
if(*((PWORD)pImageBuffer) != IMAGE_DOS_SIGNATURE)
{
printf("(RVA转换成FOA阶段)不含MZ标志,不是exe文件!\n");
return 0;
}
// 强制结构体类型转换
pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
if(*((PDWORD)((DWORD)pImageBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
{
printf("(RVA转换成FOA阶段)不是有效的PE标志!\n");
return 0;
}
// 强制结构体类型转换
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+4); // 这里必须强制类型转换
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader+pPEHeader->SizeOfOptionalHeader);
int image_panyi = pRVA; // pRVA是在内存中的偏移偏移
printf("image_panyi:%#x\n",image_panyi);
// 循环查找在那个imagebuffer节中
PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
for(DWORD i = 0;i<pPEHeader->NumberOfSections;i++,pTempSectionHeader++)
{ //判断 : Misc.VirtualSize+ VirtualAddress 内存偏移+节数据没对齐的大小>image_panyi>内存偏移 VirtualAddress (即是在文件的哪个节中)
if((image_panyi>=pTempSectionHeader->VirtualAddress) && (image_panyi<pTempSectionHeader->VirtualAddress+pTempSectionHeader->Misc.VirtualSize))
{
return image_panyi-pTempSectionHeader->VirtualAddress+pTempSectionHeader->PointerToRawData;
}
}
return 0;
}
// FOA转换成RVA
DWORD convertFOAtoRVA(DWORD pFOA,PVOID pFileBuffer,PVOID pImageBuffer)
{
// 初始化PE头部结构体
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
// 判断pImageBuffer是否有效
if(!pFileBuffer)
{
printf("(FOA转换成RVA阶段)读取到内存的pimagebuffer无效!\n");
return 0;
}
//判断是不是exe文件
if(*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)
{
printf("(FOA转换成RVA阶段)不含MZ标志,不是exe文件!\n");
return 0;
}
// 强制结构体类型转换
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
if(*((PDWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
{
printf("(FOA转换成RVA阶段)不是有效的PE标志!\n");
return 0;
}
// 强制结构体类型转换
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+4); // 这里必须强制类型转换
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader+pPEHeader->SizeOfOptionalHeader);
int file_panyi = pFOA; // pRVA是文件中的偏移偏移
printf("file_panyi:%#x\n",file_panyi);
// 循环查找在那个imagebuffer节中
PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
for(DWORD i = 0;i<pPEHeader->NumberOfSections;i++,pTempSectionHeader++)
{ //判断 : 文件对齐+文件偏移>file_panyi>文件偏移 (即是在文件的哪个节中)
if((file_panyi>=pTempSectionHeader->PointerToRawData) && (file_panyi<pTempSectionHeader->PointerToRawData+pTempSectionHeader->SizeOfRawData))
{
return file_panyi-pTempSectionHeader->PointerToRawData+pTempSectionHeader->VirtualAddress;
}
}
return 0;
printf("地址转换失败!\n");
}
void operate_pe()
{ // 初始化操作
PVOID pFileBuffer = NULL;
PVOID pImageBuffer = NULL;
PVOID pNewFileBuffer = NULL;
DWORD NewFileBufferSize = 0;
//char file_path[] = "D:\\Lib\\IPMSG2007.exe";
char file_path[] = "C:\\Windows\\System32\\notepad.exe";
char write_file_path[] = "D:\\Lib\\cp_notepad.exe";
// exe->filebuffer
int ret1 = ReadPEFile(file_path,&pFileBuffer); // &pFileBuffer(void**类型) 传递地址对其值可以进行修改
printf("exe->filebuffer 返回值为计算所得文件大小:%#x\n",ret1);
// filebuffer -> imagebuffer
int ret2 = CopyFileBufferToImageBuffer(pFileBuffer,&pImageBuffer);
printf("filebuffer -> imagebuffer返回值为计算所得文件大小:%#x\n",ret2);
//imagebuffer->newbuffer
int FileSize = CopyImageBufferToNewBuffer(pImageBuffer,&pNewFileBuffer);
printf("imagebuffer->newbuffer返回值为计算所得文件大小:%#x\n",FileSize);
//newbuffer->存盘
//int ret4 = newbuffer_write2_exe(pNewFileBuffer,FileSize, write_file_path);
//printf("newbuffer->存盘返回值为:%d\n",ret4);
int pRVA = 0x00021178;
int pFOA = 0x00020450;
int ret_FOA = convertRVAtoFOA(pRVA,pFileBuffer,pImageBuffer);
printf("内存偏移%#x 转换为文件中的偏移:%#x\n",pRVA,ret_FOA);
int ret_RVA = convertFOAtoRVA(pFOA,pFileBuffer,pImageBuffer);
printf("文件偏移%#x 转换为内存中的偏移:%#x\n",pFOA,ret_RVA);
}
int main()
{
operate_pe();
getchar();
return 0;
}