将 PID 缓存到端口映射的安全方法 Windows

Safe way to cache PID to Port Mapping Windows

我正在使用 WinDivert 通过 Windows 上的透明代理管道连接(TCP 和 UDP)。它的工作原理是使用 GETTcpTable2 等函数进行端口到 pid 查找,然后检查 PID 是否匹配或不匹配代理或其任何子进程的 PID。如果它们不匹配,它们将通过代理转发,如果它们匹配,数据包将保持不变。

我的问题是,有没有安全的方法或安全的持续时间,我可以 "cache" 端口到 pid 查找的结果?每当我有大量数据包流过时,比如说在 youtube 上观看视频,使用 WinDivert 的代码突然吞噬了我所有的 CPU,我假设这是因为对收到的每个数据包进行 TcpTable2 查找。我可以看到 UDP 并没有真正安全的持续时间,我可以假设它是绑定到端口的相同进程,但是 TCP 可能吗?

作为对 Luis 评论的补充,我认为 缓存 端口到 pid 查找的应用程序也可以保留进程的句柄(只需通过 OpenProcess).问题是,如果与进程关联的资源在关闭所有句柄之前没有被释放。这是正常的,因为在您拥有进程的有效句柄之前,您可以查询系统以获取各种信息,例如已用内存或时间。因此,您应该定期查看缓存进程是否已终止,以从缓存中清除条目并关闭句柄。

作为替代方案,您可以只保留另一个信息,例如进程的开始时间,可以通过 GetProcessTimes 访问。在缓存中查找进程 ID 时,您打开进程并控制其启动时间。如果没问题,就是正确的进程,如果没有,进程 ID 已被重用,您应该从缓存中清除该条目。

第一种方法应该更有效,因为你不必为每个数据包重新打开进程,但你必须更严格地识别终止进程以释放资源,可能使用一个线程会使用 WaitForMultipleObjectsEx 在所有进程句柄上,一旦终止就会收到警报。

第二种方式应该更容易实现。

所以,我最终在这里做的就是使用两个 std::unordered_maps。一个映射是存储端口号(作为键)和上次查询 TCPTable 以查找绑定到端口的进程 ID 的系统时间(以毫秒为单位)(键)。如果密钥不存在或上次时间大于当前系统时间加 2 秒,则需要重新查询 TCPTable 以重新检查绑定到端口的 PID。完成该检查后,我们更新第二个映射,它使用端口号作为键,returns 一个代表在上次查询中使用相关端口找到的 PID 的 int。为我们提供了 2 秒的查找缓存,将峰值 CPU 使用率从 50% 以上降至最大值 3%。