复习记忆PE导入导出表

本文介绍了如何记忆PE文件的导入导出表,遵循申一帆的记忆法,将复杂过程拆分为可管理的小片段。通过抽象概念、适当假设和形象化记忆,解决记忆难点。具体解析过程中,详细阐述了从PE结构到导入导出表的关键步骤,帮助读者理解PE加载到内存中的过程。
摘要由CSDN通过智能技术生成

由于软件安全课程要考PE文件,只能强制记忆PE结构体和字段偏移量。虽然实际中不会真的这么做,但是强制记忆还是有点好处,以合理的方式记忆之后就很难忘记。(以下仅代表个人的看法和认知)

(1) 记忆的一些原则

可以参照申一帆的记忆法,结合自己的一些假设和想象即可。从某种层面来讲,一般的问题不涉及太高深的数学(除非自己有进行深入的思考),大多数的人是可以比较轻松的解决的,即使有点困难也可以想办法来解决的。

DLL注入_修改导入表(1)中,主要是为了熟悉PE结构而逐个结构体来分析,有一连串的过程,显然是很难记住的。过程分析就像是编写程序的思想,要把所有的关系找出来。这个过程可以看成是:假设我要和远方进行通信而使用完整的一根线。实际上,远距离通信,中间要经过很多节点,记忆也应该是这样的。

其实可以把这样的记忆过程看成一个数学问题,即带方向的图形一笔画问题,但是有点要求:点与点之间的顺序是固定的(偏序关系)。举个例子:(a1 a2 a3)代表某种开展顺序,我们可以将(a1 a2 a3)拆分成 (a1 a3) (a2 a3) (a1 a2),这样就可以将一连串的过程拆分成较小的片段而最终能恢复序列,从而使记忆更轻松。较小的片段可以看成是离散的点集,通过曲线连接这些点而使事物形象化,片段与片段之间则是事物之间的关系(如果没有的话,自己假设)。这个就像是天上的星星,通过画星宿图把不相干的事物联系在一起了。

记忆难点一:抽象的概念

对于抽象的概念多读熟一点、画一画图、记(构造)一些特殊实例就可以较好的理解概念,如离散数学中的树和图的部分。

记住核心的语句。如函数的栈帧 push ebp;mov ebp,esp;可以看成esp指向下一个函数的ebp。两个函数可以看成两个队伍,大端或小段标记法看成是两个队伍追逐的方向,最终的目的是使一个队伍永远追逐不上另一个队伍,这就是bp栈帧的作用。

适当的想象。如carry outperform都有执行的意思。执行由人的手去执行,手拿东西呈现出一个c,out有远离的意思,可以想象每次过安检的时候,把行李箱放在传送带上而离我们远去即carry outperform的对象是人person。执行的英语首字母是cp,就看你平时更经常使用哪一个(权重的高低)。

适当的假设。由于我们所接触的是前人创造出来的东西,因此只要按照正常的思维,可以找到事物一些合理的解释。如我们的汉字与国外的英文,都有象形的印记,也有抽象的地方。如下面一句话:
This manual uses specific notation for data-structure formats, for symbolic representation of instructions, and for hexadecimal and binary numbers. This notation is described below.
也许使用英文的人认为for data-structure formats这个修饰比较次要放在了后面,这样就能更好的接受新的概念。
又如汇编指令mov eax, dword ptr [ebp-10],也许当初设计汇编的人认为mov操作指令最重要,放在第一个位置(实际上是地址编码的问题),而目标寄存器相对重要,放在第二个位置,这样就不会觉得很奇怪了。

记忆难点二:过于离散或连续的过程

过于离散转化成连续,过于连续化成离散。

(2) 具体过程

如果不太了解PE结构,可以阅读加密与解密第四版的第11章,相对讲的通俗易懂(https://pan.baidu[.]com/s/1ZOlGamc-v2V_0ClT_GEVPQ 提取码:y17d),加载到内存中的过程可以阅读逆向工程核心原理的第13章p108和p114(https://github.com/sv4us/ebook/tree/master/%E9%80%86%E5%90%91%E5%B7%A5%E7%A8%8B%E6%A0%B8%E5%BF%83%E5%8E%9F%E7%90%86)

记住IMAGE_DATA_DIRECTORY这个关键的中间节点,然后向前和向后回忆相关的结构体、字段和偏移量。(不要用写程序的思维来记忆,因为写程序是从头开始一个个分析)

向前

IMAGE_DATA_DIRECTORYIMAGE_OPTIONAL_HEADER的最后一个字段,距IMAGE_NT_HEADER(即PE头) +78H处。而PE头是PE00,为IMAGE_NT_HEADER的第一个字段Signature(类型是DWORD双字),而PE头是由DOS头(MZ 文件的起始)e_lfanew字段指出 距DOS头 +3CH处(类型是WORD)。(DOS的结构不重要,直接忽略,只需记住文件头偏移量+3CH)

向后

IMAGE_DATA_DIRECTORY是一个结构数组(共16项)
第一项是IMAGE_DIRECTORY_EXPORT导出表 距PE头+78H处,
第二项是IMAGE_DIRECTORY_IMPORT导入表 距PE头+80H处。(也许为别的程序服务比较重要,因此把导出表放在第一项)
IMAGE_DATA_DIRECTORY结构的第一个字段VirtualAddress指出了数据起始地址RVA,第二个字段size指出了数据块长度,类型都是DWORD双字。(储存数据起始地址RVA和数据大小来确定分配的空间)

IMAGE_DATA_DIRECTORY的第二项导入表 PE头+80H处的VirtualAddress得到了IMAGE_IMPORT_DESCRIPTOR(IID)的结构数组。每个IID结构长度5个双字20字节,最后一个单元是NULL,可以算出数组项数。

IID可以看成是操作导入表的核心,不管是导入表还是导出表,都是要将名称表写入到地址表中,记住这个原则(显然对于我们而言,看一个名称比看一串数字地址轻松,而机器认识的是地址)

IMAGE_IMPORT_DESCRIPTOR共5个字段(双字)
第1个字段是OriginalFirstThunk,指向INT(输入名称表 IMPORT_NAME_TABLE)
第4个字段是Name,指向DLL名称,RVA
第5个字段是FirstThunk,指向IAT(输入地址表 IMPORT_ADDRESS_TABLE)

可执行文件从user32.dll输入API;

IAT
PE装载器把导入函数输入到IAT:
IAT
通过第2张图片的执行过程与第一张图对应。整理一下:

IID的第4个字段Name得到库函数名称,由IID的第1个字段OriginalFirstThunk得到INT结构数组的首地址,由IID的第5个字段FirstThunk得到IAT输入地址表地址。中间通过2个api调用(LoadLibrary GetProcAdderss)。调用IID字段的顺序为(+4 +1 +5),可以看成垂直排列的3个点,可以用逆时针的螺旋线将点连接起来。

OriginalFirstThunk和FirstThunk都指向IMAGE_THUNK_DATA结构。
IMAGE_THUNK_DATA最高位为1时,函数以序号导出,Oridinal低31位为输出函数在其DLL的导出序号,最高位为0时,函数以字符串导出,AddressOfData指向用来导入函数名称的IMAGE_IMPORT_BY_NAME的数据结构RVA

typedef struct IMAGE_IMPORT_BY_NAME {
    HINT WORD ;    //函数序号,非必须
    NAME BYTE;    //导入函数的名称,大小可变,以0结尾的ascii字符串
}

IMAGE_DATA_DIRECTORY第一项是IMAGE_DIRECTORY_EXPORT导出表 距PE头+78H,由VirtualAddress得到了IMAGE_EXPORT_DIRECTORY(IED)的结构数组,结构如下。
IED
典型的输出表
IED
加载
IED

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值