找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
热搜: 活动 交友 discuz
查看: 115|回复: 0

[移动安全系列] 02-DALVIK虚拟机启动过程

[复制链接]

2万

主题

128

回帖

10万

积分

管理员

积分
105860
发表于 2021-12-30 15:54:08 | 显示全部楼层 |阅读模式 IP:山东省 移动/数据上网公共出口

登录后更精彩...O(∩_∩)O...

您需要 登录 才可以下载或查看,没有账号?立即注册

×
本帖最后由 mind 于 2021-12-30 16:32 编辑


2.1 init.rc启动脚本
Android系统在启动时,第一个启动的进程就是init进程,也就是我们说的一号进程,我们通过下图的ps命令可以看到init进程号为1。接着,init进程根据读取/init.rc文件中的配置创建并启动app_process进程,也就是我们的Zygote进程,其启动参数见下表所示。
init.rc文件:
6.png
上面的文件参数:
service:表示Zygote进程是以服务的形式启动;
/system/bin/app_process表示它对应的文件位置;
后面四个选项就是Zygote进程的启动参数,其中 --start-system-server 表示Zygote进程启动完以后,需要马上运行SystemServer进程。
下面通过ps命令获取当前运行的进程信息
接着我们查看android源码/framework/base/cmds/app_process源码的入口函数main:

通过init.rc中Zygote的启动参数,我们知道arg参数中包含了” --zygote”和”--zygote --start-system-server”参数。所以,zygote和startSystemServer变量都为true。这表示Zygote进程启动完以后,会接着启动SystemServer进程。
从上面的代码,我们知道Zygote分两种模式启动,分别是Zygote模式非Zygote模式当处于Zygote模式时系统会调用com.android.internal.os.ZygoteInit启动Zygote进程和SystemServer进程 当处于非Zygote模式时系统会调用com.android.internal.os.RuntimeInit创建应用程序的进程。


2.2 创建Zygote进程
下面,我们来分析下Zygote进程的启动过程。我们看下AndroidRuntime类的start方法的关键代码:
位置:Android源码/base/core/jni/AndroidRuntime.cpp
该方法主要做了4个工作:
(1) 调用jni_invocation.Init()方法加载libdvm.so,并初始化JniInvocation类的成员
(2) 调用startVM()方法创建虚拟机,并初始化BootClassloader和加载/system/framework下的jar包
(3) 调用startReg()方法注册Android核心类的JNI方法
(4) 最后,通过JNI机制进入Java层的com.android.internal.os.ZygoteInit类的main方法,然后创建Zygote进程的Server端Socket,并启动SystemServer进程。
其关键流程如下图所示:
7.png

下面我们分别对上面4个工作进一步分析。
2.2.1 加载libdvm.so
参考[7]
位置:Android源码/libnativehelper/JniInvocation.cpp
我们看下JniInvocation类下的Init方法:


我们首先看下JniInvocation类的定义,一共有4个成员,分别是handle_,用于保存dlopen加载libdvm.so后返回的文件句柄,以及JNI_GetDefaultJavaVMInitArgs_、JNI_CreateJavaVM_和JNI_GetCreatedJavaVMs_。
接下来继续分析Init方法。Init方法首先读取”persist.sys.dalvik.vm.lib”系统属性,该属性值可能是libdvm.so或libart.so,我们可以通过getprop命令后去persist.sys.dalvik.vm.lib属性的值,或者直接查看/system/build.prop文件。如下图,在Android4.4.3下默认还是libdvm.so,但用户可以通过在Settings=>Developer Options=>Select runtime来选择要Dalvik还是ART,如下图所示。而在Android5.0版本以后,则默认是ART。这里我们默认分析Dalvik。接下来,则调用dlopen加载libdvm.so,然后找到GetDefaultJavaVMInitArgs、CreateJavaVM和GetCreatedJavaVMs三个接口并赋值给JniInvocation类对应的3个成员。
我们可以看到,Init方法其实就是初始化JniInvocation类里面的成员,和初始化Dalvik虚拟机环境的。

8.png

获取persist.sys.dalvik.vm.lib属性的值。
TODO:了解JNI的环境
2.2.2 创建Dalvik虚拟机和初始化BootClassloader
完成JNI环境的初始化工作后,接着AndroidRuntime::start函数()调用startVM()方法创建虚拟机。我们看下startVM的代码:
位置:Android源码/base/core/jni/AndroidRuntime.cpp
startVM方法先是读取系统属性并设置Dalvik虚拟机的各项参数,然后才调用JNI_CreateJavaVM方法真正去创建Dalvik虚拟机。这些参数我们也可以通过getprop命令去获取,如下图所示。
而JNI_CreateJavaVM方法正式livdvm.so中的函数。
位置:Android源码/dalvik/vm/jni.cpp。
JNI_CreateJavaVM首先初始化JavaVM和JNIEnv变量,完成JNI环境的初始化工作。接着调用创建虚拟机的核心方法dvmStartup()方法。我们看下dvmStartup()方法的源码:
位置:Android源码/dalvik/vm/Init.cpp
dvmStartup首先对参数进行解析,然后做了很多其它工作,这里我们重点分析dvmClassStartup()方法,该方法初始化boot class loader,并加载bootclasspath路径下所有的jar包,也就是/system/framework下所有的类。也就是说Zygote进程加载了framework下所有的jar包,而由于app进程的创建是通过克隆Zygote进程的,所以说app也同样加载了所有的framework下的包。下图是BOOTCLASSPATH路径:
我们分析下dvmClassStartup()方法:
位置:Android源码/dalvik/vm/oo/Class.cpp
gDvm全局变量的loadedClasses成员是哈希表,保存了所有加载到内存中的类。
dvmClassStartup方法调用processClassPath函数进行/system/framework目录下jar包的加载工作。该方法其实就是调用加载解析dex或jar包的方法,我们会在xxx章详细介绍。
2.2.3 framework层资源加载过程
TODO:分析processClassPath加载framework资源过程
3.2.4 通过反射进入ZygoteInit类的main方法
接下来我们分析com.android.internal.os.ZygoteInit的main方法。
位置:Android源码/framework/base/core/java/com/android/internal/os/ZygoteInit.java
该main方法主要做了3件事:
(1) 注册创建Zygote进程的Server端Socket
(2) 启动SystemServer进程
(3) 进入消息循环,监听来自AMS发送过来的创建app进程的请求
2.3 创建Zygote进程Server端监听服务
    Zygote进程创建Server端Socket关键流程如下图所示:
我们从ZygoteInit类main方法的registerZygoteSocket函数源码源码开始分析:
位置:Android源码/framework/base/core/java/com/android/internal/os/ZygoteInit.java
可以看到registerZygoteSocket方法通过LocalServerSocket创建Server端Socket,并保存在ZygoteInit类的静态成员变量sServerSocket中。
接着,判断传递过来的参数是否包含”start-system-server”。我们从上面源码分析知道,Zygote启动的配置参数里面是包含该参数的。然后就调用startSystemServer来启动SystemServer进程。最后调用runSelectLoop方法进入消息循环,等待处理来自AMS发送过来的请求。
我们分析下runSelectLoop方法接收到创建app请求后的过程是怎样的。
位置:Android源码/framework/base/core/java/com/android/internal/os/ZygoteInit.java
这里,peers.get(index)返回的是ZygoteConnection对象,runSelectLoop方法实际上调用了ZygoteConnection对象的runOnce方法做进一步的处理。
位置:Android源码/framework/base/core/java/com/android/internal/os/ZygoteConnection.java
这里真正创建应用程序进程的操作是Zygote.forkAndSpecialize方法,当创建成功以后,就会调用handleChildProc来启动新创建的应用程序的进程。详细的分析我们会在3.5节讲解。
2.4 创建SystemServer进程
在3.2.3节,main入口函数调用了startSystemServer来启动SystemServer进程。SystemServer在这个过程中做了一件很重要的事情:
  • 启动重要的系统服务,如ActivityManagerService、PackageManagerService、WindowManagerService等。
它创建SystemServer的关键流程如下图所示。
下面我们一个个分析这些关键函数。
位置:Android源码/framework/base/core/java/com/android/internal/os/ZygoteInit.java
首先创建参数数组,然后传递给forkSystemServer方法并创建SystemServer进程。创建成功后,接着调用handleSystemServerProcess方法来启动SystemServer进程。
SystemServer进程启动后,就会启动各种系统服务,如AMS、PMS、WMS等。
我们接着分析handleSystemServerProcess方法。
位置:Android源码/framework/base/core/java/com/android/internal/os/ZygoteInit.java
handleSystemServerProcess调用RuntimeInit类的zygoteInit方法。
位置:Android源码/framework/base/core/java/com/android/internal/os/RuntimeInit.java
这里zygoteInit调用了一个重要的方法applicationInit方法。
位置:Android源码/framework/base/core/java/com/android/internal/os/RuntimeInit.java
applicationInit调用invokeStaticMain方法通过反射的办法调用com.android.server.SystemServer类的main方法。
我们转到SystemServer类。
位置:Android源码/framework/base/services/java/com/android/server/SystemServer.java
main方法创建ServerThread对象并进入消息循环。
class ServerThread {    …public void initAndLoop() {    …    Looper.prepareMainLooper();    …    context = ActivityManagerService.main(factoryTest);    …    pm = PackageManagerService.main(context, installer,                    factoryTest != SystemServer.FACTORY_TEST_OFF,                    onlyCore);    …    contentService = ContentService.main(context,                    factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL);    …    wm = WindowManagerService.main(context, power, display, inputManager,                    wmHandler, factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,                    !firstBoot, onlyCore);    …    Looper.loop();}}

在initAndLoop方法里,通过调用各个系统服务类下的main方法创建完成关键系统服务的创建。然后调用Looper.loop()进入消息循环。
2.5 创建应用程序的进程
    结合2.3中的内容,创建应用程序进程的关系如下图所示。
ActivityManagerService和Zygote进程服务端通过LocalSocket和LocalServerSocket进行进程间通信,而两者通信的媒介正式/dev/socket/zygote文件。在AMS的Client端,startProcessLocked函数是发送请求的关键函数,而Zygote进程Server端的runSelectLoop函数则是接收AMS发送过来的请求的关键方法。
    回到3.1中android源码/framework/base/cmds/app_process的main函数入口,当处于非Zygote模式的时候创建的是应用程序的进程。然后通过3.3的分析我们知道,当我们点击桌面app的图标,启动一个应用程序的时候,AMS会调用startProcessLocked方法发送创建app进程的请求给Zygote进程Server端的Socket。
我们看下ActivityManagerService发送创建app进程请求的关键代码:
位置:Android源码/framework/base/services/java/com/android/server/am/ActivityManagerService.java
该方法会调用Process类的start静态方法实际上是通过创建LocalSocket跟3.2.4节中Zygote进程创建的Server端Socket进行通信,而Process.start传进来的参数正是要发送给Server端要创建的应用程序的相关参数信息。
根据3.2.4节我们知道,负责接收AMS发送过来请求的是ZygoteInit类的静态成员函数runSelectLoopMode方法,该方法最终会调用Zygote.forAndSpecialize()方法创建app进程,然后调用ZygoteConnection的handleChildProc函数成员来启动该app进程。
位置:Android源码/framework/base/core/java/com/android/internal/os/ZygoteConnection.java
这里,parsedArgs.runtimeInit为true,parsedArgs.invokeWith为null。然后调用RuntimeInit的静态成员函数zygoteInit,这一步跟创建SystemServer进程的时候是一样的,不一样的是传递过去的参数。根据3.3的分析我们知道,zygoteInit方法接着会调用applicaitonInit方法,该方法中调用了invokeStaticMain方法,同样通过反射的办法调用上面传递过来的参数,去实际调用android.app.ActivityThread类的main入口函数。
位置:Android源码/framework/base/core/java/com/android/internal/os/RuntimeInit.java
从ActivityThread的main方法入口开始就是一个应用程序的启动过程。我们会在第二章的APP的启动过程做详细分析。
TODO:分析传递的参数
2.6 总结
现在我们用下面一幅图来梳理总结整个过程,对Dalvik的启动过程有一个全局的认识。


首先,Android系统在启动了init一号进程后,init进程就开始创建Zygote进程。Zygote进程主要负责两个工作:
(1) 启动SystemServer进程
(2) 创建应用程序的进程
Zygote进程在创建和初始化过程中,会调用dlopen去加载libdvm.so文件,并调用Class.cpp文件下的dvmClassStartup方法初始化BootClassloader类加载器,并加载bootclasspath,也就是/system/framework目录下的所有jar包。而由于应用程序进程的创建是通过克隆Zygote进程获得,所以,app进程中同样也加载了libdvm和/system/framework下的jar包。
然后Zygote进程会调用registerZygoteSocket()方法来创建一个Server端的Socket,然后调用runSelectLoopMode方法进入循环等待。这个Server端Socket主要负责接收ActivityManagerService发送过来的创建应用程序进程的请求,而AMS是通过调用startProcessLocked方法来发送请求的。当Zygote进程的Server端接收到来自AMS创建应用程序进程的请求后,就会调用forkAndSpecialize()方法创建。

参考:
[1] 《深入理解Android》(二)Java虚拟机Dalvik  http://www.infoq.com/cn/articles/android-in-depth-dalvik
[3]《Android系统源代码情景分析》第11、12章 Zygote和System进程的启动过程 和Android应用程序进程的启动过程http://blog.csdn.net/luoshengyang/article/details/6768304
[4]《Android Dalvivk虚拟机结构及机制剖析》第6章 Dalvik虚拟机执行流程详解
[5] Dalvik虚拟机的启动过程分析 http://blog.csdn.net/luoshengyang/article/details/8885792
[6] 理解Android虚拟机体系结构 http://android.jobbole.com/82413/
[7] Android虚拟机运行时无缝替换Dalvik虚拟机过程分析 http://blog.csdn.net/luoshengyang/article/details/18006645
[8] adb获取Android系统属性数据来源 http://blog.csdn.net/haixia_12/article/details/40857721
[9] Android应用程序进程启动过程的源代码分析 http://blog.csdn.net/luoshengyang/article/details/6747696
[10] Android进程通信套接字LocalSocket https://my.oschina.net/ososchina/blog/621761

相关文章...



文章导航前一页上一篇:01-APP的安装过程






回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|IOTsec-Zone|在线工具|CTF WiKi|CTF平台汇总|CTF show|ctfhub|棱角安全|rutracker|攻防世界|php手册|peiqi文库|CyberChef|猫捉鱼铃|手机版|小黑屋|cn-sec|分享屋 ( 鲁ICP备2021028754号 )

GMT+8, 2024-5-18 17:01

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表