Android裝置上非root的抓包實現方法(Tcpdump方法)_Android

來源:互聯網
上載者:User

通常我們在Android應用中執行某個命令時會使用“Runtime.getRuntime().exec("命令路徑")”這種方式,但是當我們執行抓包操作時,使用這條命令無論如何都不行,通過下面代碼列印結果發現,該命令一定要在root許可權下才能執行。

BufferedReader brW = new BufferedReader(new InputStreamReader(p.getErrorStream()));while((str = brW.readLine()) != null)Log.d("cwmp", "w:"+str);

但是我們的Android裝置(包括機頂盒、手機等)通常並沒有root過,apk的最高許可權也只是system許可權,這該怎麼解決?首先我們要知道,方法總比問題多,在Android裝置的/system/bin路徑下,我們會看到很多二進位檔案,這些二進位檔案可以獲得root許可權。因此,我們可以通過C語言來實現抓包功能,通過NDK把該C代碼交叉編譯成二進位檔案置於/system/bin路徑下,並賦予其root許可權,此時,這個二進位檔案就具備了抓包能力了。現在問題又來了,我們現在是想通過apk去調用這個抓包指定,抓包完成後又該怎麼通知apk呢?其實,Android可以通過socket使底層與framework層進行通訊,具體請參考Android中使用socket使底層和framework通訊的實現方法。

接下來我們將貼出關鍵實現代碼。

1、編寫socket服務端代碼fstiService.cpp,產生可執行指令碼fstiService

#define SOCKET_NAME "fstiService"#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "itv_assistance", __VA_ARGS__)#include <jni.h>#include <stdio.h>#include <stdlib.h>#include <errno.h>#include <string.h>#include <sys/types.h>#include <netinet/in.h>#include <sys/socket.h>#include <sys/wait.h>#include <sys/un.h>#include <cutils/sockets.h>#include <android/log.h>#include <unistd.h>#include <time.h>#include <sys/time.h>#include <pthread.h>pthread_t thread[2];char s_time[10]; //抓包時間子串char s_command[256]; //抓包指令子串//抓包指令:system("/system/bin/tcpdump -v -w /sdcard/te.pcap");//擷取進程tcpdump的進程號int getPid() {//for Linux C//FILE *fp = popen("ps -e | grep \'tcpdump\' | awk \'{print $1}\'", "r");//for Android(ARM)//FILE *fp = popen("ps | grep \'tcpdump\'", "r");FILE *fp = popen("ps | grep \'tcpdump\'", "r");char buff[1024] = { 0 };while (NULL != fgets(buff, sizeof(buff), fp));//取消分行符號(10)buff[strlen(buff) - 1] = '\0';pclose(fp);char dst[5] = { 0 };char *p = buff;char *q = dst;//每一行進程資訊的第二個字串為進程號while (*p != ' ')p++;while (*p == ' ')p++;while (*p != ' ')*(q++) = *(p++);*(q++) = '\0';return atoi(dst);}//截取子串(抓包時間(秒):抓包命令)void substring(char *time, char *command, char *src) {char *p = src;char *q = time;char *s = command;while (*p != '/')*(q++) = *(p++);*(q++) = '\0';//如果Tcpdump命令已添加環境變數,則添加下行代碼//否則刪除下一行代碼,client傳遞的參數格式必須為: num/tcpdump所在路徑p++;while (*p)*(s++) = *(p++);*(s++) = '\0';}//抓包線程void *thread1(void *arg) {system(s_command);}void *thread2(void *arg) {int i_time = atoi(s_time);int begin = time((time_t*) NULL);while (1) {if (time((time_t*) NULL) - begin < i_time) {//printf("目前時間(s):%ld\n", time((time_t*)NULL));continue;} else {int n = kill(getPid(), SIGKILL);LOGD("the kill process result is n=%d", n);break;}}return 0;}//建立子線程void thread_create() {int temp;memset(&thread, 0, sizeof(thread));if ((temp = pthread_create(&thread[0], NULL, thread1, NULL)) != 0)LOGD("create tcpdump thread failure");elseLOGD("create tcpdump thread success");if ((temp = pthread_create(&thread[1], NULL, thread2, NULL)) != 0)LOGD("create count thread failure");elseLOGD("create count thread success");}void thread_wait() {if (thread[0] != 0) {pthread_join(thread[0], NULL);LOGD("tcpdump thread has terminated");}if (thread[1] != 0) {//pthread_join(thread[1], NULL);printf("counter thread has terminated");}}/*** Native層Socket服務端*/int main() {int connect_number = 6;int fdListen = -1, new_fd = -1;int ret;struct sockaddr_un peeraddr;socklen_t socklen = sizeof(peeraddr);int numbytes;char buff[256];//這一步很關鍵,就是擷取init.rc中配置的名為 "fstiService" 的socket//擷取已綁定的socket,返回-1為錯誤情況fdListen = android_get_control_socket(SOCKET_NAME);if (fdListen < 0) {LOGD("failed to get socket '" SOCKET_NAME "' errno %d", errno);exit(-1);}/*** 方法說明:開始監聽(等待參數fdListen的socket串連,參數connect_number指定同時能處理的最大串連要求)* 如果串連數目達此上限則client端將收到ECONNREFUSED的錯誤。listen函數並未開始串連,只是設定* socket為listen模式,真正接收client端串連的是accept()。通常listen()會在socket()* bind()之後調用,接著才調用accept().* 傳回值:成功返回0,失敗返回-1,錯誤原因存在errno中*/ret = listen(fdListen, connect_number);LOGD("listen result %d", ret);if (ret < 0) {/*** perror(s)將一個函數發生錯誤的原因輸出到標準裝置(stderr)* 參數s所指的字串會先列印出,後面再加上錯誤原因字串*/perror("listen");exit(-1);}/*** 方法說明:accept(int s, struct sockaddr * addr, socklen_t * addrlen)用來接受參數s的socket串連。* socket必須先經bind()、listen()函數處理過,當有socket用戶端串連進來時會返回一個新的socket處理* 代碼,往後的資料傳送與讀取就是經由新的socket處理,而原來參數s的socket能繼續使用accept()來接受新的* 串連請求。連線成功時, 參數addr 所指的結構會被系統填入遠程主機的地址資料,參數addrlen為sockaddr的* 結構長度。* 傳回值:成功返回新的socket處理代碼,失敗返回-1,錯誤原因存在於errno中。*/new_fd = accept(fdListen, (struct sockaddr *) &peeraddr, &socklen);LOGD("accept_fd %d", new_fd);if (new_fd < 0) {LOGD("%d", errno);perror("accept error");exit(-1);}//迴圈等待Socket用戶端發來訊息while (1) {/*** 方法說明:recv(int s, void *buf, size_t len, unsigned int flags)用來接收* 用戶端socket傳來的資料,並把資料存到由參數buf指向的記憶體空間,參數len為可接收資料的最大長度。* 參數flags一般設0* 傳回值:失敗返回-1*/if ((numbytes = recv(new_fd, buff, sizeof(buff), 0)) == -1) {LOGD("%d", errno);perror("recv");continue;}LOGD("the parameter received from socket client is %s", buff);if(strcmp(buff, "exit") != 0){substring(s_time, s_command, buff);thread_create();thread_wait();}char result[10] = "successp";/*** 方法說明:send(int s, const void *msg, size_t len, unsigned int flags)* 參數s為已建立好串連的socket,參數msg指向欲發送的資料內容,參數len為資料長度,flags一般置0.* 傳回值:失敗返回-1,錯誤原因存在errno中*/int sendR = send(new_fd, result, strlen(result), 0);//apk退出後,buff中仍然緩衝之前的調用命令,此時會額外再執行一次抓包,固下面代用重寫buff中資料strcpy(buff, "exit");if (sendR == -1) {perror("send");close(new_fd);exit(0);}}close(new_fd);close(fdListen);return 0;}

2、配置init.rc檔案,添加如下配置

service fstiService /system/bin/fstiServicesocket fstiService stream 777 system systemclass main

此處配置了一個名為“fstiService”的服務,Android裝置開機會自動啟動並運行/system/bin/fstiService這個指令檔。服務端程式碼完成後,我們需要將其編譯成可執行指令碼fstiService,Android.mk內容如下:

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)#指定該模組在所有版本下都編譯LOCAL_MODULE_TAGS :=optionalLOCAL_MODULE := fstiServiceLOCAL_SRC_FILES := fstiService.cppLOCAL_LDLIBS := -llog#編譯成動態庫#include $(BUILD_SHARED_LIBRARY)#編譯成可執行檔include $(BUILD_EXECUTABLE)

3、Android用戶端代碼

public class SocketClient {private final String SOCKET_NAME = "fstiService";private LocalSocket client = null;private LocalSocketAddress address = null;private boolean isConnected = false;private int connectTime = 1;public SocketClient(){client = new LocalSocket();//A socket in the Android reserved namespace in /dev/socket. //Only the init process may create a socket hereaddress = new LocalSocketAddress(SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED);new ConnectSocketThread().start();}/*** 發送訊息* @param msg* @return 返回Socket服務端訊息回執*/public String sendMsg(String msg){if(!isConnected)return "Connect failure";try{BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));PrintWriter out = new PrintWriter(client.getOutputStream());out.println(msg);out.flush();return in.readLine();}catch(IOException e){e.printStackTrace();}return "Nothing Return";}/*** Socket連接線程,若串連失敗會嘗試重連3次* @author Administrator**/private class ConnectSocketThread extends Thread{@Overridepublic void run() {while(!isConnected && connectTime <= 3){try{sleep(1000);Log.d("itv_assistance", "Try to connect socket; ConnectTime: "+connectTime);client.connect(address);isConnected = true;}catch(Exception e){connectTime++;isConnected = false;Log.d("itv_assistance", "Connect Failure");}}}}/*** 關閉Socket*/public void closeSocket(){try{client.close();}catch(IOException e){e.printStackTrace();}}}

  至此,基於非root的Android裝置上的抓包實現方法就完成了,接下來就是編譯系統進行測試了,這步我沒有親自去做,而是把fstiService指令碼及init.rc設定檔的操作交給合作廠商來做了,apk是我們自己做的,經測試一切OK。

點擊下載源碼:http://xiazai.jb51.net/201611/yuanma/AndroidFstiService(jb51.net)

以上所述是小編給大家介紹的Android裝置上非root的抓包實現方法(Tcpdump方法),實現一個類比後台資料登入的效果,希望對大家有所協助,如果大家有任何疑問請給我留言,小編會及時回複大家的。在此也非常感謝大家對雲棲社區網站的支援!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.