三、导出表(ExportTable)解析

三、导出表(ExportTable )解析
数据⽬录(Data Directory)有16个_IMAGE_DATA_DIRECTORY结构体元素,该结构体数组是可选PE头中最后⼀个成员。这⼗六个元素分别存储了不同信息,分别是:导⼊表、导出表、资源、异常信息、安全证书、重定位表、调试信息、版权所有、全局指针、TLS、加载配置、绑定导⼊、IAT、延迟导⼊、COM信息、最后⼀个保留未使⽤。和程序运⾏时息息相关的表有:导出表、导⼊表、重定位表、IAT表的灯,这⼏种也是PE解析中重点研究的⼏张表。
该结构体标记了各个表(元素)在内存中的VirtualAddress与Size。VirtualAddress是内存中的偏移地址,我们要直接在⽂件中根据VirtualAddress到对应的表,就需要进⾏判断。判断VirtualAddress在哪个节,并且计算在节中的偏移量,即RVA->FOA的转换。
1、导出表基本结构:
根据_IMAGE_DATA_DIRECTORY结构体数组的第1个元素索引处导出表。⼀般情况下,dll的函数导出供其他⼈使⽤,exe将别⼈的dll的函数导⼊运⾏。
所以,⼀般.exe没有导出表(但是并⾮说.exe⼀定没有导出表)。
导出表结构:
注意:
⾥⾯的地址均是RVA,⽽如果我们不想转换成ImageBuffer就⼀定要进⾏RVA->FOA转换,根据FOA直接在FileBuffer中寻导出表。每个dll都有⼀个导出表。⽽每个导出表有三个⼦导出表(地址AddressOfFunctions、名字AddressOfNames、序号
AddressOfOrdinals)。NumberOfFunctions是函数序号最⼤值与最⼩值之间的差值,NumberOfNames是函数以名字导出的个数,⼆者可以不⼀样⼤。⼀个函数必定有地址,但不⼀定有名字(如果是以⽆名字的⽅式导出,eg:func @12 NONAME)。
我们⾃定义⼀个dll库,在导出时采⽤.def⽂件的⽅式导出:typedef struct _IMAGE_DATA_DIRECTORY{    DWORD  VirtualAddress ;    DWORD  Size ;} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY ;
1
2
3
4typedef  struct  _IMAGE_EXPORT_DIRECTORY {    DWORD  Characteristics;    //未使⽤    DWORD  TimeDateStamp;      //时间戳    WORD    MajorVersion;      //未使⽤    WORD    MinorVersion;      //未使⽤    DWORD  Name;              //指向改导出表⽂件名字符串    DWORD  Base;              //导出表的起始序号    DWORD  NumberOfFunctions;  //导出函数的个数(更准确来说是AddressOfFunctions 的元素数,⽽不是函数个数)    DWORD  NumberOfNames;      //以函数名字导出的函数个数    DWORD  AddressOfFunctions;    //导出函数地址表RVA:存储所有导出函数地址(表元素宽度为4,总⼤⼩NumberOfFunctions * 4)    DWORD  AddressOfNames;        //导出函数名称表RVA:存储函数名字符串所在的地址(表元素宽度为4,总⼤⼩为NumberOfNames * 4)    DWORD  AddressOfNameOrdinals;  //导出函数序号表RVA:存储函数序号(表元素宽度为2,总⼤⼩为NumberOfNames * 2)} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
1
2
3
4
5
6
7
8
9
10
11
12
13//dll .def EXPORTS Plus    @1          Sub      @3 NONAME  div    @5 NONAME  mul      @6
1
2
3
4
5
6
则即解析得到的信息如下(“——–”代表没有名字,Ordina显⽰的值与内存⾥的值差⼀个Base,因为在⽤代码进⾏解析时已经把Base算过了,⽽内存中没有动):
导出的函数有两个函数我们声明为noname,故其在导出表中不存在名字。则其导出表的NumberOfNames = 2,NumberOfFunctions = (6-1+1) = 6。即地址表长度为6宽度为4,Size为24;名字表长度为2,宽度为4,Size为8;序号表长度为2,宽度为2,Size为4。对应的内存图如下(名字表的地址是按照从⼩到⼤排的,地址表有与我们.def中指定了序号,因此是乱序的,如果不指定,编译器⾃动分配
的⼀般也是有序的):
虽然导出时div、sub没有序号与名字,但是⼆者是有地址的。并且由于序号计算地址表时,地址表中有些值没有映射,则填充为0。⽽⽂件
中如下所⽰(导出表在⽂件中开始地址0002DF10): Offset  to  Export  Table :[0002DF10]    Characteristics :      [00000000]    TimeDateStamp :        [59945264]    MajorVersion :          [0000]    MinorVersion :          [0000]    NameAddr :              [0002DF5C]    NameString :            [dll.dll]    Base :
                  [00000001]    NumberOfFunctions :    [00000006]    NumberOfNames :        [00000002]    AddressOfFunctions :    [0002DF38]    AddressOfNames :        [0002DF50]    AddressOfNameOrdinals : [0002DF58]    Ordina  func_FOA    name_FOA    FunctionName    0001    00001005    0002DF68    plus    0003    00001019    --------    --------    0005    00015060    --------    --------    0006    00001014    0002DF64    mul
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
①导出表中AddressOfFunction指向的地址表⼤⼩根据 NumberOfFunctions 决定:地址表⼤⼩ = NumberOfFunctions * 4; ②⽽AddressOfNames指向的名字表⼤⼩不由 NumberOfFunctions 决定,⽽由NumberOfNames决定:名字表⼤⼩ =NumberOfNames * 4;
③AddressOfNameOrdinals指向的序号表中的值是⾮准确的,应该均加上Base才是真正的序号(Base等于序号表中最⼩的值)。⽽序号表⼤⼩ = NumberOfNames * 2。
④地址表可能⼤于等于名字表,也有可能⼩于名字表,因为⼀个函数可能没有名字,也可能有多个名字。但是⼀般情况下,名字表均不会⼤于地址表。并且⼀个函数必然有地址,不⼀定有名字,名字表和序号表⼀⼀对应。
2、导出表解析:
知道⼀个函数名字func,如何到其在PE⽂件中的地址?步骤(根据三张⼦表查):
①在名字表遍历RVA地址,转换成FOA地址,然后根据FOA⽐较FOA指向的字符串与func是否相等,不相等则判断下⼀个。 ②如果相等则获取到其在名字表中的索引(下标),根据该索引获取对应的序号表中同⼀下标索引到的序号值value。
③value作为地址表的索引,索引到的值即为func()的地址。
也就是我们上⾯图中所描述的。
生态养猪场RVA->FOA的转换函数如下:
导出表的解析实现如下(省略⽂件到内存的读⼊)://输⼊RVA (内存相对偏移地址),返回FOA (⽂件偏移地址)DWORD PETool::RVAToFOA(DWORD imageAddr){    /*    * 相对虚拟地址转⽂件偏移地址    * ①获取Section 数⽬    * ②获取SectionAlignment      * ③判断需要转换的RVA 位于哪个Section 中(section[n]),    * offset = 需要转换的RVA-VirtualAddress ,计算出RVA 相对于本节的偏移地址    * ④section[n].PointerToRawData + offset 就是RVA 转换后的FOA    */    if (imageAddr > imageSize){        printf("RVAToFOA in_addr is error!%08X\n",imageAddr);        exit(EXIT_FAILURE);    }    if (imageAddr < section_header[0].PointerToRawData){        return  imageAddr;//在头部(包括节表与对齐)则直接返回    }    IMAGE_SECTION_HEADER * section = section_header;    DWORD offset = 0;    for (int  i = 0; i < sectionNum; i++){        DWORD lower = section[i].VirtualAddress;//该节下限        DWORD upper = section[i].VirtualAddress+section[i].Misc.VirtualSize;//该节上限        if (imageAddr >= lower && imageAddr <= upper){            offset = imageAddr - lower + section[i].PointerToRawData;//计算出RVA 的FOA            break ;        }    }    return  offset;}接触式位移传感器
1
2
3
4
5
6
7
8
9
10
11
缘11412
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void PETool::print_ExportTable()
{
fprintf(fp_peMess, "导出表(export table):\n");
if(dataDir[0].VirtualAddress == 0){
fprintf(fp_peMess, "\t不存在导出表!\n");
return;
}
DWORD offset = RVAToFOA(dataDir[0].VirtualAddress);
IMAGE_EXPORT_DIRECTORY * exportTb = (IMAGE_EXPORT_DIRECTORY * )(pFileBuffer + offset);
fprintf(fp_peMess, "\tOffset to Export Table:[%08X]\n",dataDir[0].VirtualAddress);
fprintf(fp_peMess, "\tCharacteristics:      [%08X]\n", exportTb->Characteristics);
fprintf(fp_peMess, "\tTimeDateStamp:        [%08X]\n", exportTb->TimeDateStamp);
fprintf(fp_peMess, "\tMajorVersion:          [%04X]\n", exportTb->MajorVersion);
fprintf(fp_peMess, "\tMinorVersion:          [%04X]\n", exportTb->MinorVersion);
fprintf(fp_peMess, "\tNameAddr:              [%08X]\n", exportTb->Name);
fprintf(fp_peMess, "\tNameString:            [%s]\n", pFileBuffer + RVAToFOA(exportTb->Name));
fprintf(fp_peMess, "\tBase:                  [%08X]\n", exportTb->Base);
fprintf(fp_peMess, "\tNumberOfFunctions:    [%08X]\n", exportTb->NumberOfFunctions);
fprintf(fp_peMess, "\tNumberOfNames:        [%08X]\n", exportTb->NumberOfNames);
fprintf(fp_peMess, "\tAddressOfFunctions:    [%08X]\n", exportTb->AddressOfFunctions);
fprintf(fp_peMess, "\tAddressOfNames:        [%08X]\n", exportTb->AddressOfNames);
fprintf(fp_peMess, "\tAddressOfNameOrdinals: [%08X]\n", exportTb->AddressOfNameOrdinals);
//打印导出表
fprintf(fp_peMess, "\n\tOrdina\tfunc_FOA\tname_FOA\tFunctionName\n");
DWORD * addrFunc = (DWORD *)(pFileBuffer + RVAToFOA(exportTb->AddressOfFunctions));
DWORD * addrName = (DWORD *)(pFileBuffer + RVAToFOA(exportTb->AddressOfNames));
WORD * addrOrdi = (WORD *)(pFileBuffer + RVAToFOA(exportTb->AddressOfNameOrdinals));
//Base--->NumberOfFunctions
DWORD i, j;
for(i = 0; i < exportTb->NumberOfFunctions; i++){//导出时序号有NumberOfFunctions个
if(addrFunc[i] == 0){
continue;//地址值为0代表该序号没有对应的函数,是空余的
}
for(j = 0; j < exportTb->NumberOfNames; j++){//序号表序号有NumberOfNames个
if(addrOrdi[j] == i){//序号表的值为地址表的索引
钢结构安装fprintf(fp_peMess, "\t%04X\t%08X\t%08X\t%s\n", i + exportTb->Base, addrFunc[i], addrName[j], pFileBuffer + addrName[j]);
break;
}
}睫毛器
//存在addrOrdi[j]时,i(索引)等于addrOrdi[j](值),不存在,则i依旧有效,i+Base依旧是序号
if(j != exportTb->NumberOfNames){
continue;//在序号表中到
台历架
}
else{//如果在序号表中没有到地址表的索引,说明函数导出是以地址导出的,匿名函数
fprintf(fp_peMess, "\t%04X\t%08X\t%s\t%s\n", i + exportTb->Base, addrFunc[i], "--------", "--------");
}
}
}

本文发布于:2024-09-22 12:32:05,感谢您对本站的认可!

本文链接:https://www.17tex.com/tex/2/265581.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:导出   地址   函数   序号   名字   表中   没有
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议