RecoverySystem升级包校验逻辑

Android OTA升级方式 Android OTA 方式 阅读详情

一、分析OTA升级包校验过程需要先了解下zip文件结构

参考文档:
https://ctf-wiki.org/en/misc/archive/zip/
https://www.cnblogs.com/li-sx/p/17531186.html
https://www.cnblogs.com/Zer0o/p/17174911.html

zip文件结构如下:

压缩源文件数据区核心目录目录结束
local file header + file data + data descriptorcentral directoryend of central directory record

1.1 压缩源文件数据区

  1. local file header :文件头用于标识该文件的开始,记录了该压缩文件的信息,文件头标识由固定值 50 4B 03 04 开头,也是 ZIP 的文件头的重要标志

  2. file data :文件数据记录了相应压缩文件的数据

  3. data descriptor :数据描述符用于标识该文件压缩结束,该结构只有在相应的 local file header 中通用标记字段的第 3 bit 设为 1 时才会出现,紧接在压缩文件源数据后

1.2 核心目录

​ 记录了压缩文件的目录信息,在这个数据区中每一条纪录对应在压缩源文件数据区中的一条数据

OffsetBytesDescription译
04Central directory file header signature = 0x02014b50核心目录文件 header 标识 =(0x02014b50)
42Version made by压缩所用的 pkware 版本
62Version needed to extract (minimum)解压所需 pkware 的最低版本
82General purpose bit flag通用位标记伪加密
102Compression method压缩方法
122File last modification time文件最后修改时间
142File last modification date文件最后修改日期
164CRC-32CRC-32 校验码
204Compressed size压缩后的大小
244Uncompressed size未压缩的大小
282File name length (n)文件名长度
302Extra field length (m)扩展域长度
322File comment length (k)文件注释长度
342Disk number where file starts文件开始位置的磁盘编号
362Internal file attributes内部文件属性
384External file attributes外部文件属性
424relative offset of local header本地文件头的相对位移
46nFile name目录文件名
46+nmExtra field扩展域
46+n+mkFile comment文件注释内容

1.3 目录结束

​ 目录结束标识存在于整个归档包的结尾,用于标记压缩的目录数据的结束。每个压缩文件必须有且只有一个 EOCD 记录

ZIP 文件头 50 4B 03 04 0A 00 00 00
ZIP 文件尾 50 4B 05 06 00 00 00 00 + 其他字符

二、校验接口如下:

public static void verifyPackage(File packageFile,ProgressListener listener,File deviceCertsZipFile) throws IOException, 		GeneralSecurityException {
	final long fileLen = packageFile.length();
	final RandomAccessFile raf = new RandomAccessFile(packageFile, "r");
	try {
		final long startTimeMillis = System.currentTimeMillis();
		if (listener != null) {
			listener.onProgress(0);
		}
		raf.seek(fileLen - 6);//定位到倒数第六个Byte
		byte[] footer = new byte[6];
		raf.readFully(footer);//读取最后6Byte到footer数组
		if (footer[2] != (byte)0xff || footer[3] != (byte)0xff) {//默认的这两位是空,别问为什么,我也不清楚为什么这么定义,如果这两位不为0xff则认为文件脚校验失败
                throw new SignatureException("no signature in file (no footer)");
		}
		final int commentSize = (footer[4] & 0xff) | ((footer[5] & 0xff) << 8);
		final int signatureStart = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8);//签名开始位置
         byte[] eocd = new byte[commentSize + 22];//zip文件结束标记,50 4B 05 06
		raf.seek(fileLen - (commentSize + 22));
         raf.readFully(eocd);
        // Check that we have found the start of the
        // end-of-central-directory record.
        if (eocd[0] != (byte)0x50 || eocd[1] != (byte)0x4b || eocd[2] != (byte)0x05 || eocd[3] != (byte)0x06) {
            throw new SignatureException("no signature in file (bad footer)");
        }
        for (int i = 4; i < eocd.length-3; ++i) {//查找文件结尾
            if (eocd[i  ] == (byte)0x50 && eocd[i+1] == (byte)0x4b && eocd[i+2] == (byte)0x05 && eocd[i+3] == (byte)0x06) {
                throw new SignatureException("EOCD marker found after start of EOCD");
            }
        }
        PKCS7 block = new PKCS7(new ByteArrayInputStream(eocd, commentSize+22-signatureStart, signatureStart));
        // Take the first certificate from the signature (packages
        // should contain only one).
        X509Certificate[] certificates = block.getCertificates();
        if (certificates == null || certificates.length == 0) {
            throw new SignatureException("signature contains no certificates");
        }
        X509Certificate cert = certificates[0];
        PublicKey signatureKey = cert.getPublicKey();//获取X.509 公共密钥
        SignerInfo[] signerInfos = block.getSignerInfos();
        if (signerInfos == null || signerInfos.length == 0) {
                throw new SignatureException("signature contains no signedData");
        }
        SignerInfo signerInfo = signerInfos[0];
        // Check that the public key of the certificate contained
        // in the package equals one of our trusted public keys.
        boolean verified = false;
        HashSet<X509Certificate> trusted = getTrustedCerts(deviceCertsZipFile == null ? DEFAULT_KEYSTORE : deviceCertsZipFile);
        for (X509Certificate c : trusted){
             if (c.getPublicKey().equals(signatureKey)) {
                    verified = true;
                    break;
             }
        }
        
        if (!verified) {
			throw new SignatureException("signature doesn't match any trusted key");
        }
        // The signature cert matches a trusted key.  Now verify that
        // the digest in the cert matches the actual file data.
        raf.seek(0);
        final ProgressListener listenerForInner = listener;
        SignerInfo verifyResult = block.verify(signerInfo, new InputStream() {
             // The signature covers all of the OTA package except the
             // archive comment and its 2-byte length.
			long toRead = fileLen - commentSize - 2;
			long soFar = 0;
             int lastPercent = 0;
             long lastPublishTime = startTimeMillis;
            
             @Override
			public int read() throws IOException {
				throw new UnsupportedOperationException();
			}
			@Override
			public int read(byte[] b, int off, int len) throws IOException {
                if (soFar >= toRead) {
                        return -1;
                }
                if (Thread.currentThread().isInterrupted()) {
                        return -1;
                }
                int size = len;
                if (soFar + size > toRead) {
					size = (int)(toRead - soFar);
                }
                int read = raf.read(b, off, size);
                soFar += read;
                if (listenerForInner != null) {
                    long now = System.currentTimeMillis();
                    int p = (int)(soFar * 100 / toRead);
                    if (p > lastPercent && now - lastPublishTime > PUBLISH_PROGRESS_INTERVAL_MS) {
                        lastPercent = p;
                        lastPublishTime = now;
                        listenerForInner.onProgress(lastPercent);
                    }
                }
                return read;
			}
        }
         final boolean interrupted = Thread.interrupted();
		if (listener != null) {
			listener.onProgress(100);
         }
		if (interrupted) {
                throw new SignatureException("verification was interrupted");
         }
         if (verifyResult == null) {
                throw new SignatureException("signature digest verification failed");
         }
	} finally {
		raf.close();
	}
    // Additionally verify the package compatibility.
    if (!readAndVerifyPackageCompatibilityEntry(packageFile)) {
            throw new SignatureException("package compatibility verification failed");
    }
}
                                     
Recovery系统升级(3)---软件流程 本章节结合源码剖析Recovery系统升级流程,从APP下载升级包后触发升级直到设备完成更新启动到Launcher的整个过程。 阅读详情

相关推荐

安卓车机ADB升级实战:从单台调试到批量刷机的避坑指南

ADB(Android Debug Bridge)是安卓调试桥,采用C/S架构实现PC与设备通信。在车机升级中,ADB用于推送升级包、刷写分区、抓取日志,是OTA、U盘、产线刷机的核心工具。掌握ADB版本管理、驱动安装、无线调试等技巧,可解决设备识别、授权、断连等常见问题。本文分享车机ADB升级的完整方案,涵盖环境准备、分区刷写、批量脚本及避坑经验,助你高效完成升级任务。

weixin_34026276的博客 469

Android SystemServer 系列专题【篇一:SystemServer启动流程】

SystemServer进程在android系统中占了举足轻重的地位,系统的所有服务和SystemUI都是由它启动。

qq_27672101的博客 2730

Android系统级应用开发实战:签名、权限、自启与OTA升级全解析

Android系统权限机制是应用开发中的关键壁垒,普通应用受沙箱限制难以访问系统级能力。理解系统签名、sharedUserId与特权应用(priv-app)的协作原理,是突破权限边界的基石。在定制ROM或工控设备场景中,开发者常需通过平台密钥签名将应用预置于系统目录,并借助权限白名单(allowlist)获取敏感API访问权。同时,开机自启(BOOT_COMPLETED)广播的时序约束与系统服务就绪状态,直接影响设备自动化流程的稳定性。OTA升级作为系统应用必备能力,涉及U盘挂载识别、签名校验及Recove

weixin_29173777的博客 245

Android OTA系统升级---原理一

最近公司有个需求需要搭建服务器,通过对终端系统进行网络升级。对相关知识进行整理。 android中OTA进行升级,首先需要使用源码中OTA升级包打包工具: build/tools/releasetools/ota_from_target_files 生成OTA包。包有两类:整体完全升级包和增量升级包。 在android系统编译环境下,在终端下使用make otapackage生成。

darwinlong的博客 4669

从TTL到短接:深度解析创维E900-S高安版三种刷机方式的原理与选择(附固件)

本文深度解析创维E900-S高安版三种刷机方式的原理与选择,包括免拆按键法、短接电阻法和TTL刷机法。针对高安芯片的特殊性,详细介绍了每种方法的操作步骤、技术原理及风险控制,并附有固件选择建议和优化技巧,帮助用户安全高效地完成刷机。

sym19862006的专栏 492

Python异步协程爬虫优化:解决aiohttp中ServerDisconnectedError的高效策略

本文深入探讨了在Python异步协程爬虫中使用aiohttp时,如何高效解决常见的ServerDisconnectedError问题。核心策略包括全局复用ClientSession、精细配置TCPConnector连接池、实施请求重试与速率限制,从而构建稳定、高效的异步爬虫架构,有效避免服务器断开连接错误。

pz890123的博客 861

Android 上层RecoverySystem类

尝试将imx51使用OTA方式进行recovery,将android_recovery.img放入/cache/分区下然后再下一次重启的时候进行分区更新,但发现放入的文件会被莫名删除,logcat中搜索到recovery相关log,tag为recoverysystem,尝试在android工程framework的os代码中寻找代码,找到相关代码,代码最后有将cache分区进行删除仅保留last_l

junjianzhang的专栏 4497

DS18B20温度传感器项目实战:从驱动编写到LCD显示,打造你的第一个温控系统

本文详细介绍了如何使用DS18B20温度传感器和LCD1602显示屏构建一个完整的温控系统。从硬件连接、OneWire协议实现到温度数据采集与LCD显示,全面解析了系统整合与工程化实现的关键步骤,帮助开发者快速掌握嵌入式温度监测系统的开发技巧。

weixin_42525328的博客 349

系统升级

系统升级

Frank.Zhang的专栏 867

Spacedesk保姆级教程:用旧平板给Windows电脑当副屏,从安装到避坑一步到位

本文提供Spacedesk保姆级教程,教你如何将旧平板变成Windows电脑的副屏,从软件安装到网络配置、显示优化及声音交互,一步步避开常见陷阱。通过WiFi实现扩展屏功能,支持4K分辨率和60Hz刷新率,让闲置设备焕发新生。

weixin_42524945的博客 1万+

LabVIEW小白福音:手把手教你用NI采集卡搭建振动信号采集系统(附源码和避坑指南)

本文为LabVIEW初学者提供详细的NI采集卡振动信号采集系统搭建指南,涵盖硬件连接、驱动安装、信号处理及性能优化等关键步骤。特别针对振动信号采集系统中的常见问题提供解决方案,并附源码和避坑指南,帮助用户快速上手并避免常见错误。

weixin_30485379的博客 103

Android OTA 升级 之 Recovery

目录1、Application层客户端的调用2、Framework层的RecoverySystem2.1 processPackage 解密升级包2.2 verifyPackage 校验升级包2.3 installPackage 安装升级包2.3.1 data目录固定block.map路径2.3.2 主系统向recovery系统写命令2.3.3 主系统的systemservice进程如何与recovery系统通信?2.3.4 为升级包申请元数据2.3.5 重启进入recovery系统3、Native层unc

qq_27672101的博客 2617

Origin画图——获取论文图中数据

有时候需要已发表论文的图的数据,这里使用Origin软件取值,并且可以实现对图形再次画图。 Step 1. 打开origin软件,点击工具,图形数字化工具。 Step 2. 打开要获取数据的图片。拉动红蓝坐标,与图的坐标轴所贴合。 Step 3. 打开要获取数据的图片。拉动红蓝坐标,与图的坐标轴所贴合。 Step 3. 设置参数横轴起始坐标为300-900,纵轴为1.8-2.6. Step 4. 点击第五个手动取点,取出图中数据点。 Step 5. 依次对原图进行取点。点击项目资源查看图。

qq_42278015的博客 5184

Android系统定制:OTA升级链路全解析(Data分区目录、接口与SELinux)

在Android系统定制开发中,OTA升级是设备持续迭代的核心能力,其实现横跨分区管理、系统服务与安全策略三大层面。升级包通常暂存于Data分区下的upgrade目录,由RecoverySystem或update_engine触发刷写,同时需要SELinux策略精确授权,确保进程级访问安全。理解这条链路的技术原理,不仅能帮助开发者高效定位avc denied等权限异常,还能支撑从系统应用到Framework层的完整升级能力建设。围绕这一主题,从目录选型、接口设计到SELinux规则编写,实际定制中的常见坑点

weixin_30522983的博客 286

springboot + netty 实现多客户端登录 超出设备 并下线

本文档旨在详细描述多平台登录控制的需求和实现方案。当前系统仅支持单设备登录,需扩展为支持多设备同时在线,并提供多种登录策略。文档详细列出四种策略:单平台登录、双平台登录、三平台登录和多平台同时在线,每种策略的具体限制和支持平台。技术实现方案包括数据结构设计、NettyUtils 工具类方法、WebSocketHandler 修改,以及相应的测试方案

Likefr 1879

Feko仿真GPS微带天线学习笔记

为了优化方便,使参数变量化,所以定义几个变量在CADFEKO左侧浏览器中如图所示定义变量,如下表贴片的尺寸切角尺寸feed =-6.4馈电点距坐标原点的距离sub_w=45基板的宽度sub_d=45基板的长度sub_h=5基板的高度epsR=5.6介电常数损耗角正切。

qq_36521926的博客 3992

安卓车机ADB升级实战:常见问题与解决方案全解析

ADB(Android Debug Bridge)是安卓调试桥,用于PC与安卓设备通信。在车机升级中,ADB通过调试通道实现固件推送、分区刷写和日志抓取,相比OTA更可靠可控。其技术价值在于支持底层固件替换和紧急修复,应用场景包括研发调试、产线批量升级和售后远程协助。然而,车机环境定制化高,常遇驱动安装、设备未授权、版本不匹配等问题。本文系统梳理ADB连接车机的核心细节与实操要点,提供常见问题速查表,助你高效完成安卓车机升级。

weixin_33857230的博客 293

ABAP CDS View 从入门到精通教程目录:ABAP CDS View 在 SAP 技术体系中的重要地位

学习 ABAP CDS View 的开发技术,体会其背后的设计理念,也就掌握了 SAP S/4HANA 底层数据模型的设计精髓。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 4777

Android行业终端开机动画动态替换实战指南

开机动画是Android系统启动时由init进程加载的视觉反馈机制,其本质依赖bootanimation.zip文件与系统启动参数ro.bootanim协同工作。实现动态替换需理解底层原理:init通过解析desc.txt控制帧序列,SELinux上下文决定文件可读性,而adb remount则是绕过只读分区的关键能力。该技术不适用于消费级手机,但在教育平板、车载中控、医疗PDA等具备工程调试通道的行业终端中具有明确价值——支持OEM定制、OTA更新和安全提示等场景。本文聚焦真实产线中可落地的App+系统协

weixin_34151004的博客 345

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Android系统OTA升级流程

Android系统进行升级的时候,有两种途径,一种是通过接口传递升级包路径自动升级,升级完之后系统自动重启;另一种是手动进入recovery模式下,选择升级包进行升级,升级完成之后停留在recovery界面,需要手动选择重启。前者多用于手机厂商的客户端在线升级,后者多用于开发和测试人员。但不管哪种,原理都是一样的,都要在recovery模式下进行升级。 1、获取升级包,可以从服务端下载,

dingfengnupt88的专栏 3万+

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Android10以上之APEX格式介绍

Android Pony EXpress (APEX) 是 Android 10 中引入的一种容器格式,用于较低级别系统模块的安装流程中。此格式可帮助更新不适用于标准 Android 应用模型的系统组件。一些示例组件包括原生服务和原生库、硬件抽象层 (HAL))、运行时 (ART) 以及类库。 “APEX”这一术语也可以指 APEX 文件。 背景 虽然 Android 支持通过软件包安装程序应用(例如 Google Play 商店应用)更新适用于标准应用模型(如服务、Activity)的模块,但是..

Android系统攻城狮 9391

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

InnoDB 内存结构总览:Change Buffer、AHI 与 Log Buffer

本文系统梳理InnoDB内存结构中的ChangeBuffer、Adaptive Hash Index(AHI)与LogBuffer。ChangeBuffer缓存二级索引页不在内存时的变更,减少随机IO,仅在页加载时合并;聚簇索引修改立即执行,因数据本身不可延后。AHI基于高频等值查询自动构建哈希索引,加速定位,仅支持等值操作。LogBuffer为redo log内存缓冲,配合磁盘日志实现崩溃恢复。三者均服务于性能优化,其中ChangeBuffer与AHI共享BufferPool空间,而LogBuffer独立

bksczm的博客 292

V8 机制、手写核心函数与 TypeScript 类型体操

或固定初始化(如数组先定长、对象先给全字段)保持类型与形状稳定;这些都比"换框架"更基础。只能处理纯数据(丢函数、Date、RegExp、循环引用报错)。生产级深拷贝要处理:原始值短路、循环引用、V8(Chrome / Node 的 JS 引擎)不是"解释一行跑一行"那么简单,它用。图:V8 执行流程——解释执行 + 热点优化,优化失败会 Deopt 回退。JS 内存自动管理,但"自动"不等于"不会漏"。是"固定间隔最多执行一次"。手写 Promise 是"懂异步"的试金石。是"停下后才执行最后一次",

qq_38468358的博客 349

Spring Boot 数据源与 MyBatis(-Plus) 自定义配置详解

本文详解 Spring Boot 中单数据源环境下 HikariCP 与 MyBatis-Plus 的自定义配置。通过手动声明 SqlSessionFactory,实现下划线转驼峰、日志输出、一级缓存策略等精细化控制。结合 HikariCP 核心参数调优(如最大连接数、连接生命周期),确保高性能与稳定性。适用于仅需访问单一数据库的场景,避免多数据源复杂性,兼顾性能与可维护性。

BADAO_LIUMANG_QIZHI的博客 206
上一篇: Android emmc信息及坏道检测
下一篇: Recovery过程
Devin.wang
博客等级 码龄8年 150粉丝 40原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值