[BUG] Invalid CEN header (encrypted entry)
APKtool fails to open a valid android package (which can be installed). The APK seems packed and encrypted. aapt/aapt2/android package installer correctly parses it. Unzip, apktool and other analysis tools fail to open it.
Information
-
Apktool Version (
apktool -version) - 2.12.0 - Operating System (Mac, Linux, Windows) - Linux
- APK From? (Playstore, ROM, Other) - Malware from web
-
Java Version (
java --version) - openjdk 11.0.29 2025-10-21
Stacktrace/Logcat
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
I: Using Apktool 2.12.0-dirty on new_chrome_v7.1.3.apk with 4 threads
Exception in thread "main" brut.androlib.exceptions.AndrolibException: brut.directory.DirectoryException: java.util.zip.ZipException: invalid CEN header (encrypted entry)
at brut.androlib.meta.ApkInfo.hasSources(ApkInfo.java:271)
at brut.androlib.ApkDecoder.decodeSources(ApkDecoder.java:111)
at brut.androlib.ApkDecoder.decode(ApkDecoder.java:83)
at brut.apktool.Main.cmdDecode(Main.java:524)
at brut.apktool.Main.main(Main.java:333)
Caused by: brut.directory.DirectoryException: java.util.zip.ZipException: invalid CEN header (encrypted entry)
at brut.directory.ZipRODirectory.<init>(ZipRODirectory.java:54)
at brut.directory.ZipRODirectory.<init>(ZipRODirectory.java:38)
at brut.directory.ExtFile.getDirectory(ExtFile.java:51)
at brut.androlib.meta.ApkInfo.hasSources(ApkInfo.java:269)
... 4 more
Caused by: java.util.zip.ZipException: invalid CEN header (encrypted entry)
at java.base/java.util.zip.ZipFile$Source.zerror(ZipFile.java:1776)
at java.base/java.util.zip.ZipFile$Source.initCEN(ZipFile.java:1726)
at java.base/java.util.zip.ZipFile$Source.<init>(ZipFile.java:1470)
at java.base/java.util.zip.ZipFile$Source.get(ZipFile.java:1433)
at java.base/java.util.zip.ZipFile$CleanableResource.<init>(ZipFile.java:743)
at java.base/java.util.zip.ZipFile$CleanableResource.get(ZipFile.java:860)
at java.base/java.util.zip.ZipFile.<init>(ZipFile.java:258)
at java.base/java.util.zip.ZipFile.<init>(ZipFile.java:187)
at java.base/java.util.zip.ZipFile.<init>(ZipFile.java:201)
at brut.directory.ZipRODirectory.<init>(ZipRODirectory.java:52)
... 7 more
Steps to Reproduce
-
apktool d malware.apk - apktool crashes
Frameworks
If this APK is from an OEM ROM (Samsung, HTC, LG). Please attach framework files
(.apks that live in /system/framework or /system/priv-app)
APK
If this APK can be freely shared, please upload/attach a link to it.
Questions to ask before submission
- Have you tried
apktool d,apktool bwithout changing anything? - yes - If you are trying to install a modified apk, did you resign it? no
- Are you using the latest apktool version? almost
Dang so now we have CEN header corruption and encrypted entry - https://github.com/iBotPeaches/Apktool/issues/3953
The challenge here is the Java spec is getting too strict and all these tools that abuse tiny spec criteria that AOSP doesn't enforce is poking a hole in this. I fear we are nearing a point of needing some user-land zip parser, which sounds like an absolute nightmare.
A few years ago (or like 7) Apktool did use Apache Commons as it had a more of a robust parser for spec - https://commons.apache.org/proper/commons-compress/zip.html
Dang so now we have CEN header corruption and encrypted entry - #3953
The challenge here is the Java spec is getting too strict and all these tools that abuse tiny spec criteria that AOSP doesn't enforce is poking a hole in this. I fear we are nearing a point of needing some user-land zip parser, which sounds like an absolute nightmare.
A few years ago (or like 7) Apktool did use Apache Commons as it had a more of a robust parser for spec - https://commons.apache.org/proper/commons-compress/zip.html
Ideally using the same ZIP package Android uses (modified java.util.zip or libziparchive?) would be the safest bet, but the amount of code, imports and low-level manipulation is quite messy. Android changes are tagged with "Android-changed" comments. https://android.googlesource.com/platform/libcore/+/refs/tags/android-16.0.0_r4/ojluni/src/main/java/java/util/zip Is it worth going that route for malware APKs though?
I do a see a benefit of research for malware, but to your point - it seems painful to bring into Apktool.
Same issue. Another apk https://drive.google.com/file/d/11oMjVJ0rjcbY6bmD5HuWhDuqoA8MbS0y/view 7z pass: apktool
apktool d 65c521753655bbd455913fcadb3bb03b0db1b6ec.apk
I: Using Apktool 2.12.1 on 65c521753655bbd455913fcadb3bb03b0db1b6ec.apk with 8 threads
Exception in thread "main" brut.androlib.exceptions.AndrolibException: brut.directory.DirectoryException: java.util.zip.ZipException: invalid CEN header (encrypted entry)
at brut.androlib.ApkDecoder.decodeSources(SourceFile:271)
at brut.androlib.ApkDecoder.decode(SourceFile:83)
at brut.apktool.Main.main(SourceFile:532)
Caused by: brut.directory.DirectoryException: java.util.zip.ZipException: invalid CEN header (encrypted entry)
at brut.directory.ZipRODirectory.<init>(SourceFile:54)
at brut.directory.ExtFile.getDirectory(SourceFile:51)
at brut.androlib.ApkDecoder.decodeSources(SourceFile:269)
... 2 more
Caused by: java.util.zip.ZipException: invalid CEN header (encrypted entry)
at java.base/java.util.zip.ZipFile$Source.zerror(ZipFile.java:1831)
at java.base/java.util.zip.ZipFile$Source.checkAndAddEntry(ZipFile.java:1205)
at java.base/java.util.zip.ZipFile$Source.initCEN(ZipFile.java:1767)
at java.base/java.util.zip.ZipFile$Source.<init>(ZipFile.java:1542)
at java.base/java.util.zip.ZipFile$Source.get(ZipFile.java:1506)
at java.base/java.util.zip.ZipFile$CleanableResource.<init>(ZipFile.java:704)
at java.base/java.util.zip.ZipFile.<init>(ZipFile.java:204)
at java.base/java.util.zip.ZipFile.<init>(ZipFile.java:150)
at java.base/java.util.zip.ZipFile.<init>(ZipFile.java:164)
at brut.directory.ZipRODirectory.<init>(SourceFile:52)
... 4 more
Like said above, this isn't an Apktool issue. Apktool uses the Java Standard Library to handle valid ZIP files. Those APKs were tempered with in a way only AOSP's libziparchive tolerates, but they are technically corrupted. Fix the APK first using this little script I've written, then Apktool can read it properly.
usage: apkfix.py <apkfile>
#!/usr/bin/env python3
# Applies the following changes to fields in Central Directory Entries and Local File Headers:
# - Sets encrypted file bit to 0 in the general purpose bit flag.
# - Sets compression method to STORE if it's not DEFLATE.
import sys
import struct
EOCD_SIG = b'\x50\x4b\x05\x06'
EOCD_MIN_LEN = 22
COMMENT_MAX_LEN = 0xFFFF
EOCD_MAX_SEARCH = EOCD_MIN_LEN + COMMENT_MAX_LEN
CDFH_SIG = b'\x50\x4b\x01\x02'
CDFH_LEN = 46
LFH_SIG = b'\x50\x4b\x03\x04'
def find_eocd(f):
f.seek(0, 2)
size = f.tell()
max_search = min(size, EOCD_MAX_SEARCH)
f.seek(-max_search, 2)
data = f.read(max_search)
idx = data.rfind(EOCD_SIG)
if idx < 0:
raise RuntimeError('EOCD not found')
data = data[idx:]
if len(data) < EOCD_MIN_LEN:
raise RuntimeError('EOCD too short')
return data
def fix_fields(f):
flags, = struct.unpack('<H', f.read(2))
if flags & 0x1:
f.seek(-2, 1)
f.write(struct.pack('<H', flags & ~0x1))
compr, = struct.unpack('<H', f.read(2))
if compr != 0 and compr != 8:
f.seek(-2, 1)
f.write(struct.pack('<H', 0))
def main():
if len(sys.argv) != 2:
print('usage: apkfix.py <apkfile>')
sys.exit(1)
with open(sys.argv[1], 'r+b') as f:
eocd_data = find_eocd(f)
cdfh_cnt, = struct.unpack('<H', eocd_data[10:12])
cd_off, = struct.unpack('<I', eocd_data[16:20])
f.seek(cd_off)
cdfh_idx = 0
while cdfh_idx < cdfh_cnt:
cdfh_off = f.tell()
cdfh_sig = f.read(4)
if cdfh_sig != CDFH_SIG:
raise RuntimeError('invalid CDFH')
f.seek(4, 1)
fix_fields(f)
f.seek(16, 1)
name_len, extra_len, comment_len = struct.unpack('<HHH', f.read(6))
f.seek(8, 1)
lfh_off, = struct.unpack('<I', f.read(4))
f.seek(lfh_off)
lfh_sig = f.read(4)
if lfh_sig != LFH_SIG:
raise RuntimeError('invalid LFH')
f.seek(2, 1)
fix_fields(f)
f.seek(cdfh_off + CDFH_LEN + name_len + extra_len + comment_len)
cdfh_idx += 1
if __name__ == '__main__':
main()
Like said above, this isn't an Apktool issue. Apktool uses the Java Standard Library to handle valid ZIP files. Those APKs were tempered with in a way only AOSP's libziparchive tolerates, but they are technically corrupted. Fix the APK first using this little script I've written, then Apktool can read it properly.
usage: apkfix.py <apkfile>#!/usr/bin/env python3
Applies the following changes to fields in Central Directory Entries and Local File Headers:
- Sets encrypted file bit to 0 in the general purpose bit flag.
- Sets compression method to STORE if it's not DEFLATE.
import sys import struct
EOCD_SIG = b'\x50\x4b\x05\x06' EOCD_MIN_LEN = 22 COMMENT_MAX_LEN = 0xFFFF EOCD_MAX_SEARCH = EOCD_MIN_LEN + COMMENT_MAX_LEN CDFH_SIG = b'\x50\x4b\x01\x02' CDFH_LEN = 46 LFH_SIG = b'\x50\x4b\x03\x04'
def find_eocd(f): f.seek(0, 2) size = f.tell() max_search = min(size, EOCD_MAX_SEARCH) f.seek(-max_search, 2) data = f.read(max_search) idx = data.rfind(EOCD_SIG) if idx < 0: raise RuntimeError('EOCD not found') data = data[idx:] if len(data) < EOCD_MIN_LEN: raise RuntimeError('EOCD too short') return data
def fix_fields(f): flags, = struct.unpack('<H', f.read(2)) if flags & 0x1: f.seek(-2, 1) f.write(struct.pack('<H', flags & ~0x1)) compr, = struct.unpack('<H', f.read(2)) if compr != 0 and compr != 8: f.seek(-2, 1) f.write(struct.pack('<H', 0))
def main(): if len(sys.argv) != 2: print('usage: apkfix.py
') sys.exit(1) with open(sys.argv[1], 'r+b') as f: eocd_data = find_eocd(f) cdfh_cnt, = struct.unpack('<H', eocd_data[10:12]) cd_off, = struct.unpack('<I', eocd_data[16:20]) f.seek(cd_off) cdfh_idx = 0 while cdfh_idx < cdfh_cnt: cdfh_off = f.tell() cdfh_sig = f.read(4) if cdfh_sig != CDFH_SIG: raise RuntimeError('invalid CDFH') f.seek(4, 1) fix_fields(f) f.seek(16, 1) name_len, extra_len, comment_len = struct.unpack('<HHH', f.read(6)) f.seek(8, 1) lfh_off, = struct.unpack('<I', f.read(4)) f.seek(lfh_off) lfh_sig = f.read(4) if lfh_sig != LFH_SIG: raise RuntimeError('invalid LFH') f.seek(2, 1) fix_fields(f) f.seek(cdfh_off + CDFH_LEN + name_len + extra_len + comment_len) cdfh_idx += 1if name == 'main': main()
Great, it works fine.