共享可写节包含重定位_下决心学习PE结构,花了一天时间整理了一份PE结构WORD文档,与大家共享...

2004-9-2 19:29

6. 引入函数

当你使用别的DLL中的代码或数据,称为引入。当PE载入时,载入器的工作之一就是定位所有引入函数及数据,使那些地址对于载入的PE可见。具体细节在后面讨论,在这里只是大概讲一下。

当你用到了一个DLL中的代码或数据,你就暗中连接到这个DLL。但是你不必为“把这些地址变得对你的代码有效”做任何事情,载入器为你做这些。方法之一就是显式连接,这样你就要确定DLL已被载入,及函数的地址。调用LOADLIBARY和GETPROCADDRESS就可以了。

当你暗式连接DLL,LOADLIBARY和GETPROCADDRESS同样还是执行了的。只不过载入器为你做了这些。载入器还保证PE文件所需得任何附加的DLL都已被载入。比如,当你连接了KERNEL32.DLL,而它又引入了NTDLL.DLL的函数,又比如当你连接了GDI32.DLL,而它又依赖于USER32, ADVAPI32,NTDLL, 和 KERNEL32 DLLs的函数,载入器会保证这些DLL被载入及函数的决议。

暗式连接时,决议过程在PE文件在载入时就发生了。如果这时有什么问题(比如这个DLL文件找不到),进程终止。

VISUAL C++ 6.0 加入了DLL的延迟载入的特征。它是暗式连接和显式连接的混合。当你延迟载入DLL,连接器做出一些和引入标准规则DLL类似的东西,但是操作系统却不管这些东西,而是在第一次调用这个DLL中的函数的时候载入(如果还没载入),然后调用GetProcAddress取得函数的地址。

对于pe文件要引入的dll都有一个对应的结构数组,每个结构指出这个dll的名字及指向一个函数指针数组的指针,这个函数指针数组就是所谓的IAT(IMORT ADDRESS TABLE)。每个输入函数,在IAT中都有一个保留槽,载入器将在那里写入真正的函数地址。最后特别重要一点的是:模块一旦载入,IAT中包含所要调用的引入函数的地址。

把所有输入函数放在IAT一个地方是很有意义的,这样无论代码中多少次调用一个引入函数,都是通过IAT中的一个函数指针。

让我们看看是怎样调用一个引入函数的。有两种情况需要考虑:有效率的和效率差的。最好的情况像下面这样:

CALL DWORD PTR [0x00405030]

直接调用[0x405030]中的函数,0x405030位于IAT部分。效率差的方式如下:

CALL 0x0040100C

...

0x0040100C:

JMP DWORD PTR [0x00405030]

这种情况,CALL把控制权转到一个子程序,子程序中的JMP指令跳转到位于IAT中的0x00405030,简单说,它多用了5字节和JMP多花的时间。

你可能惊讶引入函数就采用了这种方式,有个很好的解释,编译器无法区别引入函数的调用和普通函数调用,对于每个函数调用,编译器只产生如下指令:

CALL XXXXXXXX

XXXXXXXX是一个由连接器填入的RVA。注意,这条指令不是通过函数指针来的,而是代码中的实际地址。

为了因果的平衡,连接器必须产生一块代码来代替取代XXXXXXXX,简单的方法就是象上面所示调用一个JMP STUB.

那么JMP STUB 从那里来呢?令人惊异的是,它取自输入函数的引入库。如果你去察看一个引入库,在输入函数名字的关联处,你会发现与上面JMP STUB相似的指令。

接着,另一个问题就是如何优化这种形式,答案是你给编译器的修饰符,__declspec(import) 修饰符告诉编译器,这个函数来自另一个dll,这样编译器就会产生第一种指令。另外,编译器将给函数加上__imp_前缀然后送给连接器决议,这样可以直接把__imp_xxx送到iat,就不需要jmp stub了。

对于我们这有什么意义呢,如果你在写一个引出函数的东西并提供一个头文件的话,别忘了在函数前加上修饰符__declspec(import)

__declspec(dllimport) void Foo(void);

在winnt.h等系统头文件中就是这样做的。

7. PE 文件结构

现在让我们开始研究PE文件格式,我将从文件的头部开始,描述每个PE文件中都有的各种数据结构,然后,我将讨论更多的专门的数据结构比如引入表和资源,除非特殊说明,这些结构都定义在WINNT.H中。

一般地,这些结构都有32和64位之分,如IMAGE_NT_HEADERS32 ,IMAGE_NT_HEADER64等,他们基本上是一样的,除了64位的扩展了某些字段。通过#DEFINE WINNT.H都屏蔽了这些区别,选择那个数据结构取决于你要如何编译了(如,是否定义_WIN64)

The MS-DOS Header

每个PE文件是以一个DOS程序开始的,这让人想起WINDOWS在没有如此可观的使用者的早期年代。当可执行文件在非WINDOWS平台上运行的时候至少可以显示出一条信息表示它需要WINDOWS。

PE文件的开头是一个IMAGE_DOS_HEADER结构,结构中只有两个重要的字段e_magic and e_lfanew。e_lfanew指出pe file header的偏移,e_magic需要设定位0x5a4d,被#define 成IMAGE_DOS_SIGNATURE 它的ascii为’MZ’,Mark Zbikowski的首字母,DOS 的原始构建者之一。

The IMAGE_NT_HEADERS Header

这个结构是PE文件的主要定位信息的所在。它的偏移由IMAGE_DOS_HEADER的e_lfanew给出

确实有64和32位之分,但我在讨论中将不作考虑,他们几乎没有区别。

typedef struct _IMAGE_NT_HEADERS {

DWORD Signature;

IMAGE_FILE_HEADER FileHeader;

IMAGE_OPTIONAL_HEADER32 OptionalHeader;

} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

在一个有效的pe文件里,Signture被设为0x00004500,ascii 为’PE00’,#define IMAGE_NT_SIGNTURE 0X00004500;第二个字段是一个IMAGE_FILE_HEADER结构,它包含文件的基本信息,特别重要的是它指出了IMAGE_OPTIONAL_HEADER的大小(重要吗?);在PE文件中,IMAGE_OPTIONAL_HEADER是非常重要的,但是仍称作IMAGE_OPTIONAL_HEADER。

IMAGE_OPTIONAL_HEADER结构的末尾就是用来定位pe文件中重要信息的地址簿-数据目录,它的定义如下:

typedef struct _IMAGE_DATA_DIRECTORY {

DWORD VirtualAddress; // RVA of the data

DWORD Size; // Size of the data

};

The Section Table

紧接着IMAGE_NT_HEADERS后的就是节表,节表就是IMAGE_SECTION_HEADER的数组。IMAGE_SECTION_HEADER包含了它所关联的节的信息,如位置,长度,特征;该数组的数目由IMAGE_NT_HEADERS.FileHeader.NumberOfSections指出。具体见下图

PE中的节的大小的总和最后是要对齐的,Visual Studio 6.0中的默认值是4k,除非你使用/OPT:NOWIN98 或/ALIGN开关;在.NET中,依然用了默认的/OPT:WIN98,但是如果文件小于一特定大小时,就会采用0X200为对齐值。

.NET文档中有关于对齐的另一件有趣的事。.NET文件的内存对齐值为8K而不是普通X86平台上的4K,这样就保证了在X86平台编译的程序可以在IA-64平台上运行。如果内存对齐值为4K,那么IA-64的载入器就不能载入这个程序,因为它的页为8K。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值