近來反編譯看一些android應用,特別是涉及到底層的功能性的應用,比如遊戲加速,修改內存,掛機腳本神馬的,發現裏面的通信機制無一例外的都是使用的socket,基本上已經成爲這類應用的一種標配了。
因爲這類應用有以下的幾個共同點:
1 需要android 手機的root權限,畢竟要修改一些比較底層的東西,沒有root權限有時候木有辦法修改啊
2 有自己的so,同時比較重要的或者比較吃力的活都編譯成一個可執行的elf文件,然後讓apk應用給這個elf文件給啓動起來。關於這個elf文件放置的位置也有幾種選擇。
情況1:直接放在應用包下面的lib目錄下,然後給這個可執行文件賦予一個可執行的權限。
情況2:放在assets文件夾下面,在程序啓動的時候拷貝到files文件夾下面,賦予可執行的權限後,然後執行
情況3:也是放在assets文件夾下面,只不過安全性高一點,一般會做一個簡單的異或或者移位加密,然後修改以下擴展名,拷貝過去的時候減密一下。再執行,有時候執行了以後會把真正的elf文件再給刪除了。
3 可執行的so文件和java應用程序之間的通信使用socket,因爲畢竟可執行的so纔是真正幹活的主力,而java應用程序要給so發送指令,而so也要給java應用程序反饋一些有用的數據供上層顯示給用戶。關於socket通信的方式也有下面的幾種情況
情況1: 可執行的so作爲server端,java應用程序給其發送指令的時候主動去鏈接可執行so,發送數據,有時候so也會返回一些數據,一般這種情況so就直接把活給幹了,比如遊戲加減速。
情況2:可執行的so文件作爲客戶端,同時使用長鏈接的方式,這種情況就需要java應用程序作爲server端了,這裏的稍微巧妙一點的是不採用固定的socket的端口號,而是java應用程序在創建socket的時候使用系統自動分配的端口號。不過需要通過java應用程序在啓動可執行so的時候將端口號傳遞給so文件。
原理基本上就這麼多了,下面的是一個例子,這個例子涉及到的編程技術主要是java應用程序自己創建socket的客戶端和服務器端,然後進行通信,同時也啓動了一個so的可執行文件,通過進程之間的數據流,向可執行的so發送數據,可執行的so將接收到的數據通過log給打印出來。
進入demo例子,
點擊啓動 socket server
這個時候log會打印出來
11-04 15:56:28.341:V/cheatecore-server(5774): server port = 44034
這樣的數據,
由於serverSocket = new ServerSocket(m_port);
其中 m_port 爲0,所以端口號是系統臨時分配的。
然後socketserver 就開始等待有客戶端的socket 來鏈接
m_socket = serverSocket.accept();
其中 accept 是阻塞函數,會將這個線程阻塞掉的。這個時候
通過 adb shell 進入android手機,使用 netstat –an | busybox grep 44034 查看,發現是這樣的現象
顯然這個端口是 listen 狀態
然後點擊設置客戶端socket端口號和啓動客戶端socket
11-04 16:03:11.067: V/cheatecore(5774): 獲取服務器端的端口號:44034
11-04 16:03:15.575:V/cheatecore-client(5774): client socket begin
11-04 16:03:15.575:V/cheatecore-server(5774): a client socket come here!!!
這個時候出現了a client socket comehere!!!
這條log再次執行剛纔的
netstat –an | busybox grep 44034
發現已經變成如下的結果了
已經有隨機分配的客戶端端口號跟server端建立了鏈接了
然後再點擊獲取數據流
reader = new BufferedReader(inputreader);
serveroutwriter = new PrintWriter(newBufferedWriter(new OutputStreamWriter(sk.getOutputStream())),true);
主要獲取socket的輸入流和輸出流,也就是客戶端和服務端的tcp鏈接已經建立起來了,剩下的就是通過流來接收和發送數據了
接着點擊
啓動服務器socket 接收數據線程
String sReader = reader.readLine();
這條語句就在等着客戶端的socket數據過來呢,其中readLine是一個阻塞函數,熟悉c語言的同學,其實這個就是socket 中的 recv函數,阻塞調用了。
因爲tcp是全雙工的,並且當前狀態是長鏈接,所以服務端可以向客戶端發送數據,客戶端也可以向服務端發送數據,下一個按鈕
服務器端socket發送數據
11-04 16:11:19.810: V/cheatecore-client(5774):客戶端接收到服務端的數據:server data
11-04 16:11:19.810:V/cheatecore-server(5774): 服務端接收到客戶端的數據:clientdata
這個時候出現的log,客戶端在收到服務器的數據以後,也會向服務器端再返回數據,這樣就完成了一次交互,但是其實這個時候socket並沒有斷掉,因爲是長鏈接,可以進行多次發送
最後結束socket
關閉了socket,這個時候再用 netstat –an | busybox grep 44034
這個指令來進行查詢,發現如下圖:
剛纔建立的socket已經釋放了,再過一會查詢wait超時後也會將端口釋放掉了
測試向啓動的進程發送數據
11-04 16:15:11.278: E/cheatecore(6163):input target main function!!!!
11-04 16:15:11.278: E/cheatecore(6163):scanf input content is 44034
主要過程是首先啓動
/data/data/com.example.sockettest/files/target
這個程序
testprocee =Runtime.getRuntime().exec(cmd);
PrintWriterpoutwriter = newPrintWriter(newBufferedWriter(newOutputStreamWriter(testprocee.getOutputStream())),true);
poutwriter.println(test);
然後發送數據,完成了這次進程間數據的交互,其實這也是進程之間通信的一種方式,和利用socket完成進程之間的通信並列的。
例子見下面