Android VpnService to capture packets won't capture packets
Asked Answered
P

2

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();
    }
}
Playful answered 20/7, 2013 at 20:29 Comment(6)
Set a breakpoint at the while loop in run and tell me if it gets hit.Huda
Nope, nothing at all. Have you gotten this to work?Playful
Well I need to see more of your code. Are you starting the thread somewhere?Huda
You said after calling configure you call run, but the first thing run does is call configure again. Would help to see the complete code.Huda
I have added the full source code and some logs I get from adb logcat. Please let me know if you see something I'm doing wrong. Thanks!Playful
I am able to intercepts the packets, but how to send those packets to the actual destination? Were you able to do it finally ?Vespid
P
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!

Playful answered 23/7, 2013 at 20:30 Comment(18)
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...Oneman
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.Playful
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...Oneman
Yes, you need to in order to forward the packets to the interface, thus the outside.Playful
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!Macrobiotic
sorry it is not open source. This is production software so I also do not have a blog about this.Playful
@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?De
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.Playful
@JuanAcevedo, how did you make all the apps write headerless data to another socket?Rejoinder
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.Playful
@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?Rejoinder
Both 192.168.0.6 and 10.0.2.0 are internal IP addresses. As they say on https://mcmap.net/q/337438/-ip-address-ending-with-zero-closed, the latter might not work in practice due to some issues.Rejoinder
@JuanAcevedo how is it possible to create a socket while VpnService is active? I got thread freezed by Socket constructor.Ber
@SemyonDanilov, this worked for me. DatagramChannel channel = DatagramChannel.open(); if (protect(channel.socket())) { channel.connect(socketAddress); }Rejoinder
@SemyonDanilov, by the way. Did you manage to forward packages?Rejoinder
@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?Ber
Hi guys, can anyone share the code to just read the request and forward it to its original destination. This seems lot of work..Sarmatia
@vishalmaral check out the localVPN projectRodie
D
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 :)

Dragging answered 30/9, 2015 at 12:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.