`
ijavagos
  • 浏览: 1183021 次
  • 性别: Icon_minigender_2
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

更方便地动态调用DLL导出函数

阅读更多

原文链接:http://www.titilima.cn/?action=show&id=275

在一般情况下,动态调用DLL导出函数的方法是:

  1. 用typedef为目标函数定义函数指针类型。
  2. 用GetProcAddress获取函数指针。
  3. 用函数指针进行调用。

但是,如果要调用的函数太多的话,这个方法难免流于繁琐——有太多的typedef、太多的GetProcAddress和太多的函数指针。在本文中将给出一个通用的解决方法,使这些动态调用更加简便。

先看看我们这个函数的声明:

  1. BOOL__cdeclDllCall(
  2. PCTSTRlpszDll,//目标函数所在DLL的名称
  3. PCSTRlpszFunc,//目标函数名称
  4. intargc,//要调用的参数个数
  5. PVOIDpRet,//函数调用的返回值
  6. ...
  7. );

以MessageBoxA为例,使用方法为:

  1. intret;
  2. DllCall(_T("user32.dll"),"MessageBoxA",4,&ret,
  3. NULL,"Hello,World!","Hello",MB_ICONINFORMATION|MB_YESNO);

换用一个参数的MessageBoxIndirectA,则是:

  1. MSGBOXPARAMSAparam;
  2. ZeroMemory(¶m,sizeof(MSGBOXPARAMSA));
  3. param.cbSize=sizeof(MSGBOXPARAMSA);
  4. param.dwLanguageId=GetSystemDefaultLangID();
  5. param.dwStyle=MB_ICONINFORMATION;
  6. param.lpszCaption="Hello";
  7. param.lpszText="Hello,World";
  8. intret;
  9. DllCall(_T("user32.dll"),"MessageBoxIndirectA",1,&ret,¶m);

实现的原理是动态生成汇编代码,也就是类似这样的一段:

  1. __declspec(naked)DWORD__cdeclDllCallProc(void)
  2. {
  3. __asm
  4. {
  5. pushargn
  6. ...
  7. pusharg2
  8. pusharg1
  9. callproc
  10. ret
  11. };
  12. }

下面列出DllCall的代码,和所有可变参数函数的实现(如sprintf)都差不多。

  1. BOOL__cdeclDllCall(
  2. PCTSTRlpszDll,
  3. PCSTRlpszFunc,
  4. intargc,
  5. PVOIDpRet,
  6. ...)
  7. {
  8. va_listarglist;
  9. intret;
  10. va_start(arglist,pRet);
  11. ret=vDllCall(lpszDll,lpszFunc,argc,pRet,arglist);
  12. va_end(arglist);
  13. returnret;
  14. }

最为关键的就是vDllCall的代码了,如下:

  1. #pragmapack(push,1)
  2. typedefstruct{
  3. BYTEop;
  4. DWORD_PTRdwValue;
  5. }OPCODE,*POPCODE;
  6. #pragmapack(pop)
  7. typedefDWORD(__cdecl*DLLCALL)(void);
  8. BOOL__cdeclvDllCall(
  9. PCTSTRlpszDll,
  10. PCSTRlpszFunc,
  11. intargc,
  12. PVOIDpRet,
  13. va_listarglist)
  14. {
  15. HMODULEhDll=LoadLibrary(lpszDll);
  16. if(NULL==hDll)
  17. returnFALSE;
  18. FARPROCproc=GetProcAddress(hDll,lpszFunc);
  19. if(NULL==proc)
  20. returnFALSE;
  21. HANDLEhHeap=GetProcessHeap();
  22. POPCODEp=(POPCODE)HeapAlloc(hHeap,0,sizeof(OPCODE)*(argc+2));
  23. inti;
  24. for(i=argc-1;i>=0;--i)
  25. {
  26. //pusharg[i]
  27. p[i].op=0x68;
  28. p[i].dwValue=va_arg(arglist,DWORD_PTR);
  29. }
  30. //callproc
  31. p[argc].op=0xe8;
  32. p[argc].dwValue=(INT_PTR)proc-(INT_PTR)&p[argc+1];
  33. //ret
  34. p[argc+1].op=0xc3;
  35. p[argc+1].dwValue=0x90909090;//nopnopnopnop
  36. DLLCALLpfn=(DLLCALL)p;
  37. DWORDret=pfn();
  38. HeapFree(hHeap,0,p);
  39. FreeLibrary(hDll);
  40. if(NULL!=pRet)
  41. *(PDWORD)pRet=ret;
  42. returnTRUE;
  43. }

其中的指针p就是我们动态生成的调用代码,最后转换成DLLCALL类型的函数指针进行了调用。

最后,需要补充说明四点:

  1. DllCall只适用于__stdcall调用约定的目标函数。
  2. 这份vDllCall的代码只适用于x86的CPU,如果在WinCE的环境下(如arm或mips的CPU)使用,需要酌情重新编写动态调用的汇编代码。
  3. 其中argc参数是指实际压栈的参数个数,而不是C语言调用的参数个数。如API函数WindowFromPoint,虽然函数声明中只有一个参数,但是实际上是将POINT::x、POINT::y分别压栈的。在这种情况下,需要将argc设置为2。
  4. DllCall的返回值只获取了eax,如果有的函数会返回一个超过4字节的庞大结构,那么这个返回值将并不是你想要的。
分享到:
评论

相关推荐

    动态调用DLL中导出的类

    DLL的大部分调用都是利用.h和.lib,即使动态调用DLL也只是调用其中的导出的函数,而不是导出的类。也许很多小伙伴会说我动态调用是多此一举,但这里主要是技术交流,让大家知道这样做也是可行的。注意:类成员函数...

    VC++编写DLL导出函数及其调用方法

    实例在Visual Studio 2008 SP1 IDE中如何创建、编写和导出DLL,以及如何调用生成的DLL。

    直接加载并调用DLL中函数

    这是一个从内存(资源形式)直接加载并调用DLL中函数的例子。 xDll工程只是一个测试用的dll,附上代码,编译出的xDll.dll直接放在testLoadDll工程目录下 testLoadDll是实际测试代码,从资源直接加载Dll并调用其导出...

    C#调用C++DLL导出类

    C++DLL中包含一个类,C#端要调用这个类的函数,重新封装这个类,来供C#端调用

    dll动态链接库关键字导出函数模板

    dll动态链接库,关键字__declspec(dllexport)导出函数,四则运算,显式和隐式调用dll

    C# 调用c++ 库 参数为指针类型导出函数

    参数为指针类型导出函数 c# Csharp调用 c++库 参数为导入和导出指针两种 包含C++ DLL源码 如fun(cont char* A,char*B) A为输入参数,B为输出参数-C# CSharp call C++ DLL lib dll function param use export and ...

    QT调用dll和MFC调用QT的dll

    编写QT的dll,QT调用QT的dll,QT调用外部的dll,MFC程序调用QT的dll,

    Qt调用dll的功能函数

    本资料的内容分两部分:第一部分是QT在windows上DLL的导出和调用;第二部分是QT在linux上DLL的导出和调用。

    DLL函数和类导出

    llMain 类似于main函数或者winmain等入口函数,当加载、卸载、线程启动、线程终止时会调用,可在此申请资源或清理资源等。 DLL可以配合头文件和lib使用,或者使用LoadLibrary+GetProcAddress动态加载。

    Python中调用C++dll例子

    Python中调用C++dll例子,使用python中的ctypes。

    Qt调用dll中的功能函数

    篇内容分两部分:第一部分是 QT在 windows 上 DLL的导出和调用; 第二部分是 QT在 linux 上 DLL(os)的导出和调用; /////////////////////////////////////////////////////////////////////////////////////...

    使用VS2015编译和调用动态链接库dll

    调用dll 1.重新建立一个工程 这回选择普通的控制台程序就行了。我建了个名为myDllCall的工程。 2.把库的头文件include进来,以及连接lib文件 其中 include进来的 myDll.h 和 **#pragma comment()**的lib根据自己的...

    vc调用dev-c++动态链接库dll示例

    描述了如何从vc中调用dev-c++写的dll的过程与方法。 1、设置导出关键字__stdcall 2、修改vc调用方式为stdcall 3、实现函数调用call

    获得dll的所有输出函数

    利用这个程序可以得到一个dll的所有输出函数,这没有什么特别的,w32dsm就可以,不过w32dsm输出的是一般人看不懂得“名称修饰”,我这个程序是可以转换为C++函数声明的。 其实,这些功能微软都提供了(undname.exe和...

    深入浅出Visual C++动态链接库(DLL)编程(pdf版+doc版)

    6.5 MFC扩展DLL导出函数和变量 6.6 MFC扩展DLL的应用 第五章:VC++动态链接库编程之DLL典型实例 7.1 算法DLL 7.2纯资源DLL 7.3通信控制DLL 第六章:VC++动态链接库编程之DLL木马 6.1、DLL木马的原理 6.2、DLL木马...

    如何用VC++创建及调用DLL

    如果使用“__stdcall”调用方式,可能产生C不识别的修饰名,所以设置导出函数时要采用.def文件形式,而不是__declspec(dllexport)形式。后者会进行修饰名转换,C语言无法识别函数。 下面的代码是一个定义文件的示例...

    动态库 DLL开发与使用的优秀范例

    应用程序可以调用的DLL函数,在DLL中叫做导出函数,而在应用程序中叫做导入函数。应用程序中的导入函数与DLL文件中的导出函数进行链接有两种方式:隐式链接和显式链接。一、隐式链接 在建立一个DLL文件时,编译器会...

    dll 类导出

    c ++ 编写dll .导出函数. 导出类. 使用dll. 包括隐式调用 显示调用

    Qt.VC调用delphi编写的dll lib,详细方法

    delphi可以很方便的封装第三方库,导出dll,此方法可以很方便的生成供Qt或VC调用的Lib文件,免去动态加载的麻烦,喜欢的朋友可以试下。我就是这么用的。

    Delphi7静态动态调用VS2019 C#开发的非托管DLL且整合DLL

    C#开发的dll,导出非托管的静态函数给其它语言调用,非COM方式

Global site tag (gtag.js) - Google Analytics