初学逆向,自己的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 =导入函数地址表 数据块的长度
==================================================================================================== sungod 发表于 2019-7-22 14:56
感谢分享,不过有个小建议,建议排版一下,看着有些乱
笔记是文本文件,复制粘贴后,怎么有些地方就有点乱了,在text文件里,很整齐呢。{:1_908:} snatch2null 发表于 2019-7-22 15:03
看起来不少是直接拷贝(?)的英文原文……
楼主多多整理一下吧 发不发出来倒无所谓
嗯嗯,英文的确是拷贝,重要的地方都翻译中文做成笔记,字节是一个一个数出来的了,用来快捷定位重要信息位置呢。{:1_893:} 这个总结很好啊,节省不少时间,谢谢分享 兄弟,建议使用makedown排版一下,有点乱啊 表示我一个新手只能看得懂一点点:'(weeqw 总结的很好,收藏学习一下 ====================
仿佛看到了自己的笔记{:1_893:}
==================== 大佬,看不懂 已经复制到我的记事本了
{:1_907:} 收藏一下感谢 感谢分享,不过有个小建议,建议排版一下,看着有些乱