PE文件结构(三)

参考

书:《加密与解密》

视频:小甲鱼 解密系列 视频

输入表

输入函数,表示被程序调用但是它的代码不在程序代码中的,而在dll中的函数。对于这些函数,磁盘上的可执行文件只是保留相关的函数信息,如函数名,dll文件名等。在程序运行前,程序是没有保存这些函数在内存中的地址。当程序运行起来时,windows加载器会把相关的dll装入内存,并且将输入函数的指令与函数真在内存中正的地址联系起来。输入表(导入表)就是用来保存这些函数的信息的。

在   IMAGE_OPTIONAL_HEADER 中的 DataDirectory[16]  数组保存了 输入表的RVA跟大小。通过RVA可以在OD中加载程序通过 ImageBase+RVA 找到 输入表,或者通过RVA计算出文件偏移地址,查看磁盘中的可执行文件,通过文件偏移地址找到输入表。

输入表是以一个IMAGE_IMPORT_DESCRIPTOR(IID)数组 开始的,每一个被PE文件隐式的链接进来的dll都有一个IID,IID数组的最后一个单元用NULL表示。

IMAGE_IMPORT_DESCRIPTOR 结构:

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
	_ANONYMOUS_UNION union {              //00h
		DWORD Characteristics;
		DWORD OriginalFirstThunk;
	} DUMMYUNIONNAME;
	DWORD TimeDateStamp;                  //04h
	DWORD ForwarderChain;                 //08h
	DWORD Name;                           //0Ch
	DWORD FirstThunk;                     //10h
} IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR;

其中Name是dll名字的指针。OriginalFirstThunk指向一个IMAGE_THUNK_DATA数组叫做输入名称表Import Name Table(INT),用来保存函数,FirstThunk也指向IMAGE_THUNK_DATA数组叫做输入地址表Import Address Table(IAT)。

IMAGE_THUNK_DATA 结构:

typedef struct _IMAGE_THUNK_DATA32 {
	union {
		DWORD ForwarderString;
		DWORD Function;
		DWORD Ordinal;
		DWORD AddressOfData;
	} u1;
} IMAGE_THUNK_DATA32,*PIMAGE_THUNK_DATA32;

当IMAGE_THUNK_DATA 的值最高位为1时,表示函数是以序号方式输入,这时低31为被当作函数序号。当最高位是0时,表示函数是以字符串类型的函数名方式输入的,这时,IMAGE_THUNK_DATA 的值为指向 IMAGE_IMPORT_BY_NAME 的结构的RVA。

typedef struct _IMAGE_IMPORT_BY_NAME {
	WORD Hint;
	BYTE Name[1];
} IMAGE_IMPORT_BY_NAME,*PIMAGE_IMPORT_BY_NAME;

Hint 表示这个函数在其所驻留dll的输出表的序号,不是必须的。

Name 表示 函数名,是一个ASCII字符串以0结尾,大小不固定。

INT保存的是这个程序导入这个dll中函数信息,它是固定的不会被修改。但是IAT会在程序加载时被重写,当程序加载时,它会被PE加载器重写成 这些函数的在内存中的真正地址。即把它原来指向的IMAGE_IMPORT_BY_NAME 改成 函数真正的地址。

那为什么要两个 IMAGE_THUNK_DATA 数组?

当程序加载时,IAT 会被PE加载器重写,PE加载器先搜索INT,PE加载器迭代搜索INT数组中的每个指针,找出 INT所指向的IMAGE_IMPORT_BY_NAME结构中的函数在内存中的真正的地址,并把它替代原来IAT中的值。当完成后,INT就没有用了,程序只需要IAT就可以正常运行了。

看下面图,这个是可执行程序在磁盘中的时候:

这个是当程序被加载的是后:

实例分析:

先找到输入表RVA,通过IMAGE_OPTIONAL_HEADER 中的最后一个项  IMAGE_DATA_DIRECTORY 可以知道 输入表相对与PE文件头的偏移量为80h可以找到输入表达RVA。

图片1

但这个是RVA不是文件偏移地址。通过转换可以知道,输入表的文件偏移地址为850h,

查看850h,即IID,可以看到这个程序有两个IID,即链接了 两个dll,看到一个IID,可以知道它的OriginalFirstThunk 是 2098h  FirstThunk  为200Ch,它们转换后的文件偏移地址分别为898h 和 80Ch

图片2  IID数组

查看OriginalFirstThunk跟FirstThunk ,可以发现这里INT跟IAT的内容是一样的,先看看第一个函数的信息,因为第一位为1,所以这里00002122h 表示 IMAGE_IMPORT_BY_NAME 的RVA,转化为文件偏移值为922h

图片3  INT跟IAT

查看922h 可以看函数名。

图片4:

我们可以从上面看到程序在磁盘中时 INT 与IAT内容一样,都是指向 IMAGE_IMPORT_BY_NAME 。用OD加载程序,查看INT与IAT的内容

图片5 INT

图片6 IAT

可以发现INT没有发生变化,IAT变成了例如77D3C702h,IAT中的RVA被改成了函数的真正的地址。

查看77D3C702h,就可以看到这个函数

图片7

时间: 2024-11-04 13:16:43

PE文件结构(三)的相关文章

PE文件结构(三) 输入表

PE文件结构(三) 參考 书:<加密与解密> 视频:小甲鱼 解密系列 视频 输入表 输入函数,表示被程序调用可是它的代码不在程序代码中的,而在dll中的函数.对于这些函数.磁盘上的可执行文件仅仅是保留相关的函数信息,如函数名,dll文件名称等. 在程序执行前.程序是没有保存这些函数在内存中的地址.当程序执行起来时.windows载入器会把相关的dll装入内存.而且将输入函数的指令与函数真在内存中正的地址联系起来.输入表(导入表)就是用来保存这些函数的信息的. 在   IMAGE_OPTIONA

PE文件结构详解(三)PE导出表

上篇文章 PE文件结构详解(二)可执行文件头 的结尾出现了一个大数组,这个数组中的每一项都是一个特定的结构,通过函数获取数组中的项可以用RtlImageDirectoryEntryToData函数,DataDirectory中的每一项都可以用这个函数获取,函数原型如下: PVOID NTAPI RtlImageDirectoryEntryToData(PVOID Base, BOOLEAN MappedAsImage, USHORT Directory, PULONG Size); Base:模

PE文件结构与函数导出表——详解与实例

PE文件结构与函数导出表--详解与实例 随着windows系统从Xp升级到Win7.Win8, 从32位升级到64位,PE文件结构在整体未变的情况下发生了一些小的变动,一方面是推荐的程序装载地址未采用,另一方面,导出函数序号不再是简单的升序,而是一定程度上的进行了乱序.本文首先对PE文件结构进行了详尽的解说,接着介绍了如何得出函数导出表,整个过程采用SysWoW64目录下的wininet.dll实例进行说明.在介绍过程中,明确指出了Win7.Win8等新系统相对Xp带来的区别. 文章链接:htt

PE文件结构(五)基址重定位

PE文件结构(五) 参考 书:<加密与解密> 视频:小甲鱼 解密系列 视频 基址重定位 链接器生成一个PE文件时,它会假设程序被装入时使用的默认ImageBase基地址(VC默认exe基地址00400000h,dll基地址10000000h),并且会把代码中所有指令中用到的地址都使用默认的基地址(例如 程序代码中 push 10001000,就是把10000000h当做了基地址,把push 10001000写入到文件中).如果一个exe程序中一个dll装载时的地址与其它dll地址发生冲突(因为

PE文件结构详解

1.定位标准PE头 DOS Stub长度不固定,所以DOS头不是一个固定大小的数据结构.DOS头位于PE的起始位置,通过DOS头去定位后面标准PE头的位置就是通过字段e_lfanew. e_lfanew字段的值是一个相对偏移量,绝对定位时需要加上DOS MZ头的基地址. 也就是PE头的绝对位置是: PE_start = DOS MZ 基地址+IMAGE_DOS_HEADER.e_lfanew 2.PE文件结构 在32位系统下,最重要的部分是PE头和PE数据区. 32位系统下的PE文件被划分为:D

PE文件结构详解(四)PE导入表

PE文件结构详解(二)可执行文件头的最后展示了一个数组,PE文件结构详解(三)PE导出表中解释了其中第一项的格式,本篇文章来揭示这个数组中的第二项:IMAGE_DIRECTORY_ENTRY_IMPORT,即导入表. 也许大家注意到过,在IMAGE_DATA_DIRECTORY中,有几项的名字都和导入表有关系,其中包括:IMAGE_DIRECTORY_ENTRY_IMPORT,IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT,IMAGE_DIRECTORY_ENTRY_IAT

PE文件结构详解(一)基本概念

PE(Portable Execute) 文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任 何扩展名.那Windows是怎么区分可执行文件和非可执行文件的呢?我们调用LoadLibrary传递了一个文件名,系统是如何判断这个文件是一个合 法的动态库呢?这就涉及到PE文件结构了. PE文件的结构一般来说如下图所示:从起始位置开始依次是DOS头,NT头,节表以及具体的节. DOS头是用来兼容MS-DOS操

PE文件结构详解(六)重定位

前面两篇 PE文件结构详解(四)PE导入表 和 PE文件结构详解(五)延迟导入表 介绍了PE文件中比较常用的两种导入方式,不知道大家有没有注意到,在调用导入函数时系统生成的代码是像下面这样的: 在这里,IE的iexplorer.exe导入了Kernel32.dll的GetCommandLineA函数,可以看到这是个间接call,00401004这个地址的内存里保存了目的地址, 根据图中显示的符号信息可知,00401004这个地址是存在于iexplorer.exe模块中的,实际上也就是一项IAT的

PE文件结构(五岁以下儿童)基地搬迁

PE文件结构(五岁以下儿童) 參考 书:<加密与解密> 视频:小甲鱼 解密系列 视频 基址重定位 链接器生成一个PE文件时,它会如果程序被装入时使用的默认ImageBase基地址(VC默认exe基地址00400000h.dll基地址10000000h),而且会把代码中全部指令中用到的地址都使用默认的基地址(比如 程序代码中 push 10001000,就是把10000000h当做了基地址,把push 10001000写入到文件里).如果一个exe程序中一个dll装载时的地址与其他dll地址发生

再探.NET的PE文件结构(安全篇)

一.开篇 首先写在前面,这篇文章源于个人的研究和探索,由于.NET有自己的反射机制,可以清楚的将源码反射出来,这样你的软件就很容易被破解,当然这篇文章不会说怎么样保护你的软件不被破解,相反是借用一个软件来讲述是怎么被攻破的,也会有人说这是一篇破文,我其实这篇文章已经写了很长时间了,不知道以什么形式发出来,因为毕竟是有些破解类的东西.但是我觉得从这篇文章相反的是能够带来一些启发.大家应该都知道Reflector这个反编译软件还有一个插件是专门用来改IL的插件叫Reflexil,这里我们也要用到前面