DVM执行 java 程序的工具

  1. jvm 执行字节码原理:java 程序运行时,是由一个 java 虚拟机来解释 java 字节码的,它将这些字节码翻译成本地 CPU 的指令码,然后执行。
  2. Android 执行指令码原理:Android 应用程序打包成 dex 包后,通过系统程序 dalvikvm 创建一个虚拟机来执行参数中指定的 java 类。相对于 java 而言,负责解释并执行的就是一个虚拟机,而对于 Linux 而言,这就是一个普通的进程,它与一个只有一行代码的 Hello World 的可执行程序无本质区别。
  3. Android 启动一个虚拟机的方法跟启动任何一个可执行程序的方法是相同的,在命令行下输入可执行程序的名称,并在参数中指定要执行的 java 类即可。

dalvikvm

dalvikvm 作用:创建一个虚拟机并执行指定的 java 类

dalvikvm 命令:dalvikvm -cp 文件路径 权限类名
如:
dalvikvm -cp /data/app/Demo.dex Demo

实例演示:

  • JVM 执行 java 程序的过程:

    1. 编译成二进制文件:javac Demo.java
    2. 翻译成机器码并执行:java Demo
        /**
         * 2019-05-18
         * java code for simple Demo
         */
    public class Demo {
        public static void main(String[] args) {
            System.out.println("Demo:: Hello Android");
        }
    }
    
  • DVM 执行 java 程序过程:对于 Android 而言,可执行代码需要转化成可执行的 dex 优化文件才能被系统加载执行。核心思想是将字节码文件转 dex 后,由 dvm 翻译执行。

    1. 打包为 jar 包: jar cvf Demo.jar Demo.class
    2. jar 转 dex:dx --dex --output= Demo1.jar Demo.jar
    3. 开一个 Android 模拟器,使用 Android 原生或 Genermotion 模拟器即可。
    4. 挂载设备:adb root;adb remount
    5. 安装程序:adb push Demo1.jar /data/app/Demo.dex
    6. 执行程序:adb shell dalvikvm -cp /data/app/Demo.dex Demo
  • DVM 执行 java 程序的过程中可能遇到的错误:

    • java sdk 和 dx 工具的要求版本不一致时,解决这种转换问题一般发生在第二步,比如我本地的 java 版本是 1.8.0_101-b13,可以使用 27.0.0 中的 build-tools 下的 dx 工具。
        PARSE ERROR:
        unsupported class file version 52.0
        ...while parsing Demo.class
        1 error; aborting
    
    • push 了非 dex 优化文件到系统,执行 dalvikvm 命令,出现如下错误:
        Unable to locate class 'Demo' java.lang.ClassNotFoundException: Didn't find class "Demo" on path: DexPathList[[zip file "/data/app/Demo.jar"],nativeLibraryDirectories=[/system/lib, /vendor/lib, /system/lib, /vendor/lib]]
    at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:380)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
    Suppressed: java.io.IOException: No original dex files found for dex location /data/app/Demo.jar
        at dalvik.system.DexFile.openDexFileNative(Native Method)
        at dalvik.system.DexFile.openDexFile(DexFile.java:367)
        at dalvik.system.DexFile.<init>(DexFile.java:112)
        at dalvik.system.DexFile.<init>(DexFile.java:77)
        at dalvik.system.DexPathList.loadDexFile(DexPathList.java:359)
        at dalvik.system.DexPathList.makeElements(DexPathList.java:323)
        at dalvik.system.DexPathList.makeDexElements(DexPathList.java:263)
        at dalvik.system.DexPathList.<init>(DexPathList.java:126)
        at dalvik.system.BaseDexClassLoader.<init>(BaseDexClassLoader.java:48)
        at dalvik.system.PathClassLoader.<init>(PathClassLoader.java:64)
        at java.lang.ClassLoader.createSystemClassLoader(ClassLoader.java:224)
        at java.lang.ClassLoader.-wrap0(ClassLoader.java)
        at java.lang.ClassLoader$SystemClassLoader.<clinit>(ClassLoader.java:183)
        at java.lang.ClassLoader.getSystemClassLoader(ClassLoader.java:1097)
        Exception in thread "main" java.lang.ClassNotFoundException: Didn't find class "Demo" on path: DexPathList[[zip file "/data/app/Demo.jar"],nativeLibraryDirectories=[/system/lib, /vendor/lib, /system/lib, /vendor/lib]]
    at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:380)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
    Suppressed: java.io.IOException: No original dex files found for dex location /data/app/Demo.jar
        at dalvik.system.DexFile.openDexFileNative(Native Method)
        at dalvik.system.DexFile.openDexFile(DexFile.java:367)
        at dalvik.system.DexFile.<init>(DexFile.java:112)
        at dalvik.system.DexFile.<init>(DexFile.java:77)
        at dalvik.system.DexPathList.loadDexFile(DexPathList.java:359)
        at dalvik.system.DexPathList.makeElements(DexPathList.java:323)
        at dalvik.system.DexPathList.makeDexElements(DexPathList.java:263)
        at dalvik.system.DexPathList.<init>(DexPathList.java:126)
        at dalvik.system.BaseDexClassLoader.<init>(BaseDexClassLoader.java:48)
        at dalvik.system.PathClassLoader.<init>(PathClassLoader.java:64)
        at java.lang.ClassLoader.createSystemClassLoader(ClassLoader.java:224)
        at java.lang.ClassLoader.-wrap0(ClassLoader.java)
        at java.lang.ClassLoader$SystemClassLoader.<clinit>(ClassLoader.java:183)
        at java.lang.ClassLoader.getSystemClassLoader(ClassLoader.java:1097)
    
    
  • 这种错误很常见,和之前应用程序加载类,加载函数的错误类似,这种错误如果在 APK 程序内发生。
    出现这种情况,一般是应用程序未签名,引用的函数在系统中并不存在。而在 java 程序内,则是 java 程序不正确导致,需要确认此 jar 是否经过 dex 转化或当前虚拟机版本要求的系统优化。


dvz

dvz 作用:从 zygote 进程中孵化出一个新的进程,新的进程也是一个 Dalvik 虚拟机。该进程与 dalvikvm 启动的虚拟机相比,区别在于该进程中已经预装了 Framework 中的大部分类和资源。

dvz 命令:dvz -classpath 文件路径 权限类名
如:
dvz -classpath /system/app/Demo.apk com.my.demo.DemoActivity

关于 Demo.apk,其代码如下:

package com.my.demo;

import android.app.*;

/**
 * 2019-05-18
 * java code for dvz Demo
 */
public class DemoActivity extends Activity {
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo);
    }
    
    public static void main(String[] args) {
        System.out.println("DemoActivity::Hello Android:: DemoActivity");
    }
}

上面的 main 函数并不是该程序的入口,只是用来作为开发调试。app 的主入口在 ActivityThread 中。

遗憾的是在系统中暂无 dvz 工具,暂时也未找到 dvz 的相关资料


app_process

app_process 作用:Framework 启动过程中,加载 ZygoteInit.java 和 SystemServer.java,也可以用来调试 java 程序

app_process 命令:app_process -Djava.class.path=文件路径 路径 权限类名

如:app_process -Djava.class.path=/data/app/Demo.dex /data/app Demo

  • app_process 执行 java 程序的过程:

    1. 创建 Demo.java
    2. 执行 javac Demo.java
    3. 打包 jar 文件:java cvf Demo.jar Demo.class
    4. jar 转 dex:dx --dex --output=Demo1.jar Demo.jar
    5. 挂载设备:adb root;adb remount
    6. 将文件 push 到模拟器:adb push Demo1.jar /data/app/Demo.dex
    7. 执行命令:adb shell app_process -Djava.class.path=/data/app/Demo.dex /data/app Demo
  • app_process 命令参数:app_process [java-options] cmd-dir start-class-name [options]


由 app_process 想到的?

  • 系统命令封装:am, pm, wm, svc ...
  • 自定义命令封装:my_tool 适配不同平台工具差异。
  • 深度定制 zygoteframework,原因是:app_process 是初始化 zygote 的入口,属于安卓系统和Framework 启动的一个关键点。
  • 优化开机流程,减少开机过程耗时。

补充:

  • 补充1:
    • 执行 app_process,每次运行 Java 程序时,系统都会给其分配一个pid,并且进程名是app_process,通过追踪 pidppid,可以发现 fork 进程的大致流程为:/init --> /sbin/adbd -–> /system/bin/sh --> app_process,不同的安卓版本,会有差异,dalvikart 也不完全相同。例如在 Android 9.0 的设备上,流程则变为 /init --> zygote -->当前程序
  • 补充2:
    • app_process 启动的 Java 程序拥有shell级别的权限,所以像系统中的 am, pm, wm, svc 等程序才能执行。在 /system/bin 目录下执行:cat /system/bin/am 可以看到它其实并非一个二进制文件,而是一个可执行的 shell 脚本,由 app_process 执行了 com.android.commands.am.Am "$@",其执行原理是在当前窗口,将参数传递给 jvm,找到 Am 类的主函数,进行执行。

      console:/ # cat /system/bin/am
      #!/system/bin/sh
      
      if [ "$1" != "instrument" ] ; then
          cmd activity "$@"
      else
          base=/system
          export CLASSPATH=$base/framework/am.jar
          exec app_process $base/bin com.android.commands.am.Am "$@"
      fi
      console:/ #
      
    • 在 java 程序中执行 shellcmd 是可行的,分享一个可执行的案例,使用命令为:pm -l

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * 2019-05-18
 * java code for shell command demo, pm -l 
 */
public class DemoShellCmd {
    public static void main(String[] args) {
        System.out.println("");
        System.out.println("");
        System.out.println("DemoShellCmd::PMS 程序开始执行...");
        String cmd = "pm -l";
        try {
            Process exec = Runtime.getRuntime().exec(cmd);
            BufferedReader br = new BufferedReader(new InputStreamReader(exec.getInputStream()));
            String readLine=br.readLine();
            while(readLine!=null){
                System.out.println(readLine);
                readLine=br.readLine();
            }
            br.close();
            exec.destroy();
            exec=null;
            System.out.println("");
            System.out.println("");
            System.out.println("DemoShellCmd::PMS 程序执行完成");
        } catch (IOException e) {
            System.out.println("DemoShellCmd::PMS 程序执行异常");
            e.printStackTrace();
        }
    }
}
  • 补充3:
    • 进程状态如何查看: cat /proc/$pid/status 即可,其中 /proc 下遍布着系统运行过程中,所有进程 id 的信息,此文件夹下可以浏览你关注的进行状态,内存信息,线程信息...

小结:

  1. 如上总结了 DVM 执行 java 程序的三种工具,也是谷歌早期调试 java 程序的重要工具。其中 dvz 工具暂未发现系统中有集成,也少有此工具的相关介绍。重要的是要学会使用 dalvikvimapp_process 工具,并对 app_process 的触发和流程做拓展,在源码中理解它的工作过程。

  2. 欢迎大家下方评论区留言,拍砖,交流。

  3. qq 邮箱: 1281641968@qq.com

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345