直接上源码:
#include<stdio.h>
#include<Windows.h>
int main()
{
LPCSTR lpFileName="D:\\PyCharm Community Edition 2019.2.4\\bin\\IdeaWin32.dll";
HANDLE hFile;
hFile=CreateFileA(lpFileName,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,0,0);
if(hFile==INVALID_HANDLE_VALUE)
{
MessageBoxA(0,"文件打开失败",0,MB_OK);
return 0;
}
DWORD FileSize=GetFileSize(hFile,0);
BYTE* lpBuffer=new BYTE[FileSize];
ReadFile(hFile,lpBuffer,FileSize,0,0);
/*定义DOS头*/
PIMAGE_DOS_HEADER pDosHeader=(PIMAGE_DOS_HEADER)lpBuffer;
/*定义NT头*/
PIMAGE_NT_HEADERS pNtHeader=(PIMAGE_NT_HEADERS)(pDosHeader->e_lfanew+lpBuffer);
/*定义可选头*/
PIMAGE_OPTIONAL_HEADER pOptionalHeader=&(pNtHeader->OptionalHeader);
/*定义文件头*/
PIMAGE_FILE_HEADER pFileHeader=&(pNtHeader->FileHeader);
/*获取节表的数量*/
DWORD SectionNumber=pFileHeader->NumberOfSections;
/*获取节的位置,方法,从NT头加上整个NT头的大小*/
PIMAGE_SECTION_HEADER SectionHeader=(PIMAGE_SECTION_HEADER)(pNtHeader+0x18+pFileHeader->SizeOfOptionalHeader);
/*定位到数据目录表(通过这种方式获取到的是数据目录表的第一个元素也就是导出表),但是通过IMAGE_EXPORT_DIRECTORY* Export=(IMAGE_EXPORT_DIRECTORY*)((BYTE *)pNtHeader+0x78)这种方式得到的是导出表的结构*/
PIMAGE_DATA_DIRECTORY pDirectory=(PIMAGE_DATA_DIRECTORY)(pOptionalHeader->DataDirectory);
/*获取导出表的虚拟地址*/
DWORD ExportRVA=pDirectory->VirtualAddress;//注意这个地址是一个RVA,是数据块的起始RVA
/*开始进行转换,RVA->RAW 过程中用到的两个参数:第一个(导出表虚拟地址:ExportRVA)第二个(基址:pDosHeader)*/
/*转换的思路:1、获取节区表的开始的虚拟地址
2、获取节区表的结束地址
3、判断要转换的RVA所在节区
4、利用公式进行计算转换 RAW=RVA-VA+PointToRAW*/
for(int i=0;i<SectionNumber;i++)
{
/*计算当前节区的开始地址*/
DWORD SectionBegin=SectionHeader[i].VirtualAddress;//区块的RVA地址
/*计算当前节区的结束地址*/
DWORD SectionEnd=SectionHeader[i].VirtualAddress+SectionHeader[i].SizeOfRawData;//(这里的大小在实际使用中是区块的起始RVA+区块的大小,但是在实际使用中要考虑到文件的对齐)
/*判断要求的RVA是否在当前的节区内*/
if(ExportRVA>SectionBegin&&ExportRVA<SectionEnd)
{
/*如果在当前的节区内*/
DWORD ExportRAW=ExportRVA-SectionBegin+SectionHeader[i].PointerToRawData;
printf("转换结果:%x",ExportRAW);
}
}
}
关于地址转换的题外话:
用CreateFile获取到文件的句柄,之后用ReadFile将文件读取到缓冲区里边,这时候缓冲区里边存储的文件是源文件在磁盘上的格式,并没有按照相应的对齐粒度展开,在遍历导出表的时候,获取的是导出表的RVA和VA(这两个地址都是文件在内存中的地址,也就是按照内存中的对齐粒度对齐之后的地址),如果想要根据获取到的导出表的RVA和VA获取导出表的函数,要将地址进行转换,转换为RAW,也就是文件偏移,之后再到缓冲区里边根据地址进行遍历(因为用ReadFile将文件读取到内存中的时候,文件保持再磁盘上的对齐状态)