5

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

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

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

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

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

4

5 回答 5

3

答案是也许

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

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

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

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

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

于 2010-02-17T03:14:53.670 回答
3

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

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

于 2010-02-17T15:56:49.417 回答
2

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

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

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

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

于 2010-02-17T02:55:33.297 回答
0

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

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

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

于 2010-02-17T02:55:42.883 回答
0

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

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

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

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

于 2010-02-20T08:26:46.103 回答