NEPacketTunnelProvider Sniffer iOS

the*_*mei 7 datagram sniffing ios swift networkextension

正如我最近发现这篇论文描述了iOS使用Apple的NEPacketTunnelProvider Extension 的嗅探机制,我很好奇,这让我想从技术角度理解它.由于我通常不在这样的深层网络层工作,所以我无法理解我想要的细节.由于iOS的Charles Proxy必须做一些非常类似的事情而不需要监督设备,我认为2016年提交的论文作者现在可能仍在使用.

作者声称"必须自己实现"IP数据包解析,构建IP数据包或解析DNS响应等所有内容." 因为我想完全理解这一点,我试着自己构建它.我建一个NetworkExtension和该消息循环packetFlowNEPacketTunnelProvider.我能够获取ip数据报并尝试解析它们.我使用相应大小的无符号整数来表示源和目标ip,传输协议和ip版本,但我不确定如何处理有效负载.我的解析器使用ptr.load(fromByteOffset: <offset>, as:<DataType>.self)where ptr是a UnsafeRawPointer来访问数据包流信息.由于data可能超过了存储空间UInt64,我不知道如何以适当的方式访问和存储有效负载.

此外,我认为源IP总是192.168.20.1(设置为我的接口的NEIPv4Settings地址),我的目标IP总是192.168.2.1(我的虚拟NEDNSSettings服务器).这引出了我的第一个问题:这些是DNS查询吗?数据报包是否会声明有关实际目标的任何进一步信息?这是否意味着我必须以某种方式执行对DNS服务器的请求并将数据包重新路由到我将从该DNS查询获得的目标?

下一步是实现TCP/UDP处理,对吧?我目前的解析方法能够区分UDP,TCP并且ICMP(尽管我还没有在最后一个中进行过调查).因此,我会遍历数据报和查找是否需要一个UPDTCP会话/连接并传送数据包.我目前从概念的角度看问题:我如何知道哪个源/目标端口用于TCP/UPD连接/会话?据我所知,这些信息不是IP数据包本身的一部分(因为它是我们在传输层级而不是网络层级上需要的一些信息).

另外,我在github上找到了一个名为Specht的项目.它使用一个名为NEKit的自编写库,它也以某种方式使用该NEPacketTunnelProvider方法.当我正确地理解他们的方法时,他们设法通过编写一些观察者机制以某种方式构建本地代理服务器以处理请求,但由于我对网络和swift相对较新,我不确定我是否完全理解正确或我是否还没有找到所有那些TCP/UDP和/或DNS逻辑.该项目是否与纸张和查尔斯代理的方法相当?

最后一个问题:Charles代理在大多数情况下能够显示目标的主机名.我目前只能看到目标IP地址(这不是真正的目标IP地址,而是我的DNS服务器的地址).我如何才能将主机名视为人类可读文本?查尔斯会做nslookup某种事吗?Charles是否从数据报中获取了这些信息?

我知道这是有很多缺失的知识相当雄心勃勃的我的这个主题,搭建测试的原因类似的东西,但我仍然动机更深入了解的话题,也有一个,我已经明白了一些关键点的感觉,但遗憾的是还不足以解决难题...也许你能够给我一些更多的提示来更好地理解.如果可能有更简单的方法来存档类似的行为(在主机名级别上查看传出连接),我也会对这些感兴趣:-)

Ngh*_*ran 3

我已经发布了Beta Proxyman iOS网站) - 使用 NEPacketTunnelProvider 和网络扩展的网络嗅探器,因此我可能有经验来回答您的一些问题。

IP包、IP图、DNS、如何解析?

幸运的是,还有另一种方法可以设置 NEPacketTunnelProvider 来为您提供 HTTP 消息,而不是 IP 包(它太低级了,您必须处理解析器,DNS,...)

HTTP 消息更容易解析,因为有很多可靠的库(例如来自 nodeJS 的http-parser )

  1. 如何在 iOS 上构建网络嗅探器?

这是一个很难回答的问题,我会把它分成小块:

中间人/代理服务器

首先,您需要一个工作的 MitM 代理服务器,它能够代理和拦截 HTTP/HTTPS 流量。您可以使用SwiftNIOCocoaAsyncSocket来实现它。

它是如何工作的?

一般来说,数据流可能如下所示:

互联网 -> iPhone -> 您的网络扩展 (VPN) -> 转发到您的本地代理服务器(在网络扩展中) -> Mitm/代理服务器开始拦截或监控流量 -> 保存到本地数据库(在共享容器中) Group) -> 再次转发到目标服务器。

从主应用程序中,您可以通过读取本地数据库来接收数据。

我们需要本地数据库的原因是网络扩展和主应用程序是两个不同的进程,因此它们无法像普通应用程序一样直接通信。

显示代码?

在网络扩展中,让我们在 Host:Port 处启动代理服务器,然后初始化 NetworkSetting,如示例所示:

    private func initTunnelSettings(proxyHost: String, proxyPort: Int) -> NEPacketTunnelNetworkSettings {
    let settings: NEPacketTunnelNetworkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1")

    /* proxy settings */
    let proxySettings: NEProxySettings = NEProxySettings()
    proxySettings.httpServer = NEProxyServer(
        address: proxyHost,
        port: proxyPort
    )
    proxySettings.httpsServer = NEProxyServer(
        address: proxyHost,
        port: proxyPort
    )
    proxySettings.autoProxyConfigurationEnabled = false
    proxySettings.httpEnabled = true
    proxySettings.httpsEnabled = true
    proxySettings.excludeSimpleHostnames = true
    proxySettings.exceptionList = [
        "192.168.0.0/16",
        "10.0.0.0/8",
        "172.16.0.0/12",
        "127.0.0.1",
        "localhost",
        "*.local"
    ]
    settings.proxySettings = proxySettings

    /* ipv4 settings */
    let ipv4Settings: NEIPv4Settings = NEIPv4Settings(
        addresses: [settings.tunnelRemoteAddress],
        subnetMasks: ["255.255.255.255"]
    )
    ipv4Settings.includedRoutes = [NEIPv4Route.default()]
    ipv4Settings.excludedRoutes = [
        NEIPv4Route(destinationAddress: "192.168.0.0", subnetMask: "255.255.0.0"),
        NEIPv4Route(destinationAddress: "10.0.0.0", subnetMask: "255.0.0.0"),
        NEIPv4Route(destinationAddress: "172.16.0.0", subnetMask: "255.240.0.0")
    ]
    settings.ipv4Settings = ipv4Settings

    /* MTU */
    settings.mtu = 1500

    return settings
}
Run Code Online (Sandbox Code Playgroud)

然后启动VPN,

let networkSettings = initTunnelSettings(proxyHost: ip, proxyPort: port)

// Start
setTunnelNetworkSettings(networkSettings) { // Handle success }
Run Code Online (Sandbox Code Playgroud)

然后将包转发到本地代理服务器:

let endpoint = NWHostEndpoint(hostname: proxyIP, port: proxyPort)
self.connection = self.createTCPConnection(to: endpoint, enableTLS: false, tlsParameters: nil, delegate: nil)

    packetFlow.readPackets {[weak self] (packets, protocols) in
        guard let strongSelf = self else { return }
        for packet in packets {
            strongSelf.connection.write(packet, completionHandler: { (error) in
            })
        }

        // Repeat
        strongSelf.readPackets()
    }
Run Code Online (Sandbox Code Playgroud)

由此,您的本地服务器可以接收包然后转发到目标服务器。

不要忘记将所有流量日志保存到本地数据库,然后通知主应用程序重新加载它。

最后一个问题:Charles 代理在大多数情况下能够显示目标的主机名。我目前只能看到目标 IP 地址(这不是真正的目标 IP 地址,而是我的 DNS 服务器的地址)。我如何才能将主机名视为人类可读的文本?Charles 会以某种方式进行 nslookup 吗?查尔斯是否从数据报中获取了该信息?

由于我们不处理 IP 包,因此不需要实现 DNS 解析器。如果您需要DNS,可以像下面的代码一样配置:

        let dnsSettings = NEDNSSettings(servers: ["8.8.8.8", "1.1.1.1"])
    settings.dnsSettings = dnsSettings
Run Code Online (Sandbox Code Playgroud)

当我们收到 HTTP 消息包时,您可以免费获取主机名(从请求的 URL 或主机标头)

希望我的回答能够对您有所帮助。