-
-
[旧帖] [原创]为什么需要导入段 0.00雪花
-
发表于: 2009-4-24 14:15 2116
-
网上有很多介绍PE文件的..但是大部都是说怎么找输入表...究竟PE文件为什么要用输入表呢...我想了很久.把所想的东西写下来...也不知道对否..请高手指点...
1.为什么需要导入段?
要回答这个问题... 我们得了解可执行文件执行的过程..
①
启动一个可执行模块的时候(通常是EXE文件..).操作系统的加载程序(以后简称加载程序)会先创建一个进程,然后通
过内存映射文件.把整个exe文件映射到这个进程空间的地址空间中(通常是4GB).初始位置可能是0x40000000..
(0x40000000前面的虚拟内存干什么用呢..这个加载程序可能会把dll加载到这一部分..也可能闲着不用..如果你需要
使用,就自己用吧..)0x80000000后面通常是操作系统的范围了..就是一些系统dll和内核代码了...
②.之后,加载程序会检查exe文件的导入段...(因为导入段中包含有dll的相关信息..比如dll名称..要调用该dll的函
数名称..等还有时间戳等...)把每个dll都加载到这个进程的地址空间..
③.对于②这个过程可能会是循环的..比如某个dll中又调用的其它的dll中的函数..同样.这个其它dll中的导入段也
会包含相关dll的一些信息..加载程序同样也要把其加载进来...例如.a.dll .调用了b.dll 中的add函数..加载程序
在加载a.dll的时候.也会加载b.dll... ..(DLL.和exe文件格式是完全一样 ...),你不用担心,,这个过程不会导致死
循环..加载程序会记载每个加载过的dll的....如果已经加载了..是不会再加载的..
④.当加载程序把所有的模块都加载完毕后..就开始修复对导入符号的引用了..(为什么要修复..先把这个过程弄完,
再说...)对导入段中列出的每个符号(即函数)..加载程序会检查对应dll中的导出表...看该函数是否存在..如果存在
,那么加载程序会取得该函数的RVA(相对虚拟地址..(DLL.中函数的地址是相对的..通常是相对于0X10000000这个地址
的..另外DLL.中保存的也是相对的虚拟地址..))取得地址后..加载程序通过计算(把rva.加上该DLL被加载的基址)就
得到了该函数在进程空间里边的绝对的虚拟地址...这里..如果DLL重定位了(就是没有被载入默认的基址.这个位置已
经被占用了)...还要通过更复杂的算法了...(算法是这样的..).它会把当前映射的基址减默认的基址 再加上当前加
符号的地址...
接着.加载程序就把这个地址保存在了导入段中了(可能会同时修改代码中所有对该符号引用的地址.这一点我不清
楚...没有办法证明..因为我看到的汇编代码都是修复好的...(就是显示是的绝对的虚拟地址))..好像是什么
firstThunk吧...刚开始的时候用的是orignalthunk//
吧..我对PE文件格式不是很熟悉...
⑤.之后呢..加载程序就完成了使命了...我们代码中如果调用了..某个函数..的时候..导入段中已经有了这个函数的
地址了...就能正确的被访问了...
最后一个问题.就是为什么要修复呢...当然不修复也行...能找到符号的地址就不用导入段了...比方说函数的正确地
址硬编码到指令中..这个是不可能的...比方说系统DLL.但是,每次..系统启动的时候.映射的位置就不同的...听说这
是VISTA.的一个特性.ASLR (地址空间布局随机化..)...那xp呢..可能没有问题(同一个XP版本 比如同是SP3).如果
XPSP2..这个地址一般不会相同的.....程序肯定不能运行 ...所以硬编码肯定不行(程序的移植性更打折扣)..那通常
我们写程序的时候...都是调用那个符号,其实编译器是把符号的地址写进去了.....编译器需要根据lib文件..(lib文
件中有dll中的函数的相对地址)....中的函数地址址,把我们的符号给替换掉....
1.为什么需要导入段?
要回答这个问题... 我们得了解可执行文件执行的过程..
①
启动一个可执行模块的时候(通常是EXE文件..).操作系统的加载程序(以后简称加载程序)会先创建一个进程,然后通
过内存映射文件.把整个exe文件映射到这个进程空间的地址空间中(通常是4GB).初始位置可能是0x40000000..
(0x40000000前面的虚拟内存干什么用呢..这个加载程序可能会把dll加载到这一部分..也可能闲着不用..如果你需要
使用,就自己用吧..)0x80000000后面通常是操作系统的范围了..就是一些系统dll和内核代码了...
②.之后,加载程序会检查exe文件的导入段...(因为导入段中包含有dll的相关信息..比如dll名称..要调用该dll的函
数名称..等还有时间戳等...)把每个dll都加载到这个进程的地址空间..
③.对于②这个过程可能会是循环的..比如某个dll中又调用的其它的dll中的函数..同样.这个其它dll中的导入段也
会包含相关dll的一些信息..加载程序同样也要把其加载进来...例如.a.dll .调用了b.dll 中的add函数..加载程序
在加载a.dll的时候.也会加载b.dll... ..(DLL.和exe文件格式是完全一样 ...),你不用担心,,这个过程不会导致死
循环..加载程序会记载每个加载过的dll的....如果已经加载了..是不会再加载的..
④.当加载程序把所有的模块都加载完毕后..就开始修复对导入符号的引用了..(为什么要修复..先把这个过程弄完,
再说...)对导入段中列出的每个符号(即函数)..加载程序会检查对应dll中的导出表...看该函数是否存在..如果存在
,那么加载程序会取得该函数的RVA(相对虚拟地址..(DLL.中函数的地址是相对的..通常是相对于0X10000000这个地址
的..另外DLL.中保存的也是相对的虚拟地址..))取得地址后..加载程序通过计算(把rva.加上该DLL被加载的基址)就
得到了该函数在进程空间里边的绝对的虚拟地址...这里..如果DLL重定位了(就是没有被载入默认的基址.这个位置已
经被占用了)...还要通过更复杂的算法了...(算法是这样的..).它会把当前映射的基址减默认的基址 再加上当前加
符号的地址...
接着.加载程序就把这个地址保存在了导入段中了(可能会同时修改代码中所有对该符号引用的地址.这一点我不清
楚...没有办法证明..因为我看到的汇编代码都是修复好的...(就是显示是的绝对的虚拟地址))..好像是什么
firstThunk吧...刚开始的时候用的是orignalthunk//
吧..我对PE文件格式不是很熟悉...
⑤.之后呢..加载程序就完成了使命了...我们代码中如果调用了..某个函数..的时候..导入段中已经有了这个函数的
地址了...就能正确的被访问了...
最后一个问题.就是为什么要修复呢...当然不修复也行...能找到符号的地址就不用导入段了...比方说函数的正确地
址硬编码到指令中..这个是不可能的...比方说系统DLL.但是,每次..系统启动的时候.映射的位置就不同的...听说这
是VISTA.的一个特性.ASLR (地址空间布局随机化..)...那xp呢..可能没有问题(同一个XP版本 比如同是SP3).如果
XPSP2..这个地址一般不会相同的.....程序肯定不能运行 ...所以硬编码肯定不行(程序的移植性更打折扣)..那通常
我们写程序的时候...都是调用那个符号,其实编译器是把符号的地址写进去了.....编译器需要根据lib文件..(lib文
件中有dll中的函数的相对地址)....中的函数地址址,把我们的符号给替换掉....
赞赏
他的文章
- 程序入口 ESP (堆栈)是否都是从 0012FFC4 开始? 6063
- [分享]外挂辅助制作工具(版本1.3) 32870
- [下载]天天向上专用工具1.2版 12486
- [下载]模块管理工具1.1版本 5801
看原图
赞赏
雪币:
留言: