前言
这一块往后写的代码就越来越多了,讲静态,动态链接库的话就是为了后面去做准备的,这节课还是自己要动手实操一下,才能更加深刻的明白这是怎么一回事
课堂
代码的复用
秉持着整个项目要保持一个高内聚,低耦合的原则,所以我们在写项目的时候会发现一个问题,有时候会重复很多的代码,那么我们应该怎么去把这些具有不同功能点的代码去封装成一个一个的包呢?现在如果要多人实现代码的复用,比如我写的一段代码要给别人用,不可能直接把代码复制一份发给别人,这样如果很多人要使用,就不方便,所以可以通过下面的三种方式实现:
-
静态链接库
-
动态链接库
-
def 导出
静态链接库
创建静态链接库
这个我在 vs2019 做了,但木有截图,重截一次好麻烦,就用海东老师的课件了,都一个意思
然后如果是新一点的编译器可能在编译的时候会提示一个警告,这个小问题,直接忽略下一步即可
使用静态链接库
分析
这里静态链接库其实还不算是完全的引用了另一个 Dll,其实是将 Dll 在编译的时候就重新与自己拼到了一块,那么现在有个需求,要改一下这个 Dll 的东西,那么就还得重新再编译一遍我们的 exe 程序,所以静态链接库是一种假的模块化
那么为什么使用静态链接库的程序,就可以使用库中的函数了呢?
静态链接库的特点就是使用静态链接库的.exe 程序直接将要用到的函数在编译的时候就直接放到.exe 文件中代码块中去了
但是如果一个.exe 文件包含.dll,使用了.dll 中的函数,此时如果修改函数,.exe 文件就不用动,即不用再重新编译一次。这就是模块化的好处!!(这也是我们下面即将要说的动态链接)
动态链接库
这回我给个 vs2019 的例子吧
创建动态链接库
这里参数的说明不想写了,扒了一个博主的解释,我觉得他真的很认真,就像是把海哥的每一句话都记了下来哈哈哈哈哈,这里引用一下
使用动态链接库
隐式链接
意思说白了就是让编译器去找 Dll 的位置,不用我们在写代码了
显示链接
这个就是从写一个函数指针开始,到最后的调用函数都是我们自己去写的,之后当我们学完导出表,我们就是去模拟这个 GetProcAddress 函数,期待吧哈哈哈哈哈
分析
前面都分析过一遍了,通过反汇编,看 Dll 程序的地址,就会发现,它并不是 4000 多少多少开头了,而是一个全新的地址
正因为.exe 中使用的函数不在.exe 中,而是在.dll 模块中,所以如果对.dll 中的函数进行优化改动,就不用让.exe 也重新编译,直接在.dll 中修改完后,.exe 文件中使用的函数就是修改后的
具体例子
上面这个是 Dll 的写法,然后把那个 lib(静态链接代码在这里,但动态链接就不是了)和 Dll(程序的代码主要在这里)文件复制到要调用 Dll 程序的目录下就行,下面这个是要调用 Dll 的程序
使用.def 导出(这个也是动态链接的)
(这个和后面的导出表就息息相关了,一句精辟的话:如果要用这种方式导出的话,那么就不会在导出表的名字模块出现名字)
我们如果通过上面的方法,将我们写的代码导出,对方可以通过逆向分析得到函数名字,就可以猜到函数的功能,为了不让别人分析我们的.dll,可以通过.def 导出的方法,即通过序列号代替函数名的方式,可以达到隐藏的目的
现在编译一下,使用 DEPEND.exe 查看一下函数导出后的情况:发现 Plus 函数前面的序号为 12,Mul 和 Div 函数前面的序号分别是 13、16,但是序号为 15 的 Sub 函数名字并没有显示出来,即函数没有名字!就起到了在 dll 中隐藏函数名的作用
我们只定义了 4 个函数导出,但是由于四个函数的序号最小是@12,最大是@16,而系统计算导出函数个数的方式是通过最大序号 - 最小序号 + 1,所以会发现有五个导出函数,那多出来的@14 怎么办?就用全 0 表示即可,就是在导出表也会发现函数表那一块确实给 00000000 留了一席之地的
函数导出的两种方式
-
以名字的方式导出(编译器会默认给函数添加序号)
-
以序号的方式导出(以序号的方式导出,再加上 NONAME 关键字,就只以序号的方式导出,函数没有名字)