ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

apktool重打包报错failed to read PNG signature:原理剖析与五种修复方案

apktool重打包报错failed to read PNG signature:原理剖析与五种修复方案 前几天帮一个朋友处理 APK 重打包的问题卡在了一个看起来很离谱的报错上outapk/res/drawable/c.png: error: failed to read PNG signature: file does not start用 apktool 解包一切正常资源目录也完整但走到apktool b重打包这一步构建工具就咬住这个res/drawable/c.png不放报错信息没有常见的“文件损坏”字样而是直说“read PNG signature”——文件没有以 PNG 签名开头。这个问题在 Windows 上用 apktool 解包重打包的场景里其实相当典型尤其是近年来很多应用会做资源层面的反篡改处理。这篇文章我会从 apktool 的重打包机制讲起把报错的原理、排查路径和修复方案完整过一遍希望能帮到同样卡在这个报错上的人。1. 报错发生在 apktool 重打包的哪一步1.1 解包和打包是两条完全独立的处理链先说一个经常被新手忽略的点apktool ddecode和apktool bbuild是两条几乎独立的处理链路前者负责把 APK 里的资源解码成可读、可改的文件后者负责把这些文件重新编码成一个新 APK。解包时apktool 做的事情是把resources.arsc中的资源表解析出来把res/目录下的二进制资源文件原样导出。对于图片文件apktool 默认不会做任何“读懂图片内容”的操作它只是把文件从 APK 里复制出来再根据资源类型决定文件后缀。所以哪怕这个文件根本不是一张真的 PNG只要它在 APK 里叫c.png解包时 apktool 也会老老实实把它写成outapk/res/drawable/c.png且全程不报错。到了重打包阶段情况就完全不同了。apktool b在处理res/目录时会调用资源编译工具apktool 2.7.0 之后默认使用 aapt2旧版本默认 aapt1把每个资源重新编译进资源表再重新打包。aapt 系列工具对图片资源是“要认真读内容”的它需要解析 PNG 的 IHDR、PLTE、tRNS 等 chunk才能决定资源压缩方式、生成资源表项。只要文件被当成 PNG 却不以标准 PNG 签名开头aapt2 就会直接放弃抛出这句failed to read PNG signature。关键认知在这里解包时不检查内容打包时才检查内容。这也是为什么很多问题文件在apktool d阶段完全看不出来一到apktool b就集中爆发。1.2 报错路径里藏着的有效信息把报错拆开看outapk是解包时用-o指定的输出目录名如果你用的不是这个名字这里会跟着变化。res/drawable/c.png是资源编译阶段正在处理的文件相对路径它直接指向你解包后的工作目录文件而不是 APK 内部的虚拟路径。failed to read PNG signature是 aapt2 对图片文件做的文件头校验失败。file does not start是对失败原因的补充文件最开头不是 PNG 魔数。也就是说这个问题几乎可以 100% 确定出在文件内容本身而不是 apktool 哪里配置错了也不是 Java 环境或者系统兼容性问题。搞清楚这一点后面排查就能少走很多弯路。2. PNG 文件签名校验机制与常见误判2.1 PNG 的 8 字节魔数PNG 格式规范里明确规定了文件开头 8 个字节必须是固定魔数89 50 4E 47 0D 0A 1A 0A转换成可读字符就是\x89PNG\r\n\x1a\n。这套设计非常古老最早是为了让图片查看器在加载文件之前快速判断“这到底是不是 PNG”。89用来确认二进制文件PNG是三字母签名\r\n用于检测 DOS/Unix 换行差异\x1a用于防止老式 DOS 下type命令把文件当作文本中断最后的\n再次校验换行。这套规则从 1996 年定下之后就没有变过aapt2 处理 drawable 资源时会先打开文件读取前若干字节与这个魔数比对。2.2 为什么 aapt2 非要读懂 PNG 内容很多人不理解我只是想把资源原样打包回去工具为什么非要搞清楚图片内容原因在于资源编译不是简单地把文件塞进压缩包。aapt2 需要为每个 drawable 资源生成资源表项还要决定它是否进入resources.arsc的预优化状态。对于 PNGaapt2 还会解析图片尺寸、尝试无损压缩、处理 9-patch 数据。如果文件头就不是合法 PNG后续所有解析都无法进行。与其在一个坏文件上反复尝试不如直接拒绝编译——这是设计上的取舍也是这个报错看起来“一刀切”的原因。2.3 哪些情况会触发“文件不以 PNG 开头”从签名校验的角度看只要文件前 8 个字节不等于上面的魔数就会触发这个错误。常见的有场景真实文件头说明JPEG 改名FF D8 FF E0常见于第三方工具二次处理WebP 改名52 49 46 46RIFFAndroid 4.0 原生支持 WebP但扩展名仍是 pngGIF 改名47 49 46 38较少见但可能存在0 字节文件无内容解包时被安全软件拦截或磁盘问题截断文件不足 8 字节下载/解压不完整加密/混淆数据自定义内容部分应用会对资源做保护处理XML 文件改名3C 3F 78 6D 6C资源表异常导致的错位3. 排查 c.png 问题的完整过程Windows 实战3.1 第一步用十六进制看文件头定位问题的第一步永远是确认文件真实内容而不是靠猜。Windows 下我习惯用 PowerShell 自带的Format-HexFormat-Hex .\outapk\res\drawable\c.png | Select-Object -First 1正常 PNG 的第一行应该是00000000 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52看到89 50 4E 47打头就说明文件头没问题。如果第一行是FF D8 FF E0这是 JPEG如果是52 49 46 46这是 RIFF 容器如果是47 49 46 38这是 GIF如果整行全是00或者根本没有任何数据就要怀疑 0 字节或者截断。Linux/macOS 上更简单直接用file命令就能识别真实格式file outapk/res/drawable/c.png输出类似JPEG image data, JFIF standard 1.01或Web/P image一眼定位真实类型。3.2 第二步判断文件是“损坏”还是“挂羊头”识别出真实内容之后基本就能归类如果是 JPEG/WebP这张资源在 APK 里本来就是别的格式被叫成了.png常见于经过第三方修改工具二次处理的 APK。如果文件只有几字节或者 0 字节大概率是解包过程中文件丢失可能是杀毒软件拦截、磁盘空间不足等原因。如果是大段乱码大概率是资源保护、加密或做混淆的 APK这类文件在运行时由壳代码临时解密。还有一个容易被忽略的情况某些 APK 使用了 Android 原生支持的 WebP 图片但为了兼容旧版本客户端或绕过某些检查故意保留.png后缀。Android 的BitmapFactory在运行时是按内容识别格式的所以 APK 安装后能正常显示但 aapt2 在编译阶段是“看扩展名当 PNG读内容验签名”两套判定逻辑不一致才会导致“能装不能用 apktool 重打包”的怪现象。3.3 第三步回到原始 APK 做对照把原始 APK 用 7-Zip 或 WinRAR 当压缩包打开找到res/drawable/c.png提取到临时目录然后对比文件哈希certutil -hashfile .\original_res\res\drawable\c.png SHA256 certutil -hashfile .\outapk\res\drawable\c.png SHA256如果两边哈希一致说明问题出在原始资源本身——这个文件从一开始就不是合法 PNG。如果哈希不一致说明是解包或中间处理环节弄坏了文件。这一步能直接确定后续修复方向千万不要跳过。3.4 高发诱因里最容易踩的三个坑在多次处理这类问题的经验里高发诱因集中在三个地方资源被第三方工具批量改过。市面上很多“一键改包工具”会把图片解压出来做批量压缩再用粗糙的逻辑写回 APK很容易产生大量伪 PNG。应用自身做了资源混淆或资源加密。一些大厂的 APK 会故意把资源扩展名换掉或修改文件头让常规工具无法正常处理。apktool 能拆出来但打包不回去对这些文件来说是“预期表现”。9-patch 图被误处理。xxx.9.png本质上也是 PNG但额外带 1 像素边框数据。某些工具会把 9-patch 的边框信息弄坏导致 aapt2 读取失败。注意这里的报错是c.png没有.9前缀基本可以排除 9-patch但如果你处理的是.9.png文件报出同样错误要优先怀疑这个点。4. 五种修复方案与选择逻辑4.1 方案 A从原始 APK 还原文件首选如果原始 APK 里的res/drawable/c.png是完好的那什么都不用纠结直接把它覆盖回去。Windows 下用 7-Zip 打开原 APK定位到res/drawable/c.png右键提取然后复制到outapk\res\drawable\c.png覆盖同名文件。命令行写法# PowerShell 下可用 7z 命令需确保 7-Zip 在 PATH 中 7z e original.apk res/drawable/c.png -oC:\temp\orig copy C:\temp\orig\res\drawable\c.png .\outapk\res\drawable\c.png还原后重新执行apktool b outapk -o new.apk大概率直接通过。这个方案最大好处是保留原始资源内容UI 显示效果完全不变只要原始文件是真 PNG几乎零风险。4.2 方案 B把真实格式转成合法 PNG如果原始 APK 里的 c.png 本身就是坏的或者你手边已经找不到原始资源那么只要确认文件确实是一张图片JPEG/WebP/GIF把它转成合法 PNG 就行。用 Python 的 Pillow 最方便from PIL import Image img Image.open(outapk/res/drawable/c.png) img.save(outapk/res/drawable/c.png_fixed.png, PNG)Pillow 会按内容自动识别真实格式不需要你手动指定。转换后把c.png_fixed.png改名为c.png覆盖原文件再重新打包。如果机器上有 ffmpeg也可以ffmpeg -i outapk/res/drawable/c.png -c:v png outapk/res/drawable/c_new.png注意转换过程中如果图片带透明通道或特殊色深可能会出现肉眼可感知的轻微色差。绝大多数 UI 场景没问题但如果你在处理图标类的精细资源转换后建议肉眼确认一遍。4.3 方案 C占位替换慎用如果这个资源只是一个无关紧要的占位图、装饰元素或者你根本不在乎它的显示效果用一张合法的 1x1 透明 PNG 替换也能解决报错from PIL import Image img Image.new(RGBA, (1, 1), (0, 0, 0, 0)) img.save(blank.png, PNG)覆盖到outapk/res/drawable/c.png后重新打包。但这里必须提醒如果该资源被界面引用替换后相关图标会变成空白。动手前建议去outapk/res/values/public.xml里查一下资源名c被谁引用判断替换成本。如果是启动图标、按钮图标这类核心视觉资源别用这个方案。4.4 方案 D用-r参数绕开资源重编译如果你的目标只是修改 smali 代码完全不需要动图片资源那么从一开始解包时就用-r参数保留原始资源apktool d original.apk -r -o outapk加了-r之后apktool 不会解码res/目录和resources.arsc重打包时资源文件会被整体复制回 APK不经过 aapt2 的图片校验自然就没有这个报错。这个办法对“只改代码不改资源”的场景非常实用也是我个人遇到资源保护型 APK 时最常用的路径。另一个可尝试的选项是切换打包用的 aapt 版本apktool b outapk --use-aapt1 -o new.apk旧版 aapt1 与 aapt2 的资源校验代码路径不同某些格式层面的错误在 aapt1 下有概率放行。但这属于“碰运气”的玩法不保证成功如果你的目标是正经修复资源问题还是要优先用方案 A/B。4.5 方案选择速查表方案适用场景是否保留 UI 效果操作复杂度从原始 APK 还原原始文件完好完全保留低转成合法 PNG文件是其它图片格式基本保留中占位替换资源不重要且未被引用不保留低-r保留资源解包只改代码不动资源完全保留低切换 aapt1其它方案均无效时不变低5. 重打包链路里容易一起踩的坑5.1 解决了报错别忘了签名即使这个 PNG 报错被解决apktool b出来的 APK 也是没有签名的装不上 Android 设备。需要先对齐再签名工具在 Android SDK 的build-tools目录里zipalign -f 4 new.apk aligned.apk apksigner sign --ks your.keystore --ks-key-alias your_alias aligned.apkWindows 下把build-tools\版本号目录加到 PATH或者在命令里写全路径。签名方案建议 v1v2 同时开启因为老设备需要 v1 兼容新设备优先验证 v2。如果目标 App 用到了 v3 签名或密钥轮换还要对应处理。很多人卡在“打包成功但装不上”的最后一公里十有八九是签名这一步漏了。5.2 拆包目录里别混入杂文件apktool b会扫描res/目录下的所有文件。如果你在解包之后往res/里手动加过文件或者把工作目录建在了一个带有隐藏文件的路径里比如.git仓库那些额外文件也可能会被当成资源参与编译导致各种莫名其妙的错误。当报错文件并不存在于原始 APK 时先回头确认它是不是自己带进去的。5.3 资源保护类 APK 要“对症下药”如果排查确认 c.png 是资源保护或混淆导致的问题修复方式取决于你的目的。单纯想重打包测试-r保留资源是最干净的路径。想真正修改资源大概率需要配合专门的资源还原工具甚至要先处理壳的校验逻辑。这不是单个报错能讲完的话题但我个人的原则是先明确改这个 APK 到底要改什么不要为了绕过报错做多余操作。5.4 我的标准操作顺序把几次折腾下来的经验整理一下遇到failed to read PNG signature时我默认按这个顺序处理先对比报错文件和原始 APK 里的同名文件确认是原始问题还是中间处理弄坏的。用Format-Hex或file判断真实格式归类到“格式不对”“文件损坏”“内容加密”三类。能还原就从原始 APK 还原不能还原就把真实图片转成合法 PNG。如果只是为了改代码直接用-r重新解包。打包后做 zipalign 签名安装验证。这套顺序解决过绝大多数同类资源报错也帮我避开了很多“修一个带出三个”的连锁问题。最后说点个人体会。这个报错看起来是 apktool 的“脾气”但本质上更像一个信号APK 里有些资源并不像表面上那样。做逆向和重打包最重要的能力不是背命令行参数而是遇到报错时能顺着工具原理往下推。理解了 aapt2 为什么要读那 8 个字节的 PNG 魔数这个报错也就没什么神秘的了。
返回列表