NEPacketTunnelProvider嗅探器iOS

eagi6jfj  于 2023-03-05  发布在  iOS
关注(0)|答案(1)|浏览(305)

我最近发现了这个paper,它描述了一个使用苹果NEPacketTunnelProvider扩展的iOS嗅探机制,我对此很好奇,这让我想从技术的Angular 来理解它。由于我通常不工作在这样的深层网络层,我不能理解它的细节,我想.作为Charles Proxy的iOS必须做一些非常相似的,而不需要监督设备,我认为论文作者在2016年提出的方法现在可能仍然有效。
作者声称"像IP数据包解析、构建IP数据包或解析DNS响应这样的一切都必须自己实现。"由于我想完全理解这一点,我试着自己构建它,我构建了一个NetworkExtension和一个NEPacketTunnelProviderpacketFlow的消息循环,我能够获得ip数据报并尝试解析它们。对于源和目标IP、传输协议和IP版本,我使用了相应大小的无符号整数,但是我不确定如何处理有效载荷,我的解析器使用ptr.load(fromByteOffset: <offset>, as:<DataType>.self),其中ptrUnsafeRawPointer来访问包流信息。由于data可能超过UInt64的存储空间,我不知道如何以正确的方式访问和存储有效负载。
此外,我认为源IP始终是192.168.20.1(设置为接口的NEIPv4Settings地址),目标IP始终是192.168.2.1(虚拟的NEDNSSettings服务器)。这些是DNS查询吗?数据包是否会声明关于实际目标的任何进一步信息?这是否意味着我必须以某种方式执行对DNS服务器的请求,并将数据包重新路由到我将从DNS查询中获得的目标?
下一步是实现TCP/UDP处理,对吗?我目前的解析方法能够区分UDPTCPICMP(尽管我还没有在最后一个中进行调查)。因此,我将遍历数据报,查找它们是否需要UPDTCP会话/连接,然后传输数据报。我目前从概念的Angular 来看他们的问题:我如何知道哪个源/目标端口用于TCP/UPD连接/会话?据我所知,此信息不是IP数据包本身的一部分(因为它是传输层级别而不是网络层级别需要的一些信息)。
另外,我在github上发现了一个名为Specht的项目,它使用了一个名为NEKit的自写库,该库也使用了NEPacketTunnelProvider方法,当我正确理解他们的方法时,他们设法通过编写一些观察器机制来构建一个本地代理服务器,以处理请求,但由于我对网络和swift相对较新,我不确定我的理解是否完全正确,或者我只是没有找到所有的TCP/UDP和/或DNS逻辑。这个项目可以与论文和查尔斯代理的方法相比吗?
最后一个问题:Charles代理在大多数情况下能够显示目标的主机名。我目前只能看到目的IP地址(这不是真正的目的IP地址,而是我的DNS服务器的地址)。我如何能够看到人类可读文本的主机名?Charles是否以某种方式执行nslookup?Charles是否从数据报中获得该信息?
我知道,由于在本主题中缺少很多知识,我很有抱负,但出于测试原因构建类似的东西,但我仍然有动力更深入地研究本主题,并且感觉我已经理解了一些关键点,但不幸的是还不足以解决这个难题......也许你能给我更多的提示,以获得更好的理解。如果有更简单的方法来归档类似的行为(在主机名级别查看传出连接),我也会对这些感兴趣:-)

bwntbbo3

bwntbbo31#

我已经发布了一个Beta Proxyman iOSwebsite)-一个使用NEPacketTunnelProvider和网络扩展的网络嗅探器,所以我可能有经验回答你的一些问题。
IP包,IP图,DNS,如何解析?
幸运的是,还有另一种方法来设置NEPacketTunnelProvider,以便为您提供HTTP消息,而不是IP包(它的级别太低,您必须处理解析器、DNS等)
HTTP消息更容易解析,因为有大量可靠的库(例如nodeJS的http-parser
1.如何在iOS上构建网络嗅探器?
这是一个很难回答的问题,我会把它分成几个小块:
MitM/代理服务器
首先,您需要一个可以工作的MitM代理服务器,它能够代理和拦截HTTP/HTTPS流量。您可以使用SwiftNIOCocoaAsyncSocket来实现它。
它是如何工作的?
通常,数据流可能如下所示:
互联网-〉iPhone-〉您的网络扩展(VPN)-〉转发到您的本地代理服务器(在网络扩展中)-〉Mitm/代理服务器开始拦截或监控流量-〉保存到本地数据库(在共享容器组中)-〉再次转发到目的地服务器。
从主应用程序中,您可以通过读取本地数据库来接收数据。
我们需要本地数据库的原因是网络扩展和主应用程序是两个不同的进程,因此它们无法像普通应用程序那样直接通信。
告诉我密码?
在Network扩展中,让我们在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
}

然后启动VPN,

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

// Start
setTunnelNetworkSettings(networkSettings) { // Handle success }

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

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()
    }

本地服务器可以从那里接收包,然后转发到目标服务器。
不要忘记保存所有的流量日志到本地数据库,然后通知主应用程序重新加载它。
最后一个问题:Charles代理在大多数情况下能够显示目标的主机名。我目前只能看到目的IP地址(这不是真正的目的IP地址,而是我的DNS服务器的地址)。我如何能够看到人类可读文本的主机名?Charles是否以某种方式执行nslookup?Charles是否从数据报中获得该信息?
由于我们不处理IP包,我们不需要实现DNS解析器。如果你需要一个DNS,你可以像下面的代码配置:

let dnsSettings = NEDNSSettings(servers: ["8.8.8.8", "1.1.1.1"])
    settings.dnsSettings = dnsSettings

当我们收到HTTP消息包时,您可以免费获得主机名(从请求的URL或主机头)
希望我的回答能对你有所帮助。

相关问题