本文整理自《加密与解密》第10章

 PE文件结构

一、基本概念

PE文件结构

基地址:映射文件的起始地址被称为模块句柄,可以通过模块句柄访问内存中其他的数据结构。这个初始内存地址被称为基地址

 PE文件结构

GetModuleHandle函数返回可执行文件的基地址。

 

相对虚拟地址(RVA):RVA只是内存中的一个简单的相对于PE文件装入地址的偏移位置。

实际的内存地址被称作虚拟地址(VA)

VA=ImageBase+RVA

 

文件偏移地址:PE文件存储在磁盘上时,某个数据的位置相对于文件头的偏移量,称为文件偏移地址(File Offset)或物理地址(RAWOffset)。用WinHex打开文件所显示的地址就是文件偏移地址。

二、PE文件头

PNTHeader=ImageBase+dosHeader->e_lfanew

IMAGE_NT_HEADERS

{

+0h          DWORD Signature;        PE00

+4h         IMAGE_FILE_HEADER FileHeader;

+18h        IMAGE_OPTIONAL_HEADER32 OptionalHeader;

}

Signature4个字节

 

IMAGE_FILE_HEADER

{

WORD Machine;

WORDNumberOfSections;区块数目

DWORDTimeDateStamp;时间戳

DWORDPointerToSymbols;

DWORDNumberOfSymbols;

WORDSizeOfOptionalHeader;指出了OptionalHeader的大小,32位PE一般是00E0h,64位是00F0h

WORDCharacteristics;

}

IMAGE_FILE_HEADER结构体一共20个字节

PE文件结构

IMAGE_OPTIONAL_HEADER32

{

WORD Magic;

BYTE MajorLinkerVersion;

BYTE MinorLinkerVersion;

DWORD SizeOfCode;

DWORD SizeOfInitializedData;

DWORD SizeOfUnInitializedData;

DWORD AddressOfEntryPoint; 程序执行入口RVA

DWORD BaseOfCode;

DWORD BaseOfData;

DWORD ImgaeBase;      程序默认装入基地址

DWORD SectionAlignment;

DWORD FileAlignment;

WORD MajorOperatingSystemVersion;

WORD MinorOperatingsystemversion;

WORD MajorImageVersion;

WORD MinorImageVersion;

WORD MajorSubsybtemVersion;

WORD MinorSubsybtemVersion;

DWORD Win32VersionValue;

DWORD SizeOfImage; 映像.

装入内存后的总尺寸,一直是16

DWORD SizeoOfHeaders;

DWORD CheckSum;

WORD Subsystem;

WORD DllCharacteristics;

DWORD SizeOfStackReserve;

DWORD SizeOfStackCommit;

DWORD SizeOfHeapReserve;

DWORD SizeOfHeapCommit;

DWORD LoaderFlages;

DWORD NumberOfRvaAndSizes;        数据目录表的项数

IMAGE_DATA_DIRECTORYDataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];

}

一直到NumberOfRvaAndSizes结束,一共96字节。不加DataDirectory

PE文件结构

PE文件结构

此例中,DataDirectory一共128字节。

每个成员占8个字节,

IMAGE_DATA_DIRECTORY{

        VirtualAddress        DWORD//数据块的起始RVA

        Size                          DWORD//数据块的长度

}

三、区块

IMAGE_SECTION_HEADER

{

//IMAGE_SIZEOF_SHORT_NAME=8

BYTE Name[IMAGE_SIZEOF_SHORT_NAME];//节表名称,如“.text”

union

{

DWORD PhysicalAddress;//物理地址

DWORD VirtualSize;//指出实际的、被使用的区块大小,是区块在没对齐处理前的实际大小。

} Misc;

DWORD VirtualAddress;//该区块装载到内存中的RVA

DWORD SizeOfRawData;//该区块在磁盘文件中所占大小

DWORD PointerToRawData;//该区块在磁盘文件中的偏移,这个字段用于给出原始数据在文件中的偏移

DWORD PointerToRelocations;//重定位的偏移

DWORD PointerToLinenumbers;//行号表的偏移

WORD NumberOfRelocations;//重定位项数目

WORD NumberOfLinenumbers;//行号表的数目

DWORD Characteristics;//节属性如可读,可写,可执行等

}

每一个区块都有一个这样的结构。每个结构20字节。

PE文件结构

3.1 区块的对齐值

PE文件结构

PE文件结构

3.2        文件偏移与虚拟地址转换

PE文件结构PE文件结构

ImageBase=400000,VA=401112h

RVA=1112h,位于.text区块,RAW=1112h-1000h+400h=512h

四、输入表

4.1 输入表结构

输入表以一个IMAGE_IMPORT_DESCRIPTOR(简称IID)数组开始,每个被PE文件隐式地连接进来的DLL都有一个IID,在IID结构的最后,由一个内容为全0的IID结构作为结束。

IMAGE_IMPORT_DESCRIPTOR STRUC

        union                                                                  ;       00h

                 Characteristics                 DWORD

                 OriginalFirstThunk         DWORD

        ends

        TimeDateStamp                       DWORD         ;04h

        ForwarderChain                       DWORD         ;08h

        Name                                         DWORD         ;0Ch

        FirstThunk                               DWORD         ;10h

IMAGE_IMPORT_DESCRIPTOR       ENDS

PE文件结构

PE文件结构

PE文件结构

IMAGE_THUNK_DATA是一个指针大小的联合,每一个IMAGE_THUNK_DATA对应于一个从可执行文件输入的函数。

IMAGE_THUNK_DATA STRUC

        unionu1

                 ForwarderString              DWORD         ;

                 Function                           DWORD         ;被输入的函数的内存地址

                 Ordinal                             DWORD         ;被输入的APT的序数值

                 AddressOfData                DWORD         ;指向IMAGE_IMPORT_BY_NAME

        ends

IMAGE_THUNK_DATA ENDS

PE文件结构

IMAGE_IMPORT_BY_NAME STRUCT

Hint                          WORD;

指示本函数在其所主流DLL的输出表中的序号,该值不是必须的,一般设置为0。

        Name                        BYTE;含有输入函数的函数名。ASCII字符串

IMAGE_IMPORT_BY_NAME ENDS


4.2 输入地址表(IAT)

OriginalFirstThunk所指向的INT是不可改写的,由FirstThunk所指向IAT是由PE装载器重写的。PE装载器首先搜索OriginalFirstThunk,找到每个IMAGE_IMPORT_BY_NAME结构所指向的输入函数的地址,然后用函数真正入口地址替代由FirstThunk指向的IMAGE_THUNK_DATA数组里的元素值。

PE文件结构

4.3 输入表实例

PE文件头起始位置是B0h,输入表地址就在B0h+80h=130h处

PE文件结构

PE文件结构

130h处为00002040h,是输入表在内存中偏移量为2040h的地方,是RVA值,需转换为文件偏移量。

从下图可以看出,2040h在.rdata块中,转换为文件偏移是600h+(2040h-2000h)=640h

PE文件结构

输入表大小为0x3C,一个IID是5个双字,一共有3个IID,最后一个IID全为0

PE文件结构

每个都是4字节。

OrignalFirstThunk

TimeDateStamp

ForwardChain

Name

FirstThunk

0000208C

00000000

00000000

00002174

00002010

0000207C

00000000

00000000

000021B4

00002000

00000000

00000000

00000000

00000000

00000000

 

RVA:2174h->RAW:174+600=774h,文件偏移774h处的字符是USER32.dll

PE文件结构

RVA:208Ch->RAW:68Ch 文件偏移68Ch处的为IMAGE_THUNK_DATA数组,存储指向IMAGE_IMPORT_BY_NAME结构的地址,以一串00结束。每一个的大小是一个双字,4个字节。

PE文件结构

 

00002110

0000211c

000020f4

000020e0

00002150

00002164

00002102

000020ce

000020bc

0000212e

00002142

00000000

RVA:2110h->RAW:710h

文件偏移710h处的存的是LoadIconA,前面有两个字节的空缺,是作为函数名(Hint)引用的,可以为0.

PE文件结构

一开始的IID结构如下,可以看到,IAT数据中还是保存着AddressOfData的值

PE文件结构

根据找到的函数名,调用GetProctorAddress函数,获取函数的入口地址,用函数入口地址去掉FirstThunk指向的地址串中对应的值(IAT)。

PE文件结构

五、基址重定位

5.1 基址重定位结构定义

IMAGE_SBASE_RELOCATION STRUC

        VirtualAddress                DWORD;重定位数据开始RVA地址

        SizeOfBlock                    DWORD;重定位块的长度

        TypeOffset               WORD;重定项位数组

IMAGE_BASE_RELOCATION ENDS

PE文件结构

PE文件结构

5.2 基址重定位结构实例分析

PE文件结构

PE文件头起始位置是0x00000100,加上重定位表的偏移A0,文件偏移0x1A0处便是重定位表结构。RVA为0x5000,大小为0x10

PE文件结构

PE文件结构

 

.reloc的RVA是5000h,RAW是E00h。

PE文件结构

VirtualAddress:0x1000

SizeOfBlock:0x10

一共有(10h-8h)/2h=4h个TypeOffset

注:VirtualAddress和SizeOfBlock都是4个字节,一个8字节,这个块大小是0x10,TypeOffset的大小是2个字节。

重定位数据1:300F

重定位数据2:3023h

重定位数据3:0000h

重定位数据4:0000h

PE文件结构

PE文件结构

六、输出表

IMAGE_EXPORT_DIRECTORY

        Characteristics                 DWORD;未使用,总是0

        TimeDateStamp               DWORD;文件生成时间

        MajorVersion                   WORD;主版本号,一般为0

        MinorVersion                  WORD;次版本号,一般为0

        Name                                DWORD;模块的真实名称

        Base                                  DWORD;基数,加上序数就是函数地址数组的索引值

        NumberOfFunctions       DWORD;AddressOfFunctions阵列中的元素个数

        NumberOfNames            DWORD;AddressOfNames阵列中的元素个数

        AddressOfFunctions       DWORD;指向函数地址数组,EAT的RVA

        AddressOfNames            DWORD;函数名字的指针地址ENT的RVA

        AddressOfNameOrdinals        DWORD;指向输出***数组,输出序数表的RVA

IMAGE_EXPORT_DIRECTORY ENDS

PE文件结构

 

输出表实例:

0x0100+0x78=0x0178

输出表RVA:00004000h,大小0x45

PE文件结构

RVA:4000h->RAW:C00(这个不是计算来的,从表中看来的)

PE文件结构

输出表:

PE文件结构

                                  RVA                          RAW

Name:                    0x4032                    0xC32              DllDemo.DLL

NumberOfFunctions       0x01       

        NumberOfNames            0x01

        AddressOfFunctions       0x4028            0xC28              0x1008

        AddressOfNames            0x402c            0xC2C             0x403E->C3E->MsgBox

        AddressOfNameOrdinals        0x4030    0xC30

相关文章:

  • 2021-06-20
  • 2021-06-11
  • 2021-04-16
  • 2021-12-22
  • 2021-11-06
  • 2021-05-24
  • 2021-07-15
  • 2022-12-23
猜你喜欢
  • 2022-12-23
相关资源
相似解决方案