【问题标题】:Prevent COM from activating the server executable?防止 COM 激活服务器可执行文件?
【发布时间】:2019-07-26 16:16:13
【问题描述】:

TL;DR - 如何防止-Embedding 启动CLSCTX_LOCAL_SERVER 服务器?最好让客户立即得到一个像样的错误代码。

背景

我们有一个本机 C++ 交互式桌面应用程序,它与另一个本机 C++ 交互式桌面应用程序中的 COM 对象进行交互。

基本上,COM 被用作进程间通信机制。

现在,当“服务器”应用程序在用户进入正确状态后以交互方式启动时,它将准备 COM 接口:CoRegisterClassObject 等。

当客户端应用程序被使用,然后它是 CoCreateInstance 的 coclass 时,它将与已经运行的其他桌面应用程序通信,这是预期的。

但是,当“服务器”应用程序没有运行时,启动客户端会以交互方式启动服务器应用程序,这不是我们想要的,因为它需要相当多的处理和设置,然后客户端才能与它进行有意义的通信。

问题

因此,更有意义的是让客户端在服务器未运行的情况下仅出错,而不是让 COM 基础架构启动一个无论如何都无法有意义地为请求提供服务的交互式应用程序。

我们玩弄了以下想法:

  • 每次关闭服务器时取消注册。
    • 这似乎行不通,因为我们需要管理权限来注册/取消注册服务器。
  • 在客户端使用CLSCTX_DISABLE_AAA 标志。
    • 这似乎行得通,如果客户端指定了这个并且服务器没有运行,它将得到0x80070005 ERROR_ACCESS_DENIED,但我们不确定这是否是正确的方法。
  • 提前检查服务器应用程序以检测-Embedding 开关并立即退出应用程序。
    • 客户端应用程序将运行超时(约 2 分钟),这对用户不是很友好。
  • 来自 cmets 不要将信息写入注册表 - CoRegisterClassObject should be enough
    • 目前,我有:
      • HKCR\AppID (...)
      • HKCR\CLSID\{...} 加上子键 ProgID, VersionIndependentProgID, LocalServer32, Typelib
    • 客户端当前从 VersionIndependentProgID 解析 CLSID
    • 所以我想知道注册表的哪些部分是真正可选的。

是否有任何“标准”方法来阻止对给定的已注册 COM 类激活 COM 本地服务器可执行文件?

【问题讨论】:

  • COM 注册不需要管理员权限,但您必须在 HKCU 而不是 HKLM 下注册所有内容(这在您的上下文中似乎是有意义的)。否则你不必使用 CoCreateInstance 来获取引用,你可以使用运行对象表,从客户端检查你的服务器是否在其中注册了一些东西并调用 IRunningObjectTable::GetObject
  • 当检测到 -Embedding 参数时,您可以尝试立即发出 CoRegisterClassObject 调用然后退出。客户端将在没有超时的情况下获得 CO_E_SERVER_EXEC_FAILURE……是的,这很难看。
  • 对于 inproc com 对象,您不需要将它们放在注册表中 - 您可以在运行时注册它们的类工厂。我想知道这是否可以通过注册代理对象来应用于进程外 COM 对象...
  • CoRegisterClassObject 只是将您的对象发布到 COM(作为 oop 服务器),它对 ROT 没有任何作用。调用 CoRegisterClassObject 后,必须从服务器调用 IRunningObjectTable::Register,从客户端调用 IRunningObjectTable::GetObject。
  • 只是不要在注册表中注册您的对象 clsid。如果服务器还没有调用CoRegisterClassObject 客户端得到错误REGDB_E_CLASSNOTREG,否则调用CoRegisterClassObject 是enouth

标签: windows winapi visual-c++ com


【解决方案1】:

我的想法是这样的......

当您检测到服务器以-Embedding 启动时,只需在您的EXE 中设置一个全局标志。只有当您使用 -Embedding 标志启动时,我才会创建一个特殊的类工厂。调用 IClassFactory::CreateInstance() 时,此类工厂将返回失败代码。您不会将标准类工厂注册为使用 CoRegisterClassObject() 运行,但您只会注册始终返回失败代码的替代工厂。

是的,启动 EXE 仍然会有一点延迟,但是当 CreateInstance() 被调用时,它会立即返回失败代码,因此调用者不会有很长的超时...也许 1-仅 5 秒。

【讨论】:

  • 解决方法的好主意。这也将启用某种“自定义”错误代码,这是一个不错的奖励。我不得不说我仍然非常喜欢甚至不启动可执行文件,但就解决方法而言,这似乎很好。
  • 您可以将运行所需的几乎所有内容放在清单中。清单不允许您启动 EXE,但正如您最初指出的那样,一旦服务器运行,您就可以连接到它的类工厂。如果您的所有接口都是双接口或 IDispatch 派生的,那么事情会变得更容易一些。这样,您只需要知道接口并在清单中放置一个 条目,以便它知道如何编组接口。我会将服务器的类型库作为 TLB 文件放在与客户端相同的目录中,并在 清单条目中引用它。
【解决方案2】:

写下我到目前为止从 cmets 学到的东西:


你不必使用CoCreateInstance,但你可以使用GetActiveObject,这

检索指向已向 OLE 注册的运行对象的指针。

所以你有一种方法可以在不激活它的情况下获取一个对象,但依赖于它已经被注册。 (但这不是CoRegosterClassObject 做的,而是你需要...?)


GetActiveObject 方法类似,您可以使用IRunningObjectTable 周围的机制——这可能是GetActiveObject 在幕后使用的机制。我有点迷路了。


另一条信息是registry is claimed to be kinda "optional" 在这一切中:(释义)

CoRegisterClassObject 只是将您的对象发布到 COM(作为 oop 服务器), ...

不要在注册表中注册您的对象CLSID。 如果服务器还没有调用CoRegisterClassObject,客户端得到错误REGDB_E_CLASSNOTREG,否则调用CoRegisterClassObject就足够了。 ...

您不需要将它添加到运行对象表中 - 单个调用 CoRegisterClassObject 就足够了 - 客户端可以在此之后创建实例。无需在注册表中进行任何注册 - ...

...对于任何远程接口,您当然需要注册表中的条目Interface\{..}\ProxyStubClsid32,但不需要对于CLSID

CoRegisterClassObject 根本不需要注册表...但是你仍然需要ma​​rshal你的接口。为此,您需要Interface\{..}\ProxyStubClsid32 密钥。

如果您执行这种类型的封送处理,您需要 TypeLib - 在此处设置 {00020424-0000-0000-C000-000000000046}。 CLSIDAppID 你不需要。

CLSIDAPPID 需要启动您的应用程序,如果它没有启动(或加载 dll)。

如果您已经在运行并致电CoRegisterClassObject - 这足以让客户呼叫连接到您。 但如果没有 CLSID,客户端将无法执行您的应用程序(根本不知道是什么)。

如果您不进行自定义封送处理 - 每个接口都需要在注册表中有信息 - 哪个 dll ProxyStubClsid32 执行此封送处理。这可以是自定义 dll,或者如果是标准的 {00020424-0000-0000-C000-000000000046},那么您需要一个类型库供 oleaut32 使用。


总结中,防止应用被 Windows 激活的方法似乎是

  • 首先不要将激活所需的信息写入注册表。
    • 但只有编组所需的信息。
  • 和/或不调用 CoCreateInstance 并通过另一条路由检索对象。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-13
    • 2016-02-28
    • 2020-05-21
    • 1970-01-01
    • 1970-01-01
    • 2012-09-06
    • 2015-04-12
    • 1970-01-01
    相关资源
    最近更新 更多