【问题标题】:Trying to hook to MessageBeep system API尝试连接到 MessageBeep 系统 API
【发布时间】:2013-06-11 20:29:58
【问题描述】:

一位客户要求我解决以下令人讨厌的问题。他们有一个自定义软件,该软件倾向于在没有任何明显原因的情况下“左右”显示消息框。比如软件本身就是一个记账程序,当他们取客户的钱时,消息框可能会连续显示3到4次左右。每个消息框都会播放 Windows 默认声音。不幸的是,这个软件的编程方式,它播放的声音类型是完全错误的。例如,当消息本身只是一个信息时,它可能会显示一个警告消息框并播放警告系统声音。这一切对于使用该软件的员工来说是相当烦人的。

我试图联系分发该软件的供应商,但我遇到了死胡同。所以现在我正在寻找缓解这个问题的方法。

我最简单的解决方案是建议将扬声器静音,但不幸的是,它们需要有声音才能听到传入的电子邮件,最重要的是,以后能够播放来自它们的语音邮件。所以我的解决方案是以某种方式使消息框静音,只针对单个进程。

根据我的经验,我知道有两个 API 可能会产生这些声音:MessageBeep 和一个较旧的 Beep

我还找到了this article,它解释了如何使用 AppInit_DLLs 挂钩到系统 API。它工作得很好,除了我需要挂钩的两个 API 来自 User32.dll 而不是作者建议的 kernel32.dll。

问题部分中还有this post,它提供了从 User32.dll 挂钩到 API 的大致步骤,但是当我尝试实现它们时,没有足够的信息(据我所知)。

所以我的问题是,有谁知道如何挂钩到 User32.dll 模块中的 API?

编辑:附言。忘了提。此软件安装在 Windows 7 Professional 上,禁用了 UAC——因为它与 UAC 不兼容 :)

【问题讨论】:

  • 看起来您正在寻找的 API 应该是 MessageBoxA/MessageBoxW - 这些也负责发出哔声。

标签: c++ windows winapi dll api-hook


【解决方案1】:

作为替代方案,您可以修补您的应用程序。查找对MessageBeep 的调用并用nop 覆盖它们。

【讨论】:

  • 是的,这是另一个有效的替代方案:静态修改可执行文件,如果运气好,甚至可以更改调用参数的值,例如从 EXCLAMATION 到 ERROR。
  • @Sergius:这仅在 exe 未签名时才有效,这种情况很少见。所以我认为你在那篇关于 CodeProject 的文章中建议的替代方案是最好的选择。我只是希望你为其他 dll 扩展它。 BTW,你以后有这样的打算吗?
  • @ahmd0:我不确定您是否真的需要为您的 exe 签名。如果您的公司有证书,您也可以辞职。
  • @ahmd0:顺便说一句,我已经回答了您对 CodeProject 的评论:codeproject.com/Articles/49319/…
  • @Sergius:谢谢。我没看到。出于某种原因,当我尝试使用 GetModuleHandle("user32.dll") 从 AppInit_DLL 的 DllMain 挂钩到 MessageBeep 时,它挂钩了,但我的挂钩从未被调用。我用自己专门编写的调用MessageBeep 的exe 对其进行了测试。然后,我尝试像您之前建议的那样连接到 LoadLibraryX 函数,但那个函数也不起作用。如果您想看一下并纠正它,我可以上传整个项目的源代码?
【解决方案2】:

这是很难做到的:如果您的应用程序应该在 Vista 之前的 Windows 上以管理员身份运行,您可以通过 ::GetProcAddress() 获取 API 的地址,授予自己写入其内存的权限页面,并用“jmp”汇编指令覆盖 API 代码的开头,跳转到覆盖函数的地址。确保您的覆盖函数采用相同的参数并声明为__cdecl

扩展答案如下。

API 挂钩的“标准”技术包括以下步骤:

1:将你的DLL注入目标进程

这通常是通过首先在目标进程中为包含 DLL 的名称/路径的字符串分配内存(例如“MyHook.dll”),然后在目标进程中创建一个远程线程,其入口点是 @ 987654327@ 将 DLL 的名称作为参数传递。 This page 实现了这种技术。您将不得不与特权作斗争,但它可以保证在 Windows XP 和更早的操作系统上 100% 工作。我不确定 Vista 和后 Vista,地址空间布局随机化可能会让这很棘手。

2。挂钩 API

一旦你的 DLL 被加载到目标进程中,它的DllMain() 将自动执行,让你有机会在目标进程中运行任何你想要的东西。在您的DllMain 中,使用::LoadLibraryA() 获取包含您要挂钩的API 的库的HMODULE(例如“user32.dll”)并将其与您的API 名称一起传递给::GetProcAddress()想要挂钩(例如“MessageBeep”)以获取 API 本身的地址。 Eventaully 给自己写入该地址页面的权限,并用 jmp 指令覆盖 API 的开头,跳转到您的绕行处(即进入您要挂钩的 API 的“版本”)。请注意,您的 detour 需要与您要挂钩的 API 具有相同的签名和调用约定(通常为_cdecl),否则会唤醒怪物。

如此处所述,此技术具有一定的破坏性:您不能绕道而行回调到原始 API,因为原始 API 已被修改为跳入您的 API,您最终会得到一个非常紧凑且美观的无限循环。有许多不同的技术可以让您保留和/或回调原始 API,其中之一是挂钩 API 的 ...A() 版本,然后调用 ...W() 版本(如果不是全部的话,大部分...A() Windows API 将 ASCII 字符串转换为 UNICODE 字符串并最终调用它们的 ...W() 对应对象。

【讨论】:

  • 没有。对不起。我忘了提。它在 Windows 7 上运行,但禁用了 UAC。
  • 我在 Windows XP 上停止使用这些东西 :-( 很有可能只要禁用 UAC,它仍然可以在 Windows 7 上运行。
  • 我明白了。您在阅读了 Mhooks 之后就知道了:codefromthe70s.org/mhook22.aspx 我认为这正是我引用的代码项目示例中所做的。问题是从哪里调用 GetProcAddress()?它似乎适用于 kernel32.dll 或 ntdll.dll 的 API,但不适用于任何其他模块...
  • GetProcAddress() 获取您的 API 所在库的 HMODULE。您可以通过 LoadLibrary("user32") 获取您的 HMODULE
  • 好了 - 这是 RemoteThread 的“标准”技术:autoitscript.com/forum/topic/…
【解决方案3】:

无需花时间在自定义程序上即可做到这一点。

您可以在特定应用程序运行时将其静音,该设置将在您下次打开该应用程序时被记住。见https://superuser.com/questions/37281/how-to-disable-sound-of-certain-applications

还有可以关闭大多数系统声音的 Windows Sound Sentry,尽管我不知道 Sound Sentry 的任何应用程序设置。

【讨论】:

  • 谢谢。我知道我可以使用 Windows 混音器将所有系统声音静音,但是如何为每个应用程序执行此操作?
  • @ahmd0:正如链接的解决方案所说,您单击声音图标,打开混音器,然后将相关应用程序静音。我不知道如何从代码中做到这一点。可能是一些注册表设置或其他。
  • 如果应用程序不使用它,它就不会显示在混音器窗口中。正如我想说的,有问题的应用程序也不使用它。它所依赖的只是为它播放系统声音的 MessageBox API,因此它不会显示在混音器窗口中。
【解决方案4】:

您可以使用Deviare API hook 并在几行C# 中解决这个问题。或者您可以使用难度更大且稳定性较差的 EasyHook。

【讨论】:

  • 这是一个 C++ 问题。我还必须购买许可证才能使用该库吗?
  • 它也适用于 C++,当您的代码实例化它时,您可以在非商业通知中使用它。
猜你喜欢
  • 2013-11-21
  • 2019-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-23
  • 1970-01-01
  • 2013-06-15
  • 1970-01-01
相关资源
最近更新 更多