想象一下,您想要编写一个程序来测试 c++ dll 文件中的函数。您应该允许用户选择一个 dll(我们假设我们正在讨论 c++ dll)。他应该能够获得 dll 导出的所有函数的列表。然后,用户应该能够从列表中选择一个函数名称,手动输入参数列表(参数都是基本类型,如 int、double、bool 或 char 数组(例如c 类型字符串) ) 并尝试使用指定的参数运行选定的函数。他想知道该函数是否使用指定的参数运行,或者它们是否导致它崩溃(例如,因为它们与签名不匹配)。

主要问题是,C++ 作为一种强类型语言,要求您在编译时知道函数调用的参数的数量和类型。就我而言,我根本不知道这些参数是什么,直到用户在运行时选择它们。

我想出的唯一解决方案是使用程序集手动将参数推送到调用堆栈上。

然而,我开始明白,如果我想搞乱汇编,我最好确保我知道 dll 中的函数使用的是哪个调用约定。

所以(最后:)这是我的问题:我可以以编程方式推断出调用约定吗?Dependency Walker 不会帮助我,而且我不知道如何手动读取 PE 格式。

有帮助吗?

解决方案

答案是 或许.

如果函数名称是 C++ 修饰的,那么您可以从名称修饰中确定参数计数和类型,这是最好的情况,如果首先使用 MSVC 编写代码,则很有可能。

如果导出的函数是 stdcall 调用约定(Windows api 的默认设置),您可以确定要推送的字节数,但不能确定参数的类型。

坏消息是,对于 C 调用约定,没有任何方法可以通过查看符号名称来判断。您需要有权访问源代码或调试信息。

http://en.wikipedia.org/wiki/X86_calling_conventions

函数作为导出给出的名称不是 必需的 与链接器看到的名称有任何关系,但大多数时候,导出的名称和链接器看到的符号名称是相同的。

其他提示

您没有指定您在这里谈论的是 32 位还是 64 位,您和其他发帖者概述的困难主要适用于 32 位代码。在 64 位 Windows 上,本质上只有一种调用约定(它也在 John Knoeller 链接的维基百科文章中),这意味着您 了解调用约定(当然,任何人自己编写的调用约定除外)。

此外,根据 Microsoft x64 调用约定,不知道要调用的函数的参数数量并不会阻止您调用它,您可以根据需要/用户希望提供尽可能多的参数。这是因为您作为调用者预留了堆栈空间并随后将其清理。-- 当然,不提供正确的[数量]参数可能仍然会让被调用的函数做一些愚蠢的事情,因为您提供了无效的输入,但那是另一个故事了。

不幸的是,编译后的代码不仅仅说“这里这个函数是一个快速调用,这里这个函数是一个 stdcall”。

即使像 IDA 这样的现代反汇编器也不会默认尝试推断调用类型(idk 中可能有插件或选项)。

基本上,如果你是一个人,90% 的情况下你都会看前几条说明并告诉自己。如果它们是 pop 和 push,则为 stdcall,如果通过寄存器(尤其是 ecx)传递参数,则为 cdecl。Fastcall 也使用寄存器,但做了一些特殊的事情。我不知道。但所有这些信息都是无用的,因为你的程序显然不是人类。

如果你正在进行测试,你至少有头文件吗?这是一种非常困难的剥猫皮的方法。

如果你想知道 C++ 函数使用什么调用约定,你最好的希望是学习

  1. 声明该函数的标头,以及
  2. 编译特定 DLL 的编译器的文档。

但老实说,这整件事听起来有点混乱。为什么你的朋友希望能够做到这一点,为什么他不能通过解析声明相关函数的标头来获取他需要的信息?

本页描述了 VC++6 将参数和调用约定信息编码为符号名称的方式: http://www.bottledlight.com/docs/mangle.html

我怀疑更高版本的 VC++ 会兼容,但我还没有证实这一点。

编译器还提供了一些自动执行此操作的工具: http://msdn.microsoft.com/en-us/library/5x49w699.aspx

名称修饰仅适用于 C++ 函数;如果函数是“extern“C””那么这将不起作用。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top