1. 程式人生 > >android應用建立子程序的方法探究

android應用建立子程序的方法探究

android應用建立子程序的方法探究

1:前言

  android應用開發,當前大多數軟體還是停留在java層進行開發,然而android真正可玩的地方,偏偏是本地語言cc++,藉助JNI這個橋樑,可以使得java呼叫到本地函式,本文則從建立子程序,來進行探究android神祕的面紗。

2:首先我們先來看一個linux下的一個建立程序的簡單程式。(由於我們是編寫的手機可執行的elf檔案,因此我們需要交叉編譯環境,此工具可在android官網下載ndk開發包,按照文件進行配置NDK環境變數,此時便可以使用ndk開發包進行手機可執行程式的編寫)。

  下載好的ndk開發包,cmd進入samples\hello-jni

裡面,進行%NDK%/ndk-build,完畢之後會在libs/armeabi下面生成*.so檔案,此時便可以確定ndk配置ok

  下面我們來進行修改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

書籍或者官方文件查閱,fork.c可以參閱linux系統程式設計書籍來進行參閱。

hello-jni目錄下,執行%NDK%/ndk-build重新編譯一次,此時會在libs\armeabi下生成出一個fork程式,下面我們將此程式push進手機進行執行,檢視效果。(首先手機需要root,關於root之後的許可權管理機制,可參照Android軟體安全與逆向分析這本書進行檢視,需要下載susuperuser.apk原始碼 su-binary-master Superuser-master)

進入cmd,執行adb shell ; su ; 使用 mount -o rw,remount -t yaffs2 /dev/block/mtdblock3 /system  

掛載系統為可讀寫 ,退出重新啟動一個cmd,輸入

adb push XXX\fork  /system/bin  fork複製到system/bin目錄下,XXXfork目錄,然後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

其中子程序的pid22379,父程序為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程式來進行編寫建立子程序的程式碼了。

3java層編寫建立子程序

  通過檢視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。然而ptsptm以雙向管道的方式建立,因此pts的輸出便是ptm的輸入。Ptm的輸出便是pts的輸入。

那麼我們在父程序這邊會拿到ptmfd,這時我們向ptm裡面寫入內容,則會在pts的輸入流裡面得到,此時我們便可以藉助此機制來實現手機終端apk。使用者在介面裡面輸入ls -al,按下回車時,我們將這些內容直接通過寫入ptm來達到輸入到pts裡面,如果pts所屬的子程序是shell程式的話,此時shell會去接住ls -al字串,然後執行,輸出結果會走向pts的輸出,而我們會在ptm的輸入獲取到,此時將結果顯示出來,便完成了手機終端的功能實現。

7:結尾

  匆忙寫成,很多問題未加詳細說明,但是大多都是可以直接百度出來的。ok,收尾了.