Make embedded module so loadable
This commit is contained in:
parent
4e3e2a8637
commit
1ced48aa49
|
|
@ -191,14 +191,15 @@ public class LSPApplication extends ApplicationServiceClient {
|
||||||
HashSet<String> disabled_modules = new HashSet<>();
|
HashSet<String> disabled_modules = new HashSet<>();
|
||||||
try {
|
try {
|
||||||
for (var name : context.getAssets().list("modules")) {
|
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;
|
String cacheApkPath;
|
||||||
try (ZipFile sourceFile = new ZipFile(context.getApplicationInfo().sourceDir)) {
|
try (ZipFile sourceFile = new ZipFile(context.getApplicationInfo().sourceDir)) {
|
||||||
cacheApkPath = modulePath + sourceFile.getEntry("assets/modules/" + name).getCrc();
|
cacheApkPath = modulePath + sourceFile.getEntry("assets/modules/" + name).getCrc();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Files.exists(Paths.get(cacheApkPath))) {
|
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));
|
FileUtils.deleteFolderIfExists(Paths.get(modulePath));
|
||||||
Files.createDirectories(Paths.get(modulePath));
|
Files.createDirectories(Paths.get(modulePath));
|
||||||
try (var is = context.getAssets().open("modules/" + name)) {
|
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();
|
var module = new Module();
|
||||||
module.apkPath = cacheApkPath;
|
module.apkPath = cacheApkPath;
|
||||||
module.packageName = name;
|
module.packageName = packageName;
|
||||||
LSPApplication.modules.add(module);
|
LSPApplication.modules.add(module);
|
||||||
}
|
}
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ public class LSPApplicationStub extends Application {
|
||||||
vmInstructionSet.setAccessible(true);
|
vmInstructionSet.setAccessible(true);
|
||||||
|
|
||||||
String arch = (String) vmInstructionSet.invoke(getRuntime.invoke(null));
|
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);
|
System.load(path);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
Log.e("LSPatch", "load lspd error", e);
|
Log.e("LSPatch", "load lspd error", e);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package org.lsposed.patch;
|
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.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;
|
||||||
|
|
@ -99,7 +100,7 @@ public class LSPatch {
|
||||||
|
|
||||||
private static final ZFileOptions Z_FILE_OPTIONS = new ZFileOptions().setAlignmentRule(AlignmentRules.compose(
|
private static final ZFileOptions Z_FILE_OPTIONS = new ZFileOptions().setAlignmentRule(AlignmentRules.compose(
|
||||||
AlignmentRules.constantForSuffix(".so", 4096),
|
AlignmentRules.constantForSuffix(".so", 4096),
|
||||||
AlignmentRules.constantForSuffix(ORIGINAL_APK_ASSET_PATH, 4096)
|
AlignmentRules.constantForSuffix(".bin", 4096)
|
||||||
));
|
));
|
||||||
|
|
||||||
private static JCommander jCommander;
|
private static JCommander jCommander;
|
||||||
|
|
@ -223,7 +224,7 @@ public class LSPatch {
|
||||||
// copy so and dex files into the unzipped apk
|
// copy so and dex files into the unzipped apk
|
||||||
// do not put liblspd.so into apk!lib because x86 native bridge causes crash
|
// do not put liblspd.so into apk!lib because x86 native bridge causes crash
|
||||||
for (String arch : APK_LIB_PATH_ARRAY) {
|
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")) {
|
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
|
dstZFile.add(entryName, is, false); // no compress for so
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
|
@ -256,7 +257,7 @@ public class LSPatch {
|
||||||
throw new PatchError("Error when saving signature bypass level: " + e);
|
throw new PatchError("Error when saving signature bypass level: " + e);
|
||||||
}
|
}
|
||||||
|
|
||||||
embedModules(dstZFile);
|
var embedded = embedModules(dstZFile);
|
||||||
|
|
||||||
dstZFile.realign();
|
dstZFile.realign();
|
||||||
|
|
||||||
|
|
@ -274,9 +275,22 @@ 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);
|
nestedZip.addFileLink(name, name);
|
||||||
}
|
}
|
||||||
nestedZipLink.nestedZips.add(nestedZip);
|
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);
|
dstZFile.addZFileExtension(nestedZipLink);
|
||||||
|
|
||||||
// sign apk
|
// 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...");
|
System.out.println("Embedding modules...");
|
||||||
|
List<Pair<File, String>> list = new ArrayList<>();
|
||||||
for (var module : modules) {
|
for (var module : modules) {
|
||||||
var file = new File(module);
|
var file = new File(module);
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
|
|
@ -339,13 +354,15 @@ public class LSPatch {
|
||||||
}
|
}
|
||||||
if (triple != null) {
|
if (triple != null) {
|
||||||
try (var is = new FileInputStream(file)) {
|
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) {
|
} catch (Throwable e) {
|
||||||
System.err.println("Embed " + triple.packageName + " with error: " + e.getMessage());
|
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 {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
package org.lsposed.patch.util;
|
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.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.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;
|
||||||
|
|
@ -15,7 +17,7 @@ import java.util.Set;
|
||||||
|
|
||||||
public class NestedZipLink extends ZFileExtension {
|
public class NestedZipLink extends ZFileExtension {
|
||||||
public static class NestedZip {
|
public static class NestedZip {
|
||||||
final Set<String> links;
|
final Set<Pair<String, String>> links;
|
||||||
final ZFile zip;
|
final ZFile zip;
|
||||||
final StoredEntry entry;
|
final StoredEntry entry;
|
||||||
|
|
||||||
|
|
@ -25,8 +27,8 @@ public class NestedZipLink extends ZFileExtension {
|
||||||
this.links = new HashSet<>();
|
this.links = new HashSet<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addFileLink(String name) {
|
public void addFileLink(String srcName, String dstName) {
|
||||||
links.add(name);
|
links.add(Pair.of(srcName, dstName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,9 +73,10 @@ public class NestedZipLink extends ZFileExtension {
|
||||||
|
|
||||||
Field field_cdh_file = CentralDirectoryHeader.class.getDeclaredField("file");
|
Field field_cdh_file = CentralDirectoryHeader.class.getDeclaredField("file");
|
||||||
field_cdh_file.setAccessible(true);
|
field_cdh_file.setAccessible(true);
|
||||||
|
Field field_cdh_encodedFileName = CentralDirectoryHeader.class.getDeclaredField("encodedFileName");
|
||||||
Field field_offset = CentralDirectoryHeader.class.getDeclaredField("offset");
|
field_cdh_encodedFileName.setAccessible(true);
|
||||||
field_offset.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")
|
Method makeFree = Class.forName("com.android.tools.build.apkzlib.zip.FileUseMapEntry")
|
||||||
.getDeclaredMethod("makeUsed", long.class, long.class, Object.class);
|
.getDeclaredMethod("makeUsed", long.class, long.class, Object.class);
|
||||||
|
|
@ -89,17 +92,29 @@ public class NestedZipLink extends ZFileExtension {
|
||||||
for (var nestedZip : nestedZips) {
|
for (var nestedZip : nestedZips) {
|
||||||
long nestedZipOffset = nestedZip.entry.getCentralDirectoryHeader().getOffset();
|
long nestedZipOffset = nestedZip.entry.getCentralDirectoryHeader().getOffset();
|
||||||
for (var link : nestedZip.links) {
|
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");
|
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_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);
|
var newFileUseMapEntry = makeFree.invoke(null, 0, 1, entry);
|
||||||
entries.put(link, newFileUseMapEntry);
|
entries.put(link.getSecond(), newFileUseMapEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
computeCentralDirectory.invoke(zFile);
|
computeCentralDirectory.invoke(zFile);
|
||||||
computeEocd.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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue