android應用建立子程序的方法探究
android應用建立子程序的方法探究
1:前言
android應用開發,當前大多數軟體還是停留在java層進行開發,然而android真正可玩的地方,偏偏是本地語言c與c++,藉助JNI這個橋樑,可以使得java呼叫到本地函式,本文則從建立子程序,來進行探究android神祕的面紗。
2:首先我們先來看一個linux下的一個建立程序的簡單程式。(由於我們是編寫的手機可執行的elf檔案,因此我們需要交叉編譯環境,此工具可在android官網下載ndk開發包,按照文件進行配置NDK環境變數,此時便可以使用ndk開發包進行手機可執行程式的編寫)。
下載好的ndk開發包,cmd進入samples\hello-jni
下面我們來進行修改hello-jni\jni裡面的檔案,刪除掉此目錄裡面的所有檔案,增加Android.mk 和fork.c兩個檔案,具體內容為:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := fork
LOCAL_SRC_FILES := fork.c
include $(BUILD_EXECUTABLE)
關於Android.mk 檔案的配置,可以參照Pro Android C++ with the NDK
在hello-jni目錄下,執行%NDK%/ndk-build重新編譯一次,此時會在libs\armeabi下生成出一個fork程式,下面我們將此程式push進手機進行執行,檢視效果。(首先手機需要root,關於root之後的許可權管理機制,可參照Android軟體安全與逆向分析這本書進行檢視,需要下載su與superuser.apk原始碼 su-binary-master 與Superuser-master)
進入cmd,執行adb shell ; su ; 使用 mount -o rw,remount -t yaffs2 /dev/block/mtdblock3 /system
adb push XXX\fork /system/bin 將fork複製到system/bin目錄下,XXX為fork目錄,然後adb shell;su;cd /system/bin;chmod 777 fork;./fork
此時我們可以看到輸出資訊為:
In child process
child pid = 22379
child ppid = 22378
in parent
parent pid = 22378
parent ppid = 22301
child process exited with status 0
其中子程序的pid為22379,父程序為22378,父程序的父程序為22301,我們來看看這個程序是誰。首先手機上有可能沒有ps命令,因此需要安裝下busybox這個工具
adb shell裡面輸入 ps |grep 22301
可以看到
root 22301 22294 1492 776 c0046b20 4001fe54 S sh
root 22436 22301 1436 656 00000000 400c5438 R ps
root 22437 22301 1088 228 c0104e88 000084ec S grep
這裡可以看到是shell程序。
以上操作完成了編譯出一個可以在手機端執行的一個elf執行程式。完全是使用c進行編寫的,那麼接下來我們便需要使用java程式來進行編寫建立子程序的程式碼了。
3:java層編寫建立子程序
通過檢視jdk文件,可以看到java層有兩個類進行了建立子程序的方法。
一個為Runtime.getRuntime.exec(String command,.....)Runtime.java ,
一個為new ProcessBuilder("XXX").start() ProcessBuilder.java,由於我們需要進行跟蹤過程,因此需要下載jdk的原始碼,搜尋openjdk可以找到一個開源的jdk原始碼包。通過檢視原始碼,可以看到Runtime裡面的exec函式最終還是呼叫到了ProcessBuilder這邊來,因此實質中建立子程序的方法都在ProcessBuilder這個類裡面。繼續跟蹤start方法,我們找到了ProcessImpl.java 裡面,這個裡面呼叫關係為start()--ProcessImpl()-->create(),現在我們找到了關鍵的建立子程序的函數了,此函式宣告為static native,因此需要在c或者c++裡面查詢,以及JNI規則,我們在ProcessImpl_md.c裡面看到了實現函式Java_java_lang_ProcessImpl_create ,這個函式裡面直接呼叫了CreateProcessW來完成了建立子程序的動作,這個函式為window下開發提供出來的建立子程序的方法。Linux下應該會是fork函式,如上我們分析了建立過程,是否已經發現實質java層通過虛擬機器,直接呼叫了系統本身的fork函式來完成了建立過程,下面我們來進行測試驗證下。
開啟eclipse,新建一個android專案,名字命名為subprocess,其他使用預設,一路下去,會出現一個MainActivity.java 預設的主activity.建立一個java原始檔subporcess.java,內容為:
然後我們在MainActivity .java裡面的onCreate裡面,加入
subprocess.Test();
測試ProcessBuilderTest方法,在log裡面檢視subprocess過濾出來的資訊,則可以看到程序資訊,其中打印出來的父程序的ppid,便是你的apk應用程序id,具體驗證可以在cmd,輸入adb shell;su;ps;來進行檢視。
4:關注一下Process.java,這個原始碼需要在android原始碼裡面找,包名為android.os。從android原始碼裡面Process.java,我們可以看到很多諸如myPid();myUid();sendSignal();這些我們看到都有native宣告,那麼可以得知為原生代碼,通過查詢,在alps/frameworks/base/core/jni裡面找到android_util_Process.cpp,裡面android_os_Process_myPid即為mypid()的本地實現,此函式通過呼叫linux核心提供的獲取自己程序id的函式getpid()進行處理。
5:關注一下System.java, 在openjdk原始碼裡面,這個檔案裡面比如常用的exit();
Load();loadLibrary();通過檢視原始碼可以得知此程式碼會直接呼叫到runtime那邊。
6:虛擬終端
虛擬終端,實質上也是建立子程序,然後保持和子程序進行通訊,這裡linux核心提供了一個專門服務於虛擬終端的機制,主要通過如下幾個函式實現:
open("/dev/ptmx", O_RDWR) 建立一個主虛擬終端,返回fd。通過
devname = (char*) ptsname(ptm)) 拿到從虛擬終端,隨後建立子程序,在子程序裡面開啟從虛擬終端,返回一個fd。接著使用dup2將自己的輸入輸出和錯誤流同時指向到pts裡面,由於系統/dev/ptmx開啟提供的特殊機制,此時子程序的輸入輸出和錯誤流都指向pts。然而pts和ptm以雙向管道的方式建立,因此pts的輸出便是ptm的輸入。Ptm的輸出便是pts的輸入。
那麼我們在父程序這邊會拿到ptm的fd,這時我們向ptm裡面寫入內容,則會在pts的輸入流裡面得到,此時我們便可以藉助此機制來實現手機終端apk。使用者在介面裡面輸入ls -al,按下回車時,我們將這些內容直接通過寫入ptm來達到輸入到pts裡面,如果pts所屬的子程序是shell程式的話,此時shell會去接住ls -al字串,然後執行,輸出結果會走向pts的輸出,而我們會在ptm的輸入獲取到,此時將結果顯示出來,便完成了手機終端的功能實現。
7:結尾匆忙寫成,很多問題未加詳細說明,但是大多都是可以直接百度出來的。ok,收尾了.