diff --git a/chapter-02/README.md b/chapter-02/README.md index 660a7ec..cfefd66 100644 --- a/chapter-02/README.md +++ b/chapter-02/README.md @@ -1,6 +1,6 @@ # 第二章 系统开发环境与工具 -​ 经过第一章的学习,对`AOSP`定制进行简略的介绍后,大家应该对系统定制开发的基础有了大致的理解。所谓的系统定制,实际相当于在一款成熟的产品上进行二次开发。二次开发最耳熟能详的要属开发板二次开发领域了,比如知名经典的ARM架构开发板树霉派,厂家在出厂时提供了一些基础的系统与工具套件,开发人员可以扩展这个板子的玩法,实现玩具车、智能家居、电视盒子、路由器、小型内容服务器等多种功能的开发,这里面涉及到的二次开发知识也是相当的丰富。 +​ 经过第一章的学习,对`AOSP`定制进行简略的介绍后,大家应该对系统定制开发的基础有了大致的理解。所谓的系统定制,实际相当于在一款成熟的产品上进行二次开发。二次开发最耳熟能详的要属开发板二次开发领域了,比如知名经典的ARM架构开发板树莓派,厂家在出厂时提供了一些基础的系统与工具套件,开发人员可以扩展这个板子的玩法,实现玩具车、智能家居、电视盒子、路由器、小型内容服务器等多种功能的开发,这里面涉及到的二次开发知识也是相当的丰富。 当然,本书不讨论功能庞大复杂的系统定制,而是以安全领域为面,技术功能为切入点的形式来讨论系统二次定制的一般方法。和常见的软件项目的二次开发的学习步骤类似,不会有太大的出入,细节的区别就在于,`Android`源码相比其他软件项目要更加庞大复杂,需要做一些基础知识的储备,另外,修改编译以及测试系统是一个反复执行的动作,它占用了定制开发中所耗费的大多数时间。 @@ -25,15 +25,15 @@ ​ 如果你的系统是`Windows10`,那么你需要先查询当前系统版本,`Win10`下必须是18917或更高的版本才支持`WSL2`。在`cmd`命令行中输入`winver`命令查看当前系统版本号。 -![image-20230102183339463](.\images\image-20230102183339463.png) +![image-20230102183339463](images/image-20230102183339463.png) ​ 由于是系统自带的,所以安装起来非常方便,可以直接在控制面版->程序->启动或关闭`Window`功能中开启支持即可,如下图 -![img](.\images\69ba546fd55c4fea8ef9b5d55a9bd354.png) +![img](images/69ba546fd55c4fea8ef9b5d55a9bd354.png) ​ 或者是采用命令的方式开启虚拟机平台和`Linux`子系统,使用管理员权限启动。 -![image-20230102183708998](.\images\image-20230102183708998.png) +![image-20230102183708998](images/image-20230102183708998.png) ​ 打开Powershell,执行下面的命令开启功能。 @@ -46,7 +46,7 @@ Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-L ​ 启动完成这些特性后,重新启动计算机,就可以开始安装一个`Ubuntu`系统了。打开`Microsoft Store`应用商店输入关键字`Ubuntu`进行搜索,然后选择自己需要的版本即可,例如这里安装的是22.04版本,如下图。 -![image-20230102184626538](.\images\image-20230102184626538.png) +![image-20230102184626538](images/image-20230102184626538.png) ​ 成功获取`Ubuntu`系统后,从应用中启动系统即开始正式安装。安装过程只需要设置好用户名与密码即可。完成后会进行一个`shell`环境供用户输入。第一次启动时,需要设置用户名与密码来完成初始化配置。以后,只需要在终端工具的界面上,选择Ubuntu就可以启动了,整个启动过程中非常的快,只要数秒就会在窗口中给出一个Shell提示符,供用户输入操作。 @@ -79,27 +79,27 @@ wsl --import ubuntu22 E:\wsl2\ubuntu22_wsl E:\wsl2\ubuntu22.tar 1. 下载并安装`VMware`虚拟机,然后下载`Ubuntu22.04`系统`ISO`镜像文件。 2. `VWware`创建虚拟机,选择指定镜像 -![image-20230102194041709](.\images\image-20230102194041709.png) +![image-20230102194041709](images/image-20230102194041709.png) 3. 设置初始账号密码 -![image-20230102194243774](.\images\image-20230102194243774.png) +![image-20230102194243774](images/image-20230102194243774.png) 4. 选择虚拟机保存位置,这里不要保存在`C`盘,记得磁盘要有至少`300G`的空间 -![image-20230102194331141](.\images\image-20230102194331141.png) +![image-20230102194331141](images/image-20230102194331141.png) 5. 虚拟硬件`CPU`核心根据你的电脑配置进行调整,尽量多分点给虚拟机。 -![image-20230102194543812](.\images\image-20230102194543812.png) +![image-20230102194543812](images/image-20230102194543812.png) 6. 虚拟内存分配,至少保证`16G`以上的内存,否则可能会碰到内存不足编译失败的情况。 -![image-20230102194722427](.\images\image-20230102194722427.png) +![image-20230102194722427](images/image-20230102194722427.png) 7. 虚拟硬盘分配,这里至少分配`500G`的空间,考虑到性能,我选择的是单文件吗,这里如果选择立即分配所有磁盘空间,能提高一定的性能。如果你的电脑配置不是很高,建议你选择立即分配。 -![image-20230102194952517](.\images\image-20230102194952517.png) +![image-20230102194952517](images/image-20230102194952517.png) ​ 虚拟机开机后,将默认进入`Ubuntu`安装界面,按照提示进行选择语言,区域等待安装完成即可。 @@ -174,9 +174,9 @@ export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo/' #### 2.2.2 分支选择策略 -​ 根据自己的需求来选择合适的版本,比如想要在`Android`12的基础上进行二次开发,参考官方文档:https://source.android.com/docs/setup/about/build-numbers?hl=zh-cn,找到对应的版本描述,例如下图,可以看到各个版本号关联的代码分支,各分支版本支持哪些设备。 +​ 根据自己的需求来选择合适的版本,比如想要在`Android`12的基础上进行二次开发,参考官方文档:https://source.android.com/docs/setup/about/build-numbers?hl=zh-cn ,找到对应的版本描述,例如下图,可以看到各个版本号关联的代码分支,各分支版本支持哪些设备。 -![image-20230103220519836](.\images\image-20230103220519836.png) +![image-20230103220519836](images/image-20230103220519836.png) ​ 这么多版本,要根据自身的需求选一个适合的版本,例如我的选择策略如下: @@ -186,7 +186,7 @@ export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo/' ​ 如果该`ROM`主要在虚拟机中刷机测试的,那么选择支持版本最多的分支即可。这里我的测试设备是`pixel`3,根据上文中的选择策略找到了版本`SP1A.210812.016.A1`,对应的分支代码是`android-12.0.0_r3`,如下图。 -![image-20230103220838404](.\images\image-20230103220838404.png) +![image-20230103220838404](images/image-20230103220838404.png) #### 2.2.3 源码拉取与同步 @@ -242,7 +242,7 @@ export OUT_DIR=~/android_src/aosp12_out ​ 在开始编译前,还需要准备对应设备的驱动,根据前面选择的版本号`SP1A.210812.016.A1`,在官网地址:`https://developers.google.com/android/drivers`中找到对应的版本号。 -![image-20230103232052738](.\images\image-20230103232052738.png) +![image-20230103232052738](images/image-20230103232052738.png) ​ 第一个文件`Vendor`是用来存储厂商特定的文件,比如设备驱动程序。`Android`驱动会根据提供的这些设备驱动来正确的加载硬件。这个文件通常由设备厂商提供。如果你成功编译`Android`后,输出目录缺少`vendor.img`文件,那么你就需要检查下是否忘记导入对应型号的设备驱动了。 @@ -418,9 +418,9 @@ Common goals are: ​ 系统编译完成后,可以在编译的镜像结果中看到文件`boot.img`,这个文件是内核镜像文件。但是这个内核默认采用`Android`源码树中预编译好的内核文件,没有使用源码编译出来的内核文件,如果想要为编译的系统纳入自编译的内核,需要拉取对应分支的内核代码参与编译,并将编译结果放入`Android`源码树中的指定路径,最后再重新编译打包`Android`镜像。这样,生成的系统刷入手机后,使用的内核就是自编译的版本了。 -​ 首先,找到对应当前手机的内核分支,官网提供了详细的说明文档:https://source.android.com/docs/setup/build/building-kernels。根据下图可以看到,对应`Pixel`3测试机分支是`android-msm-crosshatch-4.9-android12`。 +​ 首先,找到对应当前手机的内核分支,官网提供了详细的说明文档:https://source.android.com/docs/setup/build/building-kernels 。根据下图可以看到,对应`Pixel`3测试机分支是`android-msm-crosshatch-4.9-android12`。 -![image-20230105221730348](.\images\image-20230105221730348.png) +![image-20230105221730348](images/image-20230105221730348.png) ​ 接下来,按照官网的说明拉取代码。 @@ -440,7 +440,7 @@ repo init -u https://android.googlesource.com/kernel/manifest -b android-msm-cro repo sync -j$(nproc --all) ``` -​ 成功拉取代码后,还需要将内核检出对应的`commit ID`,确保和官方镜像的内核分支一致。首先下载官方镜像,下载地址:https://developers.google.com/android/images,找到`Pixel 3`下的版本`SP1A.210812.016.A1`的官方镜像下载。 +​ 成功拉取代码后,还需要将内核检出对应的`commit ID`,确保和官方镜像的内核分支一致。首先下载官方镜像,下载地址:https://developers.google.com/android/images ,找到`Pixel 3`下的版本`SP1A.210812.016.A1`的官方镜像下载。 ​ 解压官方镜像,然后手机进入`bootloader`引导模式,运行官方镜像包中的`flash-all.bat`刷入手机,操作如下。 @@ -629,11 +629,11 @@ fastboot reboot fastboot ​ 这时的界面如下图,使用音量键减,切换到`Enter recovery`,然后按电源键进入`recovery`模式。 -![image-20230108190236615](.\images\image-20230108190236615.png) +![image-20230108190236615](images/image-20230108190236615.png) ​ `recovery`模式的界面,选择`Apply update from ADB` -![image-20230108190631803](.\images\image-20230108190631803.png) +![image-20230108190631803](images/image-20230108190631803.png) ​ 使用命令`adb devices`查看当前状态显示为`sideload`,即可直接通过命令`adb sideload ota.zip`进行刷机。 @@ -1003,7 +1003,7 @@ exit ​ 后面需要创建大量的子模块仓库,不可能在`web`页面上手动一个个的创建,下面使用命令来创建一个`manifests.git`仓库。这种方式需要`gitlab`账号的`Access Token`。可以在`web`中登录账号,点击右上角的用户图标,选择`Preferences`来到用户设置页面,然后进入`Access Tokens`栏目,填写`token`名称以及勾选权限,最后点击生成,例如生成的`token`为`27zctxyWZP9Txksenkxb`。流程见下图。 -![image-20230216211544482](.\images\image-20230216211544482.png) +![image-20230216211544482](images/image-20230216211544482.png) ​ 首先,在`gitlab`中手动创建一个根目录的`group`,这里创建了一个`android12_r3`的组,所有的子模块仓库都将在这个分组下。在`gitlab`页面中点击左上角`Groups->your Groups`。点击`New group`创建分组。成功创建后,记录下这个分组的`id`,比如我的根目录组`id`是6. diff --git a/chapter-03/README.md b/chapter-03/README.md index 59af2bf..6b5a9a8 100644 --- a/chapter-03/README.md +++ b/chapter-03/README.md @@ -282,7 +282,7 @@ static int run_init_process(const char *init_filename) ​ 这里能看到最后是通过`execve`拉起来了系统的第一个进程,`init`进程。总结内核启动的简单流程图如下。 -![startkernel](.\images\startkernel.png) +![startkernel](images/startkernel.png) ## 3.4 Init进程启动 @@ -1620,7 +1620,7 @@ pid-3638 W start CallStaticVoidMethod current proce ​ 结合观测到的代码流程,再看下面的一个汇总图。不需要完全理解启动过程中的所有的处理,重点是在这里留下一个大致的印象以及简单的整理。 -![image](.\images\android-boot.jpg) +![image](images/android-boot.jpg) ## 3.7 Android app应用启动 @@ -2493,7 +2493,7 @@ public void callApplicationOnCreate(Application app) { ​ 看一张经典的`Android`架构图。 -![在这里插入图片描述](.\images\android-framework.jpg) +![在这里插入图片描述](images/android-framework.jpg) ​ 从上图中可以看到`Framewok`的组成部分,它们的功能分别是: @@ -3048,11 +3048,11 @@ allow zygote apk_data_file:dir search; ​ 接着给`010 Editor`编辑器安装一个`ELF`格式解析的模板,在工具栏找到模板->模板存储库。搜索`ELF`,点击安装,操作见下图。 -![image-20230304135859598](.\images\image-20230304135859598.png) +![image-20230304135859598](images/image-20230304135859598.png) ​ 模板安装后,关闭文件,重新使用`010 Editor`打开后,将编辑方式切换为模板后,就能成功看到使用ELF格式解析so文件的结果了,如下图。 -![image-20230304140328010](.\images\image-20230304140328010.png) +![image-20230304140328010](images/image-20230304140328010.png) ​ `ELF`头部定义了`ELF`文件的基本属性和结构,也为后续的段表和节表等信息提供了重要的指导作用。加载`ELF`文件的第一步就是解析`ELF`头部后,再根据头部信息去解析其他部分的数据,`ELF`头部(`elf_header`)结构包含以下成员: @@ -3086,7 +3086,7 @@ allow zygote apk_data_file:dir search; ​ 下图是`010 Edtior`解析展示的结果图。 -![image-20230304141143199](.\images\image-20230304141143199.png) +![image-20230304141143199](images/image-20230304141143199.png) ​ `program header table`是一种用于描述可执行文件和共享库的各个段(`section`)在进程内存中的映射关系的结构,也称为段表。每个程序头表入口表示一个段。在`Linux`系统中,它是被操作系统用于将`ELF`文件加载到进程地址空间的重要数据结构之一。每个`program header table`具有相同的固定结构,相关字段如下: @@ -3108,7 +3108,7 @@ allow zygote apk_data_file:dir search; ​ 下图是编辑器中解析so看到的值 -![image-20230304142500744](.\images\image-20230304142500744.png) +![image-20230304142500744](images/image-20230304142500744.png) ​ `section header table`(节头表)是用于描述`ELF`文件中所有节(`section`)的元信息列表,也称为节表。它包含了每个节在文件中的位置、大小、类型、属性等信息。节头表的中相关字段如下: @@ -3134,7 +3134,7 @@ allow zygote apk_data_file:dir search; ​ 通过这些信息,`section header table`可以为执行链接和动态加载提供必要的元数据信息。样例数据看下图 -![image-20230304143100841](.\images\image-20230304143100841.png) +![image-20230304143100841](images/image-20230304143100841.png) ​ `ELF`文件中有各种节用于存放对应的信息,几个常见的节点存放数据的描述如下。 @@ -3152,7 +3152,7 @@ allow zygote apk_data_file:dir search; * `.shstrtab` 节点(`Section Header String Table`)存储节名称字符串,即每个节的名称和节头表中的节名称偏移量。它包含了`ELF`文件中每个节的字符串名称,方便读取程序在加载时快速访问。在`Android`中,`.shstrtab`节点是一个特殊的节,它位于节头表的末尾,可以通过`ELF`文件头的`e_shstrndx`字段找到。 -![image-20230304143003972](.\images\image-20230304143003972.png) +![image-20230304143003972](images/image-20230304143003972.png) ### 3.12.2 动态库加载流程 diff --git a/chapter-04/README.md b/chapter-04/README.md index e0adc1a..d530b68 100644 --- a/chapter-04/README.md +++ b/chapter-04/README.md @@ -36,7 +36,7 @@ ​ 下图展示的是`Google`官方的`Android`系统桌面图,以及自己编译`AOSP`的桌面图。 -![未标题-1](.\images\未标题-1.jpg) +![未标题-1](images/未标题-1.jpg) ​ 从上图中可以看到明显差异,`Google`官方的`ROM`相较于`AOSP` ROM多了一些功能和应用,例如谷歌应用套件:`Google Mobile Services(GMS)`包含了各种谷歌应用,如`Gmail、Google Maps、Play`商店等等。这些应用在`AOSP` ROM中是没有的。 @@ -69,7 +69,7 @@ ​ 在前文中和`Google`官方`ROM`对比的界面图,就是`Android`的`UI`界面中的壁纸了,壁纸是在手机主页面的背景图,壁纸可以在手机中进行切换修改,同样也可以直接修改默认的壁纸,默认壁纸的路径是`frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.png`。下图是`AOSP`中的默认壁纸。 -![image-20230305152441883](.\images\image-20230305152441883.png) +![image-20230305152441883](images/image-20230305152441883.png) ​ 知道壁纸素材的路径后,可以通过对这个素材进行替换来达到修改的目的,同样也可以通过查找设置的地方,修改默认设置选项,将壁纸切换为另一张图片来完成壁纸修改,前者的好处在于简单快捷,替换素材即可。而后者在于稳妥,随时可以调整切换回原素材。替换的方式较为简单就不再细说,这里看看通过修改设置的实现。 @@ -270,7 +270,7 @@ if __name__ == '__main__': 解压开机动画压缩包后的文件如下图。 -![image-20230424205456876](.\images\bootanimation.png) +![image-20230424205456876](images/bootanimation.png) `desc.txt`文件内容如下。 @@ -290,7 +290,7 @@ c 1 0 part5 ​ 查看其中一个目录下的文件如下图。 -![image-20230424210838393](.\images\play.png) +![image-20230424210838393](images/play.png) 对这些了解后,接着开始对其进行替换,为了便于简单演示,就不找新的素材进行替换了,直接将`androidtv`的开机动画替换为当前开机动画,找到文件`device/google/atv/products/bootanimations/bootanimation.zip`,将其复制到自定义的任意目录,例如新建目录`packages/bootstart/`,将启动动画拷贝到该目录中。然后在文件`build/make/target/product/generic_system.mk`添加配置,将其拷贝到`system/media/`目录下。相关修改如下。 diff --git a/chapter-05/README.md b/chapter-05/README.md index 1cbb38a..f6023d5 100644 --- a/chapter-05/README.md +++ b/chapter-05/README.md @@ -366,7 +366,7 @@ android_app_import { 有多种方式可以实现内置`JAR`包功能。接下来将介绍两种方法来集成一个自己编写的JAR包到系统中。首先要创建一个没有Activity的Android项目,并命名为`MyJar` ,如下图所示: -![image-20230308211039320](.\images\create_no_activity.png) +![image-20230308211039320](images/create_no_activity.png) ​ 接着简单的写两个测试函数。在最后内置成功后,将对这个函数进行调用测试是否内置成功。 @@ -520,7 +520,7 @@ protected void onCreate(Bundle savedInstanceState) { 首先准备一个测试项目,创建Native C++的项目。见下图。 -![image-20230308232615347](.\images\create_so_project.png) +![image-20230308232615347](images/create_so_project.png) ​ 这个项目并不需要启动,所以直接删除`MainActivity`文件,添加一个类来加载动态库。并且修改`cpp`中对应的函数名称,相关修改如下。 diff --git a/chapter-07/README.md b/chapter-07/README.md index 6baaba5..5a23569 100644 --- a/chapter-07/README.md +++ b/chapter-07/README.md @@ -934,7 +934,7 @@ DexFile::DexFile(const uint8_t* base, ​ 使用`010 Editor`工具,通过模板库在线安装`DEX.bt`模板,然后打开之前的样例文件,查看在例子中`header_`的真实数据。 -![image-20230325190621427](.\images\dex_header.png) +![image-20230325190621427](images/dex_header.png) ## 7.3 函数调用流程 @@ -994,7 +994,7 @@ public interface Opcodes { ​ 使用`010 Editor`工具,将样例程序解压后获得的`classes.dex`拖入`010 Editor`打开。看到结果如下。 -![image-20230325133550347](.\images\dex模板.png) +![image-20230325133550347](images/dex模板.png) ​ 接下来在`dex_class_defs`中寻找刚刚分析的目标类`MyCommon`。 @@ -1004,11 +1004,11 @@ struct class_def_item class_def[2205] public cn.rom.myjar.MyCommon 1243C4h 20h F ​ 将其展开后,能看到该`class`的详细信息,在上一节的类加载中,当`DEX`被解析后,加载的类在内存中就是以这样的结构存储着数据。 -![image-20230325134213390](.\images\def_class.png) +![image-20230325134213390](images/def_class.png) ​ 在其中的函数结构体下面的`code_item`类型的数据,就存储着该函数要执行的`java`字节码,继续展开该结构。 -![image-20230325134632085](.\images\codeitem1.png) +![image-20230325134632085](images/codeitem1.png) ​ 这里就能看到对该函数结构的描述了,`insns`中则存储着函数要执行的指令。每个指令的单位是`ushort`,即两个字节存储,将这里的三个指令转换为16进制表示则是。 diff --git a/chapter-08/README.md b/chapter-08/README.md index 57f24c5..5a8f9c5 100644 --- a/chapter-08/README.md +++ b/chapter-08/README.md @@ -980,7 +980,7 @@ D/k.myservicedem: [ROM] DefineClass write 2 /data/data/cn.rom.myservicedemo/defi 最后将这个文件传到电脑中,使用反编译工具`jadx`打开看到脱壳后的结果。 -![image-20230328004256155](.\images\tuoke.png) +![image-20230328004256155](images/tuoke.png) 在这个自动脱壳的例子中,并不限于在哪个调用时机来对其保存到文件,只要是在加载过程中,能获取到`DexFile`结果的地方,大多数都是能拿到动态加载壳所保护的目标。当你不确定的情况,可以先加上打桩信息,然后逐步去排查来判断你使用的调用时机是否可用。 diff --git a/chapter-11/README.md b/chapter-11/README.md index 4de3b4a..9af3f55 100644 --- a/chapter-11/README.md +++ b/chapter-11/README.md @@ -98,11 +98,11 @@ su ​接下来打开`ida`,选择`Debugger->Attach->Remote Arm linux/android debugger`,在`hostname`选项中填本地回环地址`127.0.0.1`,如下图。 -![image-20230403223624911](.\images\ida_debug_attach.png) +![image-20230403223624911](images/ida_debug_attach.png) ​点击`ok`后,则会展示所有`Android`中的进程,在其中进行过滤,找到目标进程。如下图 -![image-20230403223830842](.\images\ida_debug_process.png) +![image-20230403223830842](images/ida_debug_process.png) ​成功挂起调试后,检查日志中的 `ppid`,发现并没有任何变化,依然是`zygote`作为父进程。 @@ -226,7 +226,7 @@ Java_cn_rom_nativedemo_MainActivity_stringFromJNI( ​然后使用`ida`尝试对子进程进行调试,发现无法正常附加该进程了,错误如下。 -![image-20230405162058014](.\images\ida_attach_err.png) +![image-20230405162058014](images/ida_attach_err.png) ### 11.1.4 检测跟踪工具