android应用创建子进程的方法探究
1:前言
android应用开发,当前大多数软件还是停留在java层进行开发,然而android真正可玩的地方,偏偏是本地语言c与c++,借助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软件安全与逆向分析这本书进行查看,需要下载su与superuser.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目录下,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,收尾了.