Apk文件结构、生成和安装流程
什么是Apk
Apk就是Android系统的软件安装包文件。它是Android开发的产物,也是我们逆向分析重点需要关注的对象。
Apk结构
自Android诞生以来,始终以zip作为Apk的文件格式,只不过打包时将后缀修改成了.apk。使用任意能解压zip的工具都可以解压Apk包。
/Users/Downloads/app-debug123
├── AndroidManifest.xml
├── META-INF
│ ├── KEY0.RSA
│ ├── KEY0.SF
│ └── MANIFEST.MF
├── classes.dex
├── com
│ └── affinityattack
│ └── UseText.dex
├── lib
│ └── arm64-v8a
│ └── libJniTest.so
├── res
│ ├── anim
│ │ ├── abc_fade_in.xml
│ │ ├── abc_fade_out.xml
│ │ ├── abc_grow_fade_in_from_bottom.xml
│ │ ├── abc_popup_enter.xml
│ │ ├── abc_popup_exit.xml
│ │ ├── abc_shrink_fade_out_from_bottom.xml
│ │ ├── abc_slide_in_bottom.xml
│ │ ├── abc_slide_in_top.xml
│ │ ├── abc_slide_out_bottom.xml
│ │ ├── abc_slide_out_top.xml
│ │ ├── abc_tooltip_enter.xml
│ │ ├── abc_tooltip_exit.xml
│ │ ├── btn_checkbox_to_checked_box_inner_merged_animation.xml
│ │ ├── btn_checkbox_to_checked_box_outer_merged_animation.xml
│ │ ├── btn_checkbox_to_checked_icon_null_animation.xml
│ │ ├── btn_checkbox_to_unchecked_box_inner_merged_animation.xml
│ │ ├── btn_checkbox_to_unchecked_check_path_merged_animation.xml
│ │ ├── btn_checkbox_to_unchecked_icon_null_animation.xml
│ │ ├── btn_radio_to_off_mtrl_dot_group_animation.xml
│ │ ├── btn_radio_to_off_mtrl_ring_outer_animation.xml
│ │ ├── btn_radio_to_off_mtrl_ring_outer_path_animation.xml
│ │ ├── btn_radio_to_on_mtrl_dot_group_animation.xml
│ │ ├── btn_radio_to_on_mtrl_ring_outer_animation.xml
│ │ └── btn_radio_to_on_mtrl_ring_outer_path_animation.xml
│ ├── color
│ │ ├── abc_background_cache_hint_selector_material_dark.xml
│ │ ├── abc_background_cache_hint_selector_material_light.xml
│ │ ├── abc_btn_colored_borderless_text_material.xml
│ │ ├── abc_btn_colored_text_material.xml
│ │ ├── abc_hint_foreground_material_dark.xml
│ │ ├── abc_hint_foreground_material_light.xml
│ │ ├── abc_primary_text_disable_only_material_dark.xml
│ │ ├── abc_primary_text_disable_only_material_light.xml
│ │ ├── abc_primary_text_material_dark.xml
│ │ ├── abc_primary_text_material_light.xml
│ │ ├── abc_search_url_text.xml
│ │ ├── abc_secondary_text_material_dark.xml
│ │ ├── abc_secondary_text_material_light.xml
│ │ ├── abc_tint_btn_checkable.xml
│ │ ├── abc_tint_default.xml
│ │ ├── abc_tint_edittext.xml
│ │ ├── abc_tint_seek_thumb.xml
│ │ ├── abc_tint_spinner.xml
│ │ ├── abc_tint_switch_track.xml
│ │ ├── switch_thumb_material_dark.xml
│ │ └── switch_thumb_material_light.xml
│ ├── drawable
│ │ ├── abc_btn_borderless_material.xml
│ │ ├── abc_btn_check_material.xml
│ │ ├── abc_btn_check_material_anim.xml
│ │ ├── abc_btn_colored_material.xml
│ │ ├── abc_btn_default_mtrl_shape.xml
│ │ ├── abc_btn_radio_material.xml
│ │ ├── abc_btn_radio_material_anim.xml
│ │ ├── abc_cab_background_internal_bg.xml
│ │ ├── abc_cab_background_top_material.xml
│ │ ├── abc_dialog_material_background.xml
│ │ ├── abc_edit_text_material.xml
│ │ ├── abc_ic_ab_back_material.xml
│ │ ├── abc_ic_arrow_drop_right_black_24dp.xml
│ │ ├── abc_ic_clear_material.xml
│ │ ├── abc_ic_go_search_api_material.xml
│ │ ├── abc_ic_menu_overflow_material.xml
│ │ ├── abc_ic_search_api_material.xml
│ │ ├── abc_ic_voice_search_api_material.xml
│ │ ├── abc_item_background_holo_dark.xml
│ │ ├── abc_item_background_holo_light.xml
│ │ ├── abc_list_divider_material.xml
│ │ ├── abc_list_selector_background_transition_holo_dark.xml
│ │ ├── abc_list_selector_background_transition_holo_light.xml
│ │ ├── abc_list_selector_holo_dark.xml
│ │ ├── abc_list_selector_holo_light.xml
│ │ ├── abc_ratingbar_indicator_material.xml
│ │ ├── abc_ratingbar_material.xml
│ │ ├── abc_ratingbar_small_material.xml
│ │ ├── abc_seekbar_thumb_material.xml
│ │ ├── abc_seekbar_tick_mark_material.xml
│ │ ├── abc_seekbar_track_material.xml
│ │ ├── abc_spinner_textfield_background_material.xml
│ │ ├── abc_switch_thumb_material.xml
│ │ ├── abc_tab_indicator_material.xml
│ │ ├── abc_text_cursor_material.xml
│ │ ├── abc_textfield_search_material.xml
│ │ ├── abc_vector_test.xml
│ │ ├── btn_checkbox_checked_mtrl.xml
│ │ ├── btn_checkbox_checked_to_unchecked_mtrl_animation.xml
│ │ ├── btn_checkbox_unchecked_mtrl.xml
│ │ ├── btn_checkbox_unchecked_to_checked_mtrl_animation.xml
│ │ ├── btn_radio_off_mtrl.xml
│ │ ├── btn_radio_off_to_on_mtrl_animation.xml
│ │ ├── btn_radio_on_mtrl.xml
│ │ ├── btn_radio_on_to_off_mtrl_animation.xml
│ │ ├── notification_bg.xml
│ │ ├── notification_bg_low.xml
│ │ ├── notification_icon_background.xml
│ │ ├── notification_tile_bg.xml
│ │ ├── tooltip_frame_dark.xml
│ │ └── tooltip_frame_light.xml
│ ├── interpolator
│ │ ├── btn_checkbox_checked_mtrl_animation_interpolator_0.xml
│ │ ├── btn_checkbox_checked_mtrl_animation_interpolator_1.xml
│ │ ├── btn_checkbox_unchecked_mtrl_animation_interpolator_0.xml
│ │ ├── btn_checkbox_unchecked_mtrl_animation_interpolator_1.xml
│ │ ├── btn_radio_to_off_mtrl_animation_interpolator_0.xml
│ │ ├── btn_radio_to_on_mtrl_animation_interpolator_0.xml
│ │ └── fast_out_slow_in.xml
│ ├── layout
│ │ ├── abc_action_bar_title_item.xml
│ │ ├── abc_action_bar_up_container.xml
│ │ ├── abc_action_menu_item_layout.xml
│ │ ├── abc_action_menu_layout.xml
│ │ ├── abc_action_mode_bar.xml
│ │ ├── abc_action_mode_close_item_material.xml
│ │ ├── abc_activity_chooser_view.xml
│ │ ├── abc_activity_chooser_view_list_item.xml
│ │ ├── abc_alert_dialog_button_bar_material.xml
│ │ ├── abc_alert_dialog_material.xml
│ │ ├── abc_alert_dialog_title_material.xml
│ │ ├── abc_cascading_menu_item_layout.xml
│ │ ├── abc_dialog_title_material.xml
│ │ ├── abc_expanded_menu_layout.xml
│ │ ├── abc_list_menu_item_checkbox.xml
│ │ ├── abc_list_menu_item_icon.xml
│ │ ├── abc_list_menu_item_layout.xml
│ │ ├── abc_list_menu_item_radio.xml
│ │ ├── abc_popup_menu_header_item_layout.xml
│ │ ├── abc_popup_menu_item_layout.xml
│ │ ├── abc_screen_content_include.xml
│ │ ├── abc_screen_simple.xml
│ │ ├── abc_screen_simple_overlay_action_mode.xml
│ │ ├── abc_screen_toolbar.xml
│ │ ├── abc_search_dropdown_item_icons_2line.xml
│ │ ├── abc_search_view.xml
│ │ ├── abc_select_dialog_material.xml
│ │ ├── abc_tooltip.xml
│ │ ├── activity_innocent.xml
│ │ ├── activity_main.xml
│ │ ├── activity_other_attack.xml
│ │ ├── activity_strandhogg2.xml
│ │ ├── custom_dialog.xml
│ │ ├── notification_action.xml
│ │ ├── notification_action_tombstone.xml
│ │ ├── notification_template_icon_group.xml
│ │ ├── notification_template_part_chronometer.xml
│ │ ├── notification_template_part_time.xml
│ │ ├── select_dialog_item_material.xml
│ │ ├── select_dialog_multichoice_material.xml
│ │ ├── select_dialog_singlechoice_material.xml
│ │ └── support_simple_spinner_dropdown_item.xml
│ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ ├── mipmap-hdpi-v4
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ ├── mipmap-mdpi-v4
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ ├── mipmap-xhdpi-v4
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi-v4
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ └── mipmap-xxxhdpi-v4
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
└── resources.arsc
32 directories, 255 files
AndroidManifest.xml
AndroidManifest文件是每个Apk都必须定义和包含的,而且它的名字是固定的不能被修改。该文件是Android的配置文件,文件中包含了应用的版本信息、应用的包名、应用的名字、最小运行的版本,如果有引用一些SDk的话,该文件中还会有一些SDK版本的信息。在开发的过程中,我们所用的Android四大组件Activity、Provider、Service、Receiver一般情况下也都在AndroidManifest中注册,这样系统才会启动对应的组件。另外如果需要系统申请权限的话,也需要在AndroidManifest中声明。最终程序在打包时,会把AndroidManifest.xml进行简单的编译,便于Android系统识别,编译之后的格式是AXML格式。
classes.dex
Android中主要代码都是由Java编写的,普通的Java程序编译后都会生成.class文件,然后Java虚拟机去运行.class文件运行Java字节码。但是在Android中考虑各方面的因素,并没有直接使用Java虚拟机,而是使用了Dalvik和ART虚拟机(Android4.4时引入的ART虚拟机,5.0以后默认都使用ART虚拟机)。Android中虚拟机并不能直接运行.class文件,因为Dalvik虚拟机的指令码并不是标准的Java虚拟机指令码,而是使用了自己独有的一套指令集Smali。在Android项目打包时,将通过AndroidSDK中dx工具,将多个.class文件进行合并,重组,优化,转化成.dex文件。当一个dex文件中的方法数超过65535时,就会被分割成多个dex。
lib目录
该目录下存放程序依赖的native库,也就是so文件,一般so文件都是由C/C++编写。一般lib目录下会根据CPU架构不同,生成对应架构的子目录,在对应架构的子目录中存放对应架构的so。一般来说有以下几种子目录:arm64-v8a、armeabi-v7a、x86、armeabi。
arm64-v8a:64位支持,目前主流的版本,近几年的手机几乎都是64位的架构了。可以向下兼容,也兼容32位的程序。
armeabi-v7a:第7代及以上的ARM处理器。2011年以后的生产的大部分Android设备都使用它。
x86:由Intel提供的称为Houdini的指令集动态转码工具,实现对arm .so的兼容,几乎只能在平板或模拟器中见到了。
armeabi:第5代、第6代的ARM处理器,早期的手机用的比较多,缺少对浮点数计算的硬件支持,在需要大量计算时有性能瓶颈。如果程序中只包含了armeabi,那么在所有Android设备都可以运行;只包含了armeabi-v7a,除armeabi架构的设备外都可以运行;由此也可知,有些程序不能在模拟器中运行,也有可能是因为没有生成对应架构的so。
META-INF目录
保存应用的签名信息,签名信息可以验证APK文件的完整性。AndroidSDK在打包Apk时会计算Apk包中所有文件的完整性,并且把这些完SHA-1值保存到META-INF文件夹下,应用程序在安装的时候首先会根据META-INF文件夹校验APK的完整性。KEY0.RSA、KEY0.SF这两个文件名字不是确定的,这个是根据所使用的签名文件中内容生成的。KEY0.RSA:保存的是开发者使用私钥对Apk签名后的信息。MANIFEST.MF:保存了整个Apk中所有文件的SHA-1进行base64编码后的值。KEY0.SF:包括了MANIFEST.MF所有的信息,然后又加入了MANIFEST.MF文件的SHA-1并base64编码的值。当我们修改了Apk文件的内容后,一定要将该目录删除并重新签名,否则是无法安装的。
res目录
存放各种资源文件的目录。这个目录中的所有文件,最终会被映射到Android工程中的R文件中,生成对应的int型的ID,在程序中访问这些资源文件的时候,直接使用资源的ID就能进行调用了。res文件夹下可以包含多个文件夹,其中anim存放动画文件;drawable目录存放图像资源;layout目录存放布局文件;values目录存放一些特征值;xml文件夹存放任意xml文件,在运行时可以通过Resources.getXML()读取;raw是可以直接复制到设备中的任意文件,他们无需编译。
assets目录
用于存放需要打包到Apk中的静态文件,和res的不同点在于,assets目录支持任意深度的子目录,用户可以根据自己的需求任意部署文件夹架构,assets不会自动生成对应的ID,访问的时候需要通过AssetManager类。一般情况下,我们使用assets目录用于存放一些图片、存放证书文件、存放SDK中需要的一些配置文件、存放WebApp需要的js文件等等。
resources.arsc
用来记录资源文件和资源ID之间的映射关系,用来根据资源ID寻找资源。Android的res目录专门用来存放资源文件,当在代码中需要调用资源文件时,只需要调用findViewById(R.id.xx)就可以得到资源文件,每当在res文件夹下放一个文件,aapt就会自动生成对应的ID保存在.R文件,我们调用这个ID就可以,但是只有这个ID还不够,.R文件只是保证编译程序不报错,实际上在程序运行时,系统要根据ID去寻找对应的资源路径,而resources.arsc文件就是用来记录这些ID和资源文件位置对应关系的文件。
Apk生成流程
该流程为ADT时代版本,后来使用gradle进行打包,实现上可能稍有不同。
打包资源文件
项目中的AndroidManifest.xml文件和布局文件XML都会编译,然后生成相应的R.java,另外AndroidManifest.xml会被aapt(The Android Asset Packaing Tool)编译成二进制。存放在App的res目录下的资源,该类资源在App打包前大多会被编译,变成二进制文件,并会为每个该类文件赋予一个resource id。对于该类资源的访问,应用层代码则是通过resource id进行访问的。Android应用在编译过程中aapt工具会对资源文件进行编译,并生成一个resource.arsc文件,resource.arsc文件相当于一个文件索引表,记录了很多跟资源相关的信息。
处理aidl文件,生成相应的.Java文件
使用aidl(Android Interface Definition Language)工具解析接口定义文件然后生成相应的.Java代码接口供程序调用。如果在项目没有使用到aidl文件,则可以跳过这一步。
编译项目源代码,生成class文件
项目中所有的.Java代码,包括R.java和接口相关的.Java文件,都会被Java编译器(javac)编译成.class文件,生成的class文件位于工程中的bin/classes目录下。
转换所有的class文件,生成classes.dex文件
dx工具生成可供Android系统Dalvik虚拟机执行的classes.dex文件。任何第三方的libraries和.class文件都会被转换成.dex文件。dx工具的主要工作是将Java字节码转成成Dalvik字节码、压缩常量池、消除冗余信息等。
打包生成Apk文件
将没有编译的资源,编译过的资源和.dex文件都会通过apkbuilder工具打包到最终的.apk文件中。
对Apk文件进行签名
一旦APK文件生成,它必须被签名才能被安装在设备上。
对签名后的APK文件进行对齐处理
如果你发布的apk是正式版的话,就必须对APK进行对齐处理,用到的工具是zipalign。对齐的主要过程是将APK包中所有的资源文件距离文件起始偏移为4字节整数倍,这样通过内存映射访问apk文件时的速度会更快。对齐的作用就是减少运行时内存的使用。
Apk安装流程
安装方式
-
• 系统安装:开机的时候,没有安装界面
-
• adb命令安装:通过adb命令行安装,没有安装界面
-
• 第三方安装,有安装界面,通过packageInstaller.apk来处理安装及卸载的过程的界面
安装流程概述
拷贝文件到指定的目录
在Android系统中,Apk安装文件是会被保存起来的,默认情况下,用户安装的Apk首先会被拷贝到/data/app目录下,/data/app目录是用户有权限访问的目录,在安装apk的时候会自动选择该目录存放用户安装的文件,而系统的Apk文件则被放到了/system分区下,包括/system/app,/system/vendor/app,以及/system/priv-app等等,该分区只有Root权限的用户才能访问。
解压缩Apk,创建应用的数据目录
为了加快App的启动速度,Apk在安装的时候,会首先将App的可执行文件dex拷贝到/data/dalvik-cache目录,缓存起来。然后,在/data/data/目录下创建应用程序的数据目录(以应用的包名命名),存放在应用的相关数据,如数据库、xml文件、cache、二进制的so动态库等。也会将二进制的so动态库放到data/app/对应的目录下,如果有oat的过程,还会在data/app/对应目录下,生成oat文件夹保存生成的oat文件。
解析Apk的AndroidManifest.xml文件
系统在安装这个Apk的过程中,会解析Apk的AndroidManifest.xml文件,提取出这个Apk的重要信息写入到/data/system/packages.xml文件中,这些信息包括:权限、应用包名、Apk的安装位置、版本、userID等等。
显示
以上步骤都是通过PackageManagerService服务去执行的,如果我们想要在Android桌面上看到这些应用程序,还需要Launcher应用,从PackageManagerService服务中把这些安装好的应用程序信息提取出来,并以友好的方式在桌面上展现出来,例如以快捷图标的形式。
引用链接
[1]
APK文件结构和安装过程: https://blog.csdn.net/bupt073114/article/details/42298337
历史文章
原文始发于微信公众号(移动安全星球):Apk文件结构、生成和安装流程
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论