之前学习了PE格式, 为了更好的理解,决定写一个类似LoadPE 的小工具。
编译器是VS2015 ,采用MFC 框架。
此系列文章采用边介绍知识点,边写代码的形式,以免变的无聊丧失兴趣。
PE知识请参照《加密与解密》第10章
文章有错误或则不清楚的地方还请您指出。
文章有错误或则不清楚的地方还请您指出。
PE文件是windows系统中遵循PE结构的文件,比如以.exe .dll 为后缀名的文件 以及系统驱动文件。( PE结构框架看下图 )
PE文件大体分为两部分,头(包括下图中的
DOS头,PE文件头,块表 )与主体(
块 ),
PE文件从磁盘当中像内存中的映射,不是简单的 “1对1 ”的关系,而是 “拉长” 了。具体的位置表现在 块。 但是 磁盘上的数据结构与在内存中的结构是一致的。
无论PE文件在磁盘中还是在内存中,都少不了地址的概念,理解一下几个概念至关重要。
虚拟地址(VA): 在一个程序运行起来的时候,会被加载到内存中,并且每个进程都有自己的4GB,这个4GB当中的某个位置叫做**虚拟地址 **,由物理地址映射过来的,4GB的空间,并没有全部被用到。
基地址(
Imagebase
): 磁盘中的文件加载到内存当中的时候可以加载到任意位置,而这个位置就是程序的基址 。EXE默认的加载基址是400000h,DLL文件默认基址是10000000h。需要注意的是基地址不是程序的入口点。
相对虚拟地址(RVA): 为了避免PE文件中有确定的内存地址,引入了相对虚拟地址的概念。RVA是在内存中相对与载入地址(
基地址 )
的偏移量,所以你可以发现前三个概念的关系 :
虚拟地址(VA)=
基地址+
相对虚拟地址(RVA)
的偏移量,所以你可以发现前三个概念的关系 :
虚拟地址(VA)=
基地址+
相对虚拟地址(RVA)
文件偏移地址(FOA): 当PE文件储存在某个磁盘当中的时候,某个数据的位置相对于文件头的偏移量。
入口点(OEP): 首先明确一个概念就是OEP是一个RVA,,然后使用 OEP + Imagebase == 入口点的VA ,通常情况下,OEP指向的不是main 函数。
接下来依次介绍PE结构框图的每个部分
2.DOS头部
每个PE文件都是以DOS头开始的,IMAGE_DOS_HEADER 结构如下所示
(最左边是文件头的偏移量。)
IMAGE_DOS_HEADER STRUCT
{
+0h WORD e_magic // MZ(4Dh 5Ah) DOS可执行文件标记
+2h WORD e_cblp
+4h WORD e_cp
+6h WORD e_crlc
+8h WORD e_cparhdr
+0ah WORD e_minalloc
+0ch WORD e_maxalloc
+0eh WORD e_ss
+10h WORD e_sp
+12h WORD e_csum
+14h WORD e_ip
+16h WORD e_cs
+18h WORD e_lfarlc
+1ah WORD e_ovno
+1ch WORD e_res[4]
+24h WORD e_oemid
+26h WORD e_oeminfo
+29h WORD e_res2[10]
+3ch DWORD e_lfanew // RVA 指向PE文件头
} IMAGE_DOS_HEADER ENDS
(最左边是文件头的偏移量。)
IMAGE_DOS_HEADER STRUCT
{
+0h WORD e_magic // MZ(4Dh 5Ah) DOS可执行文件标记
+2h WORD e_cblp
+4h WORD e_cp
+6h WORD e_crlc
+8h WORD e_cparhdr
+0ah WORD e_minalloc
+0ch WORD e_maxalloc
+0eh WORD e_ss
+10h WORD e_sp
+12h WORD e_csum
+14h WORD e_ip
+16h WORD e_cs
+18h WORD e_lfarlc
+1ah WORD e_ovno
+1ch WORD e_res[4]
+24h WORD e_oemid
+26h WORD e_oeminfo
+29h WORD e_res2[10]
+3ch DWORD e_lfanew // RVA 指向PE文件头
} IMAGE_DOS_HEADER ENDS
需要关注的点是结构体的第一个和第二个元素。
e_magic: DOS头的标记位,值为4D5Ah。ASCII为”MZ“,判断一个文件是否为PE文件是会用
e_lfanew: 这是一个RVA,代表了PE文件头到基址的偏移量,我们可以用它来找到PE文件头的位置。
我们用010editor打开一个exe文件
IMAGE_NT_HEADERS STRUCT 结构体
IMAGE_NT_HEADERS STRUCT
{
+0h DWORD Signature
+4h IMAGE_FILE_HEADER FileHeader
+18h IMAGE_OPTIONAL_HEADER32 OptionalHeader
} IMAGE_NT_HEADERS ENDS
IMAGE_NT_HEADERS STRUCT
{
+0h DWORD Signature
+4h IMAGE_FILE_HEADER FileHeader
+18h IMAGE_OPTIONAL_HEADER32 OptionalHeader
} IMAGE_NT_HEADERS ENDS
在一个PE文件中Signature 字段被设置为4550h ,ASCII 码为”PE00 “。如上图所示。
在一个PE文件中Signature 字段被设置为4550h ,ASCII 码为”PE00 “。如上图所示。
struct IMAGE_FILE_HEADER
{
WORD Machine; //运行平台
WORD NumberOfSections; //区块表的个数
DWORD TimeDataStamp;//文件创建时间,是从1970年至今的秒数
DWORD PointerToSymbolicTable;//指向符号表的指针
DWORD NumberOfSymbols;//符号表的数目
WORD SizeOfOptionalHeader;//IMAGE_NT_HEADERS结构中OptionHeader成员的大小,对于win32平台这个值通常是0x00e0
WORD Characteristics;//文件的属性值
}
struct IMAGE_FILE_HEADER
{
WORD Machine; //运行平台
WORD NumberOfSections; //区块表的个数
DWORD TimeDataStamp;//文件创建时间,是从1970年至今的秒数
DWORD PointerToSymbolicTable;//指向符号表的指针
DWORD NumberOfSymbols;//符号表的数目
WORD SizeOfOptionalHeader;//IMAGE_NT_HEADERS结构中OptionHeader成员的大小,对于win32平台这个值通常是0x00e0
WORD Characteristics;//文件的属性值
}
在010 Editor上查看一下
IMAGE_OPTIONAL_HEADER 结构体
typedef struct _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
+2Ch DWORD BaseOfCode; // 代码的区块的起始RVA
+30h DWORD BaseOfData; // 数据的区块的起始RVA
//
// NT additional fields. 以下是属于NT结构增加的领域。
//
+34h DWORD ImageBase; // 程序的首选装载地址
+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];
// 数据目录表
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
typedef struct _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
+2Ch DWORD BaseOfCode; // 代码的区块的起始RVA
+30h DWORD BaseOfData; // 数据的区块的起始RVA
//
// NT additional fields. 以下是属于NT结构增加的领域。
//
+34h DWORD ImageBase; // 程序的首选装载地址
+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];
// 数据目录表
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2019-1-24 13:21
被admin编辑
,原因:
上传的附件: