android应用创建子进程的方法探究

时间:2021-02-16 08:54:46

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应用创建子进程的方法探究

关于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,内容为:

android应用创建子进程的方法探究

然后我们在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:虚拟终端

android应用创建子进程的方法探究

虚拟终端,实质上也是创建子进程,然后保持和子进程进行通讯,这里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,收尾了.