一、关于 TCP 重定向的实现。

TCP 重定向的实现方法有很多, 有些方法是先强制把原来的 Socket 改为阻塞模型(例如:对于消息模型的,先调WSPAsyncSelect(s, hWnd, 0, 0, err) 取消消息映射;对于事件模型的,先调用 WSPEventSelect(s, 0, 0, err) 取消事件),跟代理服务器握手成功后,再恢复原来的模型;有些方法是使用状态机;而最简单和最流行的方法是:把连接重定向到 127.0.0.1 本地自己程序监听的端口,由该程序来完成握手。之所以需要考虑这些问题,是因为当 connect 函数返回后,原来的程序便开始收发数据了,而 LSP 层还在和代理服务器协商,因为使用的是同一个 socket 句柄,所以这两个过程的数据收发是可能导致数据混乱的。

目前基本上所有 LSP 程序都是使用最后一个方法,因为还有个原因:对于网络游戏,如果你重定向到真正代理服务器,这个过程是可能消耗一定时间的,可能会导致网络游戏认为连接超时而断开连接,把连接重定向到 127.0.0.1 就没事了。

二、关于 UDP 重定向的实现。

UDP 的重定向不像 TCP 仅处理 WSPConnectWSPConnectEX 即可,它涉及到数据的收发,对应的两个函数是:WSPSendtoWSPRecvFrom,我们需要在发送的时候将真正的地址加到原数据头,然后将发送地址改为代理服务器的;在接收返回后,从头数据取出真正服务器的 IP 并去掉该头数据。我们先来看看,如果使用 API HOOK 是如何处理接收函数的:

如果需要截获实际接收的数据的话,单纯的通过 WSPRecvFrom 这个函数是很片面的,需要分几种情况说明:

1 如果 lpOverlapped 为 nil,那么是阻塞式的,直接在原函数返回后处理即可。
2 如果 lpOverlapped 不为 nil,且 lpCompletionRoutine 不为 nil,那么需要使用自己的函数替换 lpCompletionRoutine;并在调用后执行调用用户原函数。因为这时是完成例程通知模型。
3 如果 lpOverlapped 不为 nil,且 lpCompletionRoutine 为 nil,并且 lpOverlapped 的 hEvent 不为 nil,那么需要hook WSAGetOverlappedResult 。因为这时是事件通知模型。
4 如果 lpOverlapped 不为 nil,且 lpCompletionRoutine 为 nil,并且 lpOverlapped 的 hEvent 为 nil,那么需要 hook GetQueuedCompletionStatus ,因为这时是完成端口的方式。

现在的问题在于,LSP 并不提供类似 GetQueuedCompletionStatus 这种 API 函数的处理,所以如果仅使用 LSP,而且
是 ifslsp 模型的话,那么对于非阻塞模型的 UDP 都是无法解决的事情。幸好微软还提供了一个 nonifslsp 的框架,里面自带了一个透明代理层。

这里额外说说一些常识(摘录自网上):

“LSP分两种: 一种是 IFS LSP, 一种是 non IFS LSP.
简单地说, IFS LSP 制作简单,可以完成大部分的数据包监听工作; non IFS LSP制作复杂, 但是可以进行一些特殊的 overlapped I/O 操作, 如在 overlapped 初始化完成后, 调用 WSPSend (WriteFile), WSPSendTo, WSPRecv(ReadFile), WSPRecvFrom, or WSPIoctl 之前,对数据进行一些处理工作.
LSP 相互之间可以叠加, 但在 non IFS LSP 之上不可以叠加 IFS LSP. 也就是说, 如果一个 BSP 是 non IFS, 则第三方提供的 LSP 必须是 non IFS, 否则无法安装在 SPI 上.”

当然,世事无完美,nonifslsp 也存在一些兼容性问题,例如会导致 SetFileCompletionNotificationModes 函数运行不正确。详见微软介绍:https://support.microsoft.com/en-hk/help/2568167/setfilecompletionnotificationmodes-api-causes-an-i-o-completion-port-n

三、关于NSP重定向的实现。

NSP 重定向 DNS 虽然都是在 NSPLookupServiceNext 里面返回域名对应的 IP 地址,但是实际上也有很多方法。
一种是使用 UDP 自己构造 DNS 请求包返回 IP,然后替换。这个比较消耗时间。
另外一种是远程连接模式,例如:当应用程序解释域名 www.zues.pub 的时候,NSPLookupServiceNext 返回 127.8.0.0(这个 IP 是递归的),同时在内存里面添加一个对应的映射记录:www.zues.pub:127.8.0.0, 当应用程序连接到 127.8.0.0 时,127.8.0.0 这个连接与远程代理服务器协商,告诉对方连接到 www.zues.pub (代理服务器基本上都支持 IP 重定向和域名重定向),这时候使用的实际上就是代理服务器那边的 DNS 了。

标签: none

仅有一条评论

  1. 旺盛 旺盛

    请教您一下关于本地转发模块(LocalServer)的实现的几个问题:
    1. 核心问题:如何将目标地址发送到本地转模块中去呢?
    在网上找到一种实现方法:通过windows消息和共享内存实现目标地址的发送到本杝转发模块中去。但是遇到了问题2.

    2. 如何将通过socks5协议与目标地址完成连接的socket,与LocalServer中Accept的socket绑定起来呢?因为只有绑定起来才能进行数据转发呢。
    实现大概如下:
    2.1 LSP部分参考https://github.com/liulilittle/PaperAirplane的relay server的实现
    2.2 LocalServer是自己实现的,使用完成端口写的。
    2.3 运行逻辑大概如下:
    LSP在Connect()时会先调用PRXCreateTunnel函数,将目标地址写到共享内存中,发送阻塞消息给LocalServer,然后LocalSever返回一个端口号。
    LocalServer:收到PAPERAIRPLANE_PROXECONNECT_MESSAGE消息,读取共享内存中的目标地址,创建一个socket通过socks5与目标地址完成连接,并将socket保到数组将来响应AcceptEx时与socket匹配。
    LSP中Connect()接收到PRXCreateTunnel返回的端口后,才连接Local Server.
    LocalServer:Accept收到连接请求时,会新分配一个socket与LSP里的socket进行通信,但是要进行数据转发就需要与先前通过socks5与目标地址连接的socket进行绑定,但时两个处理不是同步的,绑定时会发现个暂存的完成socks5连接的的数组里有多个socket了,不能一一对应了

    请大神指点一下,期待您的回复,万分感谢!

添加新评论