【Android学习】DVM——apk生成、安装、启动、反汇编

2023-12-19 14:40

本文主要是介绍【Android学习】DVM——apk生成、安装、启动、反汇编,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

0,参考资料

《Android框架揭秘》 [韩]金泰延 宋亨周 朴知勋 李白 林起永 著 武传海 译

1,Dalvik

1)概念

Dalvik是Google公司自己设计用于Android平台的Java虚拟机。
Dalvik虚拟机是Google等厂商合作开发的Android移动设备平台的核心组成部分之一,它可以支持已转换为.dex(即Dalvik Executable)格式的Java应用程序的运行,.dex格式是专为Dalvik应用设计的一种压缩格式,适合内存和处理器速度有限的系统。

2)与Linux进程关系

Dalvik经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个Dalvik应用作为独立的Linux进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。

每个 Android 应用程序都运行在单独的 Dalvik 虚拟机内(即每个 Android 应用程序对用一条 Dalvik VM实例、对应一个Linux进程), Dalvik 专门针对同时高效地运行多个虚拟机进行优化,因此 Android 系统以方便的实现对应用程序进行隔离。

Android 运行时由两部分组成:

①Android 核心库集

提供了 Java 语言核心库所能使用的绝大部分功能。

②Dalvik 虚拟机

DVM负责运行 Android 应用程序。

2)Dalvik字节码

Android平台不用JVM来执行代码,而是把应用编译成Dalvik字节码,使用Dalvik虚拟机来执行。最终应用只包含Dalvik字节码。

过程:
Java代码 –(编译成)–> Java字节码 –(dex编译器:dx,SDK工具 编译成)Dalvik字节码(dex字节码)。

最后获得结果:classes.dex。
通过反编译classes.dex即可得到java源代码。运用安卓反编译工具dex2jar可将dex文件反编译成.jar文件,然后运用jd-gui工具即可查看反编译后得到的源代码。

3)ART(Android runtime)

①概念

谷歌Android 4.4系统新增的一种应用运行模式,与传统的Dalvik模式不同,ART模式可以实现更为流畅的安卓系统体验,对于大家来说,只要明白ART模式可让系统体验更加流畅,不过只有在安卓4.4以上系统中采用此功能。

Art上应用启动快,运行快,但是耗费更多存储空间,安装时间长,总的来说ART的功效就是”空间换时间“。

②与DVM区别

i> 处理应用程序执行的方式不同

Dalvik是依靠一个Just-In-Time(JIT)编译器去解释字节码。开发者编译后的应用代码需要通过一个解释器在用户的设备上运行,这一机制并不高效,但让应用能更容易在不同硬件和架构上运行。
ART则完全改变了这套做法,在应用安装的时候就预编译字节码到机器语言,这一机制叫Ahead-Of-Time(AOT)编译。在移除解释代码这一过程后,应用程序执行将更有效率,启动更快。

③ART优点

系统性能的显著提升
应用启动更快、运行更快、体验更流畅、触感反馈更及时
更长的电池续航能力
支持更低的硬件

④ART缺点

更大的存储空间占用,可能会增加10%-20%
更长的应用安装时间

4)JIT(实时编译器)

Android2.2引入JIT,Dalvik JIT编译器把Dalvik字节码编译成本地代码,这可以显著提高性能。

原因:
①本地代码直接由CPU执行,不需要虚拟机解释执行;
②本地代码可以被特定架构优化。

2,Android代码执行过程

1)app启动过程

点击应用图标后会去启动应用的LauncherActivity,如果LancerActivity所在的进程没有创建,还会创建新进程,整体的流程就是一个Activity的启动流程。
app启动过程

①主要角色

①Instrumentation
监控应用与系统相关的交互行为。
②AMS
组件管理调度中心,什么都不干,但是什么都管。
③ActivityStarter
Activity启动的控制器,处理Intent与Flag对Activity启动的影响,具体说来有:
1 寻找符合启动条件的Activity,如果有多个,让用户选择;
2 校验启动参数的合法性;
3 返回int参数,代表Activity是否启动成功。
④ActivityStackSupervisior
这个类的作用你从它的名字就可以看出来,它用来管理任务栈。
这是高版本才有的类,它用来管理多个ActivityStack,早期的版本只有一个ActivityStack对应着手机屏幕,后来高版本支持多屏以后,就有了多个ActivityStack,于是就引入了ActivityStackSupervisior用来管理多个ActivityStack。
⑤ActivityStack
用来管理任务栈里的Activity。
⑥ActivityThread
最终干活的人,是ActivityThread的内部类,Activity、Service、BroadcastReceiver的启动、切换、调度等各种操作都在这个类里完成。

②相关进程

i> 调用者进程

如果是在桌面启动应用就是Launcher应用进程。

ii> ActivityManagerService等所在的System Server进程

该进程主要运行着系统服务组件。

iii> Zygote进程

该进程主要用来fork新进程。

iv> 新启动的应用进程

该进程就是用来承载应用运行的进程了,它也是应用的主线程(新创建的进程就是主线程),处理组件生命周期、界面绘制等相关事情。

③整个流程

①点击桌面应用图标,Launcher进程将启动Activity(MainActivity)的请求以Binder的方式发送给了AMS。
②AMS接收到启动请求后,交付ActivityStarter处理Intent和Flag等信息,然后再交给ActivityStackSupervisior/ActivityStack
③处理Activity进栈相关流程。同时以Socket方式请求Zygote进程fork新进程。
Zygote接收到新进程创建请求后fork出新进程。
④在新进程里创建ActivityThread对象,新创建的进程就是应用的主线程,在主线程里开启Looper消息循环,开始处理创建Activity。
⑤ActivityThread利用ClassLoader去加载Activity、创建Activity实例,并回调Activity的onCreate()方法。这样便完成了Activity的启动。

④Android主要源码组成

Kernel

Android Linux 内核2.6

bionic

Android 标准C运行支持库

bootloader

Android内核加载器参考

build

Android的Build系统

cts

Android兼容性测试源

dalvik

Dalvik虚拟机

external

Android使用的开放源

frameworks

Android框架

hardware

Android HAL(Hanrdware Abstraction Layer,硬件抽象层)库源

packages

包含Android 基本应用,Content Provider等

system

Android初始化进程、蓝牙工具集等

2)Android启动过程

Androi启动过程

Linux内核

Android启动时,首先通过bootloader(系统加载器),加载Linux内核。
在Linux加载启动时,与普通Linux启动过程相同,先初始化内核,然后调用init进程。

init进程

init进程对各种设备进行初始化,运行Android framework 所需用的各种Daemon、Context Manager、Media Server、Zygote等。

在Linux中,init进程是Android启动后,由内核启动的第一个用户级进程,也是所有进程的父进程。

四大功能:
分析及允许init.rc文件

分析init.rc启动脚本文件,病根据相关文件中包含的内容,执行相应的功能。

生成设备驱动节点

应用程序访问设备驱动时,生成设备节点文件。

处理子进程终止
属性服务

保存系统允许所需的环境变量。

init执行Daemon进程

USB Daemon(usbd):管理USB连接
Android Debug Bridge Daemon(adbd):Android Debug Bridge连接管理。
Debugger Daemon(debuggerd):启动Debugger系统。
Radio Interface Layer Daemon(rild):管理无线通信连接。

Context Manager进程

Context Manager是一个管理Android系统服务的重要进程。系统服务是组成Android Framework的重要组件,提供从相机、音频、视频处理到各种应用程序直至需要的重要API。
Context Manager提供运行与Android内的各种系统服务信息。应用程序或Framework内部模块在调用系统服务时,需要先向服务管理器申请,而后通过Binder IPC(Interprocess conmmunication)调用系统服务。
在系统系统时,Android所有系统服务都要把各自的handle信息注册到Context Manager,此时,Binder IPC用来进行进程间通信。

Media Server

用于运行基于C/C++的本地系统服务,如Audio Flinger(负责音频输出)、Camera等。

Zygote进程

用于缩短Android应用程序加载时间。

每当执行Java应用程序时,Zygote进程会派生出一个子进程来执行应用程序,该子进程是用来执行Java应用程序的虚拟机,初始化JVM,并启动它。

在应用程序运行前,Zygote进程通过共享已运行的JVM代码与内存信息,缩短应用程序运行所耗费的时间。并且,它会事先将应用程序要使用的Android Framework中的类与资源加载到内存中,并组织形成所用资源的链接信息。新运行的App在使用所需资源时不必每次形成资源的链接ixnx,这会节省大量时间,提高程序运行速度。

除了Zygote,Maemo平台的Launcher、Qt extended的quick Launcher等都是基于相同的目的而被使用的。

意义:一方面提高设备资源利用率,一方面尽可能的加长设备的使用时间。

System Server进程

是Android系统的核心进程,由Zygote进程创建。
在System Server进程中可以看到它建立的Android大部分服务,如Activity Manager Service(管理应用程序的生命周期)、Location Manager Service(提供终端的地理位置信息)。
为了将运行在System Server进程中的Java系统服务提供给Android应用程序或Framework内部模块调用,需要在Context Manager中注册。
在 通过Binder IPC将java系统服务注册到基于C语言的服务管理器时,需要使用JNI(Java Native Interface)本地编程接口。JNI允许java代码与其它编程语言(如C、C++、汇编语言)编写的应用程序和库进行交互操作。
当所有Java系统服务加载完毕后,Activity Manager Service会允许HOME应用,启动过程继续进行。

3,APK

1)APK打包

①概念

Android的包文件APK分为两个部分:代码和资源,所以打包方面也分为资源打包和代码打包。

②打包流程

打包流程
1)通过AAPT工具进行资源文件(包括AndroidManifest.xml、布局文件、各种xml资源等)的打包,生成R.java文件。
2)通过AIDL工具处理AIDL文件,生成相应的Java文件。
3)通过Javac工具编译项目源码,生成Class文件。
4)通过DEX工具将所有的Class文件转换成DEX文件,该过程主要完成Java字节码转换成Dalvik字节码,压缩常量池以及清除冗余信息等工作。
5)通过ApkBuilder工具将资源文件、DEX文件打包生成APK文件。
6)利用KeyStore对生成的APK文件进行签名。
7)如果是正式版的APK,还会利用ZipAlign工具进行对齐处理,对齐的过程就是将APK文件中所有的资源文件举例文件的起始距离都偏移4字节的整数倍,这样通过内存映射访问APK文件的速度会更快。

2)APK的安装流程

apk的安装流程
1)复制APK到/data/app目录下,解压并扫描安装包。
2)资源管理器解析APK里的资源文件。
3)解析AndroidManifest文件,并在/data/data/目录下创建对应的应用数据目录。
4)然后对dex文件进行优化,并保存在dalvik-cache目录下。
5)将AndroidManifest文件解析出的四大组件信息注册到PackageManagerService中。
6)安装完成后,发送广播。

3)64k方法

①问题

随着功能的增加,方法数增多,就会出现所谓的64k方法数问题。

②产生原因

Android APK文件本质上是一个压缩文件,它包含的classes.dex文件是Dalvik字节码文件,这个dex文件中存放的就是编译后的Java代码。Dalvik可执行文件规范限制了单个.dex文件最多引用的方法数是65536个。

在DEX文件中,method、field、class等的个数使用short类型来做索引,即两个字节(65535),method、field、class等均有此限制。

APK在安装过程中会调用dexopt将DEX文件优化成ODEX文件,dexopt使用LinearAlloc来存储应用信息,关于LinearAlloc缓冲区大小,不同的版本经历了4M/8M/16M的限制,超出缓冲区时就会抛出INSTALL_FAILED_DEXOPT错误。

③解决方案–MultiDex的使用

google为了规避上述问题,推出了MultiDex解决方案解决方法数超限问题,可以配置方法数超过 64K 的应用。

④解决原理

①Android5.0之前
Dalvik在5.0之前,为每一个APK只生成一个classes.dex,所以会有上述所说的方法数超限的问题,如果我们可以将一个dex文件分成多个,在应用启动时,加载第一个(主dex)dex文件,当启动以后,再依次加载其他dex文件。这样就可以规避上述问题了。MultiDex即是实现了这样的功能
②Android5.0之后
Android5.0及更高版本使用支持从apk中加载多个dex文件的ART运行时机制,在应用安装时,加载classed(…N).dex文件并编译成一个.oat文件以支持在Android设备上运行。

⑤实现

配置build.gradle

android {compileSdkVersion 21buildToolsVersion "21.1.0"//必须使用21或之后的版本defaultConfig {...minSdkVersion 14targetSdkVersion 21...// Enabling multidex support.multiDexEnabled true}...}dependencies {compile 'com.android.support:multidex:1.0.0'}

配置Application
如果用户没有重写Application,只需修改Manifest文件中的内容:

<application...android:name="android.support.multidex.MultiDexApplication">...</application>

如果用户继承变重写了Application,可以将继承的Application换成MultiDexApplication。
或者重写attachBaseContext() 方法,

@Overrideprotected void attachBaseContext(Context base) {super.attachBaseContext(base);MultiDex.install(this);
}

特别注意,如果没有实现这部分代码,运行时会出现NoClassDefFoundError的错误,尤其是在依赖三方函数库时。
这个问题之前出现过很多次,当时是由于配置了build.gradle文件但是没有使用MultiDexApplication造成的。所以开发者一定要记得使用MultiDexApplication或者MultiDex.install(this),具体参考上面提到过的配置Application。

还有一种情况,上面提到过的,使用MultiDex机制,必然存在主dex文件和从dex文件,应用启动时所需要的类,都应放入主dex中,否则也会出现NoClassDefFoundError。这种情况可以手动添加一些类到主dex中:

multiDexKeepFile
手动加入要放到Main.dex中的类。

android/support/multidex/MultiDex.class

multiDexKeepProguard
以Proguard的方式手动加入要放到Main.dex中的类。

-keep class android.support.multidex.** {
    *;
}

然后在build.gradle中进行配置:

android {defaultConfig {multiDexEnabled truemultiDexKeepProguard file('multiDexKeep.pro') multiDexKeepFile file('multiDexKeep.txt') }
}
dependencies {compile'com.android.support:multidex:1.0.1'
}

⑥Multidex优化

multidex会加长构建应用的时间,这个必要的过程可能会拖慢你的开发进度。 为加速构建过程,我们可以在Gradle中配置productFlavors。
开发时将minSdkVersion改为21使用ART运行时机制,这样能加快构建速度。release时改为合适的minSdkVersion,这样仅在release时费时较长。
build.gradle配置如下:

android {productFlavors {dev {minSdkVersion 21}prod {minSdkVersion 14}}...buildTypes {release {...}}}dependencies {compile 'com.android.support:multidex:1.0.0'}

完成上述配置后,你可以使用结合了dev productFlavor和buildType属性的devDebug变体app。
这个变体app包含如下特性:

关闭了混淆(proguard)
支持multidex
minSdkVersion 设置为 Android API level 21.
值得注意的是:上述配置后的devDebug变种app仅能运行在Android 5.0设备上。

4)apk解压

apk文件是压缩文件,可以用压缩工具(如WinZip、7-Zip)来查看apk文件的内容。
解压后如图所示:
apk

i>assets
ii>lib
iii>META-INF

这个文件夹存储的是关于签名的一些信息。

iv>res
v>AndroidManifest.xml
vi>classes.dex

包含应用的字节码。
Android的工具包里有工具dexdump,可以把classes.dex的二进制代码转化位使人易读的格式。我们可以使用dexdump查看该文件。

vii>resources.arsc

4,Android反汇编

①下载 dex2jar工具。

点击下载dex2jar

②解压后,把classes.dex放入目录

目录示例

③CMD运行

如图,打开安装目录,运行dex2jar.bat classes.dex(因版本不同,本次举例使用 d2j-dex2jar.bat classes.dex命令)
cmd命令

运行命令后会生成文件:classes-dex2jar.jar(如图所示)
classes-dex2jar.jar

④下载jd-gui.exe

点击下载jd-gui.exe

⑤安装jd-gui,打开jar文件即可查看

jd-gui查看文件

这个apk是直接生成,没有进行代码混淆,所以代码很完整。这也说明了反逆向工程的重要性。

5,模块化编程

1)动态加载

①概念

动态加载技术就是使用类加载器加载相应的apk、dex、jar(必须含有dex文件),再通过反射获得该apk、dex、jar内部的资源(class、图片、color等等)进而供宿主app使用。

②类加载器

Android支持动态加载的两种方式是:DexClassLoader和PathClassLoader。

PathClassLoader

只能加载已经安装到Android系统的APK文件,即/data/app目录,Android默认的类加载器。

DexClassLoader

可以加载任意目录下的dex、jar、apk、zip文件,且支持从SD卡加载。

③实现

2)插件化

①概念

插件化是体现在功能拆分方面的,它将某个功能独立提取出来,独立开发,独立测试,再插入到主应用中。依次来较少主应用的规模。

②应用

  1. 开发者将插件代码封装成Jar或者APK
  2. 宿主下载或者从本地加载Jar或者APK到宿主中
  3. 将宿主调用插件中的算法或者Android特定的Class(如Activity)

③原因

一个应用程序dex文件的方法数最大不能超过65536个;
可以让应用程序实现插件化、插拔式结构,对后期维护有益。

④解决问题

代码加载

类的加载可以使用Java的ClassLoader机制,还需要组件生命周期管理。

资源加载

用AssetManager的隐藏方法addAssetPath。

⑤原理——Hook机制之动态代理

使用代理机制进行API Hook进而达到方法增强。

静态代理
动态代理

可以简单理解为JVM可以在运行时帮我们动态生成一系列的代理类。

代理Hook

如果我们自己创建代理对象,然后把原始对象替换为我们的代理对象,就可以在这个代理对象中为所欲为了;修改参数,替换返回值,称之为Hook。

整个Hook过程简要总结如下:

  1. 寻找Hook点,原则是静态变量或者单例对象,尽量Hook public的对象和方法,非public不保证每个版本都一样,需要适配。
  2. 选择合适的代理方式,如果是接口可以用动态代理;如果是类可以手动写代理也可以使用cglib。
  3. 偷梁换柱-用代理对象替换原始对象

2)热修复

①概念

热修复是体现在bug修复方面的,它实现的是不需要重新发版和重新安装,就可以去修复已知的bug。即:线上修复。

②原理

利用PathClassLoader和DexClassLoader去加载与bug类同名的类,替换掉bug类,进而达到修复bug的目的,原理是在app打包的时候阻止类打上CLASS_ISPREVERIFIED标志,然后在热修复的时候动态改变BaseDexClassLoader对象间接引用的dexElements,替换掉旧的类。

基于Xposed中的思想,通过修改c层的Method实例描述,来实现更改与之对应的java方法的行为,从而达到修复的目的。

Xposed
诞生于XDA论坛,类似一个应用平台,不同的是其提供诸多系统级的应用。可实现许多神奇的功能。Xposed需要以越狱为前提,像是iOS中的cydia。
Xposed可以修改任何程序的任何java方法(需root),github上提供了XposedInstaller,是一个android app。提供很多framework层,应用层级的程序。开发者可以为其开发一些系统或应用方面的插件,自定义android系统,它甚至可以做动态权限管理(XposedMods)。

这篇关于【Android学习】DVM——apk生成、安装、启动、反汇编的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/512584

相关文章

Python使用国内镜像加速pip安装的方法讲解

《Python使用国内镜像加速pip安装的方法讲解》在Python开发中,pip是一个非常重要的工具,用于安装和管理Python的第三方库,然而,在国内使用pip安装依赖时,往往会因为网络问题而导致速... 目录一、pip 工具简介1. 什么是 pip?2. 什么是 -i 参数?二、国内镜像源的选择三、如何

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

Python安装时常见报错以及解决方案

《Python安装时常见报错以及解决方案》:本文主要介绍在安装Python、配置环境变量、使用pip以及运行Python脚本时常见的错误及其解决方案,文中介绍的非常详细,需要的朋友可以参考下... 目录一、安装 python 时常见报错及解决方案(一)安装包下载失败(二)权限不足二、配置环境变量时常见报错及

Android里面的Service种类以及启动方式

《Android里面的Service种类以及启动方式》Android中的Service分为前台服务和后台服务,前台服务需要亮身份牌并显示通知,后台服务则有启动方式选择,包括startService和b... 目录一句话总结:一、Service 的两种类型:1. 前台服务(必须亮身份牌)2. 后台服务(偷偷干

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

浅析如何使用Swagger生成带权限控制的API文档

《浅析如何使用Swagger生成带权限控制的API文档》当涉及到权限控制时,如何生成既安全又详细的API文档就成了一个关键问题,所以这篇文章小编就来和大家好好聊聊如何用Swagger来生成带有... 目录准备工作配置 Swagger权限控制给 API 加上权限注解查看文档注意事项在咱们的开发工作里,API

MySQL8.2.0安装教程分享

《MySQL8.2.0安装教程分享》这篇文章详细介绍了如何在Windows系统上安装MySQL数据库软件,包括下载、安装、配置和设置环境变量的步骤... 目录mysql的安装图文1.python访问网址2javascript.点击3.进入Downloads向下滑动4.选择Community Server5.

Windows设置nginx启动端口的方法

《Windows设置nginx启动端口的方法》在服务器配置与开发过程中,nginx作为一款高效的HTTP和反向代理服务器,被广泛应用,而在Windows系统中,合理设置nginx的启动端口,是确保其正... 目录一、为什么要设置 nginx 启动端口二、设置步骤三、常见问题及解决一、为什么要设置 nginx

springboot启动流程过程

《springboot启动流程过程》SpringBoot简化了Spring框架的使用,通过创建`SpringApplication`对象,判断应用类型并设置初始化器和监听器,在`run`方法中,读取配... 目录springboot启动流程springboot程序启动入口1.创建SpringApplicat

树莓派启动python的实现方法

《树莓派启动python的实现方法》本文主要介绍了树莓派启动python的实现方法,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录一、RASPBerry系统设置二、使用sandroidsh连接上开发板Raspberry Pi三、运