Module nested link

This commit is contained in:
LoveSy 2021-08-29 16:16:38 +08:00
parent e6571520ae
commit c7b8cd30b5
3 changed files with 54 additions and 82 deletions

View File

@ -7,37 +7,38 @@ import java.io.IOException;
import java.util.Map; import java.util.Map;
public class NestedZip extends ZFile { public class NestedZip extends ZFile {
final Map<StoredEntry, String> links;
final ZFile target; final ZFile target;
final StoredEntry entry; 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); super(src, new ZFileOptions(), true);
this.target = target; this.target = target;
this.entry = target.add(name, directOpen(0, directSize()), mayCompress); this.entry = target.add(name.getName(this), directOpen(0, directSize()), mayCompress);
this.links = Maps.newHashMap();
} }
/** /**
* @return true if lfh is consistent with cdh otherwise inconsistent * @return true if lfh is consistent with cdh otherwise inconsistent
*/ */
public boolean addFileLink(String srcName, String dstName) throws IOException { public boolean addFileLink(StoredEntry srcEntry, String dstName) throws IOException {
var srcEntry = get(srcName); if (srcEntry == null)
if (entry == null)
throw new IOException("Entry " + srcEntry + " does not exist in nested zip"); throw new IOException("Entry " + srcEntry + " does not exist in nested zip");
var srcName = srcEntry.getCentralDirectoryHeader().getName();
var offset = srcEntry.getCentralDirectoryHeader().getOffset() + srcEntry.getLocalHeaderSize(); var offset = srcEntry.getCentralDirectoryHeader().getOffset() + srcEntry.getLocalHeaderSize();
if (srcName.equals(dstName)) { if (srcName.equals(dstName)) {
target.addNestedLink(srcName, entry, srcEntry, srcEntry.getCentralDirectoryHeader().getOffset(), true); target.addNestedLink(entry, dstName, srcEntry, srcEntry.getCentralDirectoryHeader().getOffset(), true);
return true; return true;
} else if (offset < MAX_LOCAL_EXTRA_FIELD_CONTENTS_SIZE) { } 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 true;
} }
return false; return false;
} }
public boolean addFileLink(String srcName, String dstName) throws IOException {
public Map<StoredEntry, String> getLinks() { var srcEntry = get(srcName);
return links; return addFileLink(srcEntry, dstName);
} }
public ZFile getTarget() { public ZFile getTarget() {

View File

@ -1767,23 +1767,23 @@ public class ZFile implements Closeable {
return add(makeStoredEntry(name, source, mayCompress)); return add(makeStoredEntry(name, source, mayCompress));
} }
public void addLink(String name, StoredEntry linkedEntry) public void addLink(StoredEntry linkedEntry, String dstName)
throws IOException { 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 { 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, nestedEntry, nestedOffset, dummy); var linkingEntry = new StoredEntry(dstName, this, storage, linkedEntry, nestedEntry, nestedOffset, dummy);
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); 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); return new NestedZip(name, this, src, mayCompress);
} }

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;
@ -157,7 +158,7 @@ public class LSPatch {
System.out.println("Parsing original apk..."); 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 // sign apk
System.out.println("Register apk signer..."); System.out.println("Register apk signer...");
@ -240,7 +241,7 @@ public class LSPatch {
String entryName = "assets/lib/lspd/" + arch + "/liblspd.so"; 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")) { 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 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) { } catch (Throwable e) {
// More exception info // More exception info
throw new PatchError("Error when adding native lib", e); 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); throw new PatchError("Error when saving signature bypass level", e);
} }
var embedded = embedModules(dstZFile);
// create zip link // create zip link
if (verbose) if (verbose)
System.out.println("Creating nested apk link..."); System.out.println("Creating nested apk link...");
for (StoredEntry entry : srcZFile.entries()) { for (var moduleFile : modules) {
String name = entry.getCentralDirectoryHeader().getName(); final var moduleManifest = new ManifestParser.Triple[]{null};
if (name.startsWith("classes") && name.endsWith(".dex")) continue; try (var nested = dstZFile.addNestedZip((module) -> {
if (dstZFile.get(name) != null) continue; var manifest = module.get(ANDROID_MANIFEST_XML);
if (name.equals("AndroidManifest.xml")) continue; if (manifest == null) {
if (name.startsWith("META-INF/CERT")) continue; throw new PatchError(moduleFile + " is not a valid apk file.");
if (name.equals("META-INF/MANIFEST.MF")) continue; }
srcZFile.addFileLink(name, name); 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) { // for (StoredEntry entry : srcZFile.entries()) {
// ZFile moduleZFile = ZFile.openReadOnly(pair.getFirst()); // String name = entry.getCentralDirectoryHeader().getName();
// StoredEntry moduleEntry = dstZFile.get("assets/modules/" + pair.getSecond() + ".bin"); // if (name.startsWith("classes") && name.endsWith(".dex")) continue;
// nestedZip = new NestedZip(dstZFile, moduleZFile, moduleEntry); // if (dstZFile.get(name) != null) continue;
// for (var nestedEntry : moduleZFile.entries()) { // if (name.equals("AndroidManifest.xml")) continue;
// var name = nestedEntry.getCentralDirectoryHeader().getName(); // if (name.startsWith("META-INF/CERT")) continue;
// if (name.startsWith("lib/")) // if (name.equals("META-INF/MANIFEST.MF")) continue;
// nestedZip.addFileLink(name, "assets/lib/" + pair.getSecond() + name.substring(3)); // srcZFile.addFileLink(name, name);
// }
// nestedZipLink.nestedZips.add(nestedZip);
// } // }
dstZFile.realign(); dstZFile.realign();
@ -313,49 +327,6 @@ public class LSPatch {
} }
} }
private List<Pair<File, String>> embedModules(ZFile zFile) {
System.out.println("Embedding modules...");
List<Pair<File, String>> 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 { private byte[] modifyManifestFile(InputStream is) throws IOException {
ModificationProperty property = new ModificationProperty(); ModificationProperty property = new ModificationProperty();