it-swarm.com.de

Android-Firewall mit VpnService

Ich versuche eine einfache Firewall für Android mit VpnService für BS zu implementieren. Ich wähle VpnService, weil es auf nicht gerooteten Geräten funktioniert. Es werden Verbindungen protokolliert und Sie können die Verbindung filtern. (Basierend auf IP)

Es gibt eine Anwendung, damit dies möglich ist.

Google Play App Store

Ich habe etwas recherchiert und festgestellt, dass VpnService eine Tun-Schnittstelle erstellt. Nichts mehr. (Keine VPN-Implementierung, nur ein Tunnel) Sie können dieser Schnittstelle eine Adresse zuweisen und Routen hinzufügen. Es gibt einen Dateideskriptor zurück. Sie können ausgehende Pakete lesen und eingehende Pakete schreiben.

Ich habe eine abgeleitete VpnService-Klasse erstellt und mit dem Service begonnen. Ich kann tun0 mit der VpnService.Builder-Klasse konfigurieren. Wenn ich mobiwol's connection mit adb Shell netcfg anschaue, wird eine tun0-Schnittstelle mit der Adresse 10.2.3.4/32 erstellt. Es leitet alle Pakete an dieses private Netzwerk und sendet es an das Internet. Ich versuche das gleiche. Erstellt eine Schnittstelle mit 10.0.0.2/32 Adresse. Eine Route mit der Funktion addRoute wurde hinzugefügt. 0.0.0.0/0, damit ich alle Pakete meines Netzwerks erfassen kann, soweit ich das verstanden habe. (Ich bin zu diesem Thema ziemlich neu und lerne immer noch. Ich habe Stücke über das Internet gefunden, daher bin ich nicht wirklich sicher. Korrigieren Sie mich, wenn ich falsch liege.)

Ich habe 2 Threads erstellt. Man liest aus dem Dateideskriptor und schreibt es mit einem geschützten Socket in 127.0.0.1. (Ich bin mir nicht wirklich sicher, ob ich 127.0.0.1 lesen/schreiben soll. Vielleicht ist das das Problem.)

Ich habe Pakete analysiert, die ich aus dem Dateideskriptor gelesen habe. Zum Beispiel:

01000101    byte:69     //ipv4 20byte header
00000000    byte:0      //TOS
00000000    byte:0      //Total Length
00111100    byte:60     //Total Length
11111100    byte:-4     //ID
11011011    byte:-37    //ID
01000000    byte:64     //fragment
00000000    byte:0      //"
01000000    byte:64     //TTL
00000110    byte:6      //Protocol 6 -> TCP
01011110    byte:94     //Header checksum
11001111    byte:-49    //Header checksum
00001010    byte:10     //10.0.0.2
00000000    byte:0
00000000    byte:0
00000010    byte:2
10101101    byte:-83    //173.194.39.78 //google
00111110    byte:-62
00100111    byte:39
********    byte:78

10110100    byte:-76    // IP option
01100101    byte:101
00000001    byte:1
10111011    byte:-69
                //20byte IP haeder
01101101    byte:109
.       .       //40byte data (i couldnt parse TCP header, 
                    I think its not needed when I route this in IP layer)
.       .
.       .
00000110    byte:6

Ich habe keine anderen IP-Header in den restlichen Daten gefunden. Ich denke, es sollte eine Kapselung zwischen dem 10.0.0.2-Netzwerk und dem lokalen Netzwerk (192.168.2.1) und dem Internet geben. Ich bin mir nicht sicher.

Mein eigentliches Problem ist, dass ich beim Thread für eingehende Pakete hängen bleibe. Ich kann nichts lesen Keine Antwort. Wie Sie im Screenshot sehen können, sind keine eingehenden Daten vorhanden:

Bildschirmfoto

Ich versuche, von derselben Verbindung zu lesen, die ich zum Schreiben auf 127.0.0.1 mit geschütztem Socket verwende. 

Android <-> Tun Interface (tun0) <-> Internetverbindung

Alle Pakete <-> 10.0.0.2 <-> 127.0.0.1? <-> 192.168.2.1 <-> Internet?

Ich konnte nichts Nützliches über VpnService finden. (ToyVPN-Beispiel ist einfach nutzlos) Ich habe Dokumente über Linux Tun/Tap gelesen, aber über das Tunneln zwischen Host und Remote. Ich möchte Host und Remote auf demselben Gerät. Nicht wie das Tunneln.

Wie kann ich das machen?

Edit: Code angefordert. Es ist in einem sehr frühen Stadium. Wie bereits erwähnt, handelt es sich um eine von VpnService abgeleitete Klasse. 2 Threads (Lesen und Schreiben), die im Servicethread erstellt wurden.

package com.git.firewall;

public class GITVpnService extends VpnService implements Handler.Callback, Runnable {
    private static final String TAG = "GITVpnService";

    private String mServerAddress = "127.0.0.1";
    private int mServerPort = 55555;
    private PendingIntent mConfigureIntent;

    private Handler mHandler;
    private Thread mThread;

    private ParcelFileDescriptor mInterface;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // The handler is only used to show messages.
        if (mHandler == null) {
            mHandler = new Handler(this);
        }

        // Stop the previous session by interrupting the thread.
        if (mThread != null) {
            mThread.interrupt();
        }
        // Start a new session by creating a new thread.
        mThread = new Thread(this, "VpnThread");
        mThread.start();
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        if (mThread != null) {
            mThread.interrupt();
        }
    }

    @Override
    public boolean handleMessage(Message message) {
        if (message != null) {
            Toast.makeText(this, (String)message.obj, Toast.LENGTH_SHORT).show();
        }
        return true;
    }

    @Override
    public synchronized void run() {
        try {
            Log.i(TAG, "Starting");
            InetSocketAddress server = new InetSocketAddress(
                    mServerAddress, mServerPort);

            run(server);

              } catch (Exception e) {
            Log.e(TAG, "Got " + e.toString());
            try {
                mInterface.close();
            } catch (Exception e2) {
                // ignore
            }
            Message msgObj = mHandler.obtainMessage();
            msgObj.obj = "Disconnected";
            mHandler.sendMessage(msgObj);

        } finally {

        }
    }

    DatagramChannel mTunnel = null;


    private boolean run(InetSocketAddress server) throws Exception {
        boolean connected = false;

        Android.os.Debug.waitForDebugger();

        // Create a DatagramChannel as the VPN tunnel.
        mTunnel = DatagramChannel.open();

        // Protect the tunnel before connecting to avoid loopback.
        if (!protect(mTunnel.socket())) {
            throw new IllegalStateException("Cannot protect the tunnel");
        }

        // Connect to the server.
        mTunnel.connect(server);

        // For simplicity, we use the same thread for both reading and
        // writing. Here we put the tunnel into non-blocking mode.
        mTunnel.configureBlocking(false);

        // Authenticate and configure the virtual network interface.
        handshake();

        // Now we are connected. Set the flag and show the message.
        connected = true;
        Message msgObj = mHandler.obtainMessage();
        msgObj.obj = "Connected";
        mHandler.sendMessage(msgObj);

        new Thread ()
        {
            public void run ()
                {
                    // Packets to be sent are queued in this input stream.
                    FileInputStream in = new FileInputStream(mInterface.getFileDescriptor());
                    // Allocate the buffer for a single packet.
                    ByteBuffer packet = ByteBuffer.allocate(32767);
                    int length;
                    try
                    {
                        while (true)
                        {
                            while ((length = in.read(packet.array())) > 0) {
                                    // Write the outgoing packet to the tunnel.
                                    packet.limit(length);
                                    debugPacket(packet);    // Packet size, Protocol, source, destination
                                    mTunnel.write(packet);
                                    packet.clear();

                                }
                            }
                    }
                    catch (IOException e)
                    {
                            e.printStackTrace();
                    }

            }
        }.start();

        new Thread ()
        {

            public void run ()
            {
                    DatagramChannel tunnel = mTunnel;
                    // Allocate the buffer for a single packet.
                    ByteBuffer packet = ByteBuffer.allocate(8096);
                    // Packets received need to be written to this output stream.
                    FileOutputStream out = new FileOutputStream(mInterface.getFileDescriptor());

                    while (true)
                    {
                        try
                        {
                            // Read the incoming packet from the tunnel.
                            int length;
                            while ((length = tunnel.read(packet)) > 0)
                            {
                                    // Write the incoming packet to the output stream.
                                out.write(packet.array(), 0, length);

                                packet.clear();

                            }
                        }
                        catch (IOException ioe)
                        {
                                ioe.printStackTrace();
                        }
                    }
            }
        }.start();

        return connected;
    }

    private void handshake() throws Exception {

        if (mInterface == null)
        {
            Builder builder = new Builder();

            builder.setMtu(1500);
            builder.addAddress("10.0.0.2",32);
            builder.addRoute("0.0.0.0", 0);
            //builder.addRoute("192.168.2.0",24);
            //builder.addDnsServer("8.8.8.8");

            // Close the old interface since the parameters have been changed.
            try {
                mInterface.close();
            } catch (Exception e) {
                // ignore
            }


            // Create a new interface using the builder and save the parameters.
            mInterface = builder.setSession("GIT VPN")
                    .setConfigureIntent(mConfigureIntent)
                    .establish();
        }
    }

    private void debugPacket(ByteBuffer packet)
    {
        /*
        for(int i = 0; i < length; ++i)
        {
            byte buffer = packet.get();

            Log.d(TAG, "byte:"+buffer);
        }*/



        int buffer = packet.get();
        int version;
        int headerlength;
        version = buffer >> 4;
        headerlength = buffer & 0x0F;
        headerlength *= 4;
        Log.d(TAG, "IP Version:"+version);
        Log.d(TAG, "Header Length:"+headerlength);

        String status = "";
        status += "Header Length:"+headerlength;

        buffer = packet.get();      //DSCP + EN
        buffer = packet.getChar();  //Total Length

        Log.d(TAG, "Total Length:"+buffer);

        buffer = packet.getChar();  //Identification
        buffer = packet.getChar();  //Flags + Fragment Offset
        buffer = packet.get();      //Time to Live
        buffer = packet.get();      //Protocol

        Log.d(TAG, "Protocol:"+buffer);

        status += "  Protocol:"+buffer;

        buffer = packet.getChar();  //Header checksum

        String sourceIP  = "";
        buffer = packet.get();  //Source IP 1st Octet
        sourceIP += buffer;
        sourceIP += ".";

        buffer = packet.get();  //Source IP 2nd Octet
        sourceIP += buffer;
        sourceIP += ".";

        buffer = packet.get();  //Source IP 3rd Octet
        sourceIP += buffer;
        sourceIP += ".";

        buffer = packet.get();  //Source IP 4th Octet
        sourceIP += buffer;

        Log.d(TAG, "Source IP:"+sourceIP);

        status += "   Source IP:"+sourceIP;

        String destIP  = "";
        buffer = packet.get();  //Destination IP 1st Octet
        destIP += buffer;
        destIP += ".";

        buffer = packet.get();  //Destination IP 2nd Octet
        destIP += buffer;
        destIP += ".";

        buffer = packet.get();  //Destination IP 3rd Octet
        destIP += buffer;
        destIP += ".";

        buffer = packet.get();  //Destination IP 4th Octet
        destIP += buffer;

        Log.d(TAG, "Destination IP:"+destIP);

        status += "   Destination IP:"+destIP;
        /*
        msgObj = mHandler.obtainMessage();
        msgObj.obj = status;
        mHandler.sendMessage(msgObj);
        */

        //Log.d(TAG, "version:"+packet.getInt());
        //Log.d(TAG, "version:"+packet.getInt());
        //Log.d(TAG, "version:"+packet.getInt());

    }

}
28
fatihdurmus

Eine ähnliche Frage wurde vor einigen Monaten gestellt , und obwohl die Antworten dort nicht sehr aufschlussreich sind, geben die Kommentare in der akzeptierten Antwort einen Einblick in das, was möglicherweise schief läuft.

Sie sollten berücksichtigen, welche Schicht in dem OSI-Modell Ihrer Logik liegt:

  • Eingehende und ausgehende Streams des VpnService befinden sich in der Netzwerkschicht. Sie empfangen (und sollten diese wiederum übertragen) rohe IP-Pakete, wie Sie es in Ihrer Frage beschrieben haben.

    In Ihrem Beispiel-Byte-Stream können Sie sehen, dass der eingehende Byte-Stream ein IPv4-Datagramm ist, da die ersten vier Bits 0100 (4) sind. Konsultieren Sie diese Paketstrukturspezifikation für Details zu IPv4.

  • Beim Weiterleiten der Anforderungen befinden Sie sich in der Anwendungsebene. Sie sollten die contents der UDP- oder TCP - Nutzdaten (d. h. nur ihre Daten, nicht die Header selbst) über einen DatagramSocket oder einen Socket übertragen.

    Beachten Sie, dass hierdurch die Transportschicht übersprungen wird, da bei diesen Implementierungen der UDP-Header (bei DatagramSocket) und der TCP -Header und die Optionen (bei Socket) erstellt werden.

Ihre Anwendung muss im Wesentlichen in der Lage sein, IPv4- und IPv6-Header und -Optionen zu interpretieren und zu konstruieren, und als IP-Nutzdaten die UDP-Header und TCP -Header und -Optionen.

16
Paul Lammertsma

Vielleicht ist es besser, nach Open Source-Projekten wie OpenVpn zu suchen. Es funktioniert in API Level 14 (Ice Cream Sandwich) ohne Root-Zugriff.

0
Ali