DCOM 传输
你自己的 DCOM 传输
引言
DCOM 使用端口 #135 用于计算机上的所有进程,这很糟糕。WinXp SP2 非常彻底地关闭了这个常见的漏洞,这很好。但对开发人员来说却并非如此。是的,你可以让你的客户哭泣,但要配置策略。你甚至可以设法自动调整这些设置,就像一个真正的病毒一样。但仍然存在另一个大问题 - 你在局域网中。VPN 是让你的 DCOM 项目通过互联网工作**的唯一方法**。这非常丑陋。
让我们想想令人愉快的事情:试想一下,如果你可以在服务器端选择任何端口。只需在防火墙中允许此端口,仅此而已……试想一下,你可以在客户端通过代理。无需 VPN - 无需痛苦。试想一下,你正在控制你的整个 DCOM 流量 - 你可以压缩它,加密它,计算它,总而言之 - 只是管理它。
好消息是:我们可以给 DCOM 另一条轨道!
解决方案
IRpcChannelBuffer
是答案。COM 对象之间的进程内通信基于在好的情况下直接调用。在坏的情况下,即使在两个对象之间的一个进程中,也有另外两个对象在起作用 - 代理和存根。代理从调用端获取直接调用并将整个信息打包到一个缓冲区中,然后将其发送到存根。存根解包它并对被调用的对象进行直接调用。对于这种通信,代理和存根使用 IRpcChannelBuffer
。
总而言之,如果我们实现此接口并以某种方式将其传递给存根和代理 - 它们就开始使用你自己的通道进行交互。让我们看看主要的接口函数
首先:我们可以实现我们自己的传输
virtual HRESULT STDMETHODCALLTYPE SendReceive(RPCOLEMESSAGE *, ULONG *) = 0;
我们可以用任何可以想象的方式发送和接收这些数据。其次:我们甚至可以管理内存,并使我们的传输非常有效,避免无用的复制。
virtual HRESULT STDMETHODCALLTYPE GetBuffer(RPCOLEMESSAGE *, REFIID riid) = 0;
virtual HRESULT STDMETHODCALLTYPE FreeBuffer(RPCOLEMESSAGE *pMessage) = 0;
一个合理的问题 - 如何通过我们对接口的实现让代理和存根工作?让我们看看另外两个接口 IRpcProxyBuffer
和 IRpcStubBuffer
IRpcProxyBuffer
只有两个方法
virtual HRESULT STDMETHODCALLTYPE Connect(IRpcChannelBuffer *) = 0;
virtual void STDMETHODCALLTYPE Disconnect(void) = 0;
这就是我们将代理连接到我们的通道的方式。IRpcStubBuffer
有多个方法,但只有一个有价值
virtual HRESULT STDMETHODCALLTYPE Invoke(RPCOLEMESSAGE*, IRpcChannelBuffer*)=0;
所以所有工作看起来像这样
- 将代理连接到你的
IRpcChannelBuffer
实现。 - 当它调用
SendReceive
时,将缓冲区传递给另一端。 - 在另一端,只需使用接收到的缓冲区调用
Invoke
即可。 - 将填充有输出数据的缓冲区发送回客户端。
- 从
SendReceive
返回,并带有输出缓冲区。
另一个合理的问题:如何创建存根并将其连接到服务器端的对象,以及如何在客户端创建代理?这也很简单:GetPSFactory
是答案。
HRESULT GetPSFactory(REFIID riid, IPSFactoryBuffer ** ppProxyStubFactory);
此函数通过给定的 IID 创建工厂。工厂接口由两个非常有用的方法组成
virtual HRESULT STDMETHODCALLTYPE CreateProxy
(IUnknown *pUnkOuter,REFIID riid, IRpcProxyBuffer **ppProxy, void **ppv) = 0;
virtual HRESULT STDMETHODCALLTYPE CreateStub
(REFIID riid,IUnknown *pUnkServer,IRpcStubBuffer **ppStub) = 0;
代理是将在你想要使用的远程对象中扮演角色的对象。因此,创建代理时,我们只需设置 IID 并获取接口本身和 IRpcProxyBuffer
,该接口适用于连接到我们的 IRpcChannelBuffer
。当我们调用所需接口的方法时,我们会收到对 SendReceive
的调用,并带有准备发送到另一端的缓冲区。在另一端,我们必须首先创建一个真实的对象,然后将存根连接到它。CreateStub
获取 IID 并为你提供 IRpcStubBuffer
,你可以将其连接到真实对象,调用
virtual HRESULT STDMETHODCALLTYPE Connect(IUnknown *pUnkServer) = 0;
太简单,不真实
是的,开始很容易。只拥有这些知识,你将得到什么?只有一个连接到服务器端对象的接口。对该接口的所有调用都将通过你的实现传递。这就是好的全部。但有坏消息
- 如果你的接口有一个使用另一个方法工作的接口 - DCOM 将其连接到端口 #135!
CreateProxy
并不总能创建代理,尽管代理确实存在。- 你无法使用异步调用。
- 你甚至无法通过这个代理存根对使用另一个接口 - 你需要为另一个接口重复整个过程。
- 还有许多其他事情,即使没有深入研究 DCOM 的内部世界,也无法解释…
附件
在 DCOMTransport.zip 中,你将找到一个结合了以上所有内容的源代码示例。此测试演示了如何在传输链中插入你自己的功能。在这种情况下,该功能是取消调用。
历史
- 2009年5月25日:初始发布