首页 > 其他分享 >从PE结构到LoadLibrary

从PE结构到LoadLibrary

时间:2024-11-16 17:43:10浏览次数:3  
标签:文件 IMAGE DLL 地址 内存 LoadLibrary PE 结构

从PE结构到LoadLibrary

PE是Windows平台主流可执行文件格式,.exe , .dll, .sys, .com文件都是PE格式

32位的PE文件称为PE32,64位的称为PE32+,PE文件格式在winnt.h头中有着详细的定义,PE文件头包含了一个程序在运行时需要的所有信息,包括了如何将文件加载到内存、开辟多大的堆栈空间、调用哪些DLL以及相关函数、从何处开始运行,这些信息都以结构体的形式存储在PE头中

PE文件包括四个组成部分:

  • MS-DOS(Disk Operation System) 头

  • NT头(New Technology)

  • Section table表中包含了所有的 section头

  • 所有的section实体(段实体)

image-20241022172019315

实现LoadLibrary,即把PE文件加载到内存中需要经过四步

  • 判定输入文件是否是PE格式

  • 将PE文件按照内存映像结构分块放在内存中

  • 在IAT(导入地址表)中,填入其依赖的导入函数地址

  • 利用重定位表修复需要重定位的值

LoadLibrary 是 Windows API 中的一个函数,用于动态加载 DLL文件到当前进程的地址空间,并返回一个句柄以供后续操作。

1 检查PE格式

PE文件加载,首先要检查的是该文件是否为PE格式,还需要检查该PE文件是否为DLL

  • 对于PE格式的检测,需要检查的部分是MS-DOS头中“MZ” 关键字和NT头中“PE/0/0”关键字
  • 对于DLL的检测,则需要检查NT头中的IMAGE_FILE_HEADER的Characteristics字段下IMAGE_FILE_DLL信息位
  1. MS-DOS头

    MS-DOS头是微软为了考虑PE文件对DOS文件的兼容性而添加的。 大多数情况下由编译器自动生成,通常把DOS MZ头与DOS stub(模拟对象接口)合称为DOS文件头,IMAGE_DOS_HEADER结构体如下图 ,MAGE_DOS_HEADER结构体共64字节,其中两个字段比较重要,分别是e_magic和e_lfanew

    image-20241022210054943

    e_magic需要被设置为0x5A4D,其ASCII值为“MZ” ,为DOS签名,标志着DOS头的开始

    e_lfanew字段是NT头的相对偏移,其指出NT头的文件偏移位置,共占用四个字节,位于文件开始偏移 0x3C字节中

  2. NT头

    在DOS stub后的是NT头 (IMAGE_NT_HEADERS)

    image-20241022210227149

    Signature 在一个有效的PE文件中,Signature字段必须被设置为 0x00004550,对应于ASCII字符“PE\0\0” 。

    image-20241022210254349

    IMAGE_FILE_HEADER IMAGE_FILE_HEADER结构包含了PE文件的基本信息,最重要的是其中一个字段指出了IMAGE_OPTIONAL_HEADER 的大小

    image-20241022211709827

    其中Machine,NumberOfSections, SizeOfOptionalHeader,Characteristics如果出现错误, 将导致该PE文件无法正常执行

    1. Machine 该字段说明了可执行文件的目标CPU类型,每类CPU都有唯一的Machine码
    image-20241022211802044
    1. NumberOfSections 该字段说明了在这个PE文件中节区(Section)的数目
    2. TimeDateStamp 该字段说明了该PE文件是何时被创建的
    3. PointerToSymbolTable 该字段说明了COFF符号表(基本用不到)的文件偏移位置, COFF符号表在PE文件中较为少见,通常其值为0
    4. NumberOfSymbols 如果存在COFF符号表,该字段说明了其中的符号数目
    5. SizeOfOptionalHeader 该字段说明了紧跟在IMAGE_FILE_HEADER后的数据大小 对于32位文件,该字段通常为0x00E0,对于64位文件, 该字段通常为0x00F0
    6. Characteristics 该字段用于标识文件的属性,文件是否可执行,是否为 DLL等文件信息,这些信息以比特位的方式组合起来

    IMAGE_OPTIONAL_HEADER:

    IMAGE_OPTIONAL_HEADER虽然叫做可选头,但是仅有IMAGE_FILE_HEADER并不足以定义PE文件的属性 IMAGE_OPTIONAL_HEADER定义了更多的PE文件的属性,两者结合起来描述了一个完整的PE文件

    1. Magic 当IMAGE_OPTIONAL_HEADER为IMAGE_OPTIONAL_HEADER32 (32位)时,Magic为0x10B。当其为 IMAGE_OPTIONAL_HEADER64时,Magic为0x20B。
    2. AddressOfEntryPoint 该字段的值为相对虚拟地址(加载到内存的地址),该值表明了程序最先执行的代码的启始地址,即程序入口点。
    3. ImageBase 该字段表明了PE文件被加载进内存时,文件将被优先装入的虚拟内存的地址。对于EXE来说,ImageBase通常为0x00400000; 对于DLL来说,ImageBase通常为0x10000000。装载后,EIP = ImageBase + AddressOfEntryPoint。
    4. SectionAlignmentFileAlignment PE文件的Body部分划分为若干节区,FileAlignment制定了节区在文件系统中的最小单位,SectionAlignment则指定了节区在内存中的最小单位,磁盘文件或内存的节区大小必定为FileAlignment或SectionAlignment的整数倍
    5. SizeOfImage 加载PE文件时,SizeOfImage指定了PEImage在虚拟内存中所占的空间大小
    6. SizeOfHeaders 该字段表明了整个PE文件头部的大小,该值必须是 FileAlignment的整数倍
    7. Subsystem 该字段用于区分系统驱动文件与普通可执行文件。
    8. NumberOfRvaAndSizes 该字段表明了下面出现的DataDirectory数组的个数。一般来说该值为16。
    9. DataDirectory 由IMAGE_DATA_DIRECTORY结构体构成的数组,数组的每项都有不同的意义
  3. PE格式检查:

    PE格式检查主要针对于MS-DOS头和NT头。要求MS- DOS头和NT头的签名与规定相同。其中MS-DOS头的签名为0x4D5A即ASCII码的”MZ” 。

    通过MS-DOS头中的e_lfanew成员变量找到NT头。检查NT头签名为0x50450000即ASCII的”PE\0\0”

    根据NT头中FileHeader中的Characteristics中的 IMAGE_FILE_DLL位可以判断该PE文件是否为DLL。

2 内存映像结构

将PE文件按照内存映像结构分块放在内存中 ,将PE文件从硬盘中映射到内存映像结构

  1. 最小基本单元

    计算机中,为了提高处理文件过程中,内存的效率, 使用“最小基本单元”这一概念,PE文件映射到内存后节区的起始位置应该在最小基本单元的倍数上,在最小基本单元中空余的空间填NULL

    image-20241023152925754
  2. 程序处理

    首先将PE文件的MS-DOS头,NT头以及节区头拷贝到开辟的内存空间的首地址处。

    下面的代码中pFileBuf存储了从硬盘中读取的PE数据, pFileBuf_New为依据SizeofImage开辟的新内存空间,头部大小可由可选头中的SizeOfHeaders成员变量获得

    image-20241023153035096

    Windows提供了一个宏IMAGE_FIRST_SECTION,可以根据NT头直接返回第一个节区头的指针

    由每个节区头中的PointerToRawData(指针指向的原始数据),VirtualAddress以及SizeOfRawData成员变 量,可以获知每个节区的数据在pFileBuf中的首地址, 该数据应该被放在pFileBuf_New的地址加上VirtualAddress

    image-20241023153121795

  3. RVA&VA

    RVA是在PE文件中为了避免使用确定的内存地址, 出现了相对虚拟地址(RVA),RVA是内存中相对于PE文件装入地址的偏移位置, 是一个“相对地址” ,或称为“偏移量” ,VA指的是进程装入内存后实际的内存地址,被称为虚拟地址,VA=Image Base + RVA

    其中基地址是PE文件通过Windows加载器装入内存后,该模块的初始内存地址就被称为基地址

  4. 从文件偏移到相对虚拟地址

    在以上小节的地址计算中,都是在文件映射到内存之后进行的,但是PE文件在存储时为了减少体积,FileAlignment 通常小于SectionAlignment

    当文件被映射到内存中后,同一数据在文件中的偏移量与在内存中的偏移量是不一样的,这样就存在文件偏移地址(RAW)到相对虚拟地址(RVA)之间的转换

    如果需要对存储在硬盘中的PE文件进行操作,需要将RVA转换为RAW

    由于应用程序的映射是以节区为单位做的映射,一个节区内数据的地址相对于节区的地址是不变的, 因此只需要计算各节区在磁盘与内存中起始地址的差值即可,notepad

    image-20241023153544369image-20241023153616964

    在计算某虚拟地址对应的文件偏移时,应首先查看其属于哪一节区,找到相应的差值后再进行转换, 以上述notepad为例,如给定一虚拟地址0x402854, ImageBase为0x400000,要求计算其文件偏移地址

    image-20241023153716578

3 基址重定位

重定位表是PE文件中用于支持代码在内存中动态加载到不同地址的一种机制。它解决了代码中的绝对地址在加载到不同内存地址时需要调整的问题。

由第二节中对可选头的描述可知,可选头的 ImageBase成员变量描述了程序在装入内存时优先装入的地址,在生成PE文件时,EXE文件优先装入的地址是 0x400000,DLL文件优先装入的地址是0x10000000

image-20241023154259443

在xp中没有地址随机化,EXE会被默认装入基址处,通常不需要进行基址重定位,一个可执行程序要加载的DLL有很多,不能都加载在0x10000000处 ,当地址已经被占用时,就需要加载到未被占用的空间中,此时,程序中的一些绝对地址访问过程中,就会访问或跳转到别的地址空间中,而不是访问或转到预期的位置,重定位表就是为此而产生的

在vista及以上的操作系统中,开启了地址随机化保护,EXE也会被加载到别的地址,因此EXE也有了重定位表

基址重定位结构定义

重定位表由许多重定位块串接组成,每个重定位块中存放着4KB大小PE文件内容的重定位信息

重定位块开头以IMAGE_BASE_RELOCATION开始

image-20241023154523117

  1. VirtualAddress 声明了该组重定位数据开始的相对虚拟地址,各项重定位地址与该值相加,才是需要进行重定位的相对虚拟地址
  2. SizeOfBlock 声明了该组重定位数据的大小,其中包含了 IMAGE_BASE_RELOCATION ,在IMAGE_BASE_RELOCATION之后紧随的是TypeOffset数组, 数组每项大小为两个字节,高四位代表重定位类型,低十二位值为重定位地址,最终所有的重定位块以一个VirtualAddress为0的 IMAGE_BASE_RELOCATION结构结束
image-20241023154630496

程序处理:

重定位表在PE文件中往往单独分为1个节区,名称为“.reloc” ,可以由可选头中DataDirectory中的BASERELOC Directory成员找到

整个代码共有两个循环,第一个循环遍历每个重定位块,第二个循环遍历每个重定位块中的重定位信息。根据每个重定位信息的高四位确定其是否需要重定位,需要重定位时,根据程序预期存储位置 ImageBase以及当前程序存储位置m_pFileBuf对其地址进行修正

4 导入表

导入表用于描述一个模块(EXE 或 DLL)在运行时需要从其他模块(通常是 DLL)中调用的函数或变量。

在编写程序时,会使用到大量的库函数,由于动态链接的存在,这些函数并不会都编写进二进制文件中,而是在函数调用处填入对应的导入表地址

当程序加载到内存中后,Windows加载器才将相关的DLL装入,并将调用输入函数的指令和函数实际所在地址关联起来

调用VirtualAlloc函数时,依据二进制查看得到 VirtualAlloc地址为0x47d1d8

image-20241023203318569

查看0x47d1d8处,如下图所示,其值在IDA中为未知值,这是因为该PE文件尚未装入内存中,没有在导入地址表中填写相应的地址

image-20241023203447151

当其执行后,该处的值会由Windows加载器填写, 如图所示,其值已变为0x74cf6970

image-20241023203510220

在程序装入内存时,PE加载器完成了这些工作,同样,编写一个自己的LoadLibrary也需要在导入表中填入相应的函数地址

IMAGE_IMPORT_DESCRIPTOR

PE文件头的可选映像头中,数据目录表的第二成员指向导入表,导入表由IMAGE_IMPORT_DESCRIPTOR(IID) 数组构成。

每个被PE文件导入的DLL都有一个与之对应的IID

IID中并无字段表明IID数组的长度大小,该数组最后的一个单元为NULL,由此可以计算出IID数组的项数

image-20241023204107558

  1. OriginalFirstThunk(Characteristics) 包含指向导入名称表的RVA
  2. TimeDataStamp 一个32位的时间标志
  3. ForwarderChain 当程序引用一个DLL中的API,而这个API又引用别的DLL 的API时使用
  4. Name DLL名字的指针。名称字符串以\0结尾
  5. FirstThunk 包含指向导入地址表的RVA

导入名称表(INT)的结构:

image-20241023204234055

  1. int 该字段表明本函数在DLL中的导出表序号。
  2. Name 该字段为函数名,是一个ASCII字符串,以NULL结尾

导入地址表(IAT)中填写对应函数的虚拟地址

image-20241023204319254

程序处理

与基址重定位表类似的,从可选头中的DataDirectory中的IMPORT Directory成员找到

使用LoadLibrary载入所有关联的DLL,使用 GetProcAddress获取所有函数地址,填入IAT中

程序共有两个循环,第一个循环为遍历所有需要导入的DLL,第二个循环为遍历每个DLL中需要导入的函数。将获取到的函数地址填入IAT表中即可

5 导出表

导出表用于描述一个模块(通常是 DLL)向其他模块提供的函数或变量。通过导出表,其他模块可以找到并使用 DLL 中的功能。

上面LoadLibrary已经完成了,接下来需要完成的是与LoadLibrary配套的GetProcAddress

仅仅把DLL加载到内存中是不够的,无法得到DLL导出函数的地址,这个DLL就是无效的

在DLL中,DataDirectory比EXE中多了一项 EXPORTDirectory,通过EXPORT Directory可以找到 DLL中的导出地址表

IMAGE_EXPORT_DIRECTORY

image-20241023204504158
  1. Characteristics 未定义,为0

  2. TimeDateStamp 该字段表明输出表创建时间

  3. MajorVersion 该字段表明输出表的主版本号,未使用,值为0

  4. MinorVersion 该字段表明输出表的次版本号,未使用,值为0

  5. Name 该字段指向DLL名称字符串地址

  6. Base 该字段包含用于这个可执行文件输出表的起始序数值

  7. NumberOfFunctions 该字段表明导出地址表(EAT)中的条目数量

  8. NumberOfNames 输出函数名称表的条数数量,该值小于或等于NumberOfFunctions。当函数只通过序数输出时会出现NumberOfNames小于NumberOfFunctions

  9. AddressOfFunctions 该字段指向EAT地址,EAT中存储了所有导出函数的相对虚拟地址

  10. AddressOfNames 该字段指向导出名称表(ENT)地址,ENT中存储了所有函数名称字符串的相对虚拟地址

  11. AddressOfNameOrdinals 该字段指向导出序数表地址,导出序数表中存储了所有导出函数的序数

image-20241023204724424

程序处理

从可选头中的DataDirectory中的EXPORT Directory成员找到IMAGE_EXPORT_DIRECTORY地址

从ENT中取出函数名,与要取的函数名进行对比, 一致时从EAT中得到函数地址与内存中的DLL基址相加,即得到了函数真实的地址

至此,自制的GetProcAddress也已经完成,与之前编写的LoadLibrary配合,就可以实现在内存中加载DLL并获取函数地址

总结对比

功能导入表(Import Table)导出表(Export Table)
作用描述模块需要调用的外部函数和变量描述模块提供给其他模块的函数和变量
包含内容DLL 名称、函数名称或序号、IAT函数名称、地址、序号
用途解析外部依赖关系,加载和绑定外部模块提供函数或变量接口供外部模块调用
位置依赖的模块中不存在,位于当前模块位于模块本身内部

导入表:一个 EXE 调用系统函数如 MessageBox,导入表会记录 user32.dll 的名称和 MessageBox 函数。

导出表:一个 graphics.dll 提供 DrawLineDrawCircle 两个函数,导出表记录了这些函数的名称和地址,供其他模块调用。

标签:文件,IMAGE,DLL,地址,内存,LoadLibrary,PE,结构
From: https://blog.csdn.net/LH1013886337/article/details/143820672

相关文章

  • 江苏科技大学大二《数据结构》课内实验报告模板答案
    江苏科技大学《数据结构》实验报告(2024/2025学年第1学期)学生姓名:学生学号:院系:计算机学院专业:考核得分:2024年12月实验一线性表的操作一、实验目的掌握线性表的基本操作在存储结构上的实现,其中以单链表的操作作为重点。二、实验题目1.以单......
  • 【Linux进程篇1】认识冯·诺依曼体系结构(引出进程详解)
    ---------------------------------------------------------------------------------------------------------------------------------每日鸡汤:用这生命中的每一秒,给自己一个不后悔的未来。-------------------------------------------------------------------------......
  • nternLM Camp4 L1G600 OpenCompass 评测书生大模型实践
    本任务需要使用30%A100开发机文章目录前言一、使用OpenCompass评测浦语API1.环境配置2.模型配置3.数据集配置4.运行评测二、评测本地模型1.环境配置2.数据集下载3.加载本地模型进行评测三、将本地模型通过部署成API服务再评测前言本博客是第四期书生大模型......
  • C. Penchick and BBQ Buns (python解)-codeforces
    C.PenchickandBBQBuns(python解)-codeforces原题链接:点击传送问题分析:我们需要为给定数量的BBQ包子分配填料,满足以下条件:每种填料必须至少使用两次,或者不使用。任何两个相同填料的包子之间的距离必须是一个完全平方数。思路:为了满足条件,我们可以利用完全平方数的......
  • Openlayers的多边形高级交互
    概述本文主要介绍Openlayers中,两个(或多个)多边形的高级交互,包括:并集,交集和差集运算.概念理解并集(Union):并集指的是将两个几何对象的所有区域合并在一起。即,结果是包含了两个对象所有区域的集合。如果两个几何形状有重叠部分,合并时不会重复这些部分。几何......
  • 数据结构/第二章 线性表/数据结构习题/线性表的习题/考研/期末复习
    一、选择题1.在线性表中,表尾元素(    )。A.有且仅有一个直接前驱        B.有且仅有一个直接后继C.没有直接前驱               D.有多个直接前驱2.在顺序表上按位查找一个元素的时间复杂度是(    )。A.O......
  • 洛谷 P2890 [USACO07OPEN] Cheapest Palindrome G 做题记录
    我不会区间dp。设\(f_{i,j}\)表示使得区间\([i,j]\)为回文串的最小操作代价,\(cost_{i,j}\)表示字母\(i\)删除/添加的耗费,那么显而易见的,我们有:\(f_{i,j}\to\min(f_{i,j-1}+\min(cost_{s_j,0},cost_{s_j,1}),f_{i+1,j}+\min(cost_{s_i,0},cost_{s_i,1}))\)。当\(s_i......
  • 数据结构——AVL树
    目录一.AVL树的概念二.AVL树的实现1.AVL树结点的定义2.AVL树的插入3.AVL树的删除4.AVL树的查和改5.AVL树的遍历 6.验证AVL树是否平衡7.AVL树的性能三.整体代码1.AVLTree.h2.AVLTree.cpp一.AVL树的概念二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有......
  • 在 tsconfig.json 文件中,compilerOptions.types 字段用于指定 TypeScript 编译器应该
    在tsconfig.json文件中,compilerOptions.types字段用于指定TypeScript编译器应该包含的类型声明文件。这些类型声明文件提供了类型信息,使得TypeScript能够在编译时进行类型检查和提供智能提示。你提到的配置项指定了几个常用的类型声明文件,下面是对这些配置项的详细解释:配......
  • CMPEN/EE 454 oncamera projection
    1CMPEN/EE454,Project2,Spring2024DueWedsFriday2911:59PMonCanvas1Motivationhisgoalofthisprojectistohelpyouunderstandinapracticalwaythecoursematerialoncameraprojection,triangulation,epipolargeometry,andplanewarpingYouwi......