深入解析PE文件结构之导出表获取
来源:互联网 发布:网络情缘一线牵什么梗 编辑:程序博客网 时间:2024/05/22 05:22
http://blog.csdn.net/yiyefangzhou24/article/details/7268319
最近有时间坐下来仔细研究一下PE文件结构了,以前遇到这种问题时总是拆东墙补西墙。学的不够透彻。几天来一番研究之后,和大家分享一下。
PE的文件结构从DOS头开始,其主要作用就两个一个是若是在DOS环境下输出一句话。另一个作用就是找到PE文件头的位置,这是我们要关心的,本文只注重所要关心的问题——导出表。和一些你再众多网上资料上很难找到的细节,至于整体的PE结构网上的资料中说的很详细。
首先,PE文件的加载原理这里就不多说了,整个复杂的过程都是由PE装载器完成的,过程不是一两句话能够说清楚的,这里我们只需要注意一个细节就可以了。PE装载器将PE文件已文件映射的方式从磁盘映射到内存,在映射时PE文件被加载到内存的开始位置叫做基址,我们用lPImageBase来表示。我们首先需要知道我们如何将一个PE文件加载到内存,并获得基址。
- HANDLE hfile = CreateFile("c:\\example.exe",
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
- if(hfile==INVALID_HANDLE_VALUE)
- {
- printf("Create File Failed.\n");
- return ;
- }
- HANDLE hFileMapping=CreateFileMapping(hfile,NULL,PAGE_READONLY,0,0,NULL);
- if (hFileMapping==NULL||hFileMapping==INVALID_HANDLE_VALUE)
- {
- printf("Could not create file mapping object (%d).\n", GetLastError());
- return ;
- }
- //内存映射文件的基址
- LPBYTE lpBaseAddress=(LPBYTE)MapViewOfFile(hFileMapping,FILE_MAP_READ,0,0,0);
- if (lpBaseAddress == NULL)
- {
- printf("Could not map view of file (%d).\n", GetLastError());
- return ;
- }‘
这里我们需要关心的是一个叫IMAGE_OPTIONAL_HEADER的结构,中文名称叫“可选镜像头部”,说是可选的,其实非常重要。这个结构是在IMAGE_NT_HERDERS结构中,并且是第二个成员变量。
- typedef struct IMAGE_NT_HERDERS
- {
- Signature dd?;
- FileHeader IMAGE_FILE_HEADER<>;
- OptionalHeader IMAGE_OPTIONAL_HEADER;//这里是下面结构体IMAGE_OPTIONAL_HEADER
- }
IMAGE_NT_HERDERS地址获取:
PIMAGE_DOS_HEADER pDosHeader=(PIMAGE_DOS_HEADER)lpBaseAddress;
PIMAGE_NT_HEADERS pNtHeaders=(PIMAGE_NT_HEADERS)(lpBaseAddress + pDosHeader->e_lfanew);
- /可选镜像头部
- typedef struct _IMAGE_OPTIONAL_HEADER
- {
- WORD Magic;
- BYTE MajorLinkerVersion;
- BYTE MinorLinkerVersion;
- DWORD SizeOfCode;
- DWORD SizeOfInitializedData;
- DWORD SizeOfUninitializedData;
- DWORD AddressOfEntryPoint;
- DWORD BaseOfCode;
- DWORD BaseOfData;
- DWORD ImageBase;
- DWORD SectionAlignment;
- DWORD FileAlignment;
- WORD MajorOperatingSystemVersion;
- WORD MinorOperatingSystemVersion;
- WORD MajorImageVersion;
- WORD MinorImageVersion;
- WORD MajorSubsystemVersion;
- WORD MinorSubsystemVersion;
- DWORD Win32VersionValue;
- DWORD SizeOfImage;
- DWORD SizeOfHeaders;
- DWORD CheckSum;
- WORD Subsystem;
- WORD DllCharacteristics;
- DWORD SizeOfStackReserve;
- DWORD SizeOfStackCommit;
- DWORD SizeOfHeapReserve;
- DWORD SizeOfHeapCommit;
- DWORD LoaderFlags;
- DWORD NumberOfRvaAndSizes; //这个成员的大小是下面数组的数目
- IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; //这里是下面的IMAGE_DATA_DIRECTORY
- } IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;
IMAGE_OPTIONAL_HEADER地址获取:
pNtHeaders->OptionalHeader;
然后我们需要注意IMAGE_DATA_DIRECTORY这个数组,这个数组一般有15个元素,每个元素都记录着这个PE文件的重要数据结构。我们来看看这个数组的每个元素的内容。
是不是很让人吃惊?有了这个数组,获取一些PE信息就非常简单了,一般我们或者是计算机病毒所关心的多数是导出表(EAT),导入表(IAT),表,分别是这个数组的第0个元素和第12个元素。这个方法和网上的一些EAT IAT地址获取方法有所不同。很是方便。
而pNTHeader->OptionalHeader.DataDirectory[0]又是一个结构体,这个结构体的定义如下:
- typedef struct _IMAGE_DATA_DIRECTORY {
- DWORD VirtualAddress;//指向导出表的RVA地址(相对地址)
- DWORD Size;
- } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
说以这里的EAT表的地址我们就应该这样获取:
pNTHeader->OptionalHeader.DataDirectory[0].VirtualAddress
到这里我们已经找到了PE文件的导出表相对地址了(RVA),现在的问题是,我们如何将导出表中存放的导出函数信息给弄出来。
我们先看看导出表是个什么东西:
- //导入地址表
- typedef struct _IMAGE_EXPORT_DIRECTORY
- {
- DWORD Characteristics;
- DWORD TimeDateStamp;
- WORD MajorVersion;
- WORD MinorVersion;
- DWORD Name;
- DWORD Base;
- DWORD NumberOfFunctions;
- DWORD NumberOfNames;
- DWORD AddressOfFunctions; // 函数RVA
- DWORD AddressOfNames; //函数名RVA
- DWORD AddressOfNameOrdinals; // 函数索引号RVA
- }IMAGE_EXPORT_DIRECTORY,*PIMAGE_EXPORT_DIRECTORY;
不难看出AddressOfNames;就是存放导出函数的一个数组RVA了。但是这里有一个小问题。导致我在这个问题上花了半天时间。AddressOfNames;中存放的确实是一个RVA(相对地址),但是不是直接指向函数数组的,而是指向了一个存放地址的数组也就是一个DWORD数组,这个数组的每一个元素存放一个导出函数的RVA,这样说来有点绕,我们已一张图说明:
这就给我这种菜鸟造成了一定的麻烦,诶基础语法还是很重要啊。。。
所以地址需要这样获取(这里已经加上了基址lPImageBase,后面说明)
DWORD * k=(DWORD *)(pExport->AddressOfNames+lpBaseAddress); //RVAÊý×éÊ×µØÖ·
char * functionName=(char *)(*k+lpBaseAddress);
好了地址我们都有了,那么程序也就不难了。下面贴出核心代码,我觉得这种获取地址的方法比常见方法的获取方法简单不少(菜鸟本人是这样遐想的):
- #include "windows.h"
- #include "stdio.h"
- void Doshow(char * place)
- {
- int i=0;
- HANDLE hfile = CreateFile(place,
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
- if(hfile==INVALID_HANDLE_VALUE)
- {
- printf("Create File Failed.\n");
- return ;
- }
- HANDLE hFileMapping=CreateFileMapping(hfile,NULL,PAGE_READONLY,0,0,NULL);
- if (hFileMapping==NULL||hFileMapping==INVALID_HANDLE_VALUE)
- {
- printf("Could not create file mapping object (%d).\n", GetLastError());
- return ;
- }
- //ÄÚ´æÓ³ÉäÎļþµÄ»ùÖ·
- LPBYTE lpBaseAddress=(LPBYTE)MapViewOfFile(hFileMapping,FILE_MAP_READ,0,0,0);
- if (lpBaseAddress == NULL)
- {
- printf("Could not map view of file (%d).\n", GetLastError());
- return ;
- }
- PIMAGE_DOS_HEADER pDosHeader=(PIMAGE_DOS_HEADER)lpBaseAddress;
- PIMAGE_NT_HEADERS pNtHeaders=(PIMAGE_NT_HEADERS)(lpBaseAddress + pDosHeader->e_lfanew);
- PIMAGE_EXPORT_DIRECTORY pExport=(PIMAGE_EXPORT_DIRECTORY)(pNtHeaders->OptionalHeader.DataDirectory[0].VirtualAddress+lpBaseAddress);
- int Num=pExport->NumberOfNames;
- printf("DLLÊÇ%s\n",pExport->Name+lpBaseAddress);
- printf("¹²ÓÐ%d¸öº¯Êý\n",pExport->NumberOfNames);
- printf("º¯ÊýÐòºÅ º¯ÊýÃû\n");
- for (i;i<Num;i++)
- {
- DWORD * k=(DWORD *)(pExport->AddressOfNames+lpBaseAddress+4*i); //RVAÊý×éÊ×µØÖ·
- char * functionName=(char *)(*k+lpBaseAddress);
- printf("%d %s\n",i+1,functionName);
- }
- UnmapViewOfFile(lpBaseAddress);
- CloseHandle(hFileMapping);
- CloseHandle(hfile);
- }
- int main()
- {
- char p[20];
- printf("ÇëÊäÈëÎļþλÖãº\n");
- scanf("%s",p);
- Doshow(p);
- return 1;
- }
另外我还写了界面,给大家展示一下吧。
下面的任务是尝试更加困难的IAT表的改写,如插入DLL,实现PE病毒的基本功能等等,如果可能,一定会给大家分享我的“成果”。
最后菜鸟言论,仅供娱乐。
- 深入解析PE文件结构之导出表获取
- 深入解析PE文件结构之导出表获取
- PE总结9 --PE文件结构之 解析导出表
- 解析PE结构之-----导出表
- 解析PE结构之-----导出表
- PE总结11--PE文件结构之 解析导入表
- PE总结13 --PE文件结构之 解析资源表
- PE总结15--PE文件结构之 解析资源表
- PE文件结构详解--PE导出表
- 【C++源码】PE文件结构中导出表的解析
- PE结构导出表详细解析
- PE结构导出表详细解析
- PE总结8---PE文件结构之导出表 (IMAGE_EXPORT_DIRECTORY)
- PE文件结构详解(三)PE导出表
- PE文件结构详解(三)PE导出表
- PE文件结构详解(三)PE导出表
- PE文件结构详解(三)PE导出表
- PE文件结构详解(三)PE导出表
- JS键盘事件 兼容浏览器
- jQuery读取XML文件
- plsql中循环的应用
- 多媒体文件格式分析
- 解析PE结构之-----导出表
- 深入解析PE文件结构之导出表获取
- Apache2+Tomcat7配置很详细(解决服务不能启动问题Tomcat7解压版)
- 匈牙利命名法
- 有关接口编程——写给初学ArcEngine的人
- 持续集成之部署到tomcat上(七)
- plsql中游标的应用
- Android-系统开机动画
- [Java]读取文件方法大全
- plsql中的procedure和function编程