Jerry的灌水乐园

我希望你,和我一样满腔热血,心头带伤。要狠,狠得象狼敢叫万夫莫当;要傲,傲得象兰高挂一脸秋霜。

用户操作
[即时聊天] [发私信] [加为好友]
oyljerryID:oyljerry
37664次访问,排名3348,好友0人,关注者182人。
oyljerry的文章
原创 35 篇
翻译 0 篇
转载 0 篇
评论 27 篇
oyljerry的公告


本站总访问量:

当前页访问量:


Self-Introduction
Name: Jerry
E-Mail: oyljerry@163.com
School: WHU
Astrology: Scorpio
Blood Type: B
Hobby: Movie,Music,TV
Motto: Rejoice,I Desire!


真心祝福

热点新闻    

今日天气


---------------------------------
访问量:
---------------------------------
专家链接 热门站点
  • 博客中国
  • Newlc(Symbian OS)
  • CSDN
  • 猫扑.com
  • 中天在线
最近评论
Frankwz:楼主的文章拜读了。有所得
topcoder0:关于Visual Basic,我用过最早的DOS版本,
======================================
Visual Basic的DOS版本是在windows版之后才有的,我想你也没有见过Turbo pacsal/c/basic吧?

--------------------------------------------……
xjacks:写得很不错!这种学习精神最重要!
不知为何楼主很久不写新文章了?
freasy:虽然很多地方我不能苟同,但是,前辈的见识让我非常佩服!
Senes:不得不说,这些程序有些是有问题的,比如说双向冒泡的程序在数组已经拍好序的情况下,t根本就没有被赋值,而且我觉得t=i-1才对
文章分类
收藏
    相册
    Bill Gates的豪宅
    经典图片
    美丽的母校
    我喜欢的图片
    学习VC
    CodeGuru
    CodeProject
    VC大本营
    VC知识库
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 PE学习笔记(二) 选择自 rivershan 的 Blog 收藏

    新一篇: 走进Boost | 旧一篇:  PE学习笔记(一) 选择自 rivershan 的 Blog

    五、Section Table(节表)

     节表是紧挨着 PE Header 的一结构数组。该数组成员的数目由 File Header (IMAGE_FILE_HEADER) 结构中 NumberOfSections 域的域值来决定。节表成员结构又命名为 IMAGE_SECTION_HEADER(四十字节)。其结构定义:

    typedef struct _IMAGE_SECTION_HEADER {
        BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
        union {
            DWORD   PhysicalAddress;
            DWORD   VirtualSize;
        } Misc;
        DWORD   VirtualAddress;
        DWORD   SizeOfRawData;
        DWORD   PointerToRawData;
        DWORD   PointerToRelocations;
        DWORD   PointerToLinenumbers;
        WORD    NumberOfRelocations;
        WORD    NumberOfLinenumbers;
        DWORD   Characteristics;
    } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

     IMAGE_SECTION_HEADER 结构成员含义:

    1.IMAGE_SIZEOF_SHORT_NAME:不超过8字节的节名。节名仅是个标记,我们选择任何名字甚至空着也行,不能用null结束。命名不是一个ASCIIZ字符串,所以不用null结尾。

    2.PhysicalAddress:指定文件地址。

    3.VirtualSize:这个域的意义与程序类型有关。如果是EXE,代表当节被装入内存之后的大小总和,这是在它们被调整为最接近文件对齐粒度的倍数之前的大小。稍后的SizeOfRawData则是调整后的大小。对于OBJ文档,这个域没有意义。

    4.VirtualAddress:本节的RVA(相对虚拟地址)。PE装载器将节映射至内存时会读取本值,因此如果域值是1000h,而PE文件装在地址400000h处,那么本节就被载到401000h。微软把第一个Section 的此域值设为0x1000h。对于OBJ文档,此域没意义,总为0。
     
    5.SizeOfRawData:经过文件对齐处理后节尺寸,PE装载器提取本域值了解需映射入内存的节字节数。 假设一个文件的文件对齐尺寸是0x200,如果前面的 VirtualSize 域指示本节长度是0x388字节,则本域值为0x400,表示本节是0x400字节长。在obj中,这个与表示有编译器或 組譯器 指定的真正的section 大小。

    6.PointerToRawData:这是本节基于文件的偏移量,PE装载器通过本域值找到节数据在文件中的位置。 如果你自己以内存映射的方式应设了一个PE程序(而不是由操作系统的装载器载入),那么你就必须根据此值找到本节的信息,而不是根据VirtualAddress 中的RVA值。

    7.PointerToRelocations:在OBJs中,这是以程序开始为基准的偏移量,用来指向section 的重定位信息。每个OBJ section 的重定位信息紧跟在section 信息之后。在EXEs中,这个域(一记下一个域)没有意义,总是为0。但链接器产生一个EXE,它会决定大部分的待修正纪录(fixups),只剩下基址的重定位地址以及 imported 函数的重定位地址,留待载入时在解决。两份相同的信息放在 base relocation section 和imported function section 之中,所以EXEs 不需要在每一个 section 之后又有重定位信息。

    8.PointerToLinenumbers:行号表的偏移量(以程序开始为基准)。行号表与源代码行号和其被映射到内存中的位置有关。在EXE文件中,行号信息被放在程序的最尾端。如果没有COFF行好,设为0。

    9.NumberOfRelocations:重定位表格(由PointerToRelocations 指向)中的重定位项目的个数。此域只用于OBJ中。EXE中为0。

    10.NumberOfLinenumbers:行号表格(由PointerToLinenumbers 指向)中的行号个数。

    11.Characteristics:包含标记以指示节属性,比如节是否含有可执行代码、初始化数据、未初始数据,是否可写、可读等。 下面是一些标记:

    IMAGE_SCN_TYPE_REG      Reserved.
    IMAGE_SCN_TYPE_DSECT     Reserved.
    IMAGE_SCN_TYPE_NOLOAD    Reserved.
    IMAGE_SCN_TYPE_GROUP     Reserved.
    IMAGE_SCN_TYPE_NO_PAD     Reserved.
    IMAGE_SCN_TYPE_COPY     Reserved.
    IMAGE_SCN_CNT_CODE      Section contains executable code.
    IMAGE_SCN_CNT_INITIALIZED_DATA   Section contains initialized data.
    IMAGE_SCN_CNT_UNINITIALIZED_DATA  Section contains uninitialized data.
    IMAGE_SCN_LNK_OTHER     Reserved.
    IMAGE_SCN_LNK_INFO      Reserved.
    IMAGE_SCN_TYPE_OVER     Reserved.
    IMAGE_SCN_LNK_COMDAT     Section contains COMDAT data. 
    IMAGE_SCN_MEM_FARDATA     Reserved.
    IMAGE_SCN_MEM_PURGEABLE    Reserved.
    IMAGE_SCN_MEM_16BIT     Reserved.
    IMAGE_SCN_MEM_LOCKED     Reserved.
    IMAGE_SCN_MEM_PRELOAD     Reserved.
    IMAGE_SCN_ALIGN_1BYTES     Align data on a 1-byte boundary. 
    IMAGE_SCN_ALIGN_2BYTES     Align data on a 2-byte boundary. 
    IMAGE_SCN_ALIGN_4BYTES     Align data on a 4-byte boundary. 
    IMAGE_SCN_ALIGN_8BYTES     Align data on a 8-byte boundary. 
    IMAGE_SCN_ALIGN_16BYTES    Align data on a 16-byte boundary. 
    IMAGE_SCN_ALIGN_32BYTES    Align data on a 32-byte boundary. 
    IMAGE_SCN_ALIGN_64BYTES    Align data on a 64-byte boundary. 
    IMAGE_SCN_LNK_NRELOC_OVFL    Section contains extended relocations.
    IMAGE_SCN_MEM_DISCARDABLE    Section can be discarded as needed.
    IMAGE_SCN_MEM_NOT_CACHED    Section cannot be cached.
    IMAGE_SCN_MEM_NOT_PAGED    Section cannot be paged.
    IMAGE_SCN_MEM_SHARED     Section can be shared in memory.
    IMAGE_SCN_MEM_EXECUTE     Section can be executed as code.
    IMAGE_SCN_MEM_READ      Section can be read.
    IMAGE_SCN_MEM_WRITE     Section can be written to.


     遍历节表的步骤:

    1.PE文件有效性校验。
    2.定位到 PE Header 的起始地址。
    3.从 file Header 的 NumberOfSections域获取节数。
    4.通过两种方法定位节表: ImageBase+SizeOfHeaders 或者 PE header的起始地址+ PE header结构大小。 (节表紧随 PE Header)。如果不是使用文件映射的方法,可以用SetFilePointer 直接将文件指针定位到节表。节表的文件偏移量存放在 SizeOfHeaders域里(SizeOfHeaders 是 IMAGE_OPTIONAL_HEADER 的结构成员) 。
    5.处理每个 IMAGE_SECTION_HEADER 结构。


    六、Import Table(导入表)

    6.1、导入函数:

     一个导入函数是被某模块调用的但又不在调用者模块中的函数,因而命名为"import(导入)"。导入函数实际位于一个或者更多的DLL里。调用者模块里只保留一些函数信息,包括函数名及其驻留的DLL名。

        PE 程序被载入到内存之前,存放在 PE 文件的 .data 中的内容是给装载器用来决定函数位置并修补它们以便完成image 用的。而在被载入之后,.idata内含有的是指向 EXE/DLL 的导入函数的指针。

    6.2、Data Directory:

     Data Directory 是一个 IMAGE_DATA_DIRECTORY 结构数组,共有16个成员。Data Directory 包含了PE文件中各重要数据结构的位置和尺寸信息。 每个成员包含了一个重要数据结构的信息。

     Data Directory 的每个成员都是 IMAGE_DATA_DIRECTORY 结构类型的,其定义如下所示:

    typedef struct _IMAGE_DATA_DIRECTORY {
        DWORD   VirtualAddress;
        DWORD   Size;
    } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

     IMAGE_DATA_DIRECTORY 结构成员含义:
     
    1.VirtualAddress: 实际上是数据结构的相对虚拟地址(RVA)。比如,如果该结构是关于Import Symbols的,该域就包含指向IMAGE_IMPORT_DESCRIPTOR 数组的RVA。

    2.Size: 含有VirtualAddress所指向数据结构的字节数。

    6.3、找寻PE文件中重要数据结构的一般方法:

    1、从 DOS Header 定位到 PE Header。
    2、从 Optional Header 读取 Data Directory 的地址。
    3、IMAGE_DATA_DIRECTORY 结构尺寸乘上找寻结构的索引号:比如您要找寻Import Symbols的位置信息,必须用IMAGE_DATA_DIRECTORY 结构尺寸(8 bytes)乘上1(Import Symbols 在 Data Diectory 中的索引号)。
    4、将上面的结果加上 Data Diectory 地址,我们就得到包含所查询数据结构信息的 IMAGE_DATA_DIRECTORY 结构项。

    6.4、导入表:

     Data Directory 数组第一项的 VirtualAddress 包含导入表地址。导入表实际上是一个 IMAGE_IMPORT_DESCRIPTOR 结构数组。每个结构包含PE文件导入函数的一个相关DLL的信息。该数组以一个全0的成员结尾。

     IMAGE_IMPORT_DESCRIPTOR结构组成:

    typedef struct _IMAGE_IMPORT_DESCRIPTOR {
        union {
            DWORD   Characteristics;            // 0 for terminating null import descriptor
            DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
        };
        DWORD   TimeDateStamp;                  // 0 if not bound,
                                                // -1 if bound, and real date\time stamp
                                                //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                                // O.W. date/time stamp of DLL bound to (Old BIND)

        DWORD   ForwarderChain;                 // -1 if no forwarders
        DWORD   Name;
        DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
    } IMAGE_IMPORT_DESCRIPTOR;

     IMAGE_IMPORT_DESCRIPTOR 结构成员含义:
     
    1.结构第一项是一个union子结构。事实上,这个union子结构只是给 OriginalFirstThunk 增添了个别名,您也可以称其为"Characteristics"。该成员项含有指向一个 IMAGE_THUNK_DATA 结构数组的RVA。

    2.TimeDateStamp:程序生成的时刻。此域通常为0。微软的 BIND 程序可以将此 IMAGE_IMPORT_DESCRIPTOR 所对应的dll的生成时刻写到这里来。

    3.ForwarderChain:此域涉及到 forwarding(转交),意味着一个dll 函数在调用另一个 dll。例如,在 WINNT 中,Kernel32.dll 将它的某些输出函数转交给 NTDLL.dll。应用程序可能以为它调用 Kernel32.dll,而事实上它调用的事NTDLL.dll。这个域中含有一个索引,指向 FirstThunk 数组。被这个索引所指定的函数就是一个转交函数。

    3.Name:含有指向DLL名字的RVA,即指向DLL名字的指针,也是一个ASCII字符串。

    4.FirstThunk:与 OriginalFirstThunk 非常相似,它也包含指向一个 IMAGE_THUNK_DATA 结构数组的RVA(当然这是另外一个IMAGE_THUNK_DATA 结构数组)。

     IMAGE_IMPORT_DESCRIPTOR 数组中,最重要的部分是 imported DLL 的名称以及两个 IMAGE_THUNK_DATA 数组。每个 IMAGE_THUNK_DATA 对应一个导入函数。在exe中,两个数组(分别由 Characteristics 和 FirstThunk 域指向)平行存在,并且都以 NULL 位结束符。

     为什么需要两个平行数组?第一个数组(由 Characteristics 指向)从不被修改,有时它被称为 hint-name table。第二个数组(由 FirstThunk 指向)则被装载器改写。装载器一一检查每一个 IMAGE_THUNK_DATA 并且找出它所记录的函数的地址,然后把地址写入 IMAGE_THUNK_DATA 这个 DWORD 之中。由于这个 IMAGE_THUNK_DATA 数组内容已经被装载器改写为输入函数的地址,所以它又被叫做 Import Address Table(IAT)。IAT 是一个可写区域。API Hook 就利用到这一特性。PE装载器载入PE后,FirstThunk 指向的 IMAGE_THUNK_DATA 被改写,而 Characteristics 所指向的 IMAGE_THUNK_DATA 没有被改写。所以若还反过头来查找导入函数名,PE装载器还能够根据 Characteristics 所指向的 IMAGE_THUNK_DATA 找寻到函数名。 
     
    6.4、IMAGE_THUNK_DATA:

     IMAGE_THUNK_DATA是一个DWORD类型的集合。通常我们将其解释为指向一个 IMAGE_IMPORT_BY_NAME 结构的指针。注意 IMAGE_THUNK_DATA 包含了指向一个 IMAGE_IMPORT_BY_NAME 结构的指针,而不是结构本身。

     IMAGE_THUNK_DATA 结构定义:
     
    typedef struct _IMAGE_THUNK_DATA32 {
        union {
            PBYTE   ForwarderString;
            PDWORD  Function;
            DWORD  Ordinal;
            PIMAGE_IMPORT_BY_NAME  AddressOfData;
        } u1;
    } IMAGE_THUNK_DATA32;

     IMAGE_THUNK_DATA 实在PE被载入之后才被决定的。WIN32装载器使用 IMAGE_THUNK_DATA 的初始内容(可能是函数名称也可能是函数序号)来寻找输入函数的位置。然后装载器就以获得的地址改写 IMAGE_THUNK_DATA 的内容。 

    6.5、IMAGE_IMPORT_BY_NAME:

     IMAGE_IMPORT_BY_NAME 结构定义:
     
    typedef struct _IMAGE_IMPORT_BY_NAME {
        WORD    Hint;
        BYTE    Name[1];
    } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

    1.Hint:指示本函数在其所驻留DLL的导出表中的索引号。该域被PE装载器用来在DLL的导出表里快速查询函数。该值不是必须的,一些连接器将此值设为0。

    2.Name:含有导入函数的函数名。函数名是一个ASCII字符串。注意这里虽然将Name的大小定义成字节,其实它是可变尺寸域,只不过我们没有更好方法来表示结构中的可变尺寸域。这个结构被提供用于查阅描述名字的结构。

     有些情况下一些函数仅由序数导出,也就是说不能用函数名来调用它们,只能用它们的位置来调用。此时,调用者模块中就不存在该函数的 IMAGE_IMPORT_BY_NAME 结构。不同的,对应该函数的 IMAGE_THUNK_DATA 值的低位字指示函数序数,而最高二进位 (MSB)设为1。例如,如果一个函数只由序数导出且其序数是1234h,那么对应该函数的 IMAGE_THUNK_DATA 值是80001234h。Microsoft提供了一个方便的常量来测试dword值的MSB位,就是 IMAGE_ORDINAL_FLAG32,其值为80000000h。

    6.6、列出某个PE文件的所有导入函数步骤:

    1、校验文件是否是有效的PE。
    2、从 DOS Header 定位到 PE Header。
    3、获取位于 OptionalHeader 数据目录地址。
    4、转至数据目录的第二个成员提取其VirtualAddress值。
    5、利用上值定位第一个 IMAGE_IMPORT_DESCRIPTOR 结构。
    6、检查 OriginalFirstThunk值。若不为0,顺着 OriginalFirstThunk 里的RVA值转入那个RVA数组。若 OriginalFirstThunk 为0,就改用FirstThunk值。有些连接器生成PE文件时会置OriginalFirstThunk值为0,这应该算是个bug。不过为了安全起见,我们还是检查 OriginalFirstThunk值先。
    7、对于每个数组元素,我们比对元素值是否等于IMAGE_ORDINAL_FLAG32。如果该元素值的最高二进位为1,那么函数是由序数导入的,可以从该值的低字节提取序数。
    8、如果元素值的最高二进位为0,就可将该值作为RVA转入 IMAGE_IMPORT_BY_NAME 数组,跳过 Hint 就是函数名字了。
    9、再跳至下一个数组元素提取函数名一直到数组底部(它以null结尾)。现在我们已遍历完一个DLL的导入函数,接下去处理下一个DLL。
    10、即跳转到下一个 IMAGE_IMPORT_DESCRIPTOR 并处理之,如此这般循环直到数组见底。(IMAGE_IMPORT_DESCRIPTOR 数组以一个全0域元素结尾)。

    6.7、Bound Import:

     当PE装载器装入PE文件时,检查导入表并将相关DLLs映射到进程地址空间。然后象我们这样遍历IMAGE_THUNK_DATA 数组并用导入函数的真实地址替换IMAGE_THUNK_DATAs 值。这一步需要很多时间。如果程序员能事先正确预测函数地址,PE装载器就不用每次装入PE文件时都去修正IMAGE_THUNK_DATAs 值了。Bound import就是这种思想的产物。
     
     Microsoft 出品的类似Visual Studio的编译器多提供了bind.exe这样的工具,由它检查PE文件的导入表并用导入函数的真实地址替换IMAGE_THUNK_DATA 值。当文件装入时,PE装载器必定检查地址的有效性,如果DLL版本不同于PE文件存放的相关信息,或则DLLs需要重定位,那么装载器认为原先计算的地址是无效的,它必定遍历OriginalFirstThunk指向的数组以获取导入函数新地址。

    七、Export Table(导出表)

     当PE装载器执行一个程序,它将相关DLLs都装入该进程的地址空间。然后根据主程序的导入函数信息,查找相关DLLs中的真实函数地址来修正主程序。PE装载器搜寻的是DLLs中的导出函数。PE 程序把它的导出函数相关信息放在.edata 中。

     DLL/EXE要导出一个函数给其他DLL/EXE使用,有两种实现方法: 通过函数名导出或者仅仅通过序数导出。比如某个DLL要导出名为"GetSysConfig"的函数,如果它以函数名导出,那么其他DLLs/EXEs若要调用这个函数,必须通过函数名,就是GetSysConfig。另外一个办法就是通过序数导出。序数是唯一指定DLL中某个函数的16位数字,在所指向的DLL里是独一无二的。例如在上例中,DLL可以选择通过序数导出,假设是16,那么其他DLLs/EXEs若要调用这个函数必须以该值作为GetProcAddress调用参数。这就是所谓的仅仅靠序数导出。 

    7.1 导出表是数据目录的第一个成员,又可称为 IMAGE_EXPORT_DIRECTORY。结构定义:
     
     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;

     IMAGE_EXPORT_DIRECTORY 结构成员含义:

    1.Characteristics:此域没有用途,总是为0。

    2.TimeDateStamp:程序被生成的时刻。

    3.MajorVersion/MinorVersion:无实际用途,0。

    4.Name:一个 RVA 值,指向一个 ASCIIZ 字串(dll 名称,如MYDLL.dll)。模块的真实名称。本域是必须的,因为文件名可能会改变。这种情况下,PE装载器将使用这个内部名字。

    3.Base:基数,加上序数就是函数地址数组的索引值了。

    4.NumberOfFunctions:模块导出的函数/符号总数。

    5.NumberOfNames:通过名字导出的函数/符号数目。该值不是模块导出的函数/符号总数,这是由上面的NumberOfFunctions给出。本域可以为0,表示模块可能仅仅通过序数导出。如果模块根本不导出任何函数/符号,那么数据目录中导出表的RVA为0。

    6.AddressOfFunctions:模块中有一个指向所有函数/符号的RVAs数组,本域就是指向该RVAs数组的RVA。简言之,模块中所有函数的RVAs都保存在一个数组里,本域就指向这个数组的首地址。

    7.AddressOfNames:类似上个域,模块中有一个指向所有函数名的RVAs数组,本域就是指向该RVAs数组的RVA。

    9.AddressOfNameOrdinals:RVA,指向包含上述 AddressOfNames数组中相关函数之序数的16位数组。

     导出表的设计是为了方便PE装载器工作。
     
     首先,模块必须保存所有导出函数的地址以供PE装载器查询。模块将这些信息保存在AddressOfFunctions域指向的数组中,而数组元素数目存放在NumberOfFunctions域中。 因此,如果模块导出40个函数,则AddressOfFunctions指向的数组必定有40个元素,而NumberOfFunctions值为40。

     现在如果有一些函数是通过名字导出的,那么模块必定也在文件中保留了这些信息。这些名字的RVAs存放在一数组中以供PE装载器查询。该数组由AddressOfNames指向,NumberOfNames包含名字数目。考虑一下PE装载器的工作机制,它知道函数名,并想以此获取这些函数的地址。至今为止,模块已有两个模块: 名字数组和地址数组,但两者之间还没有联系的纽带。因此我们还需要一些联系函数名及其地址的东东。PE参考指出使用到地址数组的索引作为联接,因此PE装载器在名字数组中找到匹配名字的同时,它也获取了指向地址表中对应元素的索引。而这些索引保存在由AddressOfNameOrdinals域指向的另一个数组(最后一个)中。由于该数组是起了联系名字和地址的作用,所以其元素数目必定和名字数组相同,比如,每个名字有且仅有一个相关地址,反过来则不一定: 每个地址可以有好几个名字来对应。因此我们给同一个地址取"别名"。为了起到连接作用,名字数组和索引数组必须并行地成对使用,譬如,索引数组的第一个元素必定含有第一个名字的索引,以此类推。

    7.2 如果我们有了导出函数名并想以此获取地址,可以这么做:

    1、定位到PE Header。
    2、从数据目录读取导出表的虚拟地址。
    3、定位导出表获取名字数目(NumberOfNames)。
    4、并行遍历AddressOfNames和AddressOfNameOrdinals指向的数组匹配名字。如果在AddressOfNames 指向的数组中找到匹配名字,从AddressOfNameOrdinals 指向的数组中提取索引值。例如,若发现匹配名字的RVA存放在AddressOfNames 数组的第77个元素,那就提取AddressOfNameOrdinals数组的第77个元素作为索引值。如果遍历完NumberOfNames 个元素,说明当前模块没有所要的名字。
    5、从AddressOfNameOrdinals 数组提取的数值作为AddressOfFunctions 数组的索引。也就是说,如果值是5,就必须读取AddressOfFunctions 数组的第5个元素,此值就是所要函数的RVA。

    7.3 假设我们只有函数的序数,那么怎样获取函数地址呢,可以这么做:

    1、定位到PE Header。
    2、从数据目录读取导出表的虚拟地址。
    3、定位导出表获取nBase值。
    4、减掉nBase值得到指向AddressOfFunctions 数组的索引。
    5、将该值与NumberOfFunctions作比较,大于等于后者则序数无效。
    6、通过上面的索引就可以获取AddressOfFunctions 数组中的RVA了。

    发表于 @ 2004年10月26日 15:14:00|评论(loading...)|编辑

    新一篇: 走进Boost | 旧一篇:  PE学习笔记(一) 选择自 rivershan 的 Blog

    评论:没有评论。

    发表评论  


    当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
    Csdn Blog version 3.1a
    Copyright © oyljerry