Jarvis's Blog

白帽子、全栈、IoT安全专家、嵌入式系统专家

给Pixel 7/7 Pro添加32位apk支持

很早就有传闻,Pixel 7将会是第一台纯64位Android设备,不能安装纯32位apk,笔者也拿到了一台美版Pixel 7,就尝试了一下,发现确实无法安装32位apk:

提示ISNTALL_FAILED_NO_MATCHING_ABIS,我们getporp看一下abi:

panther:/ $ getprop | grep abi
[ro.product.cpu.abi]: [arm64-v8a]
[ro.product.cpu.abilist]: [arm64-v8a]
[ro.product.cpu.abilist32]: []
[ro.product.cpu.abilist64]: [arm64-v8a]

panther:/ $ getprop | grep zygote
[ro.zygote]: [zygote64]

可以看到,abilist32是空的,整个手机只支持64位ABI,zygote使用的是zygote64,因此导致装不上。

同样的,对比一下Pixel 6的情况,发现Pixel 6是支持的:

oriole:/ $ getprop | grep abi
[ro.product.cpu.abi]: [arm64-v8a]
[ro.product.cpu.abilist]: [arm64-v8a,armeabi-v7a,armeabi]
[ro.product.cpu.abilist32]: [armeabi-v7a,armeabi]
[ro.product.cpu.abilist64]: [arm64-v8a]

oriole:/ $ getprop | grep zygote
[ro.zygote]: [zygote64_32]

支持了完整的armeabi-v7a和armeabi。并且使用的zygote是zygote64_32,可以支持32位apk。

进一步测试发现,Pixel 7设备中,存在32位lib目录,和app_process32(zygote32)程序,因为abilist中没有32位ABI,因此32位的zygote没有启动,实测也可以运行ELF32程序。

这就说明了,Pixel 7实际上只是系统层面限制不让安装,实际上32位native支持是完整的,那么,如果要让Pixel 7可以安装32位apk,是不是把abilist中的prop加回去就可以了呢?经过笔者一番折腾测试,发现是可行的。这里给出两个方案:

方案一:修改vendor.img中的build.prop文件刷机

经过最后确认,Pixel 7的ro.product.cpu.abilist属性是init进程通过读取/vendor/build.prop中的ro.vendor.product.cpu.abilist设置的,因此需要修改vendor.img,在这里给出修改方法:

首先去Google官方https://developers.google.com/android/images下载一份和手机中Build版本相同的刷机包,解压,从中得到vendor.img和vbmeta_vendor.img文件vendor.img,vbmeta.img,和vbmeta_vendor.img文件。接下来修改vendor.img中的build.prop

$ file vendor.img  
vendor.img: Linux rev 1.0 ext2 filesystem data, UUID=defad607-7fa7-5dc3-8ba5-b7843eb7d85d, volume name "vendor" (extents) (large files) (huge files)

这里看起来刷机包里面的vendor.img不是spare image,直接就是ext文件系统了,那试试直接挂载:

$ sudo mount vendor.img /mnt/test 
mount: /mnt/test: wrong fs type, bad option, bad superblock on /dev/loop16, missing codepage or helper program, or other error.

直接挂载失败,查看dmesg,发现失败原因是:

EXT4-fs (loop16): couldn’t mount RDWR because of unsupported optional features (4000)

网上搜了一下feature 0x4000是什么。

#define EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS 0x4000

通过一番测试发现,vendor可以挂载为ro,但是一旦挂载为rw,就会提示上面的错误

sudo mount -t ext4 -o loop,ro vendor.img /mnt/test #成功
sudo mount -t ext4 -o loop,rw vendor.img /mnt/test #失败

通过tun2fs查看,确实vendor.img比可以挂载为rw的ext4镜像多了一个shared_blocks feature。

$ tune2fs -l vendor.img | grep feat
Filesystem features:      ext_attr dir_index filetype extent sparse_super large_file huge_file uninit_bg dir_nlink extra_isize shared_blocks

那有没有办法去掉这个feature呢?经过了一番搜索,网上大多数人也都碰到这样的问题,但最后都没有什么特别的解决办法,找了很长时间终于在一个systemrw的项目里找到了一个可用的方法,就是用e2fsck -E unshare_block命令去掉feature,下面说过程。

首先将镜像文件扩容,否则在后面的操作中,会因为镜像没有空间而失败,我这里的vendor.img大小是665M,因为我们只需要改build.prop,稍微扩大一点就行,我这里给了670M。

fallocate -l 670M vendor.img

接下来对镜像进行操作,解除shared_blocks feature:

e2fsck -yf vendor.img
resize2fs vendor.img 670M
e2fsck -y -E unshare_blocks vendor.img

正常来说的话,这样就去掉了vendor.img的shared_blocks特性,我们重新用tun2fs看一下:

$ tune2fs -l vendor.img | grep feat
Filesystem features:      ext_attr dir_index filetype extent sparse_super large_file huge_file uninit_bg dir_nlink extra_isize

可以看到果然已经去掉了,现在尝试挂载,就可以成功挂载为rw了:

sudo mount -t ext4 -o loop,rw vendor.img /mnt/test

然后接下来我们修改build.prop文件,只需要修改如下几行就可以:

ro.vendor.product.cpu.abilist=arm64-v8a,armeabi-v7a,armeabi  #在后面加上armeabi-v7a,armeabi
ro.vendor.product.cpu.abilist32=armeabi-v7a,armeabi   #加上32位abi支持
ro.vendor.product.cpu.abilist64=arm64-v8a
ro.zygote=zygote64_32       #zygote64改为zygote64_32

修改完成以后保存:

sudo umount /mnt/test
e2fsck -yf vendor.img
resize2fs -M vendor.img  #压缩大小,去掉无用空间
e2fsck -yf vendor.img

然后接下来我们尝试将vendor.img刷回手机。但是这里还需要解决一个问题,就是Pixel 7相比Pixel 6,多了一个vbmeta_vendor分区,也就是说,开机过程中,也会对vendor分区进行校验,如果修改了vendor分区,也会因为校验不通过而无法启动,因此也需要对vbmeta_vendor分区进行patch经过多次测试,需要同时修改vbmeta和vbmeta_vendor(笔者做实验时,对vbmeta,vbmeta_system,vbmeta_vendor都进行了修改),去掉校验,修改vbmeta_vendor.img的第124个字节(其他vbmeta分区修改同理),改成03:(03的含义是disable dm-verity and verification)

然后启动到fastboot进行刷机:(解锁BL的过程教程很多,这里就不多说了)

adb reboot-bootloader
fastboot flash vbmeta vbmeta_patched.img
fastboot flash vbmeta_vendor vbmeta_vendor_patched.img
fastboot flash vbmeta_system vbmeta_system_patched.img  #可选
fastboot reboot fastboot #安卓10之后使用了动态分区,vendor分区需要启动到fastbootd才能刷
fastboot flash vendor vendor.img
fastboot reboot

注意事项:

以上刷完之后,很有可能会无法开机,因为当vbmeta/vbmeta_system/vbmeta_vendor这3个分区任何一个修改之后,都需要factory reset才可以正常启动,这一点和解锁BL的时候一样,所以如果碰到无法开机的情况,就进入fastboot尝试清除userdata再尝试开机:

fastboot -w

如果一切顺利的话,启动之后应该就可以看到32位zygote也同时在运行了,getporp查看也应该能看到abilist已经修改,此时应该可以安装32位apk。

panther:/ $ getprop | grep abi
[ro.product.cpu.abi]: [arm64-v8a]
[ro.product.cpu.abilist]: [arm64-v8a,armeabi-v7a,armeabi]
[ro.product.cpu.abilist32]: [armeabi-v7a,armeabi]
[ro.product.cpu.abilist64]: [arm64-v8a]
[ro.vendor.product.cpu.abilist]: [arm64-v8a,armeabi-v7a,armeabi]
[ro.vendor.product.cpu.abilist32]: [armeabi-v7a,armeabi]
[ro.vendor.product.cpu.abilist64]: [arm64-v8a]
panther:/ $ ps -e | grep zygote
root           988     1 14859936 167616 0                  0 S zygote64
root          1092     1 1859028 142600 0                   0 S zygote
webview_zygote 2194  988 14793052 76512 0                   0 S webview_zygote
u0_a171      22742   988 13865996 86540 0                   0 S com.android.chrome_zygote
panther:/ $

方案二:使用修改过的Magisk在系统启动过程中修改vendor/build.prop

这个方法适合于已经有Magisk的小伙伴使用,曾经我尝试过使用Magisk Module的resetprop来在启动阶段修改prop,但是失败了,阅读了Magisk源码后发现,magisk在注入init进程后才会加载module,而ro.zygote这属性,加载module之前就已经被init进程读取,因此在后面修改就无效了,所以唯一的办法就是修改magisk本身。

经过一些探索,最后发现只需要修改magisk源码的native/jni/init/rootdir.cpp文件即可,以下给出patch

--- rootdir.cpp	2022-11-13 19:25:15.462486225 +0800
+++ rootdir_patch.cpp	2022-11-13 19:24:28.287059699 +0800
@@ -61,6 +61,62 @@
     clone_attr(src, dest);
 }
 
+
+static void patch_pixel7_32bit_prop() {
+	bool isPixel7 = false;
+	if (access("/vendor/build.prop", F_OK) == 0) {
+		xmkdirs(dirname(ROOTOVL "/vendor/build.prop"), 0755);
+		FILE *fprop = xfopen(ROOTOVL "/vendor/build.new.prop", "we");
+		if (!fprop) {
+			PLOGE("%s: open %s failed", __PRETTY_FUNCTION__, "/vendor/build.prop");
+			return;
+		}
+		file_readline("/vendor/build.prop", [&](string_view line)mutable -> bool{
+		
+			if (str_contains(line, "Pixel 7")) {
+				isPixel7 = true;
+			}
+
+			if (str_starts(line, "ro.vendor.product.cpu.abilist=")) {
+				LOGD("Patch abilist\n");
+				fprintf(fprop, "ro.vendor.product.cpu.abilist=arm64-v8a,armeabi-v7a,armeabi\n");
+				return true;
+			}
+			
+			if (str_starts(line, "ro.vendor.product.cpu.abilist32=")) {
+				LOGD("Patch abilist32\n");
+				fprintf(fprop, "ro.vendor.product.cpu.abilist32=armeabi-v7a,armeabi\n");
+				return true;
+			}
+			
+			if (str_starts(line, "ro.zygote=zygote64")) {
+				LOGD("Patch zygote type\n");
+				fprintf(fprop, "ro.zygote=zygote64_32\n");
+				return true;
+			}
+			// Else just write the line
+			fprintf(fprop, "%s", line.data());
+			return true;
+		});
+
+		fclose(fprop);
+		
+		if (isPixel7) {
+			rename(ROOTOVL "/vendor/build.new.prop", ROOTOVL "/vendor/build.prop");
+			clone_attr("/vendor/build.prop", ROOTOVL "/vendor/build.prop");
+		} else {
+			int origfile = xopen("/vendor/build.prop", O_RDONLY | O_CLOEXEC);
+			auto vendordata = mmap_data("/vendor/build.prop");
+			int outfile = xopen(ROOTOVL "/vendor/build.prop", O_CREAT | O_WRONLY | O_CLOEXEC, 0);
+			xwrite(outfile, vendordata.buf, vendordata.sz);
+			fclone_attr(origfile, outfile);
+			close(origfile);
+			close(outfile);
+			unlink(ROOTOVL "/vendor/build.new.prop");
+		}
+	}
+}
+
 static void load_overlay_rc(const char *overlay) {
     auto dir = open_dir(overlay);
     if (!dir) return;
@@ -237,6 +293,9 @@
     } else {
         patch_init_rc("/init.rc", ROOTOVL "/init.rc", tmp_dir.data());
     }
+    
+    // if it is Pixel 7 Enable 32bit support
+    patch_pixel7_32bit_prop();
 
     // Extract magisk
     extract_files(false);
@@ -301,6 +360,8 @@
     if ((!treble && access("/sepolicy", F_OK) == 0) || !hijack_sepolicy()) {
         patch_sepolicy("/sepolicy", "/sepolicy");
     }
+    
+    
 
     chdir("/");
 
@@ -333,4 +394,3 @@
     execv("/sbin/magisk", argv);
     return 1;
 }
-

对于伸手党来说,可以直接到这里下载笔者已经编译好的Magisk app,对于已经安装过Magisk的小伙伴来说,只需要安装这个新的Magisk然后直接在app中重新点击安装就可以,或者重新patch init_boot.img然后用fastboot刷入:

Magisk-v25.2_patched.apk

使用Magisk同样可以达到目的。

总结

Pixel 7取消掉32位应用支持说明了Google的态度,虽然Pixel 7可以通过一定手段开启,但是可以预见在明年,armv9架构的芯片将物理不支持32位指令集,到那时候将完全不可能支持。因此,建议所有仍然只编译32位库的app尽早迁移到纯64位系统。

鸣谢

感谢ThomasKing大佬提供的思路:https://github.com/ThomasKing2014/Pixel7_32bit_helper

安卓模拟器(Android Emulator AVD)移植PixelExperiece教程

上一篇
评论
发表评论 说点什么
还没有评论
1077
4

    浙公网安备 33011002014706号