가짜 VPN을 사용하여 Android HTTP 확인

당신은 장치의 다른 응용 프로그램의 네트워크 데이터를 검사하고 다시 쓰기 위해 안드로이드 프로그램을 구축할 수 있습니까?
사실이 증명하듯이, 그래, 너는 할 수 있다.HTTP Toolkit 바로 Android VPN API에 응용 프로그램을 구축하여 이를 실현하는 것이다. 이 응용 프로그램은 장치 내의 허위 VPN 연결을 완전히 모의했다.
여기서, 나는 그것이 어떻게 작동하는지 이야기하고, 그것을 실현하는 코드를 보고, 당신에게 어떻게 자신을 위해 같은 일을 하는지 보여 주고 싶습니다.
이것은 설비의 통신 안전을 의도적으로 공격하는 것이 아니라는 것이 명확해야 한다.당신이 진정으로 이 점을 할 때 안드로이드는 설치 과정에서 사용자에게 명확한 경고와 권한 알림을 제공하고 언제든지 지속적인 사용자 인터페이스 알림이 필요하다.또한 기본적으로 암호화된 데이터의 내용을 어떤 방식으로도 읽을 수 없습니다. (다음 글에서는 HTTP 패키지가 어떻게 하는지 토론할 것입니다.)
그러나 이것은 개발자의 도구를 위해 재미있고 건설적인 용례를 열었다.예:
  • 이동통신을 검사하고 다시 써서 테스트와 디버깅을 한다HTTP Toolkit가 존재하는 이유다.
  • Android를 위한 방화벽을 구축하여 사용자 정의 규칙에 따라 전송되는 응용 프로그램의 연결을 막습니다.
  • 장치가 발송하고 수신하는 유량 지표를 기록한다.
  • 지연 또는 랜덤 주입 패킷 리셋을 추가하여 연결 문제를 시뮬레이션합니다.
  • Android VPN은 어떻게 작동합니까?

    The Android developer docs have a VPN guide, 이것은 좋은 출발점이다.
    이러한 VPN API를 사용하면 애플리케이션에 서비스를 등록할 수 있으며, 활성화되면 네트워크 터널 인터페이스를 지원하는 파일 설명자가 제공됩니다.
    그리고 전체 설비는 이 터널 인터페이스를 모든 네트워크 데이터에 사용한다.또한 VPN 서비스는 이 터널을 사용하지 않는 보호 소켓을 만들 수 있으므로 VPN 응용 프로그램은 자체 없이 네트워크와 통신할 수 있습니다.
    활성화되면 프로그램이 네트워크에 보내지 않고 일부 데이터를 보낼 때 모든 IP 패키지는 이 파일 설명자 뒤에 버퍼링됩니다.에서 데이터를 읽을 때 원본 네트워크 바이트를 직접 얻을 수 있으며, 그 바이트를 쓸 때 네트워크 인터페이스에서 직접 받는 바이트로 간주됩니다.
    이것은 응용 프로그램에서 VPN 연결을 허용하기 위한 것입니다.이 경우 애플리케이션은 보호된 개별 연결을 통해 모든 읽기 바이트를 VPN 공급업체에 직접 전달하고 디바이스에서 제대로 처리할 필요가 없습니다.그런 다음 VPN 공급업체는 이 데이터를 VPN 트래픽의 일부로 위로 전송하고 연결을 통해 응답 패킷을 응용 프로그램으로 다시 전송한 다음 생성된 패킷을 파일 설명자로 다시 씁니다.
    이것이 바로 그것의 주요 설계 목적이지만, 이것이 우리가 할 수 있는 모든 것을 의미하지는 않는다.

    VPN이 VPN이 아닐 때는 언제입니까?

    Once we have a VPN service running, our app will receive every network byte the device sends, and has the power to inject raw bytes back.

    Things get interesting if rather than forwarding these bytes to a VPN provider, we examine them, and then simply put them straight back on the real network. In that case, we get to see every network byte, but we don't interfere with the network connection of the device, and we don't need an externally hosted VPN provider to do it.

    Unfortunately that's easier said than done. Our file descriptor works with raw IP data, but Android doesn't actually have an API for us to send raw IP data anywhere else. Instead, we have higher level APIs for TCP and UDP, and the IP part is always done invisibly under the hood.

    If we want to proxy these bytes, we need to match these two APIs up. We need to:

    • When we read an IP packet from our tunnel:
      • Parse the raw packet bytes into an IP packet.
      • Parse the TCP/UDP packet within and extract its content.
      • (For TCP) Track the connection state of the overall TCP connection, and ack/fin/etc each packet in the session appropriately.
      • Send the equivalent TCP/UDP content upstream, using Android's TCP/UDP APIs.
    • When we receive a TCP/UDP response from upstream:
      • (For TCP) Match that to the tunnelled TCP connection.
      • Build our own complete TCP/UDP + IP packet data around the received data.
      • Write the resulting bytes back into the tunnel.
      • Cleanly (or messily) close connections when the upstream socket is done.

    This is quite complicated. We effectively need to reimplement UDP & TCP from scratch!

    Fortunately, we're not the first people to want to do this. Most of the existing implementations are unmaintained demos, but they are open-source so we can build upon them! My own solution is based on a GitHub proof-of-concept called ToyShark(나는 와이어샤크의 이중관어라고 생각한다), 이것은 반대로 Application Resource Optimizersource라는 낡은 AT&T 프로젝트의 일부 개원 네트워크를 바탕으로 내부를 수집한다.
    이로써 생성된 HTTP 패키지 Android 응용 프로그램은 상기 모든 기능을 실현했다.이것은 100% 무료와 개원(github.com/httptoolkit/httptoolkit-android이기 때문에 미래의 유사한 개원 실현은 이를 바탕으로 구축할 수 있다.
    이 기능은 VPN을 충당하면서 모든 트래픽을 실제 네트워크로 프록시합니다. 모든 트래픽은 기본 코드가 없고 Java NIO 만 지원됩니다.
    핵심 VPN은 src/main/java/tech/httptoolkit/android/vpn에 구현되고 그 중 하나는 자술한 파일로 구현의 세부 사항을 개술했다.우리는 아래에서 이 점을 한층 더 연구하고 그것을 확장하는 방법을 찾을 것이다.
    물론 네트워크 대역폭과 지연에 있어서 이 모든 것은 성능 손실을 가져올 것이다.그러나 어떤 현대 설비의 정상적인 사용에서도 이런 영향은 뚜렷하지 않다.벌집 연결에 있어서, 그것은 통상적으로 잠재적인 연결 성능과 부족함을 나타내며, 심지어는 와이파이에서도 상당히 받아들일 수 있는 숫자에 도달할 수 있다.

    자바 코드를 본체 코드로 다시 쓰기를 통해 이것은 더욱 개선될 수 있으나, 이것은 추가적인 복잡성을 가져올 수 있다.HTTP 패키지 용례(번거로운 일상적인 사용이 아닌 맞춤형 디버깅)는 가치가 없습니다.
    그것 때문에 우리는 현재 장치로부터 모든 네트워크 데이터 패키지를 투명하게 수신할 수 있다.원본 IP 스트림 또는 해결된 TCP/UDP 패킷을 통해 트래픽을 검사하거나 편집할 수 있습니다.무엇 때문에?

    우리 이거 어떻게 써?

    In HTTP Toolkit's case, the usage of this is very direct: we forcibly redirect all HTTP(S) traffic via the debugging proxy (which is running on your local development machine). That proxy then lets you inspect and rewrite all the traffic there as you see fit.

    There's a demo video on HTTP Toolkit's Android page의 실제 응용을 보고 싶다면.
    이를 위해 TCP 연결을 전송하는 대상 포트를 확인하고, HTTP 포트 중 하나(예: 80443,...)인 경우 주소를 다시 씁니다.TCP session setup에 다음 줄만 추가하면 됩니다.
    public Session createNewTCPSession(int ip, int port, int srcIp, int srcPort)
            throws IOException {
        // ...
    
        String ips = PacketUtil.intToIPAddress(ip);
    
        // Use the given target address, unless tcpPortRedirection has specified
        // a different target address for traffic on this port:
        SocketAddress socketAddress = tcpPortRedirection.get(port) != null
            ? tcpPortRedirection.get(port)
            : new InetSocketAddress(ips, port);
    
        channel.connect(socketAddress);
    
        // ...
    }
    
    이렇게 하면 일치하는 모든 트래픽이 에이전트에 강제로 전송되고 HTTP 트래픽에 대해 바로 알 수 있습니다.Android 10에서는 명확하게 일치하지 않는 다른 포트에서 대부분의 트래픽을 캡처하는 a VPN proxy configuration 을 설정했습니다. (비록 이 경우 강제 리디렉션이 아니라 기본 설정이 권장됩니다.)
    이것은 원격 검사와 다시 쓰기를 위해 유량을 재정비하기에 충분하다.당신은 이 점을 어떻게 확장할 수 있습니까?내가 시작할 때 언급한 다른 세 가지 용례에 대해 이야기합시다.

    전송 연결 차단

    To block outgoing connections to specific addresses or on specific ports, you just need to throw away the packets after you receive them from the VPN interface, once you've parsed them to work out where they're going.

    You can use this to block specific hosts you don't like, block DNS requests for certain addresses to build an on-device Pi-Hole 또는 짧은 신뢰할 수 있는 호스트 목록으로만 전송하여 네트워크를 완전히 잠글 수 있습니다.
    HTTP 패키지 구현에서 SessionHandler handlePacket 는 장치가 보내고자 하는 원시 패키지 데이터를 처리하는 곳입니다.보아하니 이렇다.
    public void handlePacket(@NonNull ByteBuffer stream)
            throws PacketHeaderException, IOException {
        final byte[] rawPacket = new byte[stream.limit()];
        stream.get(rawPacket, 0, stream.limit());
        stream.rewind();
    
        final IPv4Header ipHeader = IPPacketFactory.createIPv4Header(stream);
    
        // TODO: inspect ipHeader here, and 'return' to drop the packet
    
        if (ipHeader.getProtocol() == 6) {
            handleTCPPacket(stream, ipHeader);
        } else if (ipHeader.getProtocol() == 17) {
            handleUDPPacket(stream, ipHeader);
        } else if (ipHeader.getProtocol() == 1) {
            handleICMPPacket(stream, ipHeader);
        } else {
            Log.w(TAG, "Unsupported IP protocol: " + ipHeader.getProtocol());
        }
    }
    
    따라서 우리는 IP 헤더의 목표 주소나 포트에 따라 데이터 패키지를 완전히 버리고 아무것도 하지 않을 수 있다.
    여기서 데이터 패키지를 버리는 것은 사실상 데이터 패키지를 잃어버리는 것이기 때문에 원시 요청을 보내는 프로그램은 아무런 응답도 들을 수 없다.
    또는 더 복잡한 규칙에 대해 상기 handleTCPPacket 또는 handleUDPPacket 방법과 같은 특정 프로토콜 처리에서 변경할 수 있습니다.이 두 경우 모두 해결된 TCP/UDP 패킷을 확인하고 배치할 수 있습니다(또는 TCP의 경우 애플리케이션 접속 실패를 알리기 위해 인스턴트 RST 패킷을 삽입할 수 있습니다).

    기록 흐름 지표

    Want to know what your device sends and receives? Normally Android makes that more or less invisible. Within a fake VPN application like this though you have every network byte, so it's easy to examine and record data about outgoing & incoming packets.

    It's simplest to do total byte metrics by address and/or port, but you could also build more complex analyses of packet data itself. E.g. tracking the duration of TCP sessions with certain hosts, recording metrics about the unencrypted data available, or looking at DNS UDP packets to examine which hostnames you're looking up.

    For this codebase, we can easily capture outgoing traffic in the handlePacket method above. We have the raw IP packet data there, and the full TCP & UDP data is just a little more parsing away.

    To track incoming traffic, we'd need to look at the code that handles the upstream connections. For example in readTCP SocketChannelReader로부터 업스트림 TCP 데이터를 수신합니다.
    private void readTCP(@NonNull Session session) {
        // ...
    
        try {
            do {
                len = channel.read(buffer);
                if (len > 0) {
                    // We're received some TCP data from the external network:
                    sendToRequester(buffer, len, session);
                    buffer.clear();
                } else if (len == -1) {
                    // The external network connection is finished:
                    sendFin(session);
                    session.setAbortingConnection(true);
                }
            } while (len > 0);
        }
    
        // ...
    }
    
    이제 TCP 연결의 내용을 처리한 다음 VPN 인터페이스의 원래 바이트에 포장합니다.
    여기에서 읽은 TCP 데이터를 확인하고 TCP 세션의 IP 및 포트와 연결하여 디바이스의 네트워크 통신에서 뷰를 신속하게 구축할 수 있습니다.

    아날로그 연결 문제

    It's possible to simulate connection issues on the device too. That's especially useful to test how applications handle low-quality internet connections and network errors.

    Unfortunately you can't simulate all issues, as Android's APIs give us limited control of upstream traffic. We control the contents of upstream TCP & UDP packets, but not the raw network connection itself. That means, for example, we can't simulate our device sending the wrong upstream packet sequence number or corrupting a TCP checksum, but we can simulate the device receiving such packets.

    There's still a lot of interesting things you can simulate with this:

  • Incoming or outgoing packet loss, where some packets simply disappear in transit.
  • Repeated or misordered packets.
  • Random connection resets (similar to tcpkill ).
  • 패킷에 대한 지연 시간
  • 모든 상황에서 이 동작을 확률적으로 실행하기 때문에 10%의 연결이 실패하거나 패킷이 평균 500밀리초 지연됩니다.
    이렇게 할 때, 응용 프로그램에서 놀라운 결과와 오류를 자주 볼 수 있다.실제로 우리는 설비chaos engineering에서 진행하고 있다.
    이러한 랜덤 연결 재설정을 추가하면 일반적으로 매우 뚜렷한 TCP 연결 실패를 초래하고 랜덤 HTTP 요청, 원시 네트워크 플러그인 또는 다른 갑작스런 실패와 연결 끊기를 초래할 수 있다.
    또한 패킷 분실과 정렬 문제는 TCP 수준에서 처리되기 때문에 응용 프로그램 코드에는 보이지 않지만 이러한 과정은 예측할 수 없는 성능을 초래하고 응용 프로그램 단계에서 실제 문제를 일으킬 수 있다.
    일상적인 개발 과정에서 당신의 사무실이나 집에서 신속하고 신뢰할 수 있는 와이파이를 고려하면 이런 문제들을 영원히 볼 수 없기 쉽습니다. 농촌의 2G 문제를 모의하면 사람들의 시야를 넓힐 수 있습니다!
    대부분의 작업은 매우 낮은 단계에서 이루어지며, 각 원시 IP 패키지가 VPN 사이에서 전달되는 위치에 연결하기만 하면 된다.그러나 TCP 오류 시뮬레이션의 경우 다시 정렬할 패킷을 찾거나 활성 연결에 RST를 주입하려면 TCP 연결 자체에 대한 정보가 더 필요합니다.
    HTTP Toolkit 응용 프로그램에서:

  • ClientPacketWriter 는 원래 IP 데이터를 VPN(IP 패킷에 전송)으로 다시 쓰는 위치입니다.이 단계에서 우리는 IP 단계에서 전송된 데이터 패키지를 쉽게 버리거나 손상시키거나 지연시킬 수 있다.

  • handlePacket 다시 SessionHandler 에서 전송된 데이터 패키지를 버리거나 지연시키거나 다른 방식으로 반응할 수 있습니다.
  • SessionHandler는 또한 모든 연결의 TCP 흐름을 제어하여 모든 데이터 패키지를 처리하고 우리가 이 흐름에 직접 연결할 수 있도록 합니다.예를 들어, 새 연결의 50%를 만들기 위해 replySynAck 을 확장할 수 있습니다. 이 경우 연결 재설정 (호출 resetConnection 만 하면 됩니다.

  • SessionManager 스토리지 이SessionHandler가 제어하는 상태입니다.활성 연결 목록을 지정하면 무작위 활성 TCP 세션을 선택하고 원하는 기준에 따라 죽일 수 있습니다.
  • 보시다시피 Android VPN API는 기능이 강하고 잠재력이 큽니다.
    이러한 기교로 네트워크 데이터를 연결하면 재미있는 도구 세계를 구축할 수 있다.해봐!어떤 생각이나 피드백이 있습니까?아래 댓글로 알려주세요.
    당신의 안드로이드 디버깅을 새로운 수준으로 끌어올리고 싶습니까?HTTP Toolkit 모든 Android 응용 프로그램(추가 도구 포함)에 원클릭 HTTP 검사와 시뮬레이션을 제공합니다.

    좋은 웹페이지 즐겨찾기