可能在很多人想想中,只有DLL才有导出表,而Exe不应该有导出表。而在《PE文件和COFF文件格式分析——导出表》中,我却避开了这个话题。我就是想在本文中讨论下载Exe中存在导出表的场景。(转载请指明出于breaksoftware的csdn博客)

首先要说的是Exe是可以有导出表的,我用我写的PE分析工具扫描了我电脑上所有文件。发现有导出表的Exe文件还不少。比如chrome.exe。

还有OllyDBG.EXE。

我一开始还不能理解为什么要在Exe中搞导出函数。后来查了相关资料,发现这样做是为了方便开发插件,这让我一下焕然大悟。

现在思考一个过程,我们的Exe程序的逻辑可能需要若干Dll中函数来辅助。如下图

A.exe需要B.dll、C.dll和D.dll辅助。现在我们要支持插件,那么我们需要提供一些接口函数供插件使用。比如我们要提供B1()、C1()和D1()供插件使用,那如何设计呢?

最简单的办法就是不做设计,插件要动态LoadLibrary我们的B.dll、C.dll和D.dll,然后把各个函数导出来用。看似很方便,但是如果我们工程不止是3个Dll呢?暴露的函数也不止这三个呢?我想做这个系统的插件的同学想到要Load那么多DLL就会感觉烦!而且还有一个严重的问题,哪天我们想给B.dll改个名字,叫E.dll,那么使用B.dll的插件都不能正常工作了。可以见得这是个非常不稳定的方案,因为它关联的因素太多了。

那我们收敛一下方案,我们做个空壳DLL(使用《PE文件和COFF文件格式分析——导出表》介绍的类似于Kernel32.dll的AddVectoredExceptionHandler导出方法,这个方法的应用我会在之后写篇文章介绍),该DLL就导出B.dll中B1()、C.dll中C1()和D.dll中D1()的入口地址。然后插件就加载这个DLL,调用该DLL中的方法。如下图

这样就很好解决了之前的不足。貌似离最佳不远了,但是想想,是不是还可以优化呢?我们这么设计要多维护一个DLL(PluginHelper.dll),这个也就引入了一个不稳定因素。那么这个DLL可以省掉么?省掉后导出的那些函数放哪儿?

经过考虑,PluginHelper.dll的功能放在哪个DLL文件中都不合适。那只能放在A.exe中了。是的!我们让A.exe导出函数,反正我们A.exe也是要加载B.dll、C.dll和D.dll,这样还可以省下PluginHelper.dll加载如上DLL的过程。现在我写了一个工程,模拟这种插件模型。

ExeMain是我们的主程序,DllOne和DllTwo是ExeMain需要加载的DLL,它们也提供了插件需要暴露给插件的函数的实现。Plugin是个插件。

我们先看下DLLOne和DllTwo的导出函数

LIBRARY  "DllOne"
EXPORTSRet1
LIBRARY  "DllTwo"
EXPORTSRet2

那么在Exe中如何暴露这两个函数呢?看Exe中的代码

typedef int (WINAPI* RetNFunc)();extern "C" __declspec(dllexport) int MainRet1();
extern "C" __declspec(dllexport) int MainRet2();int MainRet1(){int nRet = 0;HMODULE hDllOne = LoadLibraryA("DllOne.dll");do {if ( NULL == hDllOne ) {break;}RetNFunc pFunc = (RetNFunc)GetProcAddress( hDllOne, "Ret1" );nRet = pFunc();FreeLibrary( hDllOne );hDllOne = NULL;} while (0);return nRet;
}int MainRet2(){int nRet = 0;HMODULE hDllTwo = LoadLibraryA("DllTwo.dll");do {if ( NULL == hDllTwo ) {break;}RetNFunc pFunc = (RetNFunc)GetProcAddress( hDllTwo, "Ret2" );pFunc();FreeLibrary( hDllTwo );hDllTwo = NULL;} while (0);return nRet;
}

我们看下Exe的导出函数表

至于插件的调用,我这儿不准备搞复杂的设计,我这儿将直接Load插件DLL,并调用DLL中的导出方法(该方法的调用约定是提前确定好的)。调用方法是

typedef void (WINAPI* PluginFunc)();
int _tmain(int argc, _TCHAR* argv[])
{HMODULE hPlugin = LoadLibraryA("Plugin.dll");do {if ( NULL == hPlugin ) {break;}PluginFunc pFunc = (PluginFunc)GetProcAddress( hPlugin, "PluginMain" );pFunc();FreeLibrary( hPlugin );hPlugin = NULL;} while (0);system("pause");return 0;
}

那么插件该如何写呢?插件的逻辑代码如下

LIBRARY  "Plugin"
EXPORTSPluginMain
typedef int (WINAPI* RetNFunc)();void PluginMain() {RetNFunc pFun1 = (RetNFunc)GetProcAddress( GetModuleHandle(NULL), "MainRet1" );if ( NULL != pFun1 ) {printf( "MainRet1: %d\n", pFun1() );}RetNFunc pFun2 = (RetNFunc)GetProcAddress( GetModuleHandle(NULL), "MainRet2" );if ( NULL != pFun2 ) {printf( "MainRet2: %d\n", pFun2());}
}

因为插件DLL已经被Exe加载,所以此处GetModuleHandle(NULL)会得到该进程Exe的模块句柄,GetProcAddress该句柄的导出方法,就可以获得了Exe中导出的函数入口地址了。

看!这样的插件模型是不是非常简单而且紧凑而且易用。

附上工程

PE文件和COFF文件格式分析——导出表的应用——一种插件模型相关推荐

  1. PE文件和COFF文件格式分析——导出表的应用——一种摘掉Inline钩子(Unhook)的方法

    在日常应用中,某些程序往往会被第三方程序下钩子(hook).如果被下钩子的进程是我们的进程,并且第三方钩子严重影响了我们的逻辑和流程,我们就需要把这些钩子摘掉(Unhook).本件讲述一种在32位系统 ...

  2. PE文件和COFF文件格式分析——导出表的应用——通过导出表隐性加载DLL

    通过导出表隐性加载DLL?导出表?加载DLL?还隐性?是的.如果觉得不可思议,可以先看<PE文件和COFF文件格式分析--导出表>中关于"导出地址表"的详细介绍.(转载 ...

  3. PE文件和COFF文件格式分析——导出表

    在之前的<PE可选文件头>相关博文中我们介绍了可选文件头中很多重要的属性,而其中一个非常重要的属性是(转载请指明来源于breaksoftware的CSDN博客) IMAGE_DATA_DI ...

  4. PE文件和COFF文件格式分析——RVA和RA相互计算

    之前几节一直是理论性质的东西非常多.本文将会讲到利用之前的知识得出一个一个非常有用的一个应用.(转载请指明来源于breaksoftware的csdn博客) 首先我们说下磁盘上A.exe文件和正在内存中 ...

  5. PE文件和COFF文件格式分析——节信息

    在<PE文件和COFF文件格式分析--签名.COFF文件头和可选文件头3>中,我们看到一些区块的信息都有偏移指向.而我们本文讨论的节信息是没有任何偏移指向的,所以它是紧跟在可选文件头后面的 ...

  6. PE文件和COFF文件格式分析--概述

    刚工作的时候,我听说某某大牛在做病毒分析时,只是用notepad打开病毒文件,就能大致猜到病毒的工作原理.当时我是佩服的很啊,同时我也在心中埋下了一个种子:我也得有这天.随着后来的工作进行,一些任务的 ...

  7. PE文件和COFF文件格式分析——签名、COFF文件头和可选文件头1

    本文将讨论PE文件中非常重要的一部分信息.(转载请指明来源于breakSoftware的CSDN博客) 首先说一下VC中对应的数据结构."签名.COFF文件头和可选文件头"这三部分 ...

  8. PE文件和COFF文件格式分析--MS-DOS 2.0兼容Exe文件段

    MS 2.0节是PE文件格式中第一个"节".其大致结构如下:(转载请指明来源于breaksoftware的csdn博客) 在VC\PlatformSDK\Include\WinNT ...

  9. PE文件和COFF文件格式分析——签名、COFF文件头和可选文件头2

    之前的博文中介绍了IMAGE_FILE_HEADER结构,现在来讨论比较复杂的"可选文件头"结构体.(转载请指明来自breaksoftware的csdn博客)先看下其声明 type ...

最新文章

  1. Active report-在Web 上使用 active report
  2. Xshell链接远程服务器调用Pycharm的方法
  3. 细思恐极,插上U盘就开始执行Python代码的程序
  4. Productivity Power Tools,对于Visual Studio 2017的15个扩展
  5. 解析大型.NET ERP系统 高质量.NET代码设计模式
  6. android获得手机号码代码
  7. 查看Exchange用户最后登录时间
  8. Access优已成忧,一年后,还是离开了秋色园了
  9. stm32f4 usb 升级程序_51单片机程序下载、ISP及串口基础知识
  10. Android 长截屏原理
  11. 做一个自己的PC微信无感免打扰检测僵尸粉死粉的小工具。使用c++ call
  12. GEO数据库中platform信息详解
  13. STM32——时钟、HSE、旁路模式、有源晶振
  14. 夏普Sharp MX-B401 一体机驱动
  15. Devexpress TreeList控件支持拼音首字母查询
  16. pink-jQuery
  17. 将已购买的知乎Live课堂图片下载并导出的教程
  18. 手把手教你做最实用的数据分析模板——帕累托法则
  19. WSL2配置docker和PHP
  20. 使用Blinker APP+NodeMCU控制多路接口

热门文章

  1. 力扣(LeetCode)刷题,简单+中等题(第31期)
  2. GitHub开源游戏:CityBound(模拟城市)
  3. POJ - 2513 Colored Sticks 欧拉通路+并查集+静态树
  4. java string 占位符_驳《阿里「Java开发手册」中的1个bug》?
  5. 状态估计问题学习记录(1)
  6. 剑指offer:面试题32 - III. 从上到下打印二叉树 III
  7. 利用DDA算法得到直线上的像素点坐标
  8. 在Ubuntu 14.04 64bit上使用dig
  9. leetcode-152 乘积最大子序列
  10. python面试题目