IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android加固在Android11上的问题 -> 正文阅读

[移动开发]Android加固在Android11上的问题

由于工作需求,近来要研究下apk加固。昨天在网上找了个加固代码,一顿操作下来,加固ok,在Android5和Android10上的机器上跑起来ok,到了Android11上安装出错。

错误信息如下:

adb: failed to install Xxxx.apk: Failure [-124: Failed parse during installPackageLI: Targeting R+ (version 30 and above) requires the resources.arsc of installed APKs to be stored uncompressed and aligned on a 4-byte boundary]

?看来是android11的行为变更,官方说明:

如果以 Android?11(API 级别?30)或更高版本为目标平台的应用包含压缩的?resources.arsc?文件或者如果此文件未按 4 字节边界对齐,应用将无法安装。如果存在其中任意一种情况,系统将无法对此文件进行内存映射。无法进行内存映射的资源表必须读入 RAM 中的缓冲区,从而给系统造成不必要的内存压力,并大大增加设备的 RAM 使用量。

原来在Android11上为了减少ram压力,需要resources.arsc不要压缩,并且4字节对齐。

本以为还要看源码鼓捣下,没想到文档已经说得很清楚了,还是要多看文档啊!接下来就简单了,按照以下方式打包签名即可:

1.打包APK时,不要对resources.arsc进行压缩

2.将上一步打包好的apk做V1签名

3.将V1签名的apk做zipalign四字节对齐

4.对齐后的apk进行V2签名

上代码,需要的自取,记得把zipalign、apksigner(SDK下的build-tools/30.0.2)添加到环境变量。

// 打包
System.out.println("打包APK");
File unsignedApk = new File("output/unsigned.apk");
ZipUtil.zip(apkUnzipDir, unsignedApk);
			
// 删除解压目录
FileUtils.delete("output/unzip/");
			
// 签名
System.out.println("签名APK");
File signedApk = new File("output/signed.apk");
SignUtils.apkSignature(unsignedApk, signedApk, "keystore/test.jks", "test123", "test", "test123");
System.out.println("Finished!!!");
public class ZipUtil {

	public static void unZip(File zip, File dir) {
		try {
			dir.delete();
			ZipFile zipFile = new ZipFile(zip);
			Enumeration<? extends ZipEntry> entries = zipFile.entries();
			while (entries.hasMoreElements()) {
				ZipEntry zipEntry = entries.nextElement();
				String name = zipEntry.getName();
				if (name.equals("META-INF/CERT.RSA") || name.equals("META-INF/CERT.SF")
						|| name.equals("META-INF/MANIFEST.MF")) {
					continue;
				}
				if (!zipEntry.isDirectory()) {
					File file = new File(dir, name);
					if (!file.getParentFile().exists())
						file.getParentFile().mkdirs();
					FileOutputStream fos = new FileOutputStream(file);
					InputStream is = zipFile.getInputStream(zipEntry);
					byte[] buffer = new byte[1024];
					int len;
					while ((len = is.read(buffer)) != -1) {
						fos.write(buffer, 0, len);
					}
					is.close();
					fos.close();
				}
			}
			zipFile.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 打包apk
	 * @param dir apk解压后的文件夹路径
	 * @param zip 输出打包后的apk
	 * @throws Exception
	 */
	public static void zip(File dir, File zip) throws Exception {
		zip.delete();
		CheckedOutputStream cos = new CheckedOutputStream(new FileOutputStream(zip), new CRC32());
		ZipOutputStream zos = new ZipOutputStream(cos);
		compress(dir, zos, "");
		zos.flush();
		zos.close();
	}

	private static void compress(File srcFile, ZipOutputStream zos, String basePath) throws Exception {
		if (srcFile.isDirectory()) {
			compressDir(srcFile, zos, basePath);
		} else {
			compressFile(srcFile, zos, basePath);
		}
	}

	private static void compressDir(File dir, ZipOutputStream zos, String basePath) throws Exception {
		File[] files = dir.listFiles();
		if (files.length < 1) {
			ZipEntry entry = new ZipEntry(basePath + dir.getName() + "/");
			zos.putNextEntry(entry);
			zos.closeEntry();
		}
		for (File file : files) {
			compress(file, zos, basePath + dir.getName() + "/");
		}
	}

	private static void compressFile(File file, ZipOutputStream zos, String dir) throws Exception {

		String dirName = dir + file.getName();

		String[] dirNameNew = dirName.split("/");

		StringBuffer buffer = new StringBuffer();

		if (dirNameNew.length > 1) {
			for (int i = 1; i < dirNameNew.length; i++) {
				buffer.append("/");
				buffer.append(dirNameNew[i]);

			}
		} else {
			buffer.append("/");
		}

		ZipEntry entry = new ZipEntry(buffer.toString().substring(1));
		if ("resources.arsc".equals(file.getName())) {
			entry.setMethod(ZipEntry.STORED);
			entry.setSize(file.length());
			entry.setCrc(calFileCRC32(file));
		}
		zos.putNextEntry(entry);
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
		int count;
		byte data[] = new byte[1024];
		while ((count = bis.read(data, 0, 1024)) != -1) {
			zos.write(data, 0, count);
		}
		bis.close();
		zos.closeEntry();
	}
	
	private static long calFileCRC32(File file) throws IOException {
		FileInputStream fi = new FileInputStream(file);
		CheckedInputStream checksum = new CheckedInputStream(fi, new CRC32());
		while (checksum.read() != -1) { }
		long temp = checksum.getChecksum().getValue();
		fi.close();
		checksum.close();
		return temp;
	}
}
public class SignUtils {

	private SignUtils() {
		throw new UnsupportedOperationException("u can't instantiate me...");
	}
	
	private static void exec(String[] cmd, String execName) throws IOException, InterruptedException {
		Process process = Runtime.getRuntime().exec(cmd);
		System.out.println("start " + execName);
		try {
			process.waitFor();
		} catch (InterruptedException e) {
			e.printStackTrace();
			throw e;
		}
		if (process.exitValue() != 0) {
			InputStream inputStream = process.getErrorStream();
			int len;
			byte[] buffer = new byte[2048];
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			while ((len = inputStream.read(buffer)) != -1) {
				bos.write(buffer, 0, len);
			}
			System.out.println(new String(bos.toByteArray(), "gbk"));
			throw new RuntimeException(execName + " execute fail");
		}
		System.out.println("finish " + execName);
		process.destroy();
	}

	/**
	 * V1签名
	 */
	private static String signature(File unsignedApk, String keyStore, String keyPwd, String alias, String alisaPwd)
			throws InterruptedException, IOException {
		String path = unsignedApk.getAbsolutePath();
		String v1Name = path.substring(0, path.indexOf(".apk")) + "_v1.apk";
		String cmd[] = { "cmd.exe", "/C ", "jarsigner", "-sigalg", "SHA1withRSA", "-digestalg", "SHA1", "-keystore",
				keyStore, "-storepass", keyPwd, "-keypass", alisaPwd, "-signedjar", v1Name,
				unsignedApk.getAbsolutePath(), alias };
		
		exec(cmd, "v1 sign");
		
		FileUtils.delete(path);
		
		return v1Name;
	}
	
	// zipalign -p 4 input\app-release-unsigned.apk input\app-release-unsigned.apk
	private static String apkZipalign(String v1Apk) throws IOException, InterruptedException {
		String zipalignName = v1Apk.substring(0, v1Apk.indexOf(".apk")) + "_align.apk";
		String cmd[] = {"cmd.exe", "/C ", "zipalign", "-p", "4", v1Apk, zipalignName};
		
		exec(cmd, "zipalign");
		
		FileUtils.delete(v1Apk);
		
		return zipalignName;
	}

	//apksigner.jar sign  --ks key.jks --ks-key-alias releasekey  --ks-pass pass:pp123456  --key-pass pass:pp123456  --out output.apk  input.apk    
	public static void apkSignature(File unsignedApk, File signedApk, String keyStore, String keyPwd, String alias, String alisaPwd) throws IOException, InterruptedException {
		String v1Name = signature(unsignedApk, keyStore, keyPwd, alias, alisaPwd);
		String zipalignName = apkZipalign(v1Name);
		String cmd[] = { "cmd.exe", "/C ", "apksigner", "sign", "--ks", keyStore, "--ks-pass", "pass:" + keyPwd,
				"--ks-key-alias", alias, "--key-pass", "pass:" + alisaPwd,
				"--out", signedApk.getAbsolutePath(), zipalignName };
		
		exec(cmd, "v2 sign");
		
		FileUtils.delete(zipalignName);
		FileUtils.delete(signedApk.getAbsolutePath() + ".idsig");
	}
}

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-29 09:28:21  更:2021-08-29 09:30:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/31 6:13:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码