pe文件解析:读取pe信息获取文件资源

查看过关于pe文件分析的文章:

(1)http://www.vckbase.com/document/viewdoc/?id=1334

(2)http://www.vckbase.com/document/viewdoc/?id=1335

(3)http://www.cppblog.com/aurain/archive/2009/06/29/88771.html

(4)http://www.fishc.com/a/shipin/jiemixilie/_PExilie_/2011/0429/997.html

(5)http://hi.baidu.com/6121017/blog/item/07d4ebc29d938d31e4dd3b1e.html

还有罗云彬的win32汇编后面附带的pe文件分析。

PE文件被称为可移植的执行体是Portable Execute的全称,常见的EXE、DLL、OCX、SYS、COM都是PE文件,PE文件是微软Windows操作系统上的程序文件(可能是间接被执行,如DLL)

本文实现一个读取自身自定义资源(TXT)并打印其中内容的小程序。

关于自定义资源可查看http://blog.csdn.net/qq752923276/article/details/6363810

首先看看pe文件的结构:

实际编码使用如下结构:

IMAGE_DOS_HEADER //DOS-MZ头部

IMAGE_NT_HEADERS

{ DWORD Signature;//对应上图PE文件标志 总是为PE00

    IMAGE_FILE_HEADER FileHeader;//PE文件头
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;//PE文件可选头(实际非可选)

}

//后面是一系列段头(win32汇编上表述是节表)

IMAGE_SECTION_HEADER //对应.text头部等

最后是各个实际段

上述结构都在winnt.h中有定义,具体的说明可以查看msdn或者上面附带的链接(1),都有详细说明。

再说明下RVA,

RVA是相对虚拟地址(Relative Virtual Address)的缩写,顾名思义,它是一个“相对”地址,也可以说是“偏移量”,PE文件的各种数据结构中涉及到地址的字段大部分都是以RVA表示的,准确地说,RVA就是当PE文件被装载到内存中后,某个数据的位置相对于文件头的偏移量。

由于pe的磁盘映像和内存映像不同(可查看上面附带链接(4)),而pe的地址信息大多是RVA,所以如果要读取文件中资源需要做RVA到文件实际偏移长度的转换。

关于RVA到FileAddr的转换详解:http://blog.csdn.net/jccz_zys/article/details/1526971

下面是代码:

exe附带自定义资源部分请查看上述内容说明。

// pe2.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>
#include <assert.h>

#define TEST(n) (n>>31)&0x1

int main(int argc, char* argv[])
{
	char szFile[MAX_PATH]="";
	GetModuleFileName(NULL,szFile,MAX_PATH);//获取本身路径
	FILE *fp=fopen(szFile,"rb");
	assert(fp!=NULL);
	
	IMAGE_DOS_HEADER dosHeader;
	assert(fread(&dosHeader,sizeof dosHeader,1,fp)!=-1);

	fseek(fp,dosHeader.e_lfanew,SEEK_SET);
	IMAGE_NT_HEADERS ntHeaders;
	assert(fread(&ntHeaders,sizeof ntHeaders,1,fp)!=-1);

	DWORD RsAddr=ntHeaders.OptionalHeader.DataDirectory[2].VirtualAddress;
	DWORD RsRealAddr=0;
	
	//获取资源在文件中实际偏移量  
	IMAGE_SECTION_HEADER sectionHeader;
	DWORD RsPointToRawData=0;
	for (int i=0;i<ntHeaders.FileHeader.NumberOfSections;++i)
	{
		assert(fread(§ionHeader,sizeof sectionHeader,1,fp)!=-1);
		char szName[9];
		memset(szName,0,9);
		memcpy(szName,sectionHeader.Name,8);
		puts(szName);
		if (RsAddr>=sectionHeader.VirtualAddress&&
			RsAddr<sectionHeader.VirtualAddress+sectionHeader.SizeOfRawData)
		{
			RsRealAddr=RsAddr-sectionHeader.VirtualAddress+sectionHeader.PointerToRawData;//RVA到文件实际偏移的转换
			RsPointToRawData=sectionHeader.PointerToRawData;
			printf("res fileaddr:0x%x\n",RsRealAddr);
		}
	}
	//

	fseek(fp,RsRealAddr,SEEK_SET);
loop:
	IMAGE_RESOURCE_DIRECTORY resDir;
	IMAGE_RESOURCE_DIRECTORY_ENTRY resDirEntry;
	assert(fread(&resDir,sizeof resDir,1,fp)!=-1);
	assert(fread(&resDirEntry,sizeof resDirEntry,1,fp)!=-1);
	if (TEST(resDirEntry.OffsetToData))
	{
		DWORD nextPoint=resDirEntry.OffsetToData&0x7fff;
		fseek(fp,RsRealAddr+nextPoint,SEEK_SET);
		goto loop; 
	}
	else
	{
		fseek(fp,RsRealAddr+resDirEntry.OffsetToData,SEEK_SET);
		IMAGE_RESOURCE_DATA_ENTRY resDataEntry;
		assert(fread(&resDataEntry,sizeof resDataEntry,1,fp)!=-1);
		
		DWORD ResSize=resDataEntry.Size;
		DWORD ResOffset=resDataEntry.OffsetToData-RsAddr+RsPointToRawData;//RVA到文件实际偏移的转换

		printf("res size:%d\n",ResSize);
		printf("res offset:0x%x\n",ResOffset);

		puts("get res:");
		fseek(fp,ResOffset,SEEK_SET);
		char *pRes=new char[ResSize+1];
		assert(pRes!=NULL);
		memset(pRes,0,ResSize+1);
		assert(pRes!=NULL);
		assert(fread(pRes,ResSize,1,fp)!=-1);
		puts(pRes);
		delete [] pRes;

	}
	fclose(fp);
	getchar();
	return 0;
}
完整代码: http://download.csdn.net/detail/qq752923276/4103847

你可能感兴趣的:(数据结构,汇编,image,header,null,FP)