如何检查可执行文件或 DLL 是否在发布或调试模式下生成 (C++)

How to check if an executable or DLL is build in Release or Debug mode (C++)

本文关键字:模式 调试 C++ 是否 何检查 检查 可执行文件 DLL      更新时间:2023-10-16

我需要找到EXE/DLL的模式是构建的,查看其标头。(仅使用C++,无需任何外部工具)

关于如何确定 DLL 是在发布模式下还是在调试模式下构建的旧讨论。http://forums.codeguru.com/archive/index.php/t-485996.html

但不幸的是,我没有找到任何明确的答案。

我需要找到 exe/dll 模式是构建的,查看其标头。

如果您所说的"标头"是指 PE 部分或资源(标头不会告诉您任何信息,并且程序通常不会附带其开发标头!),这是可能的,在限制范围内,并且不可靠。否则,除非您自己编写程序,否则这是完全不可能完成的工作。

通常,很难以可靠的方式做这样的事情,更何况"调试构建"是一种Microsoft Visual Studio简化,在大多数编译器中不存在。例如,使用 GCC 时,完全允许拥有一个包含调试符号的优化构建。甚至可以使用#pragma打开和关闭优化(并更改优化级别甚至目标机器!),从而在未优化的构建中具有优化的功能(或功能组),反之亦然。

调试符号的存在是你对一个没有编写的程序的最佳猜测。无论如何,不可能(实际上,以简单、自动化的方式)从生成的二进制文件中判断它是否已优化。

.debug$S.debug$T部分分别包含调试符号和调试类型。还有一些其他部分也以.debug开头,但它们已被弃用。在"调试模式"下构建且随后未被剥离的程序将包含部分或全部这些部分。
在没有外部工具的情况下使用C++,您将需要跳过DOS"MZ"存根和PE标头。在此之后是部分标题,您可以解析它们。文件格式的完整文档可以下载 此处.
最有可能的是,读取文件并为.debug执行字符串匹配将同样好。

同样,您可以查看 VERSIONINFO 或清单文件(它们还允许指定程序是否是调试版本),但这些不是强制性的。你几乎可以在这些内容中写任何你想要的东西。到目前为止,它们甚至不如寻找调试符号可靠。

另一个同样不可靠的提示是检查程序与哪些版本的系统库链接。如果是调试版本,则很可能是调试版本。但是,可以进行发布构建并仍然与调试库链接,没有什么可以阻止您这样做。

下一个最佳猜测是没有对 CRT assert 函数的调用(您可以使用简单的字符串匹配来完成),因为assert宏(通常从中调用)在定义了NDEBUG的构建中完全被剥离。不使用该符号,二进制文件中不存在字符串。
不幸的是,没有任何断言的程序将被错误地标识为"发布版本",而不管其实际构建如何,并且完全有可能重新定义assert宏以执行完全不同的操作(例如printf文本并继续)。最后,您不知道您链接的一些静态第三方库(显然已经通过了预处理器)是否包含对您不知道的assert的调用。

如果要检查自己编写的程序,可以利用优化器将完全删除可证明无法访问或未使用的内容这一事实。可能需要 2-3 次尝试才能使其恰到好处,但基本上它应该像定义变量(或导出的函数,如果您的编译器/链接器不导出未使用的符号)并从无法访问的程序位置向其写入两个或三个魔术值一样简单。优化编译器至少会将这几个冗余移动合并为一个,或者更有可能完全消除它们。
然后,您可以只对魔术值进行二进制字符串搜索。如果它们不存在,则它是优化的构建。

这个问题非常好,如前所述,没有真正明显(唯一)的指标来标记映像是否被调试或发布。

如此处和此处所述,调试目录的存在并不表示映像是否已在发布模式下构建。发布的映像是使用调试支持构建的,这是很常见的。事实上,几乎所有的Windows操作系统映像文件都是通过调试支持构建的(否则,不可能将这些已发布的图像与Microsoft符号服务器中的符号文件链接)。即使这些图像是发布图像!

甚至 .debug 部分的存在(实际上,部分名称在 PE 规范中不起作用,可以根据需要更改和设置部分的名称 - 加载器不关心它!)也不是发布与调试映像的指标。

有一个古老的倒车工具叫做LordPE。它将允许您打开两个文件并比较标题。我在VS2008中以发布和调试模式编译了一个"hello world"程序并对其进行了比较。与其他海报一样,我没有看到任何可以作为指标的东西。

但我确实发现作为指标的是二进制文件的 .text 部分中的填充。在调试版本的 .text 部分中,有超过一百个字节的值0xCC在最后一个代码字节之后。在发布版本中,没有0xCC字节。0xCC字节将在调试器中显示为 int3 或断点。

在Visual Studio中创建C++项目时,它会为你生成两个配置。这些配置的名称为"调试"和"发布"。调试配置包括生成调试信息、较少的优化以及对编辑和继续的支持。

但这只是一个起点。您可以创建任意配置,甚至可以将所有调试信息添加到发布配置中。因此,没有明确的调试或发布版本。

您可以尝试确定是否定义了预处理器符号_DEBUG。这很少更改,并且在版本资源中使用。FILEFLAGES 字段的位 0 通常表示_DEBUG符号是在编译资源时定义的。

由于我必须检查数百个 dll 和 exe,我通过在控制台模式下运行 depends.exe(ver 2.2)并在依赖项生成的输出文件中搜索"MSVCRTD"来尝试 Smerlin 的建议。

Process p = new Process();
dllWalkerPath = """+ dllWalkerPath + """;
binaryFilePath = Path.GetFullPath(binaryFilePath); //path to folder containing the dll's to be verified
string exePath = Assembly.GetEntryAssembly().Location;
string outputFilePath = Path.GetDirectoryName(exePath) + dependsOutputName;
p.StartInfo = new ProcessStartInfo(dllWalkerPath, @"/c /oc:" + outputFilePath + " " + binaryFilePath) //dllWalkerPath contains the path to depends.exe 2.2
{
    UseShellExecute = false
};
p.Start();
p.WaitForExit();