sailfish 上修改 android Q
需要
- android系统镜像:10.0.0 (QP1A.191005.007.A3, Dec 2019) - d455265945bb936a653730031af7d7a4aba70dc0c775024666a53491c9833b61
- simg2img: Android sparse image转Linux image;用来将system.img, vendor.img转为可挂载的image
- img2simg: Linux image转Android sparse image;将可挂载的image转为可刷写的image
- X-Ways Forensics: 以16进制编辑文件内容,并支持ext4文件系统格式挂载文件
- IDA Pro:反汇编支持
- ARM 架构参考手册:汇编指令支持
解压谷歌官方镜像压缩包后得到以下文件树
1 | pwd |
工作目录和其文件树
下文的操作如无特殊说明,均在sailfish/sailfish-qp1a.191005.007.a3-factory-d4552659/sailfish-qp1a.191005.007.a3/image-sailfish-qp1a.191005.007.a3中进行
1 | pwd |
关闭avb
avb功能首先对分区进行每4kb字节的数据生成校验值,并将这些校验值合并到一起生成一个校验值数据块,校验值数据块的大小取决于分区大小并将这个数据块称作avb metadata,最后将avb metadata附加到image结尾,一同刷写进android设备。
android设备启动时,首先对avb metadata的root节点进行校验,如果通过则继续启动,否则将陷入bootloop.

如果不关闭avb功能,对分区的修改会导致android设备bootloop或者修改的内容无法生效(无法生效是通过FEC向前纠错来实现的)。
avb metadata头部的magic number可以控制avb是否生效。通过修改avb metadata magic number可以关闭avb功能;
校验元数据Magic number的定义如下:
#define VERITY_METADATA_MAGIC_NUMBER 0xb001b001
#define VERITY_METADATA_MAGIC_DISABLE 0x46464f56 // “VOFF”
在
android O上可以直接修改boot.img/fstab.sailfish文件,删除其中的verity字段即可;这是因为boot.img中的root分区并没有被avb保护,所以可以直接修改root分区中的文件后重新打包root分区到boot.img中进行刷写。
在
android Q上boot.img中的root分区被移动到system.img中,所以修改boot.img是不生效的。
关闭android Q的avb:
重命名
将
system.img和vendor.img分别命名为system.img.org和vendor.img.org以备份android sparse image转linux image
使用simg2img将
system.img.org和vendor.img.org分别转为system.img.raw和vendor.img.raw1
2simg2img system.img.org system.img.raw
simg2img vendor.img.org vendor.img.raw修改avb校验元数据Magic number
使用16进制编辑器
X-Ways Forensics分别修改system.img.raw和vendor.img.raw中的校验元数据,将0xb001b001修改为0x46464f56;搜索16进制字符串
01B001B000000000定位到校验元数据头Magic number的位置修改system.img.raw,偏移:
0x7EFE5000

修改vendor.img.raw,偏移:
0x1299b000

关闭selinux
android O
修改aosp源码init.cpp: selinux_is_enforcing函数,使该函数直接返回false。
重新编译init可执行文件。
boot.img解包后替换init可执行文件。
重新打包boot.img后刷写到android设备中。
android Q
boot.img中的根分区被移动到system.img中,init可执行文件也同样被移动到system.img中。
那么就需要对system.img中的init可执行文件进行修改,但是遇到一个问题就是system.img无法挂载为可读可写。并且remount时提示“写保护”。
1
2
3
4
5sudo mount system.img.raw system_raw
mount: /home/ccint3/Desktop/sailfish/sailfish-qp1a.191005.007.a3-factory-d4552659/sailfish-qp1a.191005.007.a3/image-sailfish-qp1a.191005.007.a3/system_raw: wrong fs type, bad option, bad superblock on /dev/loop6, missing codepage or helper program, or other error.
sudo mount -r system.img.raw system_raw
sudo mount -o rw,remount system_raw
mount: /home/ccint3/Desktop/sailfish/sailfish-qp1a.191005.007.a3-factory-d4552659/sailfish-qp1a.191005.007.a3/image-sailfish-qp1a.191005.007.a3/system_raw: cannot remount /dev/loop6 read-write, is write-protected.那么只能对system.img进行16进制编辑并保存,因此就需要
X-Ways Forensics工具提供支持,这个工具支持以16进制编辑system.img,并且可以将system.img转为ext4的文件系统并编辑。阅读android Q的源码,了解如何关闭selinux
android Q上开启selinux的源码发生了变化,对selinux进行了独立的源码管理
查看IsEnforcing的调用关系,并定位到selinux.cpp: SelinuxInitialize
修改system.img中的init可执行文件
X-Ways Forensics打开system.img,并将文件转换为磁盘
将system.img磁盘中的
system/bin/init可执行文件复制出来
使用
IDA Pro x64打开init可执行文件进行分析,因为从源码中我们知道SelinuxInitialize函数最终启动了selinux,因此我们在IDA中定位SelinuxInitialize函数,在SelinuxInitialize中有一些日志字符串,我们通过这些字符串就可以定位SelinuxInitialize的位置,例如:Loading SELinux policy;在IDA中shift + F12打开字符串窗口,并搜索Loading SELinux policy获取以下结果
对字符串参考引用后,最终定位到下图的位置

反汇编后,看到下图中的代码

关键代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13// 因为编译器内联汇编的原因,IsEnforcing函数直接被替换为了常量 1
// 与 security_getenforce 的返回值做比较
if ( (unsigned int)security_getenforce() != 1 )
{
// 因为编译器内联汇编的原因,所以security_setenforce函数的参数被替换为了常量1
// 通过 security_setenforce(1) 来启动selinux
// 因此我们需要修改security_setenforce函数的参数,将参数该为0,表示关闭selinux
v6 = (android::base *)security_setenforce(1LL);
if ( (_DWORD)v6 )
{
// ...
}
}我们的最终目的是修改
security_setenforce(1)为security_setenforce(0)即可关闭selinux;因为默认请情况下
security_getenforce返回1,表示默认启动selinux;那么security_getenforce() != 1则表示不会调用security_setenforce函数。至此我们明确了需要修改的两处内容:
1:将
security_getenforce() != 1修改为security_getenforce() != 02:将
security_setenforce(1LL)修改为security_setenforce(0LL)转至汇编代码,查看对应的汇编代码并修改,汇编代码如下图所示:

偏移
6A8FC处的汇编CMP W0, #1修改为CMP W0, #0CMP W0, #0的汇编指令:0x7100001F偏移
6A904处的汇编MOV W0, #1修改为MOV W0, #0MOV W0, #0的汇编指令:0x52800000修改后如图所示

将修改的内容写到init文件中,查看代码偏移
6A8FC和6A904对应的文件偏移6A8FC和6A904;使用
X-Ways Forensics打开init文件,并跳转至文件偏移后,将汇编指令写入该位置:
修改后:

关闭adbd指纹认证
android O
修改aosp adbd源码,重新编译之后,解包system.img,替换system.img中的adbd可执行文件,然后对system.img重新打包,之后刷写进android设备即可。
android Q
实现方式同
关闭selinux类似,定位adbd源码位置,查看如何在源码级关闭指纹认证。之后使用IDA Pro修改对应的汇编,最后在X-Ways Forensics中修改system.img中adbd的文件内容。源码位置:adb/daemon/main.cpp:adbd_main
关键代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14int adbd_main(int server_port) {
// ...
// If ro.adb.secure is unset, default to no authentication required.
auth_required = android::base::GetBoolProperty("ro.adb.secure", false);
if (is_device_unlocked()) { // allows no authentication when the device is unlocked.
auth_required = android::base::GetBoolProperty("ro.adb.secure", false);
}
adbd_auth_init();
// ...
return 0;
}全局变量
auth_required为true时,开启adbd指纹认证,为false时则关闭指纹认证。该变量通过
ro.adb.secure来控制,在default.prop中设置该属性,或者修改GetBoolProperty函数的返回值。在system.img中复制出
system/bin/adbd可执行文件,并使用IDA Pro x64打开。
通过ro.adb.secure字符串参考引用来定位adbd_main函数的位置。最终定位到如下图所示:
关键代码的反汇编如下:

查看对应的汇编代码如下:

偏移
20F0处的汇编AND W8, W0, #1修改为MOV W8, #0MOV W8, #0的汇编指令:0x52800008修改如下:

最后通过
X-Ways Forensics将修改的内容应用到system.imgsystem/bin/adbd中
禁止adbd root权限降级
修改方式同关闭adbd指纹认证
源码位置:
adb/daemon/main.cpp:drop_privileges
adb/daemon/main.cpp:should_drop_privileges
关键代码如下:
1 | int adbd_main(int server_port) { |
读取ro.secure和service.adb.root属性并判断是否需要降级。
我们需要将should_drop_privileges的返回值改为false;通过字符串ro.secure使用IDA Pro x64定位到目标代码位置。并反汇编后代码如下:

需要修改的汇编位置如下:
偏移2270处汇编 AND W8, W10, #1修改为MOV W8, #0

偏移22C8处汇编ORR W21, W9, W8修改为MOV W21, #0

偏移22D0处汇编AND W8, W8, #1修改为MOV W8, #0

偏移22E8处汇编AND W21, W21, W9修改为MOV W21, #0

偏移245C处汇编550400948000F8364B0400941F1800710D420054修改为0xD503201F*5

开机启动adbd,无需打开开发者模式并勾选USB调试
android Q上通过修改vendor.img中的etc/init/init.usb.rc来影响adbd的启动
将init.usb.rc修改为:
1 | on property:sys.usb.config=mtp |
修改后的init.usb.rc相比源文件有所缩短,为了不破坏文件,我们还需要维护这个文件在ext4文件系统inode块文件大小。通过X-Ways Forensics跳转至该文件的inode数据块,统计修改后这个文件的真实大小并修改inode数据库记录即可。


最后保存修改并刷写至手机即可