用实例分析使用动态库DLL与使用静态库SLL的区别

来源:互联网 发布:中国最大源码网站 编辑:程序博客网 时间:2024/06/06 05:40

一个伟大的程序员应该知道他所写代码的每一个字节的意义,这句话说的有点夸张,但是体现了程序员应该知道其所写代码的生成的程序或软件是由什么组成的,用反汇编工具打开一个可执行文件,里面一大堆不是我们自己写的代码,那些代码是怎么来的?研究程序的PE文件,发现结构很严密,除了PE的头部(当然加载到内存中的PE文件是不包括PE的头部的,一般情况下,它的排列方式还是按照PE在磁盘中的逻辑顺序的,或者说是是按照PE被加载器生成的中间部分内存映像的逻辑结构。实际上,在内存中我们的程序是一个个节排列着的,一般情况下,代码段(.text)排在最前面,如下图是一个一般的PE结构的段:

这是使用PEID打开并查看的段的信息,是属于未加载至内存的情况下的信息,当然了,进过计算还是可以计算出加到内存中的信息的,我们看到的相关的虚拟地址就是经过计算出来的。

实际上,在内存中是没有最后的这个.reloc重定位段的,因为在内存中它是没有必要的,在把程序映射到进程的地址空间之后,重定位表已经完成了他的使命了——对磁盘中的映像到内存进行重定位。


说了这么多发现话题跑偏了,实际上这也是相关的知识,我们需要融汇贯通。

我们先来看两张图片:

(第一张是使用IDA打开的我自己编写的一个demo程序,这个程序加载了我自己编写的一个dll,这个dll中只有一个到处函数,那就是简单的add函数)。但是图片显示的是printf函数的定义,这个函数是在C静态库中的,我们的程序把这个函数所在的静态库都加载进来了,copy了一份放在了我们的程序中。

(第二张图片是我编写的加载dll的一个程序,这个程序只是简单做了调用add函数的操作)

第三章图片是我编写一个加载静态库的程序,也只是做简单的调用add函数的操作。



最后使用IDA打开两个程序:

可以看到我们把两个数1和2push之后就有了这样的语句:call ds:__imp__add,结合之前的push和现在的call很显然是调用add对1和2进行add操作,但是为什么不是add呢?那是因为这个函数是在dll中被导入的,在这个进程的地址空间中是没有这个函数的实现的。看下一幅图,这幅图是这个进程的地址空间最后的部分,在.rdata段有其函数的相关信息。

实际上,它所处的位置就是导入表,根据工具也可以看出:

导入表存储的是程序导入的DLL中函数的地址,如下是kernel32.dll中函数的地址:



接着我们再看导入静态库的程序使用IDA打开的情况:


我们接着跟进到add函数的位置:


与之前dll导入不同的是在进程的地址空间中存在add的定义,这说明,静态库被拷贝了一份到了进程的地址空间中。



在程序的最后只有kernel32.dll这一个dll。


当然了,因为我们的静态库中有一个函数,所以两个程序的大小区别不大,但是在很多的工程中的话,程序的大小将会有很大的区别了。

这也是windows程序使用dll的原因,当然了,dll还有很多的好处。


这里提出一个问题,日后再做分析:

我们的CRL(C语言运行时库)有两个版本,一个是动态的,一个是静态的。在我们上面的分析中,我们知道,我们的程序中存在printf函数的代码实现,那么显然是使用的是静态库了,那么为什么不使用的是dll?



0 0
原创粉丝点击