From c7b8cd30b5047be3e1d72684707e1be581c73a7d Mon Sep 17 00:00:00 2001 From: LoveSy Date: Sun, 29 Aug 2021 16:16:38 +0800 Subject: [PATCH] Module nested link --- .../tools/build/apkzlib/zip/NestedZip.java | 25 ++--- .../tools/build/apkzlib/zip/ZFile.java | 10 +- .../main/java/org/lsposed/patch/LSPatch.java | 101 +++++++----------- 3 files changed, 54 insertions(+), 82 deletions(-) diff --git a/apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/NestedZip.java b/apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/NestedZip.java index 3e7bd28..fce61e7 100644 --- a/apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/NestedZip.java +++ b/apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/NestedZip.java @@ -7,37 +7,38 @@ import java.io.IOException; import java.util.Map; public class NestedZip extends ZFile { - final Map links; final ZFile target; final StoredEntry entry; + public interface NameCallback { + String getName(ZFile file) throws IOException; + } - public NestedZip(String name, ZFile target, File src, boolean mayCompress) throws IOException { + public NestedZip(NameCallback 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(); + this.entry = target.add(name.getName(this), directOpen(0, directSize()), mayCompress); } /** * @return true if lfh is consistent with cdh otherwise inconsistent */ - public boolean addFileLink(String srcName, String dstName) throws IOException { - var srcEntry = get(srcName); - if (entry == null) + public boolean addFileLink(StoredEntry srcEntry, String dstName) throws IOException { + if (srcEntry == null) throw new IOException("Entry " + srcEntry + " does not exist in nested zip"); + var srcName = srcEntry.getCentralDirectoryHeader().getName(); var offset = srcEntry.getCentralDirectoryHeader().getOffset() + srcEntry.getLocalHeaderSize(); if (srcName.equals(dstName)) { - target.addNestedLink(srcName, entry, srcEntry, srcEntry.getCentralDirectoryHeader().getOffset(), true); + target.addNestedLink(entry, dstName, srcEntry, srcEntry.getCentralDirectoryHeader().getOffset(), true); return true; } else if (offset < MAX_LOCAL_EXTRA_FIELD_CONTENTS_SIZE) { - target.addNestedLink(srcName, entry, srcEntry, offset, false); + target.addNestedLink(entry, dstName, srcEntry, offset, false); return true; } return false; } - - public Map getLinks() { - return links; + public boolean addFileLink(String srcName, String dstName) throws IOException { + var srcEntry = get(srcName); + return addFileLink(srcEntry, dstName); } public ZFile getTarget() { diff --git a/apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ZFile.java b/apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ZFile.java index f8bf832..f46cf21 100644 --- a/apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ZFile.java +++ b/apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ZFile.java @@ -1767,23 +1767,23 @@ public class ZFile implements Closeable { return add(makeStoredEntry(name, source, mayCompress)); } - public void addLink(String name, StoredEntry linkedEntry) + public void addLink(StoredEntry linkedEntry, String dstName) throws IOException { - addNestedLink(name, linkedEntry, null, 0L, false); + addNestedLink(linkedEntry, dstName, null, 0L, false); } - void addNestedLink(String name, StoredEntry linkedEntry, StoredEntry nestedEntry, long nestedOffset, boolean dummy) + void addNestedLink(StoredEntry linkedEntry, String dstName, StoredEntry nestedEntry, long nestedOffset, boolean dummy) throws IOException { Preconditions.checkArgument(linkedEntry != null, "linkedEntry is null"); Preconditions.checkArgument(linkedEntry.getCentralDirectoryHeader().getOffset() < 0, "linkedEntry is not new file"); Preconditions.checkArgument(!linkedEntry.isLinkingEntry(), "linkedEntry is a linking entry"); - var linkingEntry = new StoredEntry(name, this, storage, linkedEntry, nestedEntry, nestedOffset, dummy); + var linkingEntry = new StoredEntry(dstName, this, storage, linkedEntry, nestedEntry, nestedOffset, dummy); linkingEntries.add(linkingEntry); linkedEntry.setLocalExtraNoNotify(new ExtraField(ImmutableList.builder().add(linkedEntry.getLocalExtra().getSegments().toArray(new ExtraField.Segment[0])).add(new ExtraField.LinkingEntrySegment(linkingEntry)).build())); reAdd(linkedEntry, PositionHint.LOWEST_OFFSET); } - public NestedZip addNestedZip(String name, File src, boolean mayCompress) throws IOException { + public NestedZip addNestedZip(NestedZip.NameCallback name, File src, boolean mayCompress) throws IOException { return new NestedZip(name, this, src, mayCompress); } diff --git a/patch/src/main/java/org/lsposed/patch/LSPatch.java b/patch/src/main/java/org/lsposed/patch/LSPatch.java index 64a0333..07fbbf1 100644 --- a/patch/src/main/java/org/lsposed/patch/LSPatch.java +++ b/patch/src/main/java/org/lsposed/patch/LSPatch.java @@ -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.SigningOptions; 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.ZFile; import com.android.tools.build.apkzlib.zip.ZFileOptions; @@ -157,7 +158,7 @@ public class LSPatch { System.out.println("Parsing original apk..."); - try (ZFile dstZFile = ZFile.openReadWrite(tmpApk, Z_FILE_OPTIONS); var srcZFile = dstZFile.addNestedZip(ORIGINAL_APK_ASSET_PATH, srcApkFile, false)) { + try (ZFile dstZFile = ZFile.openReadWrite(tmpApk, Z_FILE_OPTIONS); var srcZFile = dstZFile.addNestedZip((ignore) -> ORIGINAL_APK_ASSET_PATH, srcApkFile, false)) { // sign apk System.out.println("Register apk signer..."); @@ -240,7 +241,7 @@ public class LSPatch { String entryName = "assets/lib/lspd/" + arch + "/liblspd.so"; try (var is = getClass().getClassLoader().getResourceAsStream("assets/so/" + (arch.equals("arm") ? "armeabi-v7a" : (arch.equals("arm64") ? "arm64-v8a" : arch)) + "/liblspd.so")) { var entry = dstZFile.add(entryName, is, false); // no compress for so - dstZFile.addLink("assets/lib/lspd/test/" + arch + "/liblspd.so", entry); + dstZFile.addLink(entry, "assets/lib/lspd/test/" + arch + "/liblspd.so"); } catch (Throwable e) { // More exception info throw new PatchError("Error when adding native lib", e); @@ -271,32 +272,45 @@ public class LSPatch { throw new PatchError("Error when saving signature bypass level", e); } - var embedded = embedModules(dstZFile); - // create zip link if (verbose) System.out.println("Creating nested apk link..."); - for (StoredEntry entry : srcZFile.entries()) { - String name = entry.getCentralDirectoryHeader().getName(); - if (name.startsWith("classes") && name.endsWith(".dex")) continue; - if (dstZFile.get(name) != null) continue; - if (name.equals("AndroidManifest.xml")) continue; - if (name.startsWith("META-INF/CERT")) continue; - if (name.equals("META-INF/MANIFEST.MF")) continue; - srcZFile.addFileLink(name, name); + for (var moduleFile : modules) { + final var moduleManifest = new ManifestParser.Triple[]{null}; + try (var nested = dstZFile.addNestedZip((module) -> { + var manifest = module.get(ANDROID_MANIFEST_XML); + if (manifest == null) { + throw new PatchError(moduleFile + " is not a valid apk file."); + } + moduleManifest[0] = ManifestParser.parseManifestFile(manifest.open()); + if (moduleManifest[0] == null) { + throw new PatchError(moduleFile + " is not a valid apk file."); + } + return "assets/modules/" + moduleManifest[0].packageName + ".bin"; + }, new File(moduleFile), false)) { + var packageName = moduleManifest[0].packageName; + for (var nestedEntry : nested.entries()) { + var name = nestedEntry.getCentralDirectoryHeader().getName(); + if (name.startsWith("lib/")) { + var dstName = "assets/lib/" + packageName + name.substring(3); + System.out.println("add link: " + dstName); + if (!nested.addFileLink(nestedEntry, dstName)) { + throw new PatchError("module " + packageName + " is too large to embed"); + } + } + } + } } -// for (var pair : embedded) { -// ZFile moduleZFile = ZFile.openReadOnly(pair.getFirst()); -// StoredEntry moduleEntry = dstZFile.get("assets/modules/" + pair.getSecond() + ".bin"); -// nestedZip = new NestedZip(dstZFile, moduleZFile, moduleEntry); -// for (var nestedEntry : moduleZFile.entries()) { -// var name = nestedEntry.getCentralDirectoryHeader().getName(); -// if (name.startsWith("lib/")) -// nestedZip.addFileLink(name, "assets/lib/" + pair.getSecond() + name.substring(3)); -// } -// nestedZipLink.nestedZips.add(nestedZip); +// for (StoredEntry entry : srcZFile.entries()) { +// String name = entry.getCentralDirectoryHeader().getName(); +// if (name.startsWith("classes") && name.endsWith(".dex")) continue; +// if (dstZFile.get(name) != null) continue; +// if (name.equals("AndroidManifest.xml")) continue; +// if (name.startsWith("META-INF/CERT")) continue; +// if (name.equals("META-INF/MANIFEST.MF")) continue; +// srcZFile.addFileLink(name, name); // } dstZFile.realign(); @@ -313,49 +327,6 @@ public class LSPatch { } } - private List> embedModules(ZFile zFile) { - System.out.println("Embedding modules..."); - List> list = new ArrayList<>(); - for (var module : modules) { - var file = new File(module); - if (!file.exists()) { - System.err.println(file.getAbsolutePath() + " does not exist."); - } - - System.out.print("Embedding module "); - - ManifestParser.Triple triple = null; - try (JarFile jar = new JarFile(file)) { - var manifest = jar.getEntry(ANDROID_MANIFEST_XML); - if (manifest == null) { - System.out.println(); - System.err.println(file.getAbsolutePath() + " is not a valid apk file."); - continue; - } - triple = ManifestParser.parseManifestFile(jar.getInputStream(manifest)); - if (triple == null) { - System.out.println(); - System.err.println(file.getAbsolutePath() + " is not a valid apk file."); - continue; - } - System.out.println(triple.packageName); - } catch (Throwable e) { - System.out.println(); - System.err.println(e.getMessage()); - } - if (triple != null) { - try (var is = new FileInputStream(file)) { - var entryName = "assets/modules/" + triple.packageName + ".bin"; - zFile.add(entryName, is, false); - list.add(Pair.of(file, triple.packageName)); - } catch (Throwable e) { - System.err.println("Embed " + triple.packageName + " with error: " + e.getMessage()); - } - } - } - return list; - } - private byte[] modifyManifestFile(InputStream is) throws IOException { ModificationProperty property = new ModificationProperty();