`
ijavagos
  • 浏览: 1189794 次
  • 性别: 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字节的庞大结构,那么这个返回值将并不是你想要的。
分享到:
评论

相关推荐

    通信电源蓄电池组容量性充放电试验三措一案.docx

    5G通信行业、网络优化、通信工程建设资料。

    铁塔维护检测手段.docx

    5G通信行业、网络优化、通信工程建设资料

    通信设备安装施工组织方案.doc

    5G通信、网络优化与通信建设

    299-教育行业信息化与数据平台建设分享.pptx

    299-教育行业信息化与数据平台建设分享.pptx

    手写数字和字母数据集binaryalphadigs.mat

    手写数字和字母数据集binaryalphadigs.mat

    变电站视频监控解决方案.doc

    5G通信行业、网络优化、通信工程建设资料

    PEMFC电堆输出电压模型,可计算效率、输出功率、电流、消耗功率以及等效内阻

    PEMFC电堆输出电压模型,可计算效率、输出功率、电流、消耗功率以及等效内阻

    创建型 结构型 设计型设计模式相关知识

    1、 设计思路 1、 创建型设计模式 创建型设计模式主要“关注对象的创建”。 1. 单例模式 单例模式:能不用就不用 ,他的目的就是为了让一个类只创建一个实例。 用法:把对象的创建权限关闭,提供一个人公开的静态方法,实现静态方法后将实例存放于静态的字段中,方法中返回。 单例模式会长期持有一个对象不会被释放,而普通实例不用就会被释放(当然必须是GC之后才会被释放)。 单例用途;数据临时存储的地方如静态字典,数据库连接池、线程池、IOC容器实例。   1.1懒汉式 设置构造函数为私有的,避免其他外部类可以对其实例化, 创建静态类来存储实例。 在静态方法中创建实例,避免多个线程同时调用方法,我们可以加线程锁, 在方法中使用双判断语句:最外层判断是为了提高运行速率,检查如果静态字段中已经存在实例了就可以直接return;第二层判断是避免创建多个对象实例。 1.2饿汉式1 静态构造函数:由CLR保证,静态构造函数只会在启动程序时候,由CLR自行创建。并且只会创建一次,相比较于懒汉式创建的更早,并且不需要担心会

    《通信工程概预算》模拟试题2.docx

    5G通信行业、网络优化、通信工程建设资料

    毕业设计:Java项目之jsp高校规章制度管理系统(源码 + 数据库 + 说明文档)

    论文目录: 第二章 需求分析与系统总体设计 - 5 - 2.1java的特点 - 5 - 2.2技术可行性 - 5 - 2.3可靠性和安全性特点 - 6 - 2.4系统总体设计 - 6 - 2.5JSP技术介绍 - 7 - 2.5.1 什么是JSP - 7 - 2.5.2 JSP技术特点 - 7 - 2.5.3 JSP开发WEB的几种方式 - 8 - 第三章 数据库的设计与实现 - 9 - 3.1数据库的需求分析 - 9 - 3.2数据库的逻辑设计 - 10 - 3.3 数据库的结构创建 - 10 - 第四章 后台系统和数据库的配置 - 13 - 4.1后台服务器配置 - 13 - 4.2后台数据库的配置 - 13 - 4.3后台全局配置文件 - 13 - 第五章 前端网络页面的开发与设计 - 14 - 5.1登录页面 - 14 - 5.2 管理员用户页面 - 15 - 5.3 注册用户页面 - 16 - 5.4主页面 - 17 - 5.5用户注册页面 - 18 - 5.6 规章制度管理页面 - 18 - 第六章 系统的安全性 - 19 - 6.1 session和cookie的安

    ONU、分光器验收规范.doc

    5G通信行业、网络优化、通信工程建设资料。

    99-煤矿安全生产标准化基本要求及评分方法.pdf

    99-煤矿安全生产标准化基本要求及评分方法.pdf

    node-v12.22.6-sunos-x64.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    475现场通讯器用户手册

    475现场通讯器用户手册

    node-v7.7.0.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    600A钳形电流表使用手册

    600A钳形电流表使用手册

    常见宏基站认识和设计讲解.pptx

    5G通信、网络优化与通信建设

    node-v12.16.3-sunos-x64.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    数据中心机房供电需求分析.pptx

    5G通信、网络优化与通信建设

    Binomial Self-compensation

    Binomial Self-compensation for Motion Error in Dynamic 3D Scanning

Global site tag (gtag.js) - Google Analytics