在我的 C++ DLL 中导入方法,然后导出它

Posted

技术标签:

【中文标题】在我的 C++ DLL 中导入方法,然后导出它【英文标题】:Import Method in my C++ DLL and then Export it 【发布时间】:2013-09-15 12:05:51 【问题描述】:

我的工具是:

    Windows 8 作为开发环境 Visual Studio 2008 作为 IDE C++ 作为编程语言 windows XP 32bit 虚拟机和另一台 64bit 机器作为测试机

我想要达到的目标:

我正在尝试覆盖 GINA.dll,这是一个负责图形窗口登录等的 Windows DLL 文件。 我的 DLL 文件应该在 GINA 中具有相同的同名功能,当系统调用方法 X 时,我需要一些方法来代理原始 DLL 文件,它应该执行原始 MSGINA.DLL 中的 X 方法,

我使用 LoadLibrary 加载了 MsGina.dll,我可以使用 GetProcAddress 获取原始函数地址,但是如何让我的方法执行来自 msgina.dll 的同名方法?

注意:我们不能使用带有内联汇编 jmp 指令的宏,因为 X64 不支持它。

我看过这个: http://msdn.microsoft.com/en-US/library/ms686944(v=vs.85).aspx 但它使用另一个名称与原始名称不同的新功能,

我的试验中的错误: 当我尝试添加诸如

之类的方法时
    __declspec(dllexport) BOOL WlxActivateUserShell(
  _In_  PVOID pWlxContext,
  _In_  PWSTR pszDesktopName,
  _In_  PWSTR pszMprLogonScript,
  _In_  PVOID pEnvironment
);

在我的 .h 文件中和

   BOOL WlxActivateUserShell( 
  _In_  PVOID pWlxContext,  
  _In_  PWSTR pszDesktopName,   
  _In_  PWSTR pszMprLogonScript,    
  _In_  PVOID pEnvironment  
  )
    return true;

在我的 .cpp 文件中出现错误:

Error   1   error C2373: 'WlxActivateUserShell' : 
redefinition; different type modifiers  d:\xxxxxxx.h    23  Gina

如果我将方法重命名为 WlxActivateUserShellssss,例如它不会显示错误,但这意味着 windows 将无法使用它,它必须与 msgina.dll 中的原始方法具有相同的名称

【问题讨论】:

您希望如何在应用程序中使用 ProxyGina.dll? 我不会,windows 在启动时加载我的 gina.dll 而不是 msgina.dll,我把它放在 system32 中并添加一个密钥到 regedit,当我重新启动它时它会加载我的 gina,所以它有to 具有相同的界面。 为什么要删除 __declspec(dllexport) ?你用extern "C" 吗? 正确的原型是BOOL WINAPI WlxActivateUserShell(...)。尝试添加WINAPI(在两个地方),看看是否有帮助。 也试过了,结果和我记得的一样。 【参考方案1】:

我使用汇编函数来解决这个问题。 该代码可在我的github repo 上找到。 对于 32 位内联仍然使用,对于 64 位使用 asm 文件。 详情在此post。

只需运行 python 脚本,它就会为您创建项目。

顺便说一句,除了你要覆盖的函数之外,不需要知道所有函数的原型。修改生成的cpp文件中你感兴趣的函数即可。

【讨论】:

感谢 Min,好消息是我找到了您的出色应用程序并使用了它,我在 1 小时内实现了与我尝试在一个月内完成的相同目标,非常感谢这个应用程序,关于在 x64 中使用 _asm 并在帖子中解决问题,如果这些解决方法我不喜欢,我认为我无法应用此解决方案,所以我使用了您的应用程序并做了我想要的。【参考方案2】:

您似乎正在尝试编写代理 dll。无需组装。

首先,您可能想阅读this。

其次,microsoft linker 有一个 pragma,允许您指定导出的函数名称并将函数转发到另一个 dll。

Pragma 看起来像这样:

#pragma comment(linker, "/export:SwapBuffers=gdi32.SwapBuffers") //this will forward call from your dll into original dll you're "overriding".
#pragma comment(linker, "/export:TextOutA=_HookedTextOutA@20") //and this will let you use your own function

代理 dll 代码可能如下所示:

#include <windows.h>
#include <gdi32_fwd.h>
#pragma pack(1)
#include <stdio.h>
#include <stdarg.h>

static HINSTANCE hLThis = 0;
static HINSTANCE hGDI32 = 0;
static TCHAR prtBuf[0x1000];

static BOOL (WINAPI* Gdi32_TextOutA)(
  __in  HDC hdc,
  __in  int nXStart,
  __in  int nYStart,
  __in  LPSTR lpString,
  __in  int cbString
);

static HFONT (WINAPI* Gdi32_CreateFontIndirectA)(
  const LOGFONTA* lplf
); 

extern "C" HFONT WINAPI HookedCreateFontIndirectA(
  const LOGFONTA* lplf
)
    LOGFONTA lf;
    //override data here
    ...

    return Gdi32_CreateFontIndirectA(lplf);


extern "C" BOOL WINAPI HookedTextOutA(
  __in  HDC hdc,
  __in  int nXStart,
  __in  int nYStart,
  __in  LPSTR lpString,
  __in  int cbString
)
    //override data here
    ....

    return Gdi32_TextOutA(hdc, nXStart, nYStart, lpString, cbString);



//! Attach or detach this proxy.
BOOL WINAPI DllMain( HINSTANCE hInst,DWORD reason,LPVOID )

    if( reason == DLL_PROCESS_ATTACH )
    
        hLThis = hInst;
        hGDI32 = LoadLibrary( "gdi32" );
        if( !hGDI32 )
        
            return FALSE;
        
    *(void **)&Gdi32_TextOutA = (void *)GetProcAddress( hGDI32, "TextOutA");
*(void **)&Gdi32_CreateFontIndirectA = (void *)GetProcAddress( hGDI32, "CreateFontIndirectA");  
    
    else if( reason == DLL_PROCESS_DETACH )
    
        FreeLibrary( hGDI32 );
    
    return TRUE;

//! End of file.

标题:

#pragma comment(linker, "/export:AbortDoc=gdi32.AbortDoc")
#pragma comment(linker, "/export:AbortPath=gdi32.AbortPath")
#pragma comment(linker, "/export:AddFontMemResourceEx=gdi32.AddFontMemResourceEx")
#pragma comment(linker, "/export:AddFontResourceA=gdi32.AddFontResourceA")
#pragma comment(linker, "/export:AddFontResourceExA=gdi32.AddFontResourceExA")
#pragma comment(linker, "/export:AddFontResourceExW=gdi32.AddFontResourceExW")
#pragma comment(linker, "/export:AddFontResourceTracking=gdi32.AddFontResourceTracking")
#pragma comment(linker, "/export:AddFontResourceW=gdi32.AddFontResourceW")
#pragma comment(linker, "/export:AngleArc=gdi32.AngleArc")
#pragma comment(linker, "/export:AnimatePalette=gdi32.AnimatePalette")
#pragma comment(linker, "/export:AnyLinkedFonts=gdi32.AnyLinkedFonts")
#pragma comment(linker, "/export:Arc=gdi32.Arc")
#pragma comment(linker, "/export:ArcTo=gdi32.ArcTo")
#pragma comment(linker, "/export:BRUSHOBJ_hGetColorTransform=gdi32.BRUSHOBJ_hGetColorTransform")


#pragma comment(linker, "/export:CreateEnhMetaFileA=gdi32.CreateEnhMetaFileA")
#pragma comment(linker, "/export:CreateEnhMetaFileW=gdi32.CreateEnhMetaFileW")
#pragma comment(linker, "/export:CreateFontA=gdi32.CreateFontA")
//#pragma comment(linker, "/export:CreateFontIndirectA=gdi32.CreateFontIndirectA")
#pragma comment(linker, "/export:CreateFontIndirectA=_HookedCreateFontIndirectA@4")

...

#pragma comment(linker, "/export:CreateFontIndirectExA=gdi32.CreateFontIndirectExA")
#pragma comment(linker, "/export:CreateFontIndirectExW=gdi32.CreateFontIndirectExW")
#pragma comment(linker, "/export:CreateFontIndirectW=gdi32.CreateFontIndirectW")
#pragma comment(linker, "/export:SwapBuffers=gdi32.SwapBuffers")
//#pragma comment(linker, "/export:TextOutA=gdi32.TextOutA")
#pragma comment(linker, "/export:TextOutA=_HookedTextOutA@20")

...

#pragma comment(linker, "/export:bInitSystemAndFontsDirectoriesW=gdi32.bInitSystemAndFontsDirectoriesW")
#pragma comment(linker, "/export:bMakePathNameW=gdi32.bMakePathNameW")
#pragma comment(linker, "/export:cGetTTFFromFOT=gdi32.cGetTTFFromFOT")
#pragma comment(linker, "/export:gdiPlaySpoolStream=gdi32.gdiPlaySpoolStream")

【讨论】:

是的,由于@min-lin 的出色应用,我在我的代码中做了与上述类似的事情【参考方案3】:

看看here。

该链接指向一个项目,该项目为 DLL 生成骨架以创建代理。 如果我没记错的话,当我尝试做同样的事情时,他想到了很多我没有想到的事情。

另一个想法,请阅读本地 MSDN 副本中的 DEF 文件文档。我相信那里有一个关键字,基本上说“如果这个 def 文件没有定义这个函数(通过函数名或序号),将它传递给这个其他 DLL,在函数名或序号 'X' 处”。

因此,如果您只想替换 gina.dll 中的函数 VOID WINAPI DoSomething(VOID),则可以使用我描述的关键字来代理所有其他函数,无论是通过名称还是序号

我不再拥有一台真正的 Windows 开发机器,所以我无法查找它。我拒绝使用 MSDN 网站,因为找不到任何东西...

【讨论】:

以上是关于在我的 C++ DLL 中导入方法,然后导出它的主要内容,如果未能解决你的问题,请参考以下文章

在 DLL 中导出的 C++ 函数中使用 #ifdef 块

如何导出 C# dll 方法/函数以在 C++ 中使用它 [重复]

导出对象后何时释放 dll

MFC Dll 导出函数

如何隐藏 DLL 中的导出函数

如何在 JavaScript 和 TypeScript 中导入/导出?