Поставщик пакетных туннелей iOS с локальным сервером на устройстве

#ios #swift #proxy #packet-sniffers #networkextension

#iOS #swift #прокси #анализаторы пакетов #Расширение сети

Вопрос:

Я использую платформу сетевого расширения, предоставляемую Apple, для создания приложения для отслеживания / мониторинга пакетов, аналогичного Charles Proxy и Surge 4 для iOS.

На данный момент у меня есть базовая структура проекта, запущенная и запущенная с основным приложением, запускающим расширение PacketTunnelProvider, где я могу видеть, как пакеты пересылаются с помощью метода packetFlow.readPackets(completionHandler:) . Я не работаю в сети, поэтому я запутался в базовой структуре приложений такого рода. Размещают ли они на устройстве сервер, который действует как прокси, который перехватывает сетевые запросы? Может ли кто-нибудь предоставить диаграмму общего потока сетевых запросов? Т.е. Какова взаимосвязь между поставщиком пакетных туннелей, прокси-сервером, виртуальным интерфейсом и туннелем?

Если эти приложения используют локальный сервер на устройстве, как настроить настройки NEPacketTunnelNetworkSettings, чтобы разрешить подключение? Я попытался включить локальный сервер на устройстве, такой как GCDWebServer, но не смог установить связь между ними.

Например, если GCDWebServer был доступен в 192.168.1.231: 8080, как мне изменить приведенный ниже код, чтобы клиент мог взаимодействовать с сервером?

Основное приложение:

     let proxyServer = NEProxyServer(address: "192.168.1.231", port: 8080)
    
    let proxySettings = NEProxySettings()
    proxySettings.exceptionList = []
    proxySettings.httpEnabled = true
    proxySettings.httpServer = proxyServer
    
    let providerProtocol = NETunnelProviderProtocol()
    providerProtocol.providerBundleIdentifier = self.tunnelBundleId
    providerProtocol.serverAddress = "My Server"
    providerProtocol.providerConfiguration = [:]
    providerProtocol.proxySettings = proxySettings
    
    let newManager = NETunnelProviderManager()
    newManager.localizedDescription = "Custom VPN"
    newManager.protocolConfiguration = providerProtocol
    newManager.isEnabled = true
    saveLoadManager()
    self.vpnManager = newManager
  

PacketTunnelProviderExtension:

 func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
  ...
        let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.143")
        settings.ipv4Settings = NEIPv4Settings(addresses: ["198.17.203.2"], subnetMasks: ["255.255.255.255"])
        settings.ipv4Settings?.includedRoutes = [NEIPv4Route.default()]
        settings.ipv4Settings?.excludedRoutes = []
        settings.dnsSettings = NEDNSSettings(servers: ["8.8.8.8", "8.8.4.4"])

        settings.dnsSettings?.matchDomains = [""]
        self.setTunnelNetworkSettings(settings) { error in
            if let e = error {
                NSLog("Settings error %@", e.localizedDescription)
            } else {
                completionHandler(error)
                self.readPackets()
            }
        }
  ...
}
  

Ответ №1:

Я работаю над версией Proxyman для iOS, и мой опыт может вам помочь:

Размещают ли они на устройстве сервер, который действует как прокси, который перехватывает сетевые запросы?

Да, вы должны запустить прослушиватель на сетевом расширении (не в основном приложении), чтобы действовать в качестве прокси-сервера. Вы можете написать простой прокси-сервер, используя Swift NIO или CocoaAsyncSocket.

Перехват трафика HTTPS — это довольно сложная задача, но я не буду упоминать здесь, поскольку это выходит за рамки.

Может ли кто-нибудь предоставить диаграмму общего потока сетевых запросов?

Поскольку расширение сети и основное приложение — это два разных процесса, поэтому они не могут взаимодействовать напрямую, как обычные приложения.

Таким образом, поток может выглядеть следующим образом:

Интернет -> iPhone -> Ваше сетевое расширение (VPN) -> Пересылка на ваш локальный прокси-сервер -> Перехват или мониторинг -> Сохранение в локальной базе данных (в общей группе контейнеров) -> Повторная пересылка на сервер назначения.

Из основного приложения вы можете получать данные, считывая локальную базу данных.

как настроить настройки NEPacketTunnelNetworkSettings, чтобы разрешить подключение?

В расширении сети запустите прокси-сервер по адресу Host:Port, затем инициализируйте сетевую настройку, как в примере:

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

Из этого ваш локальный сервер может получать пакеты, а затем пересылать их на сервер назначения.

Комментарии:

1. Ты потрясающий Нгиа! Я ценю подробный ответ. Есть ли у вас какие-либо ресурсы, объясняющие, как перехватывать трафик HTTPS?

2. прежде чем перехватывать трафик HTTPS, я предлагаю вам реализовать простой прокси-сервер HTTP, который позволяет пересылать трафик HTTP / HTTPS и перехватывать HTTP (поскольку это обычный текст).

3. Вы можете проверить документ mitmproxy: docs.mitmproxy.org/stable/concepts-howmitmproxyworks

4. Я настроил все ваши предложения, и в основном все работает; Я могу видеть исходящие пакеты и анализировать их. Когда я запускаю приложение, отображается логотип VPN, но интернет на устройстве перестает работать. Вы знаете, почему это так? Следует отметить одну вещь: я еще не настроил сервер на устройстве. Будет ли что-то связано с настройками DNS?

5. @user1068810 я использую BinarySwift для упрощения захвата байтов данных. Если вы пойдете по этому пути, вам нужно будет создать свои собственные модели для заголовка IP, заголовка TCP и т. Д. github.com/Szaq/BinarySwift