optional头的最后一个域是一个数组列表,该数组大小为16,元素为IMAGE_DATA_DIRECTORY,至于以后平台是否会增加,说不清楚,但是FileHeader中明确给出了该数组的大小。
该数组表的第一个元素就是指向输出表的。而输出表的定义如下:
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions; // RVA from base of image
DWORD AddressOfNames; // RVA from base of image
DWORD AddressOfNameOrdinals; // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
前面就不管了,直接看重要的,该结构体说明了 总共输出项目总数[NumberOfFunctions],带名称的为NumberOfNames,由于某些个别变态函数会以序号的形式输出,所以这2个值可能不等。 接下来是指向函数入口的指针,指向名称的指针,指向序号的指针。
这里有3个对应关系,看雪书上讲得有点晕。 描述一个PE文件的输出函数需要3个变量
函数入口,函数名称,函数序号。
如果你想偷懒一次性把这个表取完,那你程序多半要崩溃,至少目前从网上找到的代码都崩溃了,手上几个现成的Delphi代码几乎读不懂,很久没碰Delphi,生疏了许多。。
numberoffunctions 告诉了我们总共有多少个函数,这时候我们可以一次性获取所有函数的入口RVA,接着numberofnames 告诉了我们有多少个被命名输出,
第一次获取的函数名,一定是第一个序号所指定的函数名称。
比如
proc_rva 表
80505450 805a5614 805f1adc 805f5312 805f1b0e
nam_etable
abcdef hhhhhhh zxzzzzzzzz
ord_table
0100 0200 0400
这时候的对应关系如下:
abcdef===>0001===>805a5614
hhhhh===>0002===>805f1adc
zxzzzzz===>0004===>805f1b0e
一句话,先读取函数入口,再读取名称,而名称的填充,则根据ordianl中的索引来查找。
红书这页讲的很晕人,尤其是winhex开10进制,举例时候的数据太特殊,过于简单。
这个问题确实比较气愤,记得大概在今年6月的时候,准备写一个PE explorer,兴致勃勃的完成了框架的
描述以及初步代码,当测试核心模块的时候,不得不因为对输出表的研究不够深入而彻底放弃,今通宵达旦,总算圆满解决掉输出表的问题。
PE文件中比较复杂的 一个是引入表,一个输出表,最复杂的则属资源描述表。
大概代码如下:
//前面有错直接返回
if(m_lasterr)return m_lasterr;
//枚举所有节,输出表的RawOffet = 输出表RVA - deltaK
//deltaK = sectionRVA - SectionRaw
IMAGE_DATA_DIRECTORY ExportDir = opheader.DataDirectory[0];
DWORD RawExportTableOffset;
BOOLEAN bExportTableFound = false;
DWORD DeltaK = 0;
DWORD i;
if(ExportDir.Size ==0 && ExportDir.VirtualAddress ==0)
{
//no export table
return 0;
}
for(i =0 ; i < fileheader.NumberOfSections; i++)
if(ExportDir.VirtualAddress >=section[i].VirtualAddress && ExportDir.VirtualAddress < section[i].Misc.VirtualSize + section[i].VirtualAddress)
{
bExportTableFound = true;
break;
}
if(bExportTableFound)
{
DeltaK = (section[i].VirtualAddress - section[i].PointerToRawData);
RawExportTableOffset =ExportDir.VirtualAddress - DeltaK;
}//这里不存在else 的情况
IMAGE_EXPORT_DIRECTORY eat;
memcpy(&eat, rawbuffer + RawExportTableOffset, sizeof IMAGE_EXPORT_DIRECTORY);
//准备处理输出函数
PE_EXPORT_TABLE export_proc = {0};
DWORD dw = 0;
for(i = 0 ; i < eat.NumberOfFunctions; i++)
{
//文件偏移
export_proc.proc_RAW = eat.AddressOfFunctions -DeltaK + i*4;
//内存相对虚拟偏移
memcpy(&export_proc.proc_RVA ,(rawbuffer + eat.AddressOfFunctions -DeltaK + i*4), 4);
export_proc.proc_ord = i;
pe_export.push_back(export_proc);
}
DWORD nameBaseRaw = eat.AddressOfNames - DeltaK;
DWORD ordBaseRaw = eat.AddressOfNameOrdinals - DeltaK;
DWORD NameVA = 0;
DWORD Ord = 0;
for(i = 0 ; i < eat.NumberOfNames; i++)
{
//获取函数名称VA
memcpy(&NameVA, rawbuffer + nameBaseRaw + i * 4, 4);
memcpy(&Ord, rawbuffer + ordBaseRaw + i*2, 2);
//将函数名称取出并转化为unicode存放到vector对应的向量中.
CHAR *str_proc_name = rawbuffer + NameVA - DeltaK
MultiByteToWideChar(CP_ACP, 0,str_proc_name, strlen(str_proc_name), pe_export[Ord].proc_name, NAME_MAXLEN);
//这就是vc工具中 hint
pe_export[Ord].proc_hint = i;
}
return 0;
转载自