浅谈PE文件结构和进程内存空间结构(2)

测试代码如下:,此代码最终生成dlltest.exe文件,并调用test.dll,主要是打印出EXE所在的堆栈地址,DLL和EXE如何协作。

#include "stdafx.h"

#include "../TestDelayDll/TestDelayDll.h"
char abc;


int cba = 0x0000a0a0;


int _tmain(int argc, _TCHAR* argv[])
{
int b = 0x1111;
char *ok = "wo shi shui";


char *xx = new char[4096];
printf("[exe]no-init=%X, init=%X, stack=%X, heap=%X, %X:%s\r\n", &abc, &cba, &b, xx, ok, ok);
//load
fnTestDelayDll();


delete []xx;


getchar();


return 0;

}


test.dll主要代码:

int fnTestDelayDll()
{
int sp = 0xabcd;
char *a = new char[1024];
printf("[dll]no-init=%X init=%X stack=%X heap=%X\r\n", &nTestDelayDll, &g_testValue, &sp, a);
delete []a;
return 42;
}

上面两段代码可能会被编译器优化掉,因此你必须关闭编译器优化才能看到真实的情况。

结果如下:



进程的内存结构如下(我自己整理的,可能不是很对,堆是各个线程公用的,栈是一个线程一个,用完还给系统):



我自己的理解是这样的,在执行一个EXE程序时,父进程负责为EXE创建进程地址空间,并将EXE文件映射到此空间,当然是以EXE文件所在的存储器为后备存储器,映射过程中是有一定流程的,首先将PE文件头(这个头包含了所有的小头)直接映射到400000开始的虚拟地址空间,然后根据头中的信息将4个段分别映射到地址空间,注意,这里的映射并不是和PE文件存储结构一致,而是根据PE头中的信息进行映射,例如.txt段在PE中偏移是400,那么按道理应该映射到400000+400=400400这是地址,可是实际上是映射到401000地址,就是因为PE头中已经指示了必须映射到此地址,其他各段以此类推。


映射完成后,系统开始找导入表,按照导入表中的信息,导入EXE依赖的DLL,例如需要导入Test.dll文件,这时候执行的映射和EXE一致,只不过地址为定位从10000000开始映射,实际上具体的系统可能不是从这边映射,因为一般我们编写的DLL都从10000000开始映射,这样2个以上的DLL会出现重叠,因此这时候系统会重定位该DLL,重定位DLL后,因为载入地址不在是10000000,所以DLL代码中所有可能引用地址的地方都需要更新,这时候系统会找到DLL的重定位段,根据重定位段记录的地址表,逐个更新地址。

对于DLL重定位我也做了一实验,让testdll.exe加载了两个DLL文件,这两个DLL文件镜像载入的虚地址都是10000000,测试发现,第一个DLL被正常映射到10000000地址,一切都是按照PE约定的虚拟地址进行映射,而第二个DLL镜像被映射到了3b0000地址,也就是被映射到了exe镜像的上面,哈哈,.txt代码段本应该是10001000的,却被映射到了3b1000地址,看来DLL镜像在映射的时候被重定位了,并且还发现,原来代码段中的内容被改掉了,果然是系统修复了地址,如图:


原始汇编:


看来DLL重定位确实会动态修改汇编代码段内容.



总体虚拟内存如下:



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值