简体   繁体   English

带有VpnService的Android防火墙

[英]Android firewall with VpnService

I'm trying to implement a simple firewall for android with VpnService for BS project. 我正在尝试使用VpnService为BS项目实现一个简单的Android防火墙。 I choose VpnService because it will be working on non-rooted devices. 我选择VpnService是因为它将在非root设备上运行。 It will log connections and let you filter connection. 它将记录连接并让您过滤连接。 (Based on IP) (基于IP)

There is an application doing this so it is possible. 有一个应用程序这样做是有可能的。

Google play app store Google Play应用商店

I did some research and found that VpnService creates a Tun interface. 我做了一些研究,发现VpnService创建了一个Tun接口。 Nothing more. 而已。 (No VPN implementation just a tunnel) It lets you give an adress to this interface and add routes. (没有VPN实现只是一个隧道)它允许您为此接口添加地址并添加路由。 It returns a file descriptor. 它返回一个文件描述符。 You can read outgoing packages and write incoming packages. 您可以读取传出的包并写入传入的包。

I created a VpnService derived class and I started service. 我创建了一个VpnService派生类,我开始服务。 I can configure tun0 with VpnService.Builder class. 我可以使用VpnService.Builder类配置tun0 When I look at mobiwol's connection with adb shell netcfg it creates a tun0 interface with 10.2.3.4/32 address. 当我查看mobiwol'sadb shell netcfg mobiwol's连接时,它会创建一个带有10.2.3.4/32地址的tun0接口。 It routes all packages to this private network and send to internet. 它将所有程序包路由到此专用网络并发送到Internet。 I'm trying the same. 我也在尝试。 Created an interface with 10.0.0.2/32 address. 创建了10.0.0.2/32地址的接口。 Added a route with addRoute function. 添加了具有addRoute功能的路由。 0.0.0.0/0 so I can capture all packages from all network as far as I understand. 0.0.0.0/0所以据我所知,我可以从所有网络捕获所有包。 (Im pretty new to this subject and still learning. I found pieces over internet so I'm not really sure. Correct me if I'm wrong.) (我对这个主题很新,还在学习。我在互联网上找到了作品,所以我不太确定。如果我错了,请纠正我。)

I created 2 threads in service. 我在服务中创建了2个线程。 One reads from file descriptor and writes it to 127.0.0.1 with a protected socket. 一个从文件描述符读取并使用受保护的套接字将其写入127.0.0.1。 ( Im not really sure if I should read/write to 127.0.0.1. Maybe this is the problem. ) (我不确定我是否应该读/写127.0.0.1。也许这就是问题所在。)

I analyzed packets that I read from file descriptor. 我分析了从文件描述符中读取的数据包。 For example: 例如:

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

I didnt find any other IP header in the rest of data. 我没有在其余数据中找到任何其他IP头。 I think there should be an encapsulation between 10.0.0.2 network to local network (192.168.2.1) and internet. 我认为应该在10.0.0.2网络到本地网络(192.168.2.1)和互联网之间进行封装。 I'm not sure. 我不确定。

My real problem is I stuck on the incoming packages thread. 我真正的问题是我坚持传入的包线程。 I can't read anything. 我看不懂任何东西。 No response. 没有反应。 As you can see in screenshot no incoming data: 正如您在屏幕截图中看到的,没有传入数据:

screenshot 截图

I'm trying to read from the same connection which I'm using for writing to 127.0.0.1 with protected socket. 我正在尝试从我用于写入带有受保护套接字的127.0.0.1的相同连接中读取。

Android <-> Tun Interface (tun0) <-> Internet connection Android < - > Tun接口(tun0)< - > Internet连接

All packages <-> 10.0.0.2 <-> 127.0.0.1? 所有包< - > 10.0.0.2 < - > 127.0.0.1? <-> 192.168.2.1 <-> Internet? < - > 192.168.2.1 < - >互联网?

I couldnt find anything helpful about VpnService. 我找不到任何关于VpnService的帮助。 (ToyVPN example is just useless) I read documents about Linux Tun/Tap but its about tunnelling between host and remote. (ToyVPN示例是无用的)我阅读有关Linux Tun / Tap的文档,但它关于主机和远程之间的隧道。 I want host and remote on same device. 我想在同一设备上使用主机和远程设备。 Not like tunneling. 不喜欢隧道。

How can I do this? 我怎样才能做到这一点?

Edit: Code requested. 编辑:请求的代码。 It is in very early stage. 现在还处于初期阶段。 As I mentioned before it is a VpnService derived class. 正如我之前提到的,它是一个VpnService派生类。 2 threads (reading and writing) created in service thread. 在服务线程中创建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());

    }

}

A similar question was asked a few months ago , and while the answers there aren't very insightful, the comments in the accepted answer give some insight into what may be going wrong. 几个月前就提出了一个类似的问题 ,虽然答案并不是很有见地,但接受答案中的评论可以让我们深入了解可能出现的问题。

You should bear in mind which layer in the OSI model your logic resides: 您应该记住您的逻辑所在的OSI模型哪个层:

  • Incoming and outgoing streams of the VpnService are in the network layer; VpnService的传入和传出流都在网络层中; you are receiving (and should in turn be transmitting) raw IP packets, as you describe in your question. 正如您在问题中所描述的那样,您正在接收(并且应该反过来传输)原始IP数据包。

    In your sample byte stream, you can see that the incoming byte stream is an IPv4 datagram as the first four bits are 0100 (4). 在示例字节流中,您可以看到传入的字节流是IPv4数据报,因为前四位是0100 (4)。 Consult this packet structure specification for details on IPv4. 有关IPv4的详细信息,请参阅此数据包结构规范

  • When forwarding the requests, you are in the application layer; 转发请求时,您处于应用程序层; you should be transmitting the contents of the UDP or TCP payload (ie only their data, not the headers themselves) using respectively a DatagramSocket or a Socket. 你应该分别使用DatagramSocket或Socket传输UDP或TCP有效载荷的内容 (即只有它们的数据,而不是标题本身)。

    Bear in mind that this skips the transport layer as those implementations take care of constructing the UDP header (in case of DatagramSocket) and the TCP header and options (in case of Socket). 请记住,这会跳过传输层,因为这些实现负责构建UDP头(在DatagramSocket的情况下)和TCP头和选项(在Socket的情况下)。

Your application will essentially need to be able to interpret and construct IPv4 and IPv6 headers and options, and as the IP payload, the UDP headers and TCP headers and options. 您的应用程序基本上需要能够解释和构建IPv4和IPv6标头和选项,以及IP有效负载,UDP标头和TCP标头和选项。

Maybe it's better to look for open source projects like OpenVpn . 也许最好寻找像OpenVpn这样的开源项目。 It works in API level 14+ (Ice Cream Sandwhich) without Root Access . 它适用于没有Root Access的API级别14+(Ice Cream Sandwhich)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM