在 ABI 级别的 Visual Studio 中使用 C++ 重新实现 DLL 方法变量

Posted

技术标签:

【中文标题】在 ABI 级别的 Visual Studio 中使用 C++ 重新实现 DLL 方法变量【英文标题】:Reimplimenting a DLL method variable with C++ in visual studio at ABI level 【发布时间】:2013-11-21 06:53:24 【问题描述】:

我有一个 C++ 中的第三方 DLL,我想用我自己制作的一个替换它... 这是一个洁净室类型的练习,仅用于学习目的;我希望制作一个替换 DLL,它可以与链接到原始 DLL 的程序一起使用——无需重新编译应用程序。

我正在使用与制作原始 DLL 相同的 Visual Studio 编译器版本 (9),但我没有 DLL 的原始源代码。

DLL 由一个 C++ 类和一些用于处理构造函数/析构函数的外部“C”函数组成,因此所有内存管理都与 DLL 隔离。

我使用dependency walker 来检查原始DLL,并对链接器符号进行解码/取消装饰——并尝试为类和方法编写原型;然后我编写了一个 python 脚本来获取从我的类中编译的目标代码 - 并制作一个 .def 文件,该文件从我的 obj 代码中选择最接近的损坏符号与原始 DLL 导出进行比较; (允许一些限定符变体,但不允许名称变体)然后我使用该 .def 文件从我的 obj 代码构建一个 DLL,以在 DLL 中具有相同的 ABI 排序。

当涉及到列出类型时,我处于依赖walker 无法区分我的 DLL 和原始 DLL 的阶段——尽管 在几个损坏的名称中存在细微差别我想解决...

一个成员很难弄清楚,因为它不是一个函数,但应该是成员数据,例如:修饰的 ?pzSambaAddress@HWInterface@@2PBDB 显示为:

char const * const HWInterface::pzSambaAddress ; // in dependency walker

而且我不确定dependency walker是否解码错误,因为 我无法弄清楚如何在我的头文件中远程实现任何东西,这会将符号导出到 .obj 文件,更不用说导出到 DLL 了。

什么样的定义可以创造出这样的东西?

如果我将它输入(如上所示)到我的头文件中,它是一个常量字符串——因此我认为它必须在构造函数方法中初始化,如下所示:

HWInterface::HWInterface(HWInterface const & iface) : pzSambaAddress("dummy") 
    std :: cout << this -> pzSambaAddress; // access it, to force compiler

但是当我编译它时,pzSambaAddress 根本不会出现在 obj 文件中。 显然是因为它不是在类实例化之前分配的内存位置。

例如:dumpbin /SYMBOLS HWInterface.obj | grep "pzSam" 什么也没找到。

我可以将 static 关键字添加到 pzSambaAddress 的定义中,并为整个类初始化一次。

char const * const HWInterface::pzSambaAddress="a samba name constant.";

然后名称会变成:?pzSambaAddress@HWInterface@@2QBDB

但是dependency walker 并没有说它是静态的……@@2QBDB 也不是很混乱@@2PBDB……这也意味着我也不能再用构造函数初始化单个实例。

HWInterface.cpp(25) : error C2438: 'pzSambaAddress' : 无法通过构造函数初始化静态类数据

所以 Q1:导出符号的原因是静态常量吗?依赖 walker 只是没有说“静态”——还是有其他方法可以生成/初始化它?

其次:

有什么更好的去修饰和提供有关深奥限定符的信息吗?

当我在一个目标文件上运行 dumpbin 时,我得到了依赖 walker 没有显示的各种限定符(在其他符号上,不是我们一直在讨论的示例)。 dumpbin.exe /symbols myOwnFile.obj

但由于我没有原始 DLL 的 obj 文件,也没有 .lib,因此该开关不起作用。在 DLL 上运行 dumpbin.exe /symbols 什么也没给我。 在 DLL 上运行 dumpbin.exe /exports 只会给我错误的名称。 还有一个 VC++ 控制台应用程序“undname.exe”,但通常它根本不会取消修饰命令行上传递的名称,而是返回的大部分名称仍然被破坏。

我在网上查了很多资料,但只能找到部分准确/不完整的信息,这不足以解决我刚刚提出的问题。

Wikipedia on mangling names

关于在哪里可以找到更详细/更准确的可视化 C++ 解编程序的任何想法?

【问题讨论】:

【参考方案1】:
class HWInterface 
    public:
    __declspec(dllexport) 
    static char const *  pzSambaAddress;
;

char const*  HWInterface::pzSambaAddress = "hello";

然后:

C:\temp>cl /LD test.cpp
...

C:\temp>dumpbin /exports test.dll
...
Dump of file test.dll
...    
    ordinal hint RVA      name

          1    0 00008000 ?pzSambaAddress@HWInterface@@2PBDB

您可以使用 MSVC 附带的 undname 实用程序来解码损坏的名称:

C:\temp>undname ?pzSambaAddress@HWInterface@@2PBDB
Microsoft (R) C++ Name Undecorator
Copyright (C) Microsoft Corporation. All rights reserved.

Undecoration of :- "?pzSambaAddress@HWInterface@@2PBDB"
is :- "public: static char const * const HWInterface::pzSambaAddress"

但是,您会注意到undname 表示其中应该有一个额外的const。如您所见,添加额外的 const 会使您得到一个稍微不同的重命名(在结尾处使用“Q”而不是“P”:

使用static char const * const 代替static char const * 会产生?pzSambaAddress@HWInterface@@2QBDB

undname 从 C 运行时 _unDNameEx 导入一个函数,我假设它用于解开名称(并且我假设 Dependency Walker 也使用它 - 显然是通过 DBGHELP.DLL 中的接口)。看起来拆解器中存在错误。

GNU 工具有一个类似的实用程序 c++filt,用于解码 g++ 错误名称。

【讨论】:

是的,我可以清楚地看到 Visual Studio 提供的一些未装饰库中存在错误;你用的是什么版本?我拥有的 undname.exe 的最佳版本是 md5sum : ff8f053c9a896be915b4cb9b5f6feb3e ,它并不总是正确地取消命名项目;例如: undname ?bIsBluetoothAddress@HWInterface@@UAE_NABV?@DV?@DV?@D@ATL@@@@@ATL@@@Z 将返回 ?bIsBluetoothAddress@HWInterface@@UAE_NABV?@DV?@DV?@D @ATL@@@@@ATL@@@Z 绝对没有取消修饰名称。 我想我找到了问题所在;有不同版本的 msvcrt.dll 可以在我的系统上运行;而且很可能使用的是旧版本。 不...我明确链接到 msvcrt.dll 版本 7.0.7601.17744 md5:9dc80a8aaaaac397bdab3c67165a824,它有完全相同的问题。奇怪的是,Dependency walker 比直接链接到 dll 版本 7 的 exe 做得更好。当我在自身(最新版本)上运行依赖 walker 时,它并没有显示自己链接到 DBGHELP.DLL 或 msvcrt.dll。所以,我认为较新的依赖遍历器可能不会使用微软的库。它报告: bool HWInterface::bIsBluetoothAddress(class ATL::CStringT> > const &)

以上是关于在 ABI 级别的 Visual Studio 中使用 C++ 重新实现 DLL 方法变量的主要内容,如果未能解决你的问题,请参考以下文章

Visual Studio 警告级别的含义?

Visual Studio 2019 无法在 Watch 窗口中查看应用级别定义的静态变量

Visual Studio 2010 默认缩放级别

markdown 在visual studio中设置编译器警告级别

当 Visual Studio 2017 与 std 标头不兼容时,如何使用最高警告级别(墙)?

Visual Studio:Android SDK 设置(API 级别 19 和 21、22、23)