Android的VpnService捕获数据包无法捕获数据包

34

我已经搜索了几个小时,但还是找不到答案。请帮帮我。

我想在Android中使用VpnService来获取网络数据包,就像应用程序tPacketCapture一样。

我开始使用谷歌的ToyVpn示例代码,并对其进行修改,以便我不将数据发送到服务器。然而,我不确定这是否正确。

我的配置方法在调用establish()之前使用wlan IP地址来进行binder.addAddress()。我正在使用Nexus 7,并使用“adb shell netcfg | grep wlan0”获取地址:

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

然后将其添加到我的方法中:

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

调用此方法后,我调用修改后的run方法,将一个字符串作为参数传递,这并不重要,因为我没有在任何地方使用它。
    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;
            }
        }
    }
}

当我执行adb logcat时,什么也没有发生。我这样做对吗?我觉得我漏掉了什么。
谢谢!
编辑:
从日志中,我看到以下行:
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

所以看起来正在连接。

完整源代码:

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

在run函数的while循环处设置一个断点,并告诉我它是否被触发。 - spartygw
没有,什么都没有。你已经让它工作了吗? - Juan Acevedo
我需要看到更多你的代码。你是否在某个地方启动了线程? - spartygw
你说在调用configure之后你调用了run,但是run的第一件事就是再次调用configure。看到完整的代码会有帮助。 - spartygw
我已经添加了完整的源代码和一些从adb logcat获取的日志。如果您发现我做错了什么,请告诉我。谢谢! - Juan Acevedo
2
我能够拦截数据包,但如何将这些数据包发送到实际目的地?你最终能做到吗? - thedarkpassenger
2个回答

27

好的,这并不容易,但我已经弄清楚如何捕获数据包了。由于我对网络不是非常熟悉(但是我的新工作需要我熟悉),所以我在正确设置一切方面遇到了困难。基本上,在 VpnService.builder 中设置正确的路由后,我成功地接收到了数据包。

所以:

builder.addAddress("192.168.0.6", 24); // was wrong, you need to put an internal IP (10.0.2.0 for example)
builder.addRoute("0.0.0.0", 0); // needs to be this.

你不需要通过builder.addDnsServer()建立一个DnsServer来使其工作。希望这对任何人有所帮助!


3
我能够转发数据包。如果我没记错的话,我只需要读取TCP头,并打开另一个套接字发送不带头的数据包到目标地址。当你收到回复后,必须在将其发送回输出流之前添加TCP头部。这适用于TCP协议(我只关心它)。另外,SSL需要进行SSL握手。 - Juan Acevedo
1
是的,您需要这样做才能将数据包转发到接口,从而到达外部。 - Juan Acevedo
1
抱歉,它不是开源的。这是生产软件,所以我也没有关于它的博客。 - Juan Acevedo
3
是的,TCP头必须被反转,就像您的浏览器(例如)从服务器接收响应一样。此外,还需要添加IP头。 - Juan Acevedo
1
你只需要分解带有数据包的TCP头部,将其从数据包中移除,并将其发送到相应的套接字。不确定这是否是你所要求的。 - Juan Acevedo
显示剩余13条评论

4
我的配置方法在调用establish()之前使用wlan ip地址来进行binder.addAddress()。我正在使用nexus 7并使用“adb shell netcfg | grep wlan0”获取地址: wlan0 UP 192.168.0.6/24 0x00001043 10:bf:48:bf:5f:9d
我编写了一个简单的Python脚本来从adb图形化地展示netcfg。 它每秒更新一次。

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

享受 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接