簡體   English   中英

帶有VpnService的Android防火牆

[英]Android firewall with VpnService

我正在嘗試使用VpnService為BS項目實現一個簡單的Android防火牆。 我選擇VpnService是因為它將在非root設備上運行。 它將記錄連接並讓您過濾連接。 (基於IP)

有一個應用程序這樣做是有可能的。

Google Play應用商店

我做了一些研究,發現VpnService創建了一個Tun接口。 而已。 (沒有VPN實現只是一個隧道)它允許您為此接口添加地址並添加路由。 它返回一個文件描述符。 您可以讀取傳出的包並寫入傳入的包。

我創建了一個VpnService派生類,我開始服務。 我可以使用VpnService.Builder類配置tun0 當我查看mobiwol'sadb shell netcfg mobiwol's連接時,它會創建一個帶有10.2.3.4/32地址的tun0接口。 它將所有程序包路由到此專用網絡並發送到Internet。 我也在嘗試。 創建了10.0.0.2/32地址的接口。 添加了具有addRoute功能的路由。 0.0.0.0/0所以據我所知,我可以從所有網絡捕獲所有包。 (我對這個主題很新,還在學習。我在互聯網上找到了作品,所以我不太確定。如果我錯了,請糾正我。)

我在服務中創建了2個線程。 一個從文件描述符讀取並使用受保護的套接字將其寫入127.0.0.1。 (我不確定我是否應該讀/寫127.0.0.1。也許這就是問題所在。)

我分析了從文件描述符中讀取的數據包。 例如:

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

我沒有在其余數據中找到任何其他IP頭。 我認為應該在10.0.0.2網絡到本地網絡(192.168.2.1)和互聯網之間進行封裝。 我不確定。

我真正的問題是我堅持傳入的包線程。 我看不懂任何東西。 沒有反應。 正如您在屏幕截圖中看到的,沒有傳入數據:

截圖

我正在嘗試從我用於寫入帶有受保護套接字的127.0.0.1的相同連接中讀取。

Android < - > Tun接口(tun0)< - > Internet連接

所有包< - > 10.0.0.2 < - > 127.0.0.1? < - > 192.168.2.1 < - >互聯網?

我找不到任何關於VpnService的幫助。 (ToyVPN示例是無用的)我閱讀有關Linux Tun / Tap的文檔,但它關於主機和遠程之間的隧道。 我想在同一設備上使用主機和遠程設備。 不喜歡隧道。

我怎樣才能做到這一點?

編輯:請求的代碼。 現在還處於初期階段。 正如我之前提到的,它是一個VpnService派生類。 在服務線程中創建2個線程(讀取和寫入)。

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

    }

}

幾個月前就提出了一個類似的問題 ,雖然答案並不是很有見地,但接受答案中的評論可以讓我們深入了解可能出現的問題。

您應該記住您的邏輯所在的OSI模型哪個層:

  • VpnService的傳入和傳出流都在網絡層中; 正如您在問題中所描述的那樣,您正在接收(並且應該反過來傳輸)原始IP數據包。

    在示例字節流中,您可以看到傳入的字節流是IPv4數據報,因為前四位是0100 (4)。 有關IPv4的詳細信息,請參閱此數據包結構規范

  • 轉發請求時,您處於應用程序層; 你應該分別使用DatagramSocket或Socket傳輸UDP或TCP有效載荷的內容 (即只有它們的數據,而不是標題本身)。

    請記住,這會跳過傳輸層,因為這些實現負責構建UDP頭(在DatagramSocket的情況下)和TCP頭和選項(在Socket的情況下)。

您的應用程序基本上需要能夠解釋和構建IPv4和IPv6標頭和選項,以及IP有效負載,UDP標頭和TCP標頭和選項。

也許最好尋找像OpenVpn這樣的開源項目。 它適用於沒有Root Access的API級別14+(Ice Cream Sandwhich)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM