还是接着之前那篇关于制作导入库的工具的后续。另一种方法,虽然局限性更大,但是可以满足大多数情况了。
灵感很简单,在查看关于DLL调用方面的文章的时候,突然看到了一篇关于MinGW下制作lib文件之类内容的。因为我以前用过MinGW编译器,同样也搞过这个DLL导入的方面。于是就想起来当时用dlltool工具的时候,确实有看到它能够支持对导入名进行调整的参数。但是那个时候并没有对这方面有多少深入的研究,其实说白了就是没有理解那些参数实际想要表达的到底是什么意思。这下有了前面对VC下隐式调用DLL里函数的经验,一下子知道了那些到底是想表达什么,比如导入名中去掉prefix,或者kill掉后面的@nn,之类之类。
然后呢,前面在研究导入库格式的时候,看微软提供的那个规格说明文档,里面也有提到这方面的东西,比如去掉prefix或者kill掉后面的@nn之类。
说简单点,就是正常情况下比如写了一个这样的def文件
LIBRARY MyDLL EXPORTS function dosomething
此时如果用lib.exe工具(其实就是link.exe /lib,因为WDK中没有lib.exe所以提一下)从这个def文件生成lib文件,那么这个lib文件将从DLL导入function和dosomething,同时向链接器提供_function和_dosomething。这样的话对于__cdecl方式导出的函数是没有问题的。但是如果DLL的导出符号仍然是function和dosomething,但是调用方式却是__stdcall,就会遇上了问题:如果function有一个int型参数,那么链接器将去寻找_function@4而不是_function,因为找不到符号,所以链接失败了。
如果DLL中导出的符号名是function@4,此时生成的lib就会从DLL导入function@4并向链接器提供_function@4,但是实际上以__stdcall方式调用,但是最后导出的时候不带@nn的情况是非常多的。最典型的情况就是Windows API。而且实话说吧因为@nn这样的实在不怎么好看,所以我自己写DLL的时候也会通过使用DEF文件的方式或者#pragma comment(linker, "/EXPORT:xxxxxx")这样的预编译语句让导出的函数不带如此的后缀。
于是这样需求就变得简单:让生成的导入库从DLL引入的时候是不带后缀的,但是向链接器提供的符号是带后缀的。
这并非不可能,调用Windows API的时候就是这样:Windows SDK里面那些LIB文件,就是实现这样的功能。对其格式进行研究,根本就是和普通的导入库一样。除了……呃,除了某些细节上的参数。
于是我的目的就是修改这些细节上的参数。Microsoft PE and COFF Specification里也提供了这方面的详细资料。
方法的话,遍历LIB文件中的每个member,LIB文件的格式可以从上面提到的资料中找到,不会太复杂。注意每个member的第一个字节似乎是对其到偶数边界的,没有对齐的用\n来补。在遍历的过程中分析前四个字节就知道这个member是不是声明了一个导入函数,如果是,根据导入的格式分析其头部信息,然后获得DLL名、函数名等。
在这个导入member的“头部信息”中,有关于导入名应该如何计算的属性。但是LIB.exe工具却没有这方面的参数可以调整,默认就是先前面给你加一个_下划线,然后方式是NoPrefix就是再把这个下划线去掉。也就是说,def文件中如果写的是function,那么它就先加一个下划线,变成_function,作为向链接器提供的符号。然后导入名生成方式是NoPrefix,也就是链接器在链接的时候会再从_function去掉前缀的下划线变回function作为从DLL导入的名。
这个导入名的计算方式有
IMPORT_ORDINAL:通过标号导入,此时不会计算导入名
IMPORT_NAME:原原本本的,和向链接器提供的符号保持一致
IMPORT_NAME_NOPREFIX:不带前缀的,LIB.EXE生成的就是这种。前缀是指?、@或者可选的下划线_
IMPORT_NAME_UNDECORATE:不带前缀并且只取@之前的部分,此时如果向链接器提供的符号名是_function@4,那么导入名就是function,也就是我们需要的。
有了这些资料,思路就非常清楚,比如有以下两个函数
__declspec(dllimport) int __stdcall add2(int, int); __declspec(dllimport) int __stdcall add3(int, int, int);
然后DLL里的导出名分别是add2和add3而不是add2@8和add3@12,此时可以写这样一个DEF文件
LIBRARY doadd EXPORTS add2@8 add3@12
此时生成的lib文件中,向链接器提供的符号这一边已经是所需要的_add2@8和_add3@12了。然后这个时候要修正的是导入名计算方式,因为现在是IMPORT_NAME_NOPREFIX,要改成IMPORT_NAME_UNDECORATE才行。这个时候就要用十六进制编辑器来修改,但是手工做这种事情实在太麻烦,这种事情应该给程序去做。所以就写了个工具,来解决这种问题。
图片为修改一个函数的导入名计算方式后的截图。改完以后保存,就可以实现目的了。
这里给出这个自制工具的下载:(上传时间:2014年2月13日。工具有修改的话,这里可能不会同步更新。)
点击下载 工具
运行需要.net framework 2.0环境。修改完成后记得保存。