作者:不及格的程序员-八神
1. 背景资料
通常开发者获取到的通用dll函数库都会有说明文件,里面对dll库中公开的函数都会有api接口说明,比如函数的名称,返回值,参数类型以及参数的数量.
但是有时开发者也会找到一些dll库函数,这些库函数只能获取到函数名称,而没有关于函数的参数信息.
这种dll一般是来自第三方的,或是偷来的(我的项目中就是属于此类,我知道这个dll函数的功能很不错,我就是想将它嵌入到我的程序中.)).
对于前者,具有资料完备API编程信息,我们可以在c++,vb或.NET平台中轻松编写调用接口来执行dll中的函数,而后者由于没有参数信息,想调用就不那么简单了.
通常系统自带的dll函数库都会有api说明,或者也可以通过某些工具来获取dll库中的函数描述信息.
可是一些第三方的dll函数可能不会将函数信息写到dll中,这样我们就不能够获取参数信息了,也就无法调用了,真的没办法吗?
2. 解决办法
即然没有参数信息就没有办法声明函数的调用接口,不过没关系.
我想到一种简单的方式:用汇编语言来直接调用.
首先要有完整的第三方程序,先看看第三方程序在汇编语言层面上是如何调用函数的(通过反汇编).
通过调试软件(如:windbg,od,softace等)将第三方的程序运行起来,然后在我们需要的那个dll模块函数上加断点,分析它的汇编码.
其实很简单的,如果函数需要参数,那就看看它是通过栈的方式传的参数还是通过寄存器来传的参数.
一般汇编语言中参数都是传的参数地址,而不是值.另外传递参数的规则也要弄清.
如果传参方式是栈方式传参数,还要区分是调用者清除栈还是被调用者清除栈.
比如下面代码是调用md5加密,我就是用栈方式传递两个指针参数给函数.
mov eax,dword ptr [cb] //传入待加密字符串地址
push eax //参数2 入栈
mov eax,dword ptr [result] //反回值字符串地址
push eax //参数1 入栈
call pFun //执行函数
结果result里就是我们加密后的结果了.
上面代码使用的是调用者清除栈方式,而清除栈方式是被调用者清除栈(通过反汇编可以发现指定函数里是使用哪种清栈方式.): 如果需要我们清除栈,那就在上面代码的最后写 add esp,8 代表释放两个指针参数的空间8个字节.
关于函数清栈方式参见我的另一篇随笔:函数的可变数目参数 与 栈
3. 如何加载dll与函数地址
我使用win32 api 来加载dll库与函数地址,使用方式:
HINSTANCE dll = LoadLibrary(L"d:\\*******.dll");
FARPROC pFun = GetProcAddress(dll,"要调用的函数名称");
_asm
{
mov eax,dword ptr [cb]
push eax
mov eax,dword ptr [result]
push eax
call pFun
}
4. 注意事项
需要特别注意的是,对于(传入的/返回的)参数指针指向的数据区域大小需要多次试验,因为有时从汇编中很难发现局部变量的长度,所以尽量将我们声明的变量地址空间多一些.
还有就是dll函数的清栈方式,要仔细观察反汇编的结果,看看它用的是哪种调用方式,这样才能保障栈平横,不至于缓冲区溢出.
GetProcAddress
显式链接到 DLL 的进程调用 GetProcAddress 来获取 DLL 导出函数的地址。 使用返回的函数指针调用 DLL 函数。 GetProcAddress 将(由 LoadLibrary、AfxLoadLibrary 或 GetModuleHandle 返回的)DLL 模块句柄和要调用的函数名或函数的导出序号用作参数。
由于是通过指针调用 DLL 函数并且没有编译时类型检查,需确保函数的参数是正确的,以便不会超出在堆栈上分配的内存和不会导致访问冲突。 帮助提供类型安全的一种方法是查看导出函数的函数原型,并创建函数指针的匹配 typedef。 例如:
typedef UINT (CALLBACK* LPFNDLLFUNC1)(DWORD,UINT);
...
HINSTANCE hDLL;               // Handle to DLL
LPFNDLLFUNC1 lpfnDllFunc1;    // Function pointer
DWORD dwParam1;
UINT  uParam2, uReturnVal;
hDLL = LoadLibrary("MyDLL");
if (hDLL != NULL)
{
   lpfnDllFunc1 = (LPFNDLLFUNC1)GetProcAddress(hDLL,
                                           "DLLFunc1");
   if (!lpfnDllFunc1)
   {
      // handle the error
      FreeLibrary(hDLL);
      return SOME_ERROR_CODE;
   }
   else
   {
      // call the function
      uReturnVal = lpfnDllFunc1(dwParam1, uParam2);
   }
}
调用 GetProcAddress 时指定所需函数的方式取决于 DLL 的生成方式。
仅当要链接到的 DLL 是用模块定义 (.def) 文件生成的,并且序号在 DLL 的 .def 文件的 EXPORTS 部分中与函数一起列出时,才能获取导出序号。 如果 DLL 具有许多导出函数,则相对于使用函数名,使用导出序号调用 GetProcAddress 的速度稍快一些,因为导出序号是 DLL 导出表的索引。 使用导出序号,GetProcAddress 可直接定位函数,而不是将指定名称与 DLL 导出表中的函数名进行比较。 但是,仅当有权控制 .def 文件中导出函数的序号分配时,才应使用导出序号调用 GetProcAddress。
南来地,北往的,上班的,下岗的,走过路过不要错过!
======================个性签名=====================
之前认为Apple 的iOS 设计的要比 Android 稳定,我错了吗?
下载的许多客户端程序/游戏程序,经常会Crash,是程序写的不好(内存泄漏?刚启动也会吗?)还是iOS本身的不稳定!!!
如果在Android手机中可以简单联接到ddms,就可以查看系统log,很容易看到程序为什么出错,在iPhone中如何得知呢?试试Organizer吧,分析一下Device logs,也许有用.
 
 
 
         
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号