cn01574978 发表于 2019-7-22 09:18

初学逆向,自己的PE分析笔记,适合新人,高手略过或指点哈。

本帖最后由 cn01574978 于 2019-7-22 09:36 编辑


exe文件的dos头

结构序数:1-2-3-4-5-6-7-8-9-10-11-12-13-14---15-16-17----18-19 =
所占字节:2+2+2+2+2+2+2+2+2+ 2+ 2+ 2+ 2+ 2+ 2*4+ 2+ 2+ 2*10+ 4 = 2*(14+4+2+10)+4
                                                               = 60d+4d
                                                               = 64d   (字节)(十进制)
                                                               = 3ch+4h(字节)(十六进制)
                                                               = 40h   (字节)(十六进制)
0.
IMAGE_DOS_HEADER STRUCT
+0h WORD   e_magic   // Magic DOS signature MZ(4Dh 5Ah)    DOS可执行文件标记
+2h WORD   e_cblp      // Bytes on last page of file      文件的最后一页上的字节
+4h WORD   e_cp      // Pages in file                     文件页面
+6h WORD   e_crlc      // Relocations                     重定位
+8h WORD   e_cparhdr   // Size of header in paragraphs
+0a hWORDe_minalloc// Minimun extra paragraphs needs
+0c hWORDe_maxalloc// Maximun extra paragraphs needs
+0e hWORDe_ss      // intial(relative)SS value         DOS代码的初始化堆栈SS
+10 hWORDe_sp      // intial SP value                  DOS代码的初始化堆栈指针SP
+12 hWORDe_csum      // Checksum
+14 hWORDe_ip      // intial IP value                  DOS代码的初始化指令入口[指针IP]
+16 hWORDe_cs      // intial(relative)CS value         DOS代码的初始堆栈入口
+18 hWORDe_lfarlc    // File Address of relocation table
+1a hWORDe_ovno      // Overlay number
+1c hWORDe_res    // Reserved words
+24 hWORDe_oemid   // OEM identifier(for e_oeminfo)
+26 hWORDe_oeminfo   // OEM information;e_oemid specific
+28 hWORDe_res2// Reserved words
+3c hDWORD e_lfanew    //Offset to start of PE header      指向PE文件头
} IMAGE_DOS_HEADER ENDS

====================================================================================================

exe文件的PE头
由上面DOS头最后一个字段 hDWORD e_lfanew 所指向 (4字节)
下面PENT头的第一个字段DWORDSignature ; 4字节
结构序数:1- 2- 3 =
所占字节:4+20+ 224=248      (字节)(十进制)
                     =4h+14h+Eo(字节)(十六进制)
                     =F8         (字节)(十六进制)
00.
IMAGE_NT_HEADERS {
DWORD               Signature;   //签名(4个字节)
IMAGE_FILE_HEADER   FileHeader;    //文件头指向下面第一子结构
IMAGE_OPTIONAL_HEADER OptionalHeader;//可选头指向下面第二子结构
} IMAGE_NT_HEADERS

1.------------------------
exe文件的PENT头的第二字段指向的文件头第一子结构

结构序数:1-2-3-4-5-6-7 =
所占字节:2+2+4+4+4+2+2 =      20d (字节)(十进制)
          16h-04h+2h =12h+2h = 14h (字节)(十六进制)
                           = 20d (字节)(十进制)                     
IMAGE_FILE_HEADER {
+04h      WORD                  Machine;             // 运行平台
+06hWORD                  NumberOfSections;               // 文件的区块数目 (重要)
+08h      DWORD               TimeDateStamp;                         // 文件创建日期和时间1970.1.1 创建的妙
+0ChDWORD               PointerToSymbolTable;// 指向符号表(主要用于调试)
+10h         DWORD               NumberOfSymbols;               // 符号表中符号个数(同上)
+14hWORD                  SizeOfOptionalHeader;// IMAGE_OPTIONAL_HEADER32 结构大小
+16hWORD                  Characteristics;               
                // 文件属性 通过宏定义 定义于 winnt.h 内的 IMAGE_FILE_** 的值 或运算定义多个
    Value                        Meaning
    IMAGE_FILE_RELOCS_STRIPPED   
    0x0001
    Relocation information was stripped from the file. The file must be loaded at its preferred base address. If the base address is not available, the loader reports an error.
   
    IMAGE_FILE_EXECUTABLE_IMAGE
    0x0002
    The file is executable (there are no unresolved external references).
   
    IMAGE_FILE_LINE_NUMS_STRIPPED
    0x0004
    COFF line numbers were stripped from the file.
   
    IMAGE_FILE_LOCAL_SYMS_STRIPPED
    0x0008
    COFF symbol table entries were stripped from file.
   
    IMAGE_FILE_AGGRESIVE_WS_TRIM
    0x0010
    Aggressively trim the working set. This value is obsolete as of Windows 2000.
   
    IMAGE_FILE_LARGE_ADDRESS_AWARE
    0x0020
    The application can handle addresses larger than 2 GB.
   
    IMAGE_FILE_BYTES_REVERSED_LO
    0x0080
    The bytes of the word are reversed. This flag is obsolete.
   
    IMAGE_FILE_32BIT_MACHINE
    0x0100
    The computer supports 32-bit words.
   
    IMAGE_FILE_DEBUG_STRIPPED
    0x0200
    Debugging information was removed and stored separately in another file.
   
    IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP
    0x0400
    If the image is on removable media, copy it toand run it from the swap file.
   
    IMAGE_FILE_NET_RUN_FROM_SWAP
    0x0800
    If the image is on the network, copy it to and run it from the swap file.
   
    IMAGE_FILE_SYSTEM
    0x1000
    The image is a system file.
   
    IMAGE_FILE_DLL
    0x2000
    The image is a DLL file. While it is an executable file, it cannot be run directly.
   
    IMAGE_FILE_UP_SYSTEM_ONLY
    0x4000
    The file should be run only on a uniprocessor computer.
   
    IMAGE_FILE_BYTES_REVERSED_HI
    0x8000
    The bytes of the word are reversed. This flag is obsolete.

} IMAGE_FILE_HEADER

2.------------------------
exe文件的PENT头的第三字段指向的文件头第二子结构
结构序数:1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24-25-26-27-28-29-30--------31
所占字节:2+1+1+4+4+4+4+4+4+ 4+ 4+ 4+ 2+ 2+ 2+ 2+ 2+ 2+ 4+ 4+ 4+ 4+ 2+ 2+ 4+ 4+ 4+ 4+ 4+ 4+ 16*(4+4)
          =4*24 + 16*(4+4)
          =96d(倒数第二个字段相对PE头顶偏移) + 16 *(8)
          =224d                                       (字节)(十进制)
          =78h(倒数第二个字段相对PE头顶偏移) + 0Fh*(8)(字节)(十六进制)
          =E0                                           (字节)(十六进制)
         

IMAGE_OPTIONAL_HEADER
//
// Standard fields.
//
+18h WORD Magic;            // 标志字, ROM 映像(0107h),普通可执行文件(010Bh)
+1Ah BYTE MajorLinkerVersion; // 链接程序的主版本号
+1Bh BYTE MinorLinkerVersion; // 链接程序的次版本号
+1Ch DWORD SizeOfCode;      // 所有含代码的节的总大小
+20h DWORD SizeOfInitializedData;// 所有含已初始化数据的节的总大小
+24h DWORD SizeOfUninitializedData;// 所有含未初始化数据的节的大小
+28h DWORD AddressOfEntryPoint;   
   // 程序执行入口RVA 相对dos头基址 (重要)
   // DLL文件一般填充0的
+2Ch DWORD BaseOfCode;            
   // 代码的区块的起始RVA 相对dos头基址(重要)
+30h DWORD BaseOfData;            
   // 数据的区块的起始RVA 相对dos头基址(重要)
//
// NT additional fields. 以下是属于NT结构增加的领域。
//
+34h DWORD ImageBase;      
   // 程序的首选装载入内存的地址(重要)
    指出文件的优先装入地址,也就是当文件被执行时,
    若可能,Windows优先将文件装入到ImageBase 字段指向的地址中,
    只有指定的地址已经被其他的模块使用时,文件才被装入到其他地址中,
    链接器产生可执行文件的时候对应 这个 地址来生成机器码。
    当文件被装入这个地址时不需要进行重定位操作,装入速度最快,
    若文件被装载到其他的地址,将不得不进行重定位操作。
    在链接的时候,可以自定义优先装载地址,如果不指定这个选项话,
    一般地EXE文件的默认装载地址是    0040 0000h,
    而DLL文件的默认优先装土地址被定位1000 0000h。
+38h DWORD SectionAlignment; // 内存中的区块的对齐大小
+3Ch DWORD FileAlignment;    // 文件中(磁盘中)的区块的对齐大小

+40h WORD MajorOperatingSystemVersion; // 要求操作系统最低版本号的主版本号
+42h WORD MinorOperatingSystemVersion; // 要求操作系统最低版本号的副版本号
+44h WORD MajorImageVersion;         // 可运行于操作系统的主版本号
+46h WORD MinorImageVersion;         // 可运行于操作系统的次版本号
+48h WORD MajorSubsystemVersion;       // 要求最低子系统版本的主版本号
+4Ah WORD MinorSubsystemVersion;       // 要求最低子系统版本的次版本号

+4Ch DWORD Win32VersionValue; // 莫须有字段,不被病毒利用的话一般为0
+50h DWORD SizeOfImage;       // 映像装入内存后的总尺寸
+54h DWORD SizeOfHeaders;   // 所有头 + 区块表的尺寸大小
+58h DWORD CheckSum;          // 映像的校检和
+5Ch WORD Subsystem;          // 可执行文件期望的子系统

+5Eh WORD DllCharacteristics; // DllMain()函数何时被调用,默认为 0
+60h DWORD SizeOfStackReserve;// 初始化时的栈大小
+64h DWORD SizeOfStackCommit; // 初始化时实际提交的栈大小
+68h DWORD SizeOfHeapReserve; // 初始化时保留的堆大小
+6Ch DWORD SizeOfHeapCommit;// 初始化时实际提交的堆大小

+70h DWORD LoaderFlags; // 与调试有关,默认为 0

+74h DWORD NumberOfRvaAndSizes; // 下边数据目录的项数,这个字段自Windows NT 发布以来 // 一直是16
+78h IMAGE_DATA_DIRECTORY DataDirectory; (重要)
    //数据目录表(重要)IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16 个元素       (重要)
    //每个元素 又指向下面的 一个 IMAGE_DATA_DIRECTORY STRUCT(结构)
} IMAGE_OPTIONAL_HEADER32


2.1.----------------------------

exe文件的PE_NT头的第三字段指向的文件头第二子结构最后一个数组字段
                        指向的IMAGE_DATA_DIRECTORY STRUCT(结构)
                           
数据目录表结构相对PE_NT头偏移78h开始

IMAGE_DATA_DIRECTORY STRUCT
{
                DWORD VirtualAddress ;数据的起始RVA
                DWORD isize          ;数据块的长度
}
IMAGE_DATA_DIRECTORY ENDS


78h+0*8= 78h   IMAGE_DIRECTORY_ENTRY_EXPORT       导出表             (重要)
78h+1*8= 80h   IMAGE_DIRECTORY_ENTRY_IMPORT       导入表             (重要)
78h+2*8= 88h   IMAGE_DIRECTORY_ENTRY_RESOURCE   资源               (重要)
78h+3*8= 90h   IMAGE_DIRECTORY_ENTRY_EXCEPTION    异常(具体资料不详)
78h+4*8= 98h   IMAGE_DIRECTORY_ENTRY_SECURITY   安全(具体资料不详)
78h+5*8= 0a0hIMAGE_DIRECTORY_ENTRY_BASERELOC    重定位表         (重要)
78h+6*8= 0a8hIMAGE_DIRECTORY_ENTRY_DEBUG      调试信息
78h+7*8= 0b0hIMAGE_DIRECTORY_ENTRY_ARCHITECTURE 版权信息

78h+8*8= 0b8hIMAGE_DIRECTORY_ENTRY_GLOBALPTR    具体资料不详
78h+9*8= 0c0hIMAGE_DIRECTORY_ENTRY_TLS          Thread Local Storage
78h+a*8= 0c8hIMAGE_DIRECTORY_ENTRY_LOAD_CONFIG具体资料不详
78h+b*8= 0d0hIMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 具体资料不详

78h+c*8= 0d8hIMAGE_DIRECTORY_ENTRY_IAT          导入函数地址表   (重要)

78h+d*8= 0e0hIMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 具体资料不详
78h+e*8= 0e8hIMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 具体资料不详
78h+f*8= 0f0h未使用

====================================================================


(2) = _FileHeader.NumberOfSections//以file头的NumberOfSections字段来定义节的个数?
PEaddr+f8 = PEaddr+78h+10h*8h
此处 定义 _FileHeader.NumberOfSections = X = 1 为第一个节 来说明
节区表头结构 相对PE_NT头偏移78h+10h*8h+(X-1)*28 = 0f8h处开始
结构序数:1-2-3-4-5-6-7-8-9-10 =
所占字节:8+4+4+4+4+4+4+2+2+ 4
          =4*10
          =40d                                        (字节)(十进制)
          =28h                                        (字节)(十六进制)
          =一个节所占的字节数(节数在_FileHeader.NumberOfSections结构中定义 是个变量 不定的)
          =FileHeader.NumberOfSections * 28
         
typedef struct _IMAGE_SECTION_HEADER
{
78h+10h*8h+(X-1)*28h+00h = 0f8hBYTE Name;   // 节表名称,如“.text”
                                                         // IMAGE_SIZEOF_SHORT_NAME=8
78h+10h*8h+(X-1)*28h+08h = 100hunion
                                                                                                   {
                                                                                     DWORD PhysicalAddress;   // 物理地址
                                                                                     DWORD VirtualSize;       // 真实长度,这两个值是一个联合结构,可以使用其中的任何一个,一般是取后一个
                                                                                                            // 该表对应的区块的大小,这是区块的数据在没有进行对齐处理前的实际大小。                                 
                                                                                                   } Misc;
78h+10h*8h+(X-1)*28h+0ch = 104hDWORD VirtualAddress;            // 节区的 RVA 地址               (重要)
                                                   //   该区块装载到内存中的RVA 地址。
                                                   //   这个地址是按照内存页来对齐的,
                                                   //   因此它的数值总是 optional.SectionAlignment 的值的整数倍。
                                                   //   在Microsoft 工具中,第一个快的默认 RVA 总为1000h。
                                                   //   在OBJ 中,该字段没有意义地,并被设为0。
78h+10h*8h+(X-1)*28h+10h = 108hDWORD SizeOfRawData;             // 在文件中对齐后的尺寸            (重要)
                                                   //   该区块在磁盘中所占的大小。
                                                   //   在可执行文件中,该字段是已经被FileAlignment 潜规则处理过的长度。
78h+10h*8h+(X-1)*28h+14h = 10chDWORD PointerToRawData;          // 在文件中的偏移量                (重要)
                                                   //   该区块在磁盘中的偏移。这个数值是从文件头开始算起的偏移量哦。
                                                   //   第一个节的偏移地址 + 上面的 第一个节点大小 = 第二个节点偏移了
----                                                   
78h+10h*8h+(X-1)*28h+18h = 110hDWORD PointerToRelocations;      // 在OBJ文件中使用,重定位的偏移
78h+10h*8h+(X-1)*28h+1ch = 114hDWORD PointerToLinenumbers;      // 行号表的偏移(供调试使用地)
78h+10h*8h+(X-1)*28h+20h = 118hWORD NumberOfRelocations;      // 在OBJ文件中使用,重定位项数目
78h+10h*8h+(X-1)*28h+22h = 11ahWORD NumberOfLinenumbers;      // 行号表中行号的数目
-----
78h+10h*8h+(X-1)*28h+24h = 11chDWORD Characteristics;         // 节属性如可读,可写,可执行等    (重要)
                        具体内容可以参考MSDN在线文档:http://msdn.microsoft.com/en-us/library/ms680341%28v=vs.85%29.aspx
-------------------
                        IMAGE_SCN_CNT_CODE
                        0x00000020
                        
                        The section contains executable code.
                        包含代码,常与 0x10000000一起设置。
-------------------                        
                        IMAGE_SCN_CNT_INITIALIZED_DATA
                        0x00000040
                        
                        The section contains initialized data.
                        该区块包含以初始化的数据。
---------------------                        
                        IMAGE_SCN_CNT_UNINITIALIZED_DATA
                        0x00000080
                        
                        The section contains uninitialized data.
                        该区块包含未初始化的数据。
----------------------                        
                        IMAGE_SCN_MEM_DISCARDABLE
                        0x02000000
                        
                        The section can be discarded as needed.
                        该区块可被丢弃,因为当它一旦被装入后,
                        进程就不在需要它了,典型的如重定位区块。
---------------------------                        
                        IMAGE_SCN_MEM_SHARED
                        0x10000000
                        The section can be shared in memory.
                        该区块为共享区块。
-----------------------------------------                        
                        IMAGE_SCN_MEM_EXECUTE
                        0x20000000
                        
                        The section can be executed as code.
                        该区块可以执行。通常当0x00000020被设置
                        时候,该标志也被设置。
---------------------------------------                        
                        IMAGE_SCN_MEM_READ
                        0x40000000
                        
                        The section can be read.
                        该区块可读,可执行文件中的区块总是设置该
                        标志。
--------------------------------------------                        
                        IMAGE_SCN_MEM_WRITE
                        0x80000000
                        The section can be written to.
                        该区块可写。
-----------------------------------------------
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;


到此为止:以最后一个节区的所有数据为文件结束,如下:
    X 为最后一个节区
    (78h+10h*8h+(X-1)*28h+14h = 10chDWORD PointerToRawData) + (78h+10h*8h+(X-1)*28h+10h = 108hDWORD SizeOfRawData)
                                  最后一个节区的 偏移地址   +   最后一个节区的 大小==文件的最后结束 地址。
   
=========================================================

下面根据 IMAGE_OPTIONAL_HEADER 的最后一个字段 的数据目录(输出表、输入表、资源表 和 重定位表 等等)
         +78h IMAGE_DATA_DIRECTORY DataDirectory
         和 节表头
         IMAGE_SECTION_HEADER 的所有节点信息
         来对 所有RVA(虚拟相对偏移地址)进行换算成 文件中磁盘上的 文件偏移地址
         RVA = 文件加载进内存中,系统给定的虚拟地址的偏移,知道RVA 换算 文件偏移
         换算方法如下:
                                                步骤一:循环扫描区块表得出每个区块在内存中的起始 RVA
                                                      (根据IMAGE_SECTION_HEADER 中的VirtualAddress 字段),
                                                      并根据区块的大小(根据IMAGE_SECTION_HEADER 中的SizeOfRawData 字段)
                                                      算出区块的结束 RVA(两者相加即可),最后判断目标 RVA 是否落在该区块内。
                                                
                                                步骤二:通过步骤一定位了目标 RVA 处于具体的某个区块中后,
                                                      那么用目标 RVA 减去该区块的起始 RVA ,
                                                      这样就能得到目标 RVA 相对于起始地址的偏移量 RVA2.
                                                
                                                步骤三:在区块表中获取该区块在文件中所处的偏移地址
                                                      (根据IMAGE_SECTION_HEADER 中的PointerToRawData 字段),
                                                      将这个偏移值加上步骤二得到的 RVA2 值,
                                                      就得到了真正的文件偏移地址。
         根据步骤便可以得到 数据目录 中任何一个表 的文件偏移,
         也可以 用反汇编工具定位一个有用的RVA(如:某函数的RVA)来换算文件偏移。
         
---------------------------------------------------
下面根据以上方法获得 数据目录中 directory
    一、输入表
    二、输出表
    三、重定位表
    四、资源表
   
    4个表的文件偏移来定位它们各表的结构,以获得各表中 所指定的信息(如:某输入输出函数 、 某资源、)。
------------------------------------------------------
------------------------------------------------------

一、 先说说 输入表 函数的查找 总共有 三层结构

IMAGE_IMPORT_DESCRIPTOR STRUCT
{
union
DWORD   ?Characteristics            
DWORD   ?OriginalFirstThunk   // 它指向first thunk, IMAGE_THUNK_DATA ,该 thunk 拥有 Hint 和 Function name 的地址。
                                    //   同样是 第二层结构我们称为 INT 是单独的一项,而且不能被改写。(重要)
ends

DWORD   ?TimeDateStamp                     
DWORD   ?ForwarderChain                     
DWORD   ?Name                     // 它表示DLL 名称的相对虚地址(译注:相对一个用null作为结束符的ASCII字符串的一个RVA,
                                    //   该字符串是该导入DLL文件的名称,如:KERNEL32.DLL)。         (次要)               
DWORD   ?FirstThunk               // 它包含由 IMAGE_THUNK_DATA 定义的 first thunk数组的虚地址,
                                    //   通过loader用函数虚地址初始化thunk。
                                    //   First Thunk缺席下,它指向first thunk:Hints和The Function names的thunks。
                                    //   同样是 第二层结构我们称为 IAT事实上是由 PE 装载器重写的。(重要)
}                        
IMAGE_IMPORT_DESCRIPTOR

---------
输入表 由第一层指向的第二层结构
当 IMAGE_THUNK_DATA 值的最高位为 1时,表示函数以序号方式输入,这时候低 31位被看作一个函数序号。
当 IMAGE_THUNK_DATA 值的最高位为 0时,表示函数以字符串类型的函数名方式输入,这时双字的值是一个 RVA,
                                    指向一个 IMAGE_IMPORT_BY_NAME 结构。(演示请看小甲鱼解密系列视频讲座)
IMAGE_THUNK_DATA STRUC    (重要)
                union u1
                              DWORD?   ForwarderString             ; 指向一个转向者字符串的RVA
                              DWORD?   Function                  ; 被输入的函数的内存地址
                              DWORD?   Ordinal                     ; 被输入的API 的序数值
                              DWORD?   AddressOfData               ; 指向 IMAGE_IMPORT_BY_NAME
                ends
IMAGE_THUNK_DATA ENDS
---------

输入表 由第二层指向的第三层结构
IMAGE_IMPORT_BY_NAME 结构仅仅只有一个字型数据的大小,存有一个输入函数的相关信息结构
IMAGE_IMPORT_BY_NAME STRUCT(重要)
{
                WORD    ?   Hint      //结构中的 Hint 字段也表示函数的序号,不过这个字段是可选的,有些编译器总是将它设置为 0
                BYTE    ?   Name      //Name 字段定义了导入函数的名称字符串,这是一个以 0 为结尾的字符串。
}
IMAGE_IMPORT_BY_NAME ENDS

                  PE 装载器首先搜索 OriginalFirstThunk ,找到之后加载程序迭代搜索数组中的每个指针,
                找到每个 IMAGE_IMPORT_BY_NAME 结构所指向的输入函数的地址,
                然后加载器用函数真正入口地址来替代由 FirstThunk 数组中的一个入口,因此我们称为输入地址表(IAT)。

------------------------------------------------------
------------------------------------------------------

二、输出表(导出表) 详解一层结构

IMAGE_EXPORT_DIRECTORY STRUCT
{
      DWORD      ?Characteristics                            ; 未使用,总是定义为0
      DWORD      ?TimeDateStamp                        ; 文件生成时间
      WORD      ?MajorVersion                              ; 未使用,总是定义为0
      WORD      ?MinorVersion                              ; 未使用,总是定义为0
      DWORD      ?Name                                              ; 模块的真实名称
      DWORD      ?Base                                             ; 基数,加上序数就是函数地址数组的索引值
      DWORD      ?NumberOfFunctions                     ; 导出函数的总数
      DWORD      ?NumberOfNames                        ; 以名称方式导出的函数的总数
      DWORD      ?AddressOfFunctions                     ; 指向输出函数地址的RVA
      DWORD      ?AddressOfNames                              ; 指向输出函数名字的RVA
      DWORD      ?AddressOfNameOrdinals               ; 指向输出函数序号的RVA
}
IMAGE_EXPORT_DIRECTORY ENDS

(1). 从序号查找函数入口地址

模拟一下Windows 装载器查找导出函数入口地址的整个过程。如果已知函数的导出序号,如何得到函数的入口地址呢 ?

                Windows 装载器的工作步骤如下:
                        1.定位到PE 文件头
                        
                        2.从PE 文件头中的 IMAGE_OPTIONAL_HEADER32 结构中取出数据目录表,并从第一个数据目录中得到导出表的RVA
                        
                        3.从导出表的 Base 字段得到起始序号
                        
                        4.将需要查找的导出序号减去起始序号,得到函数在入口地址表中的索引
                        
                        5.检测索引值是否大于导出表的 NumberOfFunctions 字段的值,如果大于后者的话,说明输入的序号是无效的
                        
                        6.用这个索引值在 AddressOfFunctions 字段指向的导出函数入口地址表中取出相应的项目,
                        这就是函数入口地址的RVA 值,当函数被装入内存的时候,这个RVA 值加上模块实际装入的基地址,就得到了函数真正的入口地址

(2). 从函数名称查找入口地址

如果已知函数的名称,如何得到函数的入口地址呢?与使用序号来获取入口地址相比,这个过程要相对复杂一点

                Windows 装载器的工作步骤如下:
                        1.最初的步骤是一样的,那就是首先得到导出表的地址
                        
                        2.从导出表的 NumberOfNames 字段得到已命名函数的总数,并以这个数字作为循环的次数来构造一个循环
                        
                        3.从 AddressOfNames 字段指向得到的函数名称地址表的第一项开始,在循环中将每一项定义的函数名与要查找的函数名相比较,
                        如果没有任何一个函数名是符合的,表示文件中没有指定名称的函数
                        
                        4.如果某一项定义的函数名与要查找的函数名符合,那么记下这个函数名在字符串地址表中的索引值,
                        然后在 AddressOfNamesOrdinals 指向的数组中以同样的索引值取出数组项的值,我们这里假设这个值是x
                        
                        5.最后,以 x 值作为索引值,在 AddressOfFunctions 字段指向的函数入口地址表中获取的 RVA 就是函数的入口地址

------------------------------------------------------
------------------------------------------------------

三、基址重定位详解一层结构

    但凡涉及到直接寻址的指令都需要进行重定位处理
      ( 那什么是直接寻址? 咱在零基础入门学习汇编里边讲得很清楚啦,
      只要在机器码中看到有地址的,那就叫直接寻址……那有没有间接的?
      肯定哈,间接的就是地址被间接的保存起来,例如存放在寄存器eax,
      然后通过访问寄存器来获取地址,那就叫间接 )

IMAGE_BASE_RELOCATION STRUC
{
                DWORD      ?VirtualAddress          ; 重定位数据开始的RVA 地址
                DWORD      ?SizeOfBlock             ; 重定位块得长度
                DWORD      ?TypeOffset            ; 重定项位数组
}
IMAGE_BASE_RELOCATIONENDS

VirtualAddress 是 Base Relocation Table 的位置它是一个 RVA 值;
SizeOfBlock    是 Base Relocation Table 的大小;
TypeOffset   是一个数组,数组每项大小为两个字节(16位),
                   它由高 4位和低 12位组成,高 4位代表重定位类型,低 12位是重定位地址,
                   它与 VirtualAddress 相加即是指向PE 映像中需要修改的那个代码的地址。
    下面是高4位所代表的类型值:
                        IMAGE_REL_BASED_ABSOLUTE         (0) 使块按照32位对齐,位置为0。
                        IMAGE_REL_BASED_HIGH               (1) 高16位必须应用于偏移量所指高字16位。
                        IMAGE_REL_BASED_LOW                (2) 低16位必须应用于偏移量所指低字16位。
                        IMAGE_REL_BASED_HIGHLOW            (3) 全部32位应用于所有32位。
                        IMAGE_REL_BASED_HIGHADJ            (4) 需要32位,高16位位于偏移量,低16位位于下一个偏移量数组元素,
                                                                  组合为一个带符号数,加上32位的一个数,
                                                                  然后加上8000然后把高16位保存在偏移量的16位域内。
                        IMAGE_REL_BASED_MIPS_JMPADDR       (5)Unknown
                        IMAGE_REL_BASED_SECTION            (6)Unknown
                        IMAGE_REL_BASED_REL32            (7)Unknown


------------------------------------------------------
------------------------------------------------------
五、资源表详解三层 同样的结构 不同的 意义 ????????

IMAGE_RESOURCE_DIRECTORY 这个结构,该结构长度为 16 字节,共有 6 个字段,定义如下:

IMAGE_RESOURCE_DIRECTORY STRUCT
{
                DWORD      ?Characteristics         ;理论上为资源的属性,不过事实上总是0
                DWORD      ?TimeDateStamp             ;资源的产生时刻
                DWORD      ?MajorVersion            ;理论上为资源的版本,不过事实上总是0
                DWORD      ?MinorVersion         
                DWORD      ?NumberOfNamedEntries      ;以名称(字符串)命名的入口数量(重要)
                DWORD      ?NumberOfIdEntries         ;以ID(整型数字)命名的入口数量(重要)
}
IMAGE_RESOURCE_DIRECTORY ENDS

                其实在这里边我们唯一要注意的就是 NameberOfNamedEntries 和 NumberOfIdEntries,
                              它们说明了本目录中目录项的数量。两者加起来就是本目录中的目录项总和。
                              也就是后边跟着的IMAGE_RESOURCE_DIRECTORY_ENTRY 数目。
                              
                              
IMAGE_RESOURCE_DIRECTORY_ENTRY 紧跟在资源目录结构后,此结构长度为 8 个字节,包含 2 个字段。该结构定义如下:

IMAGE_RESOURCE_DIRECTORY_ENTRY STRUCT
{
DWORD    ?Name                     ;目录项的名称字符串指针或ID
                                    Name 字段完全是个百变精灵,改字段定义的是目录项的名称或ID。
                                           当结构用于第一层目录时,定义的是资源类型;
                                           当结构定义于第二层目录时,定义的是资源的名称;
                                           当结构用于第三层目录时,定义的是代码页编号。
                                    注意:当最高位为 0的时候,表示字段的值作为 ID 使用;
                                          而最高位为 1的时候,字段的低位作为指针使用(资源名称字符串是使用 UNICODE编码),
                                          但是这个指针不是直接指向字符串哦,
                                          而是指向一个 IMAGE_RESOURCE_DIR_STRING_U 结构的。
                                          
                                    IMAGE_RESOURCE_DIR_STRING_U STRUCT
                                                                                                                                                      {
                                                                                                                                                      DWORD       ?Length                ; 字符串的长度
                                                                                                                                                      DWORD       ?NameString            ; UNICODE字符串,由于字符串是不定长的。由Length 制定长度
                                                                                                                                                      }
                                                                                                                                                      IMAGE_RESOURCE_DIR_STRING_U ENDS
DWORD    ?OffsetToData             ;目录项指针
                                    OffsetOfData 字段是一个指针,
                                          当最高位为 1 时,低位数据指向下一层目录块的其实地址;
                                          当最高位为 0 时,指针指向 IMAGE_RESOURCE_DATA_ENTRY 结构。
}
IMAGE_RESOURCE_DIRECTORY_ENTRY ENDS


资源数据入口
    经过三层 IAMGE_RESOURCE_DIRECTORY_ENTRY
       (一般是3层,偶尔更年期少一些。第一层资源类型,第二层资源名,第三层是资源的 Language),
       第三层目录结构中的 OffsetOfData 指向 IMAGE_RESOURCE_DATA_ENTRY 结构。
       该结构描述了资源数据的位置和大小,定义如下:
IMAGE_RESOURCE_DATA_ENTRY STRUCT
{
DWORD   ?OffsetToData         ; 资源数据的RVA
DWORD   ?Size               ; 资源数据的长度
DWORD   ?CodePage             ; 代码页, 一般为0
DWORD   ?Reserved             ; 保留字段
}
IMAGE_RESOURCE_DATA_ENTRY ENDS

                              此处的 IMAGE_RESOURCE_DATA_ENTRY 结构就是真正的资源数据了。
                              结构中的 OffsetToData 指向资源数据的指针,其为 RVA 值。
                              
                              
资源查找流程步骤 (注:下面是以ID索引的流程,如果有名字索引的资源,还必须索引 IMAGE_RESOURCE_DIR_STRING_U 结构)
    一层 IMAGE_RESOURCE_DIRECTORY      资源类型 和 资源总数
         IMAGE_RESOURCE_DIRECTORY_ENTRY资源总数有几个 这里就有几个这个结构
                                       此处 IMAGE_RESOURCE_DIRECTORY_ENTRY.OffsetToData
                                              当最高位为 1 时,低位数据指向下一层目录块的其实地址;
                                              当最高位为 0 时,指针指向 IMAGE_RESOURCE_DATA_ENTRY 结构。
         
         二层由 上层 IMAGE_RESOURCE_DIRECTORY_ENTRY.OffsetToData 相对资源地址的偏移地址 指向的 IMAGE_RESOURCE_DIRECTORY
            IMAGE_RESOURCE_DIRECTORY      资源名 和 总数
            IMAGE_RESOURCE_DIRECTORY_ENTRY总数有几个 这里就有几个这个结构
                                              此处 IMAGE_RESOURCE_DIRECTORY_ENTRY.OffsetToData
                                                当最高位为 1 时,低位数据指向下一层目录块的其实地址;
                                                当最高位为 0 时,指针指向 IMAGE_RESOURCE_DATA_ENTRY 结构。
            
                        三层由 上层 IMAGE_RESOURCE_DIRECTORY_ENTRY.OffsetToData 相对资源地址的偏移地址 指向的 IMAGE_RESOURCE_DIRECTORY
                               IMAGE_RESOURCE_DIRECTORY      资源 Language 和 总数
                               IMAGE_RESOURCE_DIRECTORY_ENTRY总数有几个 这里就有几个这个结构
                                                               此处 IMAGE_RESOURCE_DIRECTORY_ENTRY.OffsetToData
                                                      当最高位为 0 时,指针指向 IMAGE_RESOURCE_DATA_ENTRY 结构。
                              
                               真正的资源数据 由 上层 IMAGE_RESOURCE_DIRECTORY_ENTRY.OffsetToData 想对资源地址的偏移地址
                                 指向的 IMAGE_RESOURCE_DATA_ENTRY STRUCT 结构
                                 
                                 最后:
                                       由   IMAGE_RESOURCE_DATA_ENTRY.OffsetToData 指向的第一个资源的 RVA 地址
                                       再由 IMAGE_RESOURCE_DATA_ENTRY.Size         算出第一个资源的 大小
                                       
                                       最终:
                                           根据RVA 和 大小 得到 第一个资源的 的所有二进制 代码。
                                          
                                       


======================================================
======================================================
======================================================
======================================================
DOs头和PE头 小结
exe文件中要数据索引方式
    下面定义各个结构变量:
_DosHeader      : IMAGE_DOS_HEADER      DOS 头   结构
_NtHeader       : IMAGE_NT_HEADER       NT头   结构
_FileHeader   : IMAGE_FILE_HEADER_    文件头   结构
_OptionalHeader : IMAGE_OPTIONAL_HEADER 可选头   结构
_DataDirectory: IMAGE_DATA_DIRECTORY数据目录 结构
PEaddr          : DWORD               PE偏移地址

   下面对PE头文件中的重要数据进行索引
+0h(2)=_DosHeader.e_magic            =WORD DOS可执行文件标记MZ(4Dh 5Ah)
+3ch(4)=PEaddr = _DosHeader.e_lfanew   =DWORD 指向PE文件头的偏移地址

PEaddr+0h      = _NtHeader                           = PE头    偏移地址
(2) = _NtHeader.Signature               = WORD 签名(4个字节) PE..

PEaddr+4h      = _FileHeader = _NtHeader.FileHeader   = 文件头 偏移地址
(2) = _FileHeader.Machine;               = 运行平台
(2) = _FileHeader.NumberOfSections         = WORD 文件的区块数目
(2) = _FileHeader.SizeOfOptionalHeader   = WORD IMAGE_OPTIONAL_HEADER32 结构大小

PEaddr+18      = _OptionalHeader = _NtHeader.OptionalHeader = 可选头 偏移地址
(2) = _OptionalHeader.Magic                =标志字, ROM 映像(0107h),普通可执行文件(010Bh)
(4) = _OptionalHeader.AddressOfEntryPoint=程序执行入口    RVA 相对基址的地址 (重要)
(4) = _OptionalHeader.BaseOfCode         =代码的区块的起始RVA 相对基址的地址 (重要)
(4) = _OptionalHeader.BaseOfData         =数据的区块的起始RVA 相对基址的地址 (重要)
(4) = _OptionalHeader.ImageBase            =程序的首选装载入内存的地址         (重要)
(4) = _OptionalHeader.SectionAlignment   =内存中的区块的对齐大小             (重要)
(4) = _OptionalHeader.FileAlignment      =文件中(磁盘中)的区块的对齐大小   (重要)
(4) = _OptionalHeader.SizeOfImage          =映像装入内存后的总尺寸
(4) = _OptionalHeader.NumberOfRvaAndSizes=下边数据目录的项数,这个字段自Windows NT 发布以来 一直是16

//数据目录表 对齐节表地址需要换算
PEaddr+78             = _DataDirectory[..] = _OptionalHeader.DataDirectory
PEaddr+78             = _DataDirectory[..] = _OptionalHeader.DataDirectory = 16个数据目录结构数组 偏移地址
(4)    = _DataDirectory.VirtualAddress=导出表 数据的起始RVA(重要)
(4)= _DataDirectory.isize         =导出表 数据块的长度

(4)    = _DataDirectory.VirtualAddress=导入表 数据的起始RVA(重要)
(4)= _DataDirectory.isize         =导入表 数据块的长度

(4)    = _DataDirectory.VirtualAddress=资源表 数据的起始RVA(重要)
(4)= _DataDirectory.isize         =资源表 数据块的长度

(4)    = _DataDirectory.VirtualAddress=重定位表 数据的起始RVA(重要)
(4)= _DataDirectory.isize         =重定位表 数据块的长度

(4)   = _DataDirectory.VirtualAddress =导入函数地址表 数据的起始RVA(重要)
(4) = _DataDirectory.isize          =导入函数地址表 数据块的长度





====================================================================================================

cn01574978 发表于 2019-7-22 16:17

sungod 发表于 2019-7-22 14:56
感谢分享,不过有个小建议,建议排版一下,看着有些乱

笔记是文本文件,复制粘贴后,怎么有些地方就有点乱了,在text文件里,很整齐呢。{:1_908:}

cn01574978 发表于 2019-7-22 16:14

snatch2null 发表于 2019-7-22 15:03
看起来不少是直接拷贝(?)的英文原文……
楼主多多整理一下吧 发不发出来倒无所谓

嗯嗯,英文的确是拷贝,重要的地方都翻译中文做成笔记,字节是一个一个数出来的了,用来快捷定位重要信息位置呢。{:1_893:}

wapjlpl 发表于 2019-7-22 12:40

这个总结很好啊,节省不少时间,谢谢分享

LoongKing 发表于 2019-7-22 12:40

兄弟,建议使用makedown排版一下,有点乱啊

Trample 发表于 2019-7-22 12:44

表示我一个新手只能看得懂一点点:'(weeqw

帅得一笔 发表于 2019-7-22 12:57

总结的很好,收藏学习一下

wuxiaojie 发表于 2019-7-22 13:36

====================
仿佛看到了自己的笔记{:1_893:}
====================

PRETEXT 发表于 2019-7-22 14:08

大佬,看不懂

niuyanpeng 发表于 2019-7-22 14:39

已经复制到我的记事本了
{:1_907:}

乖小墨 发表于 2019-7-22 14:52

收藏一下感谢

sungod 发表于 2019-7-22 14:56

感谢分享,不过有个小建议,建议排版一下,看着有些乱
页: [1] 2 3 4 5 6 7
查看完整版本: 初学逆向,自己的PE分析笔记,适合新人,高手略过或指点哈。