欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

复习记忆PE导入导出表

程序员文章站 2022-06-03 14:05:40
...

由于软件安全课程要考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;

复习记忆PE导入导出表
PE装载器把导入函数输入到IAT:
复习记忆PE导入导出表
通过第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)的结构数组,结构如下。
复习记忆PE导入导出表
典型的输出表
复习记忆PE导入导出表
加载
复习记忆PE导入导出表

相关标签: Binary