編輯:關於Android編程
在使用android手機過程中,發現自己手機的流量經常被一些不小心下載的帶廣告的應用偷走了。自己在各個市場上搜了下,絕大部分防火牆都是需要手機root的。其中多數又是修改自著名的Droidwall,例如github上這個項目:https://github.com/skullone/android_firewall。其原理是在root後的機器使用root權限來配置iptable,利用linux這個自帶的防火牆實行流量控制。
但root本身破壞了android的安全機制,反而容易導致系統被惡意代碼利用。在googleplay上,終於找到一款noroot firewall。它利用了android的vpnservice技術,實現了免root的防火牆。很感興趣,於是在網上找了些資料,又自己實現了一個免root防火牆,這裡分享下自己的經驗。
Android系統支持配置VPN service。可以在設置->更多->VPN中添加。但系統只支持部分VPN協議。如果用戶想實現自己的VPN協議,那怎麼辦呢?為了支持這種擴展,Android提供了VpnService類。這是一個Service的子類。一旦start了該service,它會創建一個類似於應用代理的服務。任何應用外出的包,都會先發給該服務,然後該服務再轉發到網絡上。於是這個VpnService就成為需要使用網絡的應用和網絡服務器之間的一個中間人。這就提供了一個機會來控制外出流量。
那VpnService是如何跟client應用通信的呢?這裡利用了linux的TUN/TAP機制。TUN/TAP提供了一種虛擬的軟網絡接口(相對於物理網卡而言)。開發人員可以打開這個軟接口設備,獲取一個文件描述符(設備即文件),然後read就是從其中讀數據,write就是向其中寫數據。不同於物理網卡接口是把數據發送到網上或者接收自網上(其實是寫/讀到內核裡,然後網卡驅動發/收到網上),該虛擬網絡接口是讀/寫到應用空間。當建立其TUN/TAP後,client應用本來要發送到實際網卡的包,都會發送給這個虛擬的tun/tap,此時vpnservice就可以read到client發送的數據;vpnservice可以寫數據到tun/tap中,此時client如果recv或read,那就會讀取這個虛擬網口,獲取vpnservice寫的數據。這個我估計是通過修改路由表實現的,使得所有的網樓包都優先走tun/tap虛擬網口。
tun和tap的區別是前者這有IP頭和IP負載(即三層和以上),而tap包括數據鏈路層頭(二層和以上)。在Android中,實際是一個tun,讀寫的數據是IP原始報文。為了驗證vpnservice創建了一個虛擬網口,可以下載noroot filewall,然後通過adb shell netcfg。如果已經打開了vpnservice,則名字是tun0的接口,其狀態是UP。
關於tun/tap可以參考http://backreference.org/2010/03/26/tuntap-interface-tutorial/和https://www.kernel.org/doc/Documentation/networking/tuntap.txt。這是linux的機制,而不僅僅是android的。
那VpnService如果把收到的數據發送到網上呢?不同於普通linux允許用戶發送原始ip報文,Android安全機制只允許socket發送普通的tcp/udp報文。這就要求我們把通過tun收到的ip報文進行解包,獲取其tcp/udp payload(即應用層數據),然後通過send發送到服務器。在接收的時候,需要把服務器過來的recv的數據,添加tcp/udp頭和ip頭,然後write到tun中。對於tcp具體而言要復雜很多:
某種程度上,這裡要重現實現一個tcp協議棧,不過要比完整的簡單很多。
首先要start service,這一般在activity提供一個button實現。並不是直接startservice,而是要先打開一個需要用戶同意的activity,當用戶同意時,才能打開service。這是為了防止惡意vpnservice偷窺網絡數據。
參考這段代碼(在activity中):
public void enableVpnService() { Intent intent = MyVpnService.prepare(getApplicationContext()); if (intent != null) { startActivityForResult(intent, 0); } else { onActivityResult(0, RESULT_OK, null); } } @Override protected void onActivityResult(int request, int result, Intent data) { if (result == RESULT_OK) { Intent intent = new Intent(this, MyVpnService.class); startService(intent); } }
然後在service的onstartcommand中,需要創建tun。
為了方便VpnService創建tun,Android提供了一個Builder類VpnService.Builder。例如我們可以調用:
Builder builder = new Builder(); builder.addAddress("10.0.8.1", 32).addRoute("0.0.0.0", 0).setSession("Firewall") .setMtu(1500); ParcelFileDescriptor interface = builder.establish();
此interface就代表著tun。只可以對其進行讀寫。為了避免ANR,最好是創建一個thread來做。
對於真實跟外界交互的socket,如果你對其不做任何處理,那數據會再次發送到tun中,造成死循環。所以此時要調用VpnService.protect保證該socket發送的數據都直接發到物理網卡。
Android提供了一個ToyVpn的sample project,可以搜索看其代碼,也可以在eclipse創建android sample code來看。另外可以參考下這篇介紹文章。
MVP 在 Android 上的使用其實已經流行了有挺長的一段時間,包括我們公司,經過我們Android端小伙伴們的思考與才華 我們的產品也是采取的MVP模式。今天主要是
public class TvControlActivity extends Activity { private TvControlActivity tvCont
我們經常會看到有一些app的banner界面可以實現循環播放多個廣告圖片和手動滑動循環的效果。看到那樣的效果,相信大家都會想到ViewPager,但是ViewPager並
首先明確一下 android中的坐標系統 :屏幕的左上角是坐標系統原點(0,0),原點向右延伸是X軸正方向,原點向下延伸是Y軸正方向。 一、View的坐標 需要注意vie