【问题标题】:Can I programatically deduce the calling convention used by a C++ dll?我可以以编程方式推断 C++ dll 使用的调用约定吗?
【发布时间】:2011-01-17 16:35:36
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • 为什么不让用户也指定调用约定?默认为 stdcall(这是微软编译器现在生成的 dll 上的内容),但允许用户更改它。
  • 好吧,该死的。我只是没想到!我必须检查我是否允许这样做,但如果我允许,这将是一个有效的解决方法:)))

标签: c++ dll assembly calling-convention name-decoration


【解决方案1】:

不幸的是,编译后的代码不只是说'这里这个函数是一个快速调用,而这里这个是stdcall'。

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

基本上,如果您是人类,您 90% 的时间都会查看前几条指令并告诉您。如果它们是pop和push,它的stdcall,如果它通过寄存器(尤其是ecx)传递参数,那么它的cdecl。 Fastcall 也使用寄存器,但做了一些特别的事情……我不知道。但是所有这些信息都是无用的,因为您的程序显然不会是人类。

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

【讨论】:

【解决方案2】:

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

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

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

【讨论】:

  • 我知道,这听起来不仅仅是一团糟:)我正在为我正在实习的公司做这件事,这只是他们需要提供的一个功能。尴尬的是事实上,我从我们的一个竞争对手那里看到了一个完全符合我描述的程序,所以我知道它是可行的。我不确定我的方法是否可行,但我已经没有其他想法:(
  • 前段时间我读到一篇文章,关于 A 公司如何总是抢先竞争对手,因为他们使用不同的语言。他们有可能在做类似的事情吗?也许他们有一个脚本层或其他一些奇怪的东西?
  • 另外,如果您有样品,请采用科学方法,并尝试弄清楚他们是如何做到的。
【解决方案3】:

答案是也许

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

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

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

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

函数作为导出的名称​​要求与链接器看到的名称有任何关系,但大多数情况下,导出的名称和符号名称链接器看到的是相同的。

【讨论】:

  • 好吧,我几乎认为我无法获得有关函数参数的任何信息。我​​很乐意找到一种方法来“强制输入”用户定义的参数列出一个函数,看看它是否崩溃。
  • @Emil:你应该知道调用约定在调用者或被调用者是否弹出参数方面存在差异。
【解决方案4】:

您没有在此处指定您是在谈论 32 位还是 64 位,您和其他发帖人概述的困难主要适用于 32 位代码。在 64 位 Windows 上,基本上只有一个调用约定(它也在由 John Knoeller 链接的维基百科文章中),这意味着您确实知道调用约定(当然除了任何人自己做饭)。

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

【讨论】:

    【解决方案5】:

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

    我怀疑更高版本的 VC++ 会兼容,但我还没有确认。

    还有一些伴随编译器的自动化工具:http://msdn.microsoft.com/en-us/library/5x49w699.aspx

    名称 mangling 仅适用于 C++ 函数;如果一个函数是 'extern "C"' 那么这将不起作用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-02-13
      • 2018-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-30
      • 1970-01-01
      相关资源
      最近更新 更多