第三方应用破坏了我们的WCF应用程序

26
我们的应用程序使用WCF通过命名管道在两个进程之间进行通信(注意:两个进程都不是Windows服务)。我们的应用程序已经在现场运行了几年,没有发生任何问题。
现在我们收到报告称第三方应用程序(具体来说是Garmin Express)正在破坏我们的应用程序。我已经在公司安装了Garmin Express并确认了这种行为。特别是“Garmin Core Update Service”在运行时会导致我们的应用程序失败。
当Garmin服务正在运行时,我们的应用程序的“服务”端开始运行,并且在创建WCF端点时没有任何问题。但是当客户端启动并尝试连接到服务时,它会失败并显示EndpointNotFoundException,就好像服务根本没有运行一样。
此时,我可以从服务控制面板中停止Garmin服务,然后重新运行客户端,而无需甚至重新启动我们自己的服务。如果我再次启动Garmin服务,进一步尝试启动客户端将失败。因此,至少可以证明我们的WCF服务一直在运行,而Garmin软件以某种方式阻止了我们的客户端连接到它的能力。
我们为端点地址使用我们自己的名称(例如类似于net.pipe://localhost/MyPrivateApplication)。我尝试更改此地址以使用各种其他名称,但对问题没有影响。
另一个应用程序怎么可能仅通过运行就破坏我们的应用程序使用WCF的能力呢?
更新:按要求,这是服务端的代码片段。我从我们的原始代码中简化了它,以尝试隔离问题。到目前为止,我所做的任何更改都没有对问题产生任何影响。
MyService service = new MyService();
ServiceHost host = new ServiceHost(service);
string hostAddress = new Uri("net.pipe://localhost/MyWCFConnection");
host.AddServiceEndpoint(typeof(IMyService), new NetNamedPipeBinding(), hostAddress);
host.Open();

您的服务如何指定其服务URL?是基于相对地址还是绝对地址?也许您可以发布代码/配置片段,展示您的服务端点是如何建立的。 - Chris Dickson
我已经用代码片段更新了我的帖子。我相当确定这是一个绝对地址,对吗?再次声明,无论Garmin服务是否运行,此服务端代码都可以成功执行。只有客户端受到影响。 - ObjetDart
是的,那是一个绝对地址,所以比我的第一个建议更微妙一些。 - Chris Dickson
2
我已经给Garmin发了邮件,他们回复说正在测试修复此问题,并预计在2.2.x版本中发布。 - RandomEngy
你好,我需要在本地重现这个问题。您有受影响的Garmin软件版本号吗? - Cameron VanNatta
4个回答

17

针对你的问题“另一个应用程序如何通过简单运行而破坏我们自己的应用程序”的可能答案:

  1. 另一个应用程序也使用WCF NetNamedPipeBinding。
  2. 两个应用程序都使用基本URL和相对URL创建服务端点。
  3. 应用程序选择的基础地址和HostNameComparisonMode使得客户端WCF堆栈在定位服务元数据时在URL变体之一上发生名称冲突。

我不知道Garmin服务是否确实使用WCF NetNamedPipeBinding,但这是您应该调查的一种可能性。可以通过始终使用NetNamedPipe端点的绝对URL来避免该问题。


好的,根据问题的更新,我们现在知道Garmin服务正在使用WCF NetNamedPipeBinding,并且我们知道您的应用程序使用绝对地址注册其服务,因此上面的解释不是完整的故事。

这里是另一个假设:

  1. 假设Garmin服务运行在一个拥有安全特权SeCreateGlobalPrivilege的进程中(除非专门编码禁用该特权,否则Windows服务都会拥有该特权)。
  2. 假设它还使用net.pipe://localhost和相对终结点地址注册其WCF命名管道终结点。
  3. 现在,它的服务元数据将使用在Global命名空间中具有名称的共享内存映射对象进行发布。
  4. 您的服务应用程序不是Windows服务。我的假设是它的进程没有安全特权SeCreateGlobalPrivilege。如果是这种情况,它的服务元数据将仅使用在其本地会话命名空间中的共享内存映射对象进行发布。
  5. 现在,在Garmin服务正在运行时,您的客户端进程尝试启动连接... WCF客户端侧通道堆栈NetNamedPipeBinding元素尝试基于您的服务URL net.pipe://localhost/MyWCFConnection查找您的服务的服务元数据。如上面的链接所解释的那样,它将依次使用各种变体的服务URL来派生包含元数据的共享内存对象的名称。首先,它会先在Global命名空间中查找完整的变体列表,然后再查找Local命名空间。
  6. 在这种情况下,第一次尝试的名称是从"net.pipe://+/MyWCFConnection"派生的名称,可能无法在Global命名空间中找到具有此名称的对象。
  7. 然而,第二次尝试将基于变体"net.pipe://+/",并且这将匹配Garmin服务在Global命名空间中发布的共享内存映射的名称。由于搜索顺序,它永远不会到达您的服务在本地会话命名空间中发布的元数据。
  8. 您的客户端尝试连接到Garmin服务的管道。假设Garmin服务实施了一些安全措施,导致您的客户端被拒绝访问(例如,它可以在其管道上设置ACL)。结果很可能会出现EndpointNotFoundException。[稍后编辑: 实际上,最有可能发生的是您的客户端实际上正在连接到Garmin服务,启动帧协议的前导握手,并收到一个帧协议故障(http://schemas.microsoft.com/ws/2006/05/framing/faults/EndpointNotFound),因为在Via记录中请求的URL将不匹配Garmin服务所期望的内容。然后,该绑定将断开连接,并将此故障作为EndpointNotFoundException呈现给您的客户端代码。]

你能做些什么呢?我建议:

  • 如果以上假设或类似假设可以被证实,并且Garmin正在使用基本加相对地址,其基本地址仅为net.pipe://localhost,最好让他们拥有这个问题:他们可以通过将基本地址更改为更有可能唯一的内容来轻松解决此类问题。
  • 您可以尝试找到某种方式让您的服务应用程序以安全特权SeCreateGlobalPrivilege运行,以绕过此问题:在不使其成为Windows服务或作为管理员运行的情况下,这并不容易,但也许不是不可能。然后,您的元数据也将在全局命名空间中发布,并且客户端的搜索将在Garmin之前找到它。
  • [稍后编辑]也许有一个解决方法,涉及将绑定的HostNameComparisonMode属性设置为Exact,并使用localhost的同义词作为服务URL的主机部分(例如net.pipe://127.0.0.1/MyWCFConnection)。这可能会引导搜索避开Garmin的变量,以便您的客户端有机会考虑本地会话命名空间中的名称。我不知道它是否有效,但值得尝试,我想。
  • 而且可能很难成功:您的公司是否与微软有产品支持关系?可以说这是WCF中的一个严重的设计缺陷:如果您对此提出异议,您可能有可能让微软发布一个QFE补丁,例如提供一个绑定属性,以告诉客户端堆栈仅尝试本地命名空间。

我相当确定Garmin服务也使用WCF NetNamedPipeBinding,基于这篇文章中的信息。当Garmin服务启动时,会出现一个新的命名管道,其名称采用与其他使用命名管道的WCF服务相同的GUID样式名称。 - ObjetDart
我已确认,如果我将服务端进程以管理员身份运行,问题就会消失。这确实支持了你的第二个理论。虽然这不会帮助我们解决现场的问题,但至少现在我们有了一个可能的解释。 - ObjetDart
所以,听起来我们陷入了困境。我们现在需要在现场解决这个问题,不能等待Garmin可能或可能不会在其产品发布周期的闲暇时修复它,这可能需要几个月甚至几年的时间。我们产品的服务端进程部分在最终用户帐户下运行,据我所知,没有办法使用SeCreateGlobalPrivilege运行它。 - ObjetDart
1
请查看上面的编辑,了解您可能要做的进一步想法。 - Chris Dickson
3
谢谢您提供的解决方法,我实际上已经尝试了这种方法(将比较模式设置为Exact并尝试使用除本地主机以外的主机名)。不幸的是,它似乎并没有起到帮助作用。令人惊讶的是,客户端似乎没有办法控制搜索模式绕过全局表。 - ObjetDart

10
我找到了一种方法来显示哪些应用程序使用了 net.pipe(尽管不一定是使用不当的应用程序)。
首先,下载 Sysinternals(Microsoft)的 Handle 应用程序。作为一个旁注:Process Explorer 也可以让你搜索句柄。
然后以管理员身份打开命令提示符,并运行 Handle.exe net.pipe 命令(不含引号)。这将列出所有当前正在使用 net.pipe 的应用程序。从那里,您可以逐个杀死或禁用它们,直到发现问题应用程序。我几乎从未有超过 4-5 个进程在使用它。如果未能以管理员身份运行命令提示符,则可能会给出零个或只有无关的结果。
下面是我发现的所有干扰 net.pipe 的应用程序:
  • "HP Support Solutions Framework Service" - only some versions affected

  • "Garmin Core Update Service" - fixed in newer versions but out of box is broken

  • "WBE Service" - used by a couple dell laptops in conjunction with a wireless docking station

  • "Intel(R) Security Assist" Service - I saw on a couple of Win10 laptops early 2016.

  • "Baraccuda WSA Service" - Web Security Agent. Probably would upset a customer if you disabled this.

  • "DropboxOEM.exe" - A variant of Dropbox for inclusion in store-bought PC's. Only noticed on Win10 so far. This one is unique, because it is the first I've found that is not a windows service, to the best I can tell.

  • "MTC Service" - Installed on some Getac brand PC's. Unsure what it does.

  • "pcdrcui.exe" - Not a service, but runs as admin. Component of Dell's SupportAssist.

  • "Mitchell1/Shopkey SE Connection" or "ShopHubService" or "Mitchel1/Shopkey Data Backup Service" - Data synchronization service. Unsure what all it does.

  • Procore Drive (Procore DriveService.exe). Uses net.pipe://+/:

     Procore DriveService.exe pid: 4204   type: Section        43C: \BaseNamedObjects\net.pipe:EbmV0LnBpcGU6Ly8rLw==
    
  • Keynetix.Cloud.Launcher.Service.exe. Uses net.pipe://+/:

     Keynetix.Cloud.Launcher.Service.exe pid: 5524   type: Section        4B8: \BaseNamedObjects\net.pipe:EbmV0LnBpcGU6Ly8rLw==
    
  • RevitAccelerator.exe (part of Autodesk Revit). This only gets run elevated immediately after installing Revit. Also, this issue is fixed in Autodesk Revit 2020.

  • Wonderware InTouch IData Service (SE.Scada.Asb.InTouchDataService.exe) that comes with the Wonderware InTouch HMI system

  • WKSSTrayNotification.exe (not a service, but a tray application part of ADDISON software)

  • EisBär SCADA V3.0 (service) by Alexander Maier GmbH

软件制造商 DATEV 在此处提供另一个德语列表 (已归档)。

我支持一个需要使用 net.pipe 的应用程序,因此我会在发现更多使用该服务的服务时更新此列表。


剥离名称中包括前导net.pipe:E的部分将有助于确定罪犯,因为名称其余部分是Base64编码(也可以参见此处)。因此,以上面的\BaseNamedObjects\net.pipe:EbmV0LnBpcGU6Ly8rLw==为例,我们可以使用PowerShell解码名称:
$ [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String("bmV0LnBpcGU6Ly8rLw=="))
net.pipe://+/

2
非常感谢您提供的帮助,这个回复非常有用,为我们节省了很多麻烦!我们遇到了与OP完全相同的情况,并且通过按照您的说明进行识别,找到了罪魁祸首;在我们的情况下,它是随Wonderware InTouch HMI系统一起提供的“Wonderware InTouch IData Service”(SE.Scada.Asb.InTouchDataService.exe)(我不知道它确切的作用)。突然终止此进程使我们基于WCF的应用程序再次正常运行。 - Golvellius
2
在我们的情况下,它是 WKSSTrayNotification.exe(不是服务,而是 Addison Software 的托盘应用程序的一部分)。这是德国软件编辑Datev的一个知识库文章,其中包含另一个罪犯列表:https://www.datev.de/dnlexom/client/app/index.html#/document/1080431 - Andreas

8
我们在微软已经确定了问题所在,是 Garmin Core Update Service 创建命名管道的方式。命名管道可以在不同的作用域(全局和本地)中创建。全局作用域基本上是机器范围内的,而本地作用域则特定于用户。

Garmin的应用程序

  1. 正在以系统服务的身份运行,因此监听命名管道服务的作用域是全局的。
  2. 侦听“net.pipe://localhost/”根地址(例如,没有任何子路径/段)。
  3. 使用StrongWildcard主机名比较模式
  4. 1到3项意味着Garmin的应用程序基本上是任何未匹配更具体内容的net-pipe连接的万能接收器。
  5. 这也意味着Garmin完全阻止了所有使用本地作用域的侦听器。

理想的解决方法是更改Garmin应用程序,使其使用更具体的URL注册其net.pipe侦听器。


3
我认为Microsoft应该计划修复或热修复它。我们刚刚发现,"HP Support Solutions Framework Service"服务也导致了这个问题。 - Pep
为什么不能使用appcompat shims来修复现有软件? - 0xC0000022L
1
使用.NET Framework 4.6.2,可以通过应用程序配置开关来解决@0xC0000022L问题。请参见我的答案https://dev59.com/lmQo5IYBdhLWcg3whPvI#74371039。 - riQQ

1
使用针对.NET Framework 4.x编译的应用程序,并在.NET Framework 4.6.2或更高版本上运行,可以通过不重新编译来解决该问题。有一个新的App.config开关,可以更改WCF命名管道的查找行为,以使用最佳匹配的基地址。
因此,要解决该问题,需要执行以下操作:
  • 使用唯一的基地址和相对路径,例如net.pipe://127.0.0.1/<MySpecificApplication>(将<MySpecificApplication>替换为您的唯一相对路径)
  • 在WCF客户端项目中添加一个App.config文件(用于桌面应用程序)或Web.config文件(用于IIS和ASP.NET Core模块),并添加以下内容。如果该配置文件已经存在于项目中,请将XML元素添加到尚不存在的配置中。
  • 对于不需要重新编译的现有桌面安装:复制该文件并将其重命名为<name-of-the-wcf-client-exe-file>.exe.config,然后将其复制到应用程序文件夹中exe <name-of-the-wcf-client-exe-file>.exe旁边。
<configuration>
    <appSettings>
        <add key="wcf:useBestMatchNamedPipeUri" value="true" />
    </appSettings>
</configuration>

我确认这个修复方案适用于针对.NET Framework 4.7.2编译的桌面应用程序,并在.NET Framework 4.8上运行时解决了问题。
详细解释:
WCF命名管道使用一个间接方式,通过文件映射对象 / 段对象调用连接的基地址来查找要连接的命名管道。
默认查找策略首先尝试在全局命名空间中查找对象(以管理员权限运行的服务和应用程序在此处创建其部分),然后在本地命名空间中查找对象(未以管理员身份运行的应用程序在此处创建其部分)。
在全局命名空间中,它从指定的基地址开始(例如net.pipe://127.0.0.1/<MySpecificApplication>),然后沿着路径层次结构向上走,直到达到根目录(例如net.pipe://127.0.0.1/)。只有在全局命名空间中未找到匹配项时,才会搜索并沿着层次结构遍历本地命名空间。
使用 wcf:useBestMatchNamedPipeUri=true 进行查找。
顺序保持不变(先全局命名空间,然后是本地命名空间,并从每个命名空间中最具体的基地址向根地址),但是查找不会在第一个匹配项处停止,而是寻找最佳匹配项(基地址的最长子字符串)。
来源:
- .NET Framework 源代码 System.ServiceModel.Channels.PipeConnection.GetPipeName(Uri uri, IPipeTransportFactorySettings transportFactorySettings) - https://web.archive.org/web/20110920061444/http://blogs.charteris.com/blogs/chrisdi/archive/2010/10/20/locating-a-wcf-named-pipe-endpoint.aspx

NetNamedPipeBinding best match

WCF has a new app setting that can be set on client applications to ensure they always connect to the service listening on the URI that best matches the one that they request. With this app setting set to false (the default), it is possible for clients using NetNamedPipeBinding to attempt to connect to a service listening on a URI that is a substring of the requested URI.

For example, a client tries to connect to a service listening at net.pipe://localhost/Service1, but a different service on that machine running with administrator privilege is listening at net.pipe://localhost. With this app setting set to false, the client would attempt to connect to the wrong service. After setting the app setting to true, the client will always connect to the best matching service.

Note

Clients using NetNamedPipeBinding find services based on the service's base address (if it exists) rather than the full endpoint address. To ensure this setting always works the service should use a unique base address.

To enable this change, add the following app setting to your client application's App.config or Web.config file: XML

<configuration>
    <appSettings>
        <add key="wcf:useBestMatchNamedPipeUri" value="true" />
    </appSettings>
</configuration>
来源:https://learn.microsoft.com/zh-cn/dotnet/framework/whats-new/#windows-communication-foundation

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