我可以在Visual Studio的即时窗口中调用Win32 API吗?

13

我正在调试一个 C++ Win32 应用程序,我想从该进程的上下文中调用任意 Win32 API,就好像该程序运行了这行代码一样:

DestroyWindow(0x00021c0e);

但是在立即窗口输入这个会得到:

CXX0017: Error: symbol "DestroyWindow" not found

编辑:通过使用函数的完整名称,{,,user32.dll}_NtUserDestroyWindow@4,我可以让立即窗口理解我所指的函数并显示该函数的地址:

{,,user32.dll}_NtUserDestroyWindow@4
0x76600454 _NtUserDestroyWindow@4

但当我尝试调用它时,会发生这种情况:

{,,user32.dll}_NtUserDestroyWindow@4(0x00021c0e);
CXX0004: Error: syntax error

我能否像这样从即时窗口调用C函数,或者我走错了路?

3个回答

7

一旦您拥有函数地址(就像您在更新的问题中所做的那样),您可以尝试将其强制转换为函数指针并调用它:

(*(BOOL (*)(HWND))0x76600454)((HWND)0x00021c0e)

这段代码的第一部分将地址转换为 BOOL (*)(HWND),它是一个指向函数的指针,该函数接受一个 HWND 参数并返回 BOOL。然后,解引用并调用函数指针。确保参数正确,否则会发生不好的事情。在64位系统上,HWND 可能是64位,因此您可能无法将参数作为 int 传递。

编辑:请参阅评论以获取完整信息。


1
谢谢你的想法,但是... 这给了我 CXX0004: Error: syntax error。将 Windows 类型 (BOOLHWND) 替换为原始类型 (int 在两种情况下) 可以改善情况,但是我接着得到 CXX0014: Error: missing operand。(顺便说一句,我在一个 32 位进程中。) - RichieHindle
1
据说VS调试器在解析函数指针类型方面表现很差。尝试为其创建一个typedef。在您的代码中加入typedef BOOL (*DESTROYWINDOW)(HWND);,然后在调试器中编写(*(DESTROYWINDOW)0x76600454)((void*)0x00021c0e) - Adam Rosenfield
@Adam:耶!(但有所保留)。通过将您的typedef添加到我的代码中,并在代码中实际使用typedef,以便编译器不会丢弃定义,并在我使用typedef的编译单元内中断进程的执行,现在我可以从立即窗口调用DestroyWindow。谢谢!虽然必须这样做有点麻烦,但至少是可行的。 - RichieHindle
1
这太糟糕了。我刚刚也有同样的经历。这是一个巨大的Visual Studio失败。 - i_am_jorf
有人知道如何在NTSD中使用.call来实现吗? - i_am_jorf

3
我认为问题在于 C++ EE 在解析 DestroyWindow 的上下文时出现了问题。请尝试以下操作:
{,,user32}DestroyWindow(0x00021c0e);

我不确定方法调用语法是否支持这种限定符的风格(过去只在强制类型转换中使用它)。但值得一试。

编辑 你可能需要在}后面添加!,也可能不需要。我已经有一段时间没有使用这种语法了,经常将其与等效的windbg混淆。


谢谢您的建议,但我已经尝试过了,它不起作用。我也尝试过将其装饰为“_DestroyWindow@4”等,一直到完全限定的“{,,user32.dll}_DestroyWindow@4”,但都没有成功。 - RichieHindle
@JaredPar:感谢您帮助改善情况-请查看我编辑过的问题。现在我可以让它理解我指的是哪个函数,但我仍然无法让它调用它。 - RichieHindle
1
@Richie,谢谢!我也点赞了你的回答。哇,顺便说一句。 - JaredPar
@JaredPar:谢谢...稍微有点尴尬,我没有早些时候想出如何获得正确的函数名称,因为我今年早些时候写了一篇关于这个问题的博客文章:http://entrian.com/blog/setting-a-visual-studio-breakpoint-on-a-win32-api-function-in-user32dll/ 8-) - RichieHindle

1

我找到了一个解决方法,但我仍然希望立即窗口能够正常工作。

这个解决方法是:

  • 获取函数的地址,如问题所示
  • 使用反汇编窗口转到该地址,并在那里设置断点
  • 对应用程序执行某些操作,使其调用DestroyWindow
  • 向上跟踪调用堆栈,找到DestroyWindow的调用者,它看起来像这样:

    6D096A9D push ecx
    6D096A9E call dword ptr ds:[6D0BB4B8h]

  • push ecx指令上设置断点,并清除DestroyWindow上的断点

  • 点击继续,再次对应用程序执行某些操作,使其调用该代码
  • 记录下ecx的值
  • 在调试器中更改ecx的值为所需值,并跳过push/call
  • 恢复ecx的值,并使用“设置下一条语句”返回到push,然后继续

虽然有点冗长,但它是有效的。它假设您可以随意调用适当的API。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接