[2/2] New zip magic

This commit is contained in:
LoveSy 2021-08-29 04:04:12 +08:00
parent 3cc7496b55
commit 0c51939e6d
6 changed files with 102 additions and 70 deletions

View File

@ -152,7 +152,7 @@ public class CentralDirectoryHeader implements Cloneable {
file = zFile; file = zFile;
} }
public CentralDirectoryHeader link(String name, byte[] encodedFileName, GPFlags flags) { public CentralDirectoryHeader link(String name, byte[] encodedFileName, GPFlags flags, ZFile file) {
var newData = new CentralDirectoryHeader(name, var newData = new CentralDirectoryHeader(name,
encodedFileName, encodedFileName,
uncompressedSize, uncompressedSize,
@ -162,7 +162,7 @@ public class CentralDirectoryHeader implements Cloneable {
lastModTime, lastModTime,
lastModDate); lastModDate);
newData.extraField = extraField; newData.extraField = extraField;
newData.offset = offset; newData.offset = -1;
newData.internalAttributes = internalAttributes; newData.internalAttributes = internalAttributes;
newData.externalAttributes = externalAttributes; newData.externalAttributes = externalAttributes;
newData.comment = comment; newData.comment = comment;

View File

@ -0,0 +1,46 @@
package com.android.tools.build.apkzlib.zip;
import com.google.common.collect.Maps;
import java.io.File;
import java.io.IOException;
import java.util.Map;
public class NestedZip extends ZFile {
final Map<StoredEntry, String> links;
final ZFile target;
final StoredEntry entry;
public NestedZip(String name, ZFile target, File src, boolean mayCompress) throws IOException {
super(src, new ZFileOptions(), true);
this.target = target;
this.entry = target.add(name, directOpen(0, directSize()), mayCompress);
this.links = Maps.newHashMap();
}
public boolean addFileLink(String srcName, String dstName) throws IOException {
var srcEntry = get(srcName);
if (entry == null)
throw new IOException("Entry " + srcEntry + " does not exist in nested zip");
var offset = srcEntry.getCentralDirectoryHeader().getOffset() + srcEntry.getLocalHeaderSize();
if (offset < MAX_LOCAL_EXTRA_FIELD_CONTENTS_SIZE) {
target.addNestedLink(srcName, entry, srcEntry, (int) offset);
return true;
} else {
links.put(srcEntry, dstName);
return false;
}
}
public Map<StoredEntry, String> getLinks() {
return links;
}
public ZFile getTarget() {
return target;
}
public StoredEntry getEntry() {
return entry;
}
}

View File

@ -25,7 +25,6 @@ import com.google.common.base.Preconditions;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.base.Suppliers; import com.google.common.base.Suppliers;
import com.google.common.base.Verify; import com.google.common.base.Verify;
import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
@ -159,7 +158,7 @@ public class StoredEntry {
private final StoredEntry linkedEntry; private final StoredEntry linkedEntry;
/** Offset of the nested link */ /** Offset of the nested link */
private final int nestedLinkOffset = 0; private final int nestedOffset;
/** /**
* Creates a new stored entry. * Creates a new stored entry.
@ -177,30 +176,34 @@ public class StoredEntry {
@Nullable ProcessedAndRawByteSources source, @Nullable ProcessedAndRawByteSources source,
ByteStorage storage) ByteStorage storage)
throws IOException { throws IOException {
this(header, file, source, storage, null); this(header, file, source, storage, null, 0);
} }
StoredEntry( StoredEntry(
String name, String name,
ZFile file, ZFile file,
ByteStorage storage, ByteStorage storage,
StoredEntry linkedEntry) StoredEntry linkedEntry,
StoredEntry nestedEntry,
int nestedOffset)
throws IOException { throws IOException {
this(linkedEntry.linkingCentralDirectoryHeader(name), file, linkedEntry.getSource(), storage, linkedEntry); this((nestedEntry == null ? linkedEntry: nestedEntry).linkingCentralDirectoryHeader(name, file),
file, linkedEntry.getSource(), storage, linkedEntry, nestedOffset);
} }
private CentralDirectoryHeader linkingCentralDirectoryHeader(String name) { private CentralDirectoryHeader linkingCentralDirectoryHeader(String name, ZFile file) {
boolean encodeWithUtf8 = !EncodeUtils.canAsciiEncode(name); boolean encodeWithUtf8 = !EncodeUtils.canAsciiEncode(name);
GPFlags flags = GPFlags.make(encodeWithUtf8); GPFlags flags = GPFlags.make(encodeWithUtf8);
return cdh.link(name, EncodeUtils.encode(name, flags), flags); return cdh.link(name, EncodeUtils.encode(name, flags), flags, file);
} }
StoredEntry( private StoredEntry(
CentralDirectoryHeader header, CentralDirectoryHeader header,
ZFile file, ZFile file,
@Nullable ProcessedAndRawByteSources source, @Nullable ProcessedAndRawByteSources source,
ByteStorage storage, ByteStorage storage,
StoredEntry linkedEntry) StoredEntry linkedEntry,
int nestedOffset)
throws IOException { throws IOException {
cdh = header; cdh = header;
this.file = file; this.file = file;
@ -208,6 +211,7 @@ public class StoredEntry {
verifyLog = file.makeVerifyLog(); verifyLog = file.makeVerifyLog();
this.storage = storage; this.storage = storage;
this.linkedEntry = linkedEntry; this.linkedEntry = linkedEntry;
this.nestedOffset = nestedOffset;
if (header.getOffset() >= 0) { if (header.getOffset() >= 0) {
readLocalHeader(); readLocalHeader();
@ -725,7 +729,7 @@ public class StoredEntry {
F_COMPRESSED_SIZE.write(out, compressInfo.getCompressedSize()); F_COMPRESSED_SIZE.write(out, compressInfo.getCompressedSize());
F_UNCOMPRESSED_SIZE.write(out, cdh.getUncompressedSize()); F_UNCOMPRESSED_SIZE.write(out, cdh.getUncompressedSize());
F_FILE_NAME_LENGTH.write(out, cdh.getEncodedFileName().length); F_FILE_NAME_LENGTH.write(out, cdh.getEncodedFileName().length);
F_EXTRA_LENGTH.write(out, localExtra.size() + extraOffset); F_EXTRA_LENGTH.write(out, localExtra.size() + extraOffset + nestedOffset);
out.put(cdh.getEncodedFileName()); out.put(cdh.getEncodedFileName());
localExtra.write(out); localExtra.write(out);

View File

@ -223,7 +223,7 @@ public class ZFile implements Closeable {
* Minimum size for the extra field when we have to add one. We rely on the alignment segment to * Minimum size for the extra field when we have to add one. We rely on the alignment segment to
* do that so the minimum size for the extra field is the minimum size of an alignment segment. * do that so the minimum size for the extra field is the minimum size of an alignment segment.
*/ */
private static final int MINIMUM_EXTRA_FIELD_SIZE = ExtraField.AlignmentSegment.MINIMUM_SIZE; protected static final int MINIMUM_EXTRA_FIELD_SIZE = ExtraField.AlignmentSegment.MINIMUM_SIZE;
/** /**
* Maximum size of the extra field. * Maximum size of the extra field.
@ -231,10 +231,10 @@ public class ZFile implements Closeable {
* <p>Theoretically, this is (1 << 16) - 1 = 65535 and not (1 < 15) -1 = 32767. However, due to * <p>Theoretically, this is (1 << 16) - 1 = 65535 and not (1 < 15) -1 = 32767. However, due to
* http://b.android.com/221703, we need to keep this limited. * http://b.android.com/221703, we need to keep this limited.
*/ */
private static final int MAX_LOCAL_EXTRA_FIELD_CONTENTS_SIZE = (1 << 15) - 1; protected static final int MAX_LOCAL_EXTRA_FIELD_CONTENTS_SIZE = (1 << 15) - 1;
/** File zip file. */ /** File zip file. */
private final File file; protected final File file;
/** /**
* The random access file used to access the zip file. This will be {@code null} if and only if * The random access file used to access the zip file. This will be {@code null} if and only if
@ -1768,18 +1768,23 @@ public class ZFile implements Closeable {
} }
public void addLink(String name, StoredEntry linkedEntry) public void addLink(String name, StoredEntry linkedEntry)
throws CloneNotSupportedException,IOException { throws IOException {
addNestedLink(name, linkedEntry, null, 0);
}
void addNestedLink(String name, StoredEntry linkedEntry, StoredEntry nestedEntry, int nestedOffset)
throws IOException {
Preconditions.checkArgument(linkedEntry != null, "linkedEntry is null"); Preconditions.checkArgument(linkedEntry != null, "linkedEntry is null");
Preconditions.checkArgument(linkedEntry.getCentralDirectoryHeader().getOffset() < 0, "linkedEntry is not new file"); Preconditions.checkArgument(linkedEntry.getCentralDirectoryHeader().getOffset() < 0, "linkedEntry is not new file");
Preconditions.checkArgument(!linkedEntry.isLinkingEntry(), "linkedEntry is a linking entry"); Preconditions.checkArgument(!linkedEntry.isLinkingEntry(), "linkedEntry is a linking entry");
var linkingEntry = new StoredEntry(name, this, storage, linkedEntry); var linkingEntry = new StoredEntry(name, this, storage, linkedEntry, nestedEntry, nestedOffset);
linkingEntries.add(linkingEntry); linkingEntries.add(linkingEntry);
linkedEntry.setLocalExtraNoNotify(new ExtraField(ImmutableList.<ExtraField.Segment>builder().add(linkedEntry.getLocalExtra().getSegments().toArray(new ExtraField.Segment[0])).add(new ExtraField.LinkingEntrySegment(linkingEntry)).build())); linkedEntry.setLocalExtraNoNotify(new ExtraField(ImmutableList.<ExtraField.Segment>builder().add(linkedEntry.getLocalExtra().getSegments().toArray(new ExtraField.Segment[0])).add(new ExtraField.LinkingEntrySegment(linkingEntry)).build()));
reAdd(linkedEntry, PositionHint.LOWEST_OFFSET);
} }
public void addNestedZipWithLinks(Map<String, String> nameMapping) public NestedZip addNestedZip(String name, File src, boolean mayCompress) throws IOException {
throws CloneNotSupportedException,IOException { return new NestedZip(name, this, src, mayCompress);
// TODO: nested link
} }

View File

@ -4,6 +4,7 @@ import com.android.apksig.internal.util.Pair;
import com.android.tools.build.apkzlib.sign.SigningExtension; import com.android.tools.build.apkzlib.sign.SigningExtension;
import com.android.tools.build.apkzlib.sign.SigningOptions; import com.android.tools.build.apkzlib.sign.SigningOptions;
import com.android.tools.build.apkzlib.zip.AlignmentRules; import com.android.tools.build.apkzlib.zip.AlignmentRules;
import com.android.tools.build.apkzlib.zip.NestedZip;
import com.android.tools.build.apkzlib.zip.StoredEntry; import com.android.tools.build.apkzlib.zip.StoredEntry;
import com.android.tools.build.apkzlib.zip.ZFile; import com.android.tools.build.apkzlib.zip.ZFile;
import com.android.tools.build.apkzlib.zip.ZFileOptions; import com.android.tools.build.apkzlib.zip.ZFileOptions;
@ -20,7 +21,6 @@ import org.lsposed.lspatch.share.Constants;
import org.lsposed.patch.util.ApkSignatureHelper; import org.lsposed.patch.util.ApkSignatureHelper;
import org.lsposed.patch.util.ManifestParser; import org.lsposed.patch.util.ManifestParser;
import org.lsposed.patch.util.NestedZipLink; import org.lsposed.patch.util.NestedZipLink;
import org.lsposed.patch.util.NestedZipLink.NestedZip;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@ -159,10 +159,7 @@ public class LSPatch {
System.out.println("Parsing original apk..."); System.out.println("Parsing original apk...");
try (ZFile srcZFile = ZFile.openReadOnly(srcApkFile); ZFile dstZFile = ZFile.openReadWrite(tmpApk, Z_FILE_OPTIONS)) { try (ZFile dstZFile = ZFile.openReadWrite(tmpApk, Z_FILE_OPTIONS); var srcZFile = dstZFile.addNestedZip(ORIGINAL_APK_ASSET_PATH, srcApkFile, false)) {
// copy origin apk to assets
dstZFile.add(ORIGINAL_APK_ASSET_PATH, new FileInputStream(srcApkFile), false);
if (sigbypassLevel > 0) { if (sigbypassLevel > 0) {
// save the apk original signature info, to support crack signature. // save the apk original signature info, to support crack signature.
String originalSignature = ApkSignatureHelper.getApkSignInfo(srcApkFile.getAbsolutePath()); String originalSignature = ApkSignatureHelper.getApkSignInfo(srcApkFile.getAbsolutePath());
@ -259,8 +256,6 @@ public class LSPatch {
var embedded = embedModules(dstZFile); var embedded = embedModules(dstZFile);
dstZFile.realign();
// create zip link // create zip link
if (verbose) if (verbose)
System.out.println("Creating nested apk link..."); System.out.println("Creating nested apk link...");
@ -286,8 +281,6 @@ public class LSPatch {
} }
NestedZipLink nestedZipLink = new NestedZipLink(dstZFile, signingExtension); NestedZipLink nestedZipLink = new NestedZipLink(dstZFile, signingExtension);
StoredEntry originalZipEntry = dstZFile.get(ORIGINAL_APK_ASSET_PATH);
NestedZip nestedZip = new NestedZip(srcZFile, originalZipEntry);
for (StoredEntry entry : srcZFile.entries()) { for (StoredEntry entry : srcZFile.entries()) {
String name = entry.getCentralDirectoryHeader().getName(); String name = entry.getCentralDirectoryHeader().getName();
if (name.startsWith("classes") && name.endsWith(".dex")) continue; if (name.startsWith("classes") && name.endsWith(".dex")) continue;
@ -295,21 +288,21 @@ public class LSPatch {
if (name.equals("AndroidManifest.xml")) continue; if (name.equals("AndroidManifest.xml")) continue;
if (name.startsWith("META-INF/CERT")) continue; if (name.startsWith("META-INF/CERT")) continue;
if (name.equals("META-INF/MANIFEST.MF")) continue; if (name.equals("META-INF/MANIFEST.MF")) continue;
nestedZip.addFileLink(name, name); srcZFile.addFileLink(name, name);
} }
nestedZipLink.nestedZips.add(nestedZip); nestedZipLink.nestedZips.add(srcZFile);
for (var pair : embedded) { // for (var pair : embedded) {
ZFile moduleZFile = ZFile.openReadOnly(pair.getFirst()); // ZFile moduleZFile = ZFile.openReadOnly(pair.getFirst());
StoredEntry moduleEntry = dstZFile.get("assets/modules/" + pair.getSecond() + ".bin"); // StoredEntry moduleEntry = dstZFile.get("assets/modules/" + pair.getSecond() + ".bin");
nestedZip = new NestedZip(moduleZFile, moduleEntry); // nestedZip = new NestedZip(dstZFile, moduleZFile, moduleEntry);
for (var nestedEntry : moduleZFile.entries()) { // for (var nestedEntry : moduleZFile.entries()) {
var name = nestedEntry.getCentralDirectoryHeader().getName(); // var name = nestedEntry.getCentralDirectoryHeader().getName();
if (name.startsWith("lib/")) // if (name.startsWith("lib/"))
nestedZip.addFileLink(name, "assets/lib/" + pair.getSecond() + name.substring(3)); // nestedZip.addFileLink(name, "assets/lib/" + pair.getSecond() + name.substring(3));
} // }
nestedZipLink.nestedZips.add(nestedZip); // nestedZipLink.nestedZips.add(nestedZip);
} // }
try { try {
nestedZipLink.register(); nestedZipLink.register();
@ -317,6 +310,8 @@ public class LSPatch {
throw new PatchError("Failed to create link", e); throw new PatchError("Failed to create link", e);
} }
dstZFile.realign();
System.out.println("Done. Output APK: " + outputFile.getAbsolutePath()); System.out.println("Done. Output APK: " + outputFile.getAbsolutePath());
} finally { } finally {
try { try {

View File

@ -6,6 +6,7 @@ import com.android.tools.build.apkzlib.sign.SigningExtension;
import com.android.tools.build.apkzlib.utils.IOExceptionRunnable; import com.android.tools.build.apkzlib.utils.IOExceptionRunnable;
import com.android.tools.build.apkzlib.zip.CentralDirectoryHeader; import com.android.tools.build.apkzlib.zip.CentralDirectoryHeader;
import com.android.tools.build.apkzlib.zip.EncodeUtils; import com.android.tools.build.apkzlib.zip.EncodeUtils;
import com.android.tools.build.apkzlib.zip.NestedZip;
import com.android.tools.build.apkzlib.zip.StoredEntry; import com.android.tools.build.apkzlib.zip.StoredEntry;
import com.android.tools.build.apkzlib.zip.ZFile; import com.android.tools.build.apkzlib.zip.ZFile;
import com.android.tools.build.apkzlib.zip.ZFileExtension; import com.android.tools.build.apkzlib.zip.ZFileExtension;
@ -21,21 +22,6 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
public class NestedZipLink extends ZFileExtension { public class NestedZipLink extends ZFileExtension {
public static class NestedZip {
final Set<Pair<String, String>> links;
final ZFile zip;
final StoredEntry entry;
public NestedZip(ZFile zip, StoredEntry entry) {
this.zip = zip;
this.entry = entry;
this.links = new HashSet<>();
}
public void addFileLink(String srcName, String dstName) {
links.add(Pair.of(srcName, dstName));
}
}
public final ZFile zFile; public final ZFile zFile;
@ -61,11 +47,9 @@ public class NestedZipLink extends ZFileExtension {
var signer = (ApkSignerEngine) signerField.get(signingExtension); var signer = (ApkSignerEngine) signerField.get(signingExtension);
for (var nestedZip : nestedZips) { for (var nestedZip : nestedZips) {
for (var link : nestedZip.links) { for (var link : nestedZip.getLinks().entrySet()) {
var entry = nestedZip.zip.get(link.getFirst()); var entry = link.getKey();
if (entry == null) notifySigner(signer, link.getValue(), entry);
throw new IOException("Entry " + link + " does not exist in nested zip");
notifySigner(signer, link.getFirst(), entry);
} }
} }
@ -119,18 +103,16 @@ public class NestedZipLink extends ZFileExtension {
var entries = (Map<String, Object>) field_entries.get(zFile); var entries = (Map<String, Object>) field_entries.get(zFile);
for (var nestedZip : nestedZips) { for (var nestedZip : nestedZips) {
long nestedZipOffset = nestedZip.entry.getCentralDirectoryHeader().getOffset(); long nestedZipOffset = nestedZip.getEntry().getCentralDirectoryHeader().getOffset();
for (var link : nestedZip.links) { for (var link : nestedZip.getLinks().entrySet()) {
var entry = nestedZip.zip.get(link.getFirst()); var entry = link.getKey();
if (entry == null)
throw new IOException("Entry " + link + " does not exist in nested zip");
CentralDirectoryHeader cdh = entry.getCentralDirectoryHeader(); CentralDirectoryHeader cdh = entry.getCentralDirectoryHeader();
field_entry_file.set(entry, zFile); field_entry_file.set(entry, zFile);
field_cdh_file.set(cdh, zFile); field_cdh_file.set(cdh, zFile);
field_cdh_encodedFileName.set(cdh, encodeFileName(link.getSecond())); field_cdh_encodedFileName.set(cdh, encodeFileName(link.getValue()));
field_cdh_offset.set(cdh, nestedZipOffset + cdh.getOffset() + nestedZip.entry.getLocalHeaderSize()); field_cdh_offset.set(cdh, nestedZipOffset + cdh.getOffset() + nestedZip.getEntry().getLocalHeaderSize());
var newFileUseMapEntry = makeFree.invoke(null, 0, 1, entry); var newFileUseMapEntry = makeFree.invoke(null, 0, 1, entry);
entries.put(link.getSecond(), newFileUseMapEntry); entries.put(link.getValue(), newFileUseMapEntry);
} }
} }
computeCentralDirectory.invoke(zFile); computeCentralDirectory.invoke(zFile);