34

I been searching for my answer for a couple of hours now and I can't figure it out. Please help.

What I want to do is to use the VpnService in Android to grab network packets like the application tPacketCapture

I started by using the ToyVpn sample code from google and modifying it so I don't send the data to a server. However, I'm not sure if this is correct.

My configure method uses the wlan ip address for binder.addAddress() before calling establish(). I am using a nexus 7 and I used "adb shell netcfg | grep wlan0" to get the address:

wlan0 UP 192.168.0.6/24 0x00001043 10:bf:48:bf:5f:9d

And add it in my method:

    private void configure() throws Exception {
    // If the old interface has exactly the same parameters, use it!
    if (mInterface != null) {
        Log.i(TAG, "Using the previous interface");
        return;
    }

    // Configure a builder while parsing the parameters.
    Builder builder = new Builder();
    builder.setMtu(1500);
    builder.addAddress("192.168.0.6", 24);

    try {
        mInterface.close();
    } catch (Exception e) {
        // ignore
    }

    mInterface = builder.establish();
}

After calling this, I call the run method which I modified to pass a String instead of a InetSocketAddress and this is not important because I am not using it anywhere:

    private void run(String run) throws Exception {
    configure();

    FileInputStream in = new FileInputStream(mInterface.getFileDescriptor());

    // Allocate the buffer for a single packet.
    ByteBuffer packet = ByteBuffer.allocate(32767);

    // We use a timer to determine the status of the tunnel. It
    // works on both sides. A positive value means sending, and
    // any other means receiving. We start with receiving.
    int timer = 0;

    // We keep forwarding packets till something goes wrong.
    while (true) {
        // Assume that we did not make any progress in this iteration.
        boolean idle = true;

        // Read the outgoing packet from the input stream.
        int length = in.read(packet.array());
        if (length > 0) {

            Log.i(TAG,"************new packet");
            while (packet.hasRemaining()) {
                Log.i(TAG,""+packet.get());
                //System.out.print((char) packet.get());
            }

            // Write the outgoing packet to the tunnel.
            packet.limit(length);
            //  tunnel.write(packet);
            packet.clear();

            // There might be more outgoing packets.
            idle = false;

            // If we were receiving, switch to sending.
            if (timer < 1) {
                timer = 1;
            }
        }
    }
}

When I do adb logcat, nothing is happening. Am I going about this correctly? I feel like I am missing something.

Thank you!

EDIT:

From the logs I see the following lines:

I/ActivityManager(  460): START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.android.toyvpn/.ToyVpnClient} from pid 10247
I/ActivityManager(  460): Start proc com.example.android.toyvpn for activity com.example.android.toyvpn/.ToyVpnClient: pid=10287 uid=10122 gids={50122, 3003, 1028}
I/ActivityManager(  460): Displayed com.example.android.toyvpn/.ToyVpnClient: +1s144ms
I/Vpn     (  460): Switched from [Legacy VPN] to com.example.android.toyvpn
D/Vpn     (  460): setting state=IDLE, reason=prepare
I/ToyVpnService(10287): running vpnService
D/Vpn     (  460): setting state=CONNECTING, reason=establish
D/VpnJni  (  460): Address added on tun0: 192.168.0.6/24
I/Vpn     (  460): Established by com.example.android.toyvpn.ToyVpnService on tun0
W/ContextImpl(  460): Calling a method in the system process without a qualified user: android.app.ContextImpl.bindService:1406 com.android.server.connectivity.Vpn.establish:289 com.android.server.ConnectivityService.establishVpn:3263 android.net.IConnectivityManager$Stub.onTransact:504 android.os.Binder.execTransact:351 
D/Vpn     (  460): setting state=AUTHENTICATING, reason=establish

So it seems to be connecting.

Full source:

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

    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, "ToyVpnThread");
        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, message.what, Toast.LENGTH_SHORT).show();
        }
        return true;
    }

    @Override
    public synchronized void run() {
        Log.i(TAG,"running vpnService");
        try {
            runVpnConnection();
        } catch (Exception e) {
            e.printStackTrace();
            //Log.e(TAG, "Got " + e.toString());
        } finally {
            try {
                mInterface.close();
            } catch (Exception e) {
                // ignore
            }
            mInterface = null;

            mHandler.sendEmptyMessage(R.string.disconnected);
            Log.i(TAG, "Exiting");
        }
    }

    private boolean runVpnConnection() throws Exception {

        configure();

        FileInputStream in = new FileInputStream(mInterface.getFileDescriptor());

        // Allocate the buffer for a single packet.
        ByteBuffer packet = ByteBuffer.allocate(32767);

        // We keep forwarding packets till something goes wrong.
        while (true) {
            // Assume that we did not make any progress in this iteration.
            boolean idle = true;

            // Read the outgoing packet from the input stream.
            int length = in.read(packet.array());
            if (length > 0) {

                Log.i(TAG,"************new packet");
                System.exit(-1);
                while (packet.hasRemaining()) {
                    Log.i(TAG,""+packet.get());
                    //System.out.print((char) packet.get());
                }
                packet.limit(length);
                //  tunnel.write(packet);
                packet.clear();

                // There might be more outgoing packets.
                idle = false;
            }
            Thread.sleep(50);
        }
    }

    public String getLocalIpAddress()
    {
        try {
            for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
                NetworkInterface intf = en.nextElement();
                for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
                    InetAddress inetAddress = enumIpAddr.nextElement();
                    Log.i(TAG,"****** INET ADDRESS ******");
                    Log.i(TAG,"address: "+inetAddress.getHostAddress());
                    Log.i(TAG,"hostname: "+inetAddress.getHostName());
                    Log.i(TAG,"address.toString(): "+inetAddress.getHostAddress().toString());
                    if (!inetAddress.isLoopbackAddress()) {
                        //IPAddresses.setText(inetAddress.getHostAddress().toString());
                        Log.i(TAG,"IS NOT LOOPBACK ADDRESS: "+inetAddress.getHostAddress().toString());
                        return inetAddress.getHostAddress().toString();
                    } else{
                        Log.i(TAG,"It is a loopback address");
                    }
                }
            }
        } catch (SocketException ex) {
            String LOG_TAG = null;
            Log.e(LOG_TAG, ex.toString());
        }

        return null;
    }

    private void configure() throws Exception {
        // If the old interface has exactly the same parameters, use it!
        if (mInterface != null) {
            Log.i(TAG, "Using the previous interface");
            return;
        }

        // Configure a builder while parsing the parameters.
        Builder builder = new Builder();
        builder.setMtu(1500);
        builder.addAddress("192.168.0.6", 24);
        try {
            mInterface.close();
        } catch (Exception e) {
            // ignore
        }

        mInterface = builder.establish();
    }
}
jwueller
  • 30,582
  • 4
  • 66
  • 70
Juan Acevedo
  • 1,768
  • 2
  • 20
  • 39

2 Answers2

27

Ok, it was not easy at all but I figured out how to capture packets. Since I am not extremely familiar with networking (but this new job is requesting that I am) I had difficulty with setting everything correctly. Basically after setting the right route in the VpnService.builder I got to receiving packets correctly.

So:

builder.addAddress("192.168.0.6", 24); // was wrong, you need to put an internal IP (10.0.2.0 for example)

and

builder.addRoute("0.0.0.0", 0); // needs to be this.

you don't need to set up a DnsServer through builder.addDnsServer() to make it work. Hope this helps anyone!

jwueller
  • 30,582
  • 4
  • 66
  • 70
Juan Acevedo
  • 1,768
  • 2
  • 20
  • 39
  • Nice work...Were you able to forward the packets to the correct destination and how did you save the packets..did you do it in a pcap file... – anz Nov 04 '13 at 12:24
  • 3
    I was able to forward packets. If I remember correctly I just needed to read the TCP header and open another socket to that destination and send it headerless. When you get back a response, you must add the TCP header before sending it back to the output stream. This is for TCP (which I only cared about). Also SSL needs an SSL handshake. – Juan Acevedo Nov 04 '13 at 21:18
  • Thanks for your reply...With some modifications to your code I was able to start the VPN service and capture the packets but I was not able to visit any URL using the browser after the VPN service started..Do I have to implement what you mentioned above to achieve the same... – anz Nov 05 '13 at 06:05
  • 1
    Yes, you need to in order to forward the packets to the interface, thus the outside. – Juan Acevedo Nov 05 '13 at 21:28
  • Is your project open-source? I want to learn how to remove and add packet headers. If not open-source, is there a blog about it? Thanks a lot! – Saswat Anand Nov 15 '13 at 06:48
  • 1
    sorry it is not open source. This is production software so I also do not have a blog about this. – Juan Acevedo Nov 15 '13 at 21:13
  • @JuanAcevedo You mention that you need to add the TCP header before sending it to the output stream. Do you construct a new TCP header, and reverse the source and destination ports for the response? What about the IP header; do you add that too? – Paul Lammertsma Nov 19 '13 at 17:41
  • 3
    Yes, the TCP header needs to be reversed for the response just as if your browser(for example) was getting a response from the server. Also the IP header needs to be added. – Juan Acevedo Nov 19 '13 at 20:39
  • @JuanAcevedo, how did you make all the apps write headerless data to another socket? – Maksim Dmitriev May 08 '14 at 09:26
  • 1
    You just need to break down the TCP header that comes with the packet, remove that from the packet, and send it to the corresponding socket. Not sure if this is what you are asking. – Juan Acevedo May 08 '14 at 21:13
  • @JuanAcevedo, OK. I read an array from the TUN device. What exactly do I need to write to another socket? The IP header + the data or just the data? And do I need to create a socket to send the data to its destination for every IP packet taking into account its [identification field](http://en.wikipedia.org/wiki/IPv4_header#Identification)? – Maksim Dmitriev May 13 '14 at 15:15
  • 1
    Both `192.168.0.6` and `10.0.2.0` are internal IP addresses. As they say on http://stackoverflow.com/a/14915309/1065835, the latter might not work in practice due to some issues. – Maksim Dmitriev Feb 01 '15 at 14:35
  • @JuanAcevedo how is it possible to create a socket while `VpnService` is active? I got thread freezed by `Socket` constructor. – Semyon Danilov Feb 24 '15 at 14:19
  • @SemyonDanilov, this worked for me. `DatagramChannel channel = DatagramChannel.open(); if (protect(channel.socket())) { channel.connect(socketAddress); }` – Maksim Dmitriev Mar 04 '15 at 18:50
  • @SemyonDanilov, by the way. Did you manage to forward packages? – Maksim Dmitriev Mar 04 '15 at 19:14
  • 1
    @MaksimDmitriev no, I can only capture them. I hope that your snippet will help me:) But `DatagramChannel` is for UDP, isn't it? So do I have to implement TCP over it? – Semyon Danilov Mar 04 '15 at 22:23
  • Hi guys, can anyone share the code to just read the request and forward it to its original destination. This seems lot of work.. – Vishal Maral Mar 09 '17 at 09:17
  • @vishalmaral check out the localVPN project – ospider Mar 26 '17 at 06:01
4

My configure method uses the wlan ip address for binder.addAddress() before >calling establish(). I am using a nexus 7 and I used "adb shell netcfg | grep >wlan0" to get the address:

wlan0 UP 192.168.0.6/24 0x00001043 10:bf:48:bf:5f:9d

I have wrote a simple script in python to show you netcfg graphically from adb. It is updating every second.

enter image description here https://github.com/ilanben/graphical_netcfg

Enjoy :)

Ilan.b
  • 583
  • 6
  • 19