Make embedded module so loadable

This commit is contained in:
Nullptr 2021-08-27 01:51:57 +08:00
parent 4e3e2a8637
commit 1ced48aa49
4 changed files with 54 additions and 21 deletions

View File

@ -191,14 +191,15 @@ public class LSPApplication extends ApplicationServiceClient {
HashSet<String> disabled_modules = new HashSet<>();
try {
for (var name : context.getAssets().list("modules")) {
String modulePath = context.getCacheDir() + "/lspatch/" + name + "/";
String packageName = name.substring(0, name.length() - 4);
String modulePath = context.getCacheDir() + "/lspatch/" + packageName + "/";
String cacheApkPath;
try (ZipFile sourceFile = new ZipFile(context.getApplicationInfo().sourceDir)) {
cacheApkPath = modulePath + sourceFile.getEntry("assets/modules/" + name).getCrc();
}
if (!Files.exists(Paths.get(cacheApkPath))) {
Log.i(TAG, "extract module apk: " + name);
Log.i(TAG, "extract module apk: " + packageName);
FileUtils.deleteFolderIfExists(Paths.get(modulePath));
Files.createDirectories(Paths.get(modulePath));
try (var is = context.getAssets().open("modules/" + name)) {
@ -206,10 +207,10 @@ public class LSPApplication extends ApplicationServiceClient {
}
}
embedded_modules.add(name);
embedded_modules.add(packageName);
var module = new Module();
module.apkPath = cacheApkPath;
module.packageName = name;
module.packageName = packageName;
LSPApplication.modules.add(module);
}
} catch (Throwable ignored) {

View File

@ -34,7 +34,7 @@ public class LSPApplicationStub extends Application {
vmInstructionSet.setAccessible(true);
String arch = (String) vmInstructionSet.invoke(getRuntime.invoke(null));
String path = LSPApplicationStub.class.getClassLoader().getResource("assets/lib/" + arch + "/liblspd.so").getPath().substring(5);
String path = LSPApplicationStub.class.getClassLoader().getResource("assets/lib/lspd/" + arch + "/liblspd.so").getPath().substring(5);
System.load(path);
} catch (Throwable e) {
Log.e("LSPatch", "load lspd error", e);

View File

@ -1,5 +1,6 @@
package org.lsposed.patch;
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;
@ -99,7 +100,7 @@ public class LSPatch {
private static final ZFileOptions Z_FILE_OPTIONS = new ZFileOptions().setAlignmentRule(AlignmentRules.compose(
AlignmentRules.constantForSuffix(".so", 4096),
AlignmentRules.constantForSuffix(ORIGINAL_APK_ASSET_PATH, 4096)
AlignmentRules.constantForSuffix(".bin", 4096)
));
private static JCommander jCommander;
@ -223,7 +224,7 @@ public class LSPatch {
// copy so and dex files into the unzipped apk
// do not put liblspd.so into apk!lib because x86 native bridge causes crash
for (String arch : APK_LIB_PATH_ARRAY) {
String entryName = "assets/lib/" + 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")) {
dstZFile.add(entryName, is, false); // no compress for so
} catch (Throwable e) {
@ -256,7 +257,7 @@ public class LSPatch {
throw new PatchError("Error when saving signature bypass level: " + e);
}
embedModules(dstZFile);
var embedded = embedModules(dstZFile);
dstZFile.realign();
@ -274,9 +275,22 @@ public class LSPatch {
if (name.equals("AndroidManifest.xml")) continue;
if (name.startsWith("META-INF/CERT")) continue;
if (name.equals("META-INF/MANIFEST.MF")) continue;
nestedZip.addFileLink(name);
nestedZip.addFileLink(name, name);
}
nestedZipLink.nestedZips.add(nestedZip);
for (var pair : embedded) {
ZFile moduleZFile = ZFile.openReadOnly(pair.getFirst());
StoredEntry moduleEntry = dstZFile.get("assets/modules/" + pair.getSecond() + ".bin");
nestedZip = new NestedZip(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);
}
dstZFile.addZFileExtension(nestedZipLink);
// sign apk
@ -308,8 +322,9 @@ public class LSPatch {
}
}
private void embedModules(ZFile zFile) {
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()) {
@ -339,13 +354,15 @@ public class LSPatch {
}
if (triple != null) {
try (var is = new FileInputStream(file)) {
zFile.add("assets/modules/" + triple.packageName, is);
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 {

View File

@ -1,7 +1,9 @@
package org.lsposed.patch.util;
import com.android.apksig.internal.util.Pair;
import com.android.tools.build.apkzlib.utils.IOExceptionRunnable;
import com.android.tools.build.apkzlib.zip.CentralDirectoryHeader;
import com.android.tools.build.apkzlib.zip.EncodeUtils;
import com.android.tools.build.apkzlib.zip.StoredEntry;
import com.android.tools.build.apkzlib.zip.ZFile;
import com.android.tools.build.apkzlib.zip.ZFileExtension;
@ -15,7 +17,7 @@ import java.util.Set;
public class NestedZipLink extends ZFileExtension {
public static class NestedZip {
final Set<String> links;
final Set<Pair<String, String>> links;
final ZFile zip;
final StoredEntry entry;
@ -25,8 +27,8 @@ public class NestedZipLink extends ZFileExtension {
this.links = new HashSet<>();
}
public void addFileLink(String name) {
links.add(name);
public void addFileLink(String srcName, String dstName) {
links.add(Pair.of(srcName, dstName));
}
}
@ -71,9 +73,10 @@ public class NestedZipLink extends ZFileExtension {
Field field_cdh_file = CentralDirectoryHeader.class.getDeclaredField("file");
field_cdh_file.setAccessible(true);
Field field_offset = CentralDirectoryHeader.class.getDeclaredField("offset");
field_offset.setAccessible(true);
Field field_cdh_encodedFileName = CentralDirectoryHeader.class.getDeclaredField("encodedFileName");
field_cdh_encodedFileName.setAccessible(true);
Field field_cdh_offset = CentralDirectoryHeader.class.getDeclaredField("offset");
field_cdh_offset.setAccessible(true);
Method makeFree = Class.forName("com.android.tools.build.apkzlib.zip.FileUseMapEntry")
.getDeclaredMethod("makeUsed", long.class, long.class, Object.class);
@ -89,17 +92,29 @@ public class NestedZipLink extends ZFileExtension {
for (var nestedZip : nestedZips) {
long nestedZipOffset = nestedZip.entry.getCentralDirectoryHeader().getOffset();
for (var link : nestedZip.links) {
var entry = nestedZip.zip.get(link);
var entry = nestedZip.zip.get(link.getFirst());
if (entry == null) throw new IOException("Entry " + link + " does not exist in nested zip");
CentralDirectoryHeader cdh = entry.getCentralDirectoryHeader();
field_entry_file.set(entry, zFile);
field_cdh_file.set(cdh, zFile);
field_offset.set(cdh, nestedZipOffset + cdh.getOffset() + nestedZip.entry.getLocalHeaderSize());
field_cdh_encodedFileName.set(cdh, encodeFileName(link.getSecond()));
field_cdh_offset.set(cdh, nestedZipOffset + cdh.getOffset() + nestedZip.entry.getLocalHeaderSize());
var newFileUseMapEntry = makeFree.invoke(null, 0, 1, entry);
entries.put(link, newFileUseMapEntry);
entries.put(link.getSecond(), newFileUseMapEntry);
}
}
computeCentralDirectory.invoke(zFile);
computeEocd.invoke(zFile);
}
private byte[] encodeFileName(String name) throws Exception {
Class<?> GPFlags = Class.forName("com.android.tools.build.apkzlib.zip.GPFlags");
Method make = GPFlags.getDeclaredMethod("make", boolean.class);
make.setAccessible(true);
Method encode = EncodeUtils.class.getDeclaredMethod("encode", String.class, GPFlags);
boolean encodeWithUtf8 = !EncodeUtils.canAsciiEncode(name);
var flags = make.invoke(null, encodeWithUtf8);
return (byte[]) encode.invoke(null, name, flags);
}
}