diff --git a/app/build.gradle b/app/build.gradle index 4dc2e4c..fb586e8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -85,4 +85,6 @@ dependencies { compileOnly project(":hiddenapi-stubs") implementation project(':share') implementation project(':imanager') + + implementation 'com.google.code.gson:gson:2.8.8' } diff --git a/app/src/main/java/org/lsposed/lspatch/loader/LSPApplication.java b/app/src/main/java/org/lsposed/lspatch/loader/LSPApplication.java index a158f6b..7b7d132 100644 --- a/app/src/main/java/org/lsposed/lspatch/loader/LSPApplication.java +++ b/app/src/main/java/org/lsposed/lspatch/loader/LSPApplication.java @@ -1,8 +1,8 @@ package org.lsposed.lspatch.loader; import static android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE; +import static org.lsposed.lspatch.share.Constants.CONFIG_ASSET_PATH; import static org.lsposed.lspatch.share.Constants.ORIGINAL_APK_ASSET_PATH; -import static org.lsposed.lspatch.share.Constants.ORIGINAL_APP_COMPONENT_FACTORY_ASSET_PATH; import static org.lsposed.lspd.service.ConfigFileManager.loadModule; import android.app.ActivityThread; @@ -22,19 +22,25 @@ import android.system.Os; import android.util.ArrayMap; import android.util.Log; +import com.google.gson.Gson; + import org.lsposed.lspatch.loader.util.FileUtils; import org.lsposed.lspatch.loader.util.XLog; import org.lsposed.lspatch.share.Constants; +import org.lsposed.lspatch.share.PatchConfig; import org.lsposed.lspd.config.ApplicationServiceClient; import org.lsposed.lspd.core.Main; import org.lsposed.lspd.models.Module; import org.lsposed.lspd.nativebridge.SigBypass; +import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.lang.reflect.Field; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.attribute.PosixFilePermissions; @@ -53,14 +59,12 @@ import hidden.HiddenApiBridge; */ @SuppressWarnings("unused") public class LSPApplication extends ApplicationServiceClient { - private static final String ORIGINAL_SIGNATURE_ASSET_PATH = "original_signature_info.ini"; - private static final String USE_MANAGER_CONTROL_PATH = "use_manager.ini"; private static final String TAG = "LSPatch"; private static ActivityThread activityThread; private static LoadedApk appLoadedApk; - private static boolean useManager; - private static String originalSignature = null; + + private static PatchConfig config; private static ManagerResolver managerResolver = null; final static public int FIRST_APP_ZYGOTE_ISOLATED_UID = 90000; @@ -75,30 +79,23 @@ public class LSPApplication extends ApplicationServiceClient { } public static void onLoad() { - cacheSigbypassLv = -1; - if (isIsolated()) { - XLog.d(TAG, "skip isolated process"); + XLog.d(TAG, "Skip isolated process"); return; } activityThread = ActivityThread.currentActivityThread(); var context = createLoadedApkWithContext(); if (context == null) { - XLog.e(TAG, "create context err"); + XLog.e(TAG, "Error when creating context"); return; } - useManager = Boolean.parseBoolean(FileUtils.readTextFromAssets(context, USE_MANAGER_CONTROL_PATH)); - originalSignature = FileUtils.readTextFromAssets(context, ORIGINAL_SIGNATURE_ASSET_PATH); - - if (useManager) try { + if (config.useManager) try { managerResolver = new ManagerResolver(context); } catch (RemoteException e) { Log.e(TAG, "Failed to instantiate manager resolver", e); } - XLog.d(TAG, "original signature info " + originalSignature); - instance = new LSPApplication(); serviceClient = instance; try { @@ -111,6 +108,7 @@ public class LSPApplication extends ApplicationServiceClient { // WARN: Since it uses `XResource`, the following class should not be initialized // before forkPostCommon is invoke. Otherwise, you will get failure of XResources LSPLoader.initModules(appLoadedApk); + Log.i(TAG, "Modules initialized"); } catch (Throwable e) { Log.e(TAG, "Do hook", e); } @@ -118,12 +116,21 @@ public class LSPApplication extends ApplicationServiceClient { private static Context createLoadedApkWithContext() { try { - var baseClassLoader = LSPApplication.class.getClassLoader().getParent(); - var mBoundApplication = XposedHelpers.getObjectField(activityThread, "mBoundApplication"); var stubLoadedApk = (LoadedApk) XposedHelpers.getObjectField(mBoundApplication, "info"); var appInfo = (ApplicationInfo) XposedHelpers.getObjectField(mBoundApplication, "appInfo"); var compatInfo = (CompatibilityInfo) XposedHelpers.getObjectField(mBoundApplication, "compatInfo"); + var baseClassLoader = stubLoadedApk.getClassLoader(); + + try (var is = baseClassLoader.getResourceAsStream(CONFIG_ASSET_PATH)) { + BufferedReader streamReader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); + config = new Gson().fromJson(streamReader, PatchConfig.class); + } catch (IOException e) { + Log.e(TAG, "Failed to load config file"); + return null; + } + Log.i(TAG, "Use manager: " + config.useManager); + Log.i(TAG, "Signature bypass level: " + config.sigBypassLevel); String originPath = appInfo.dataDir + "/cache/lspatch/origin/"; String cacheApkPath; @@ -133,10 +140,10 @@ public class LSPApplication extends ApplicationServiceClient { appInfo.sourceDir = cacheApkPath; appInfo.publicSourceDir = cacheApkPath; - appInfo.appComponentFactory = FileUtils.readTextFromInputStream(baseClassLoader.getResourceAsStream(ORIGINAL_APP_COMPONENT_FACTORY_ASSET_PATH)); + appInfo.appComponentFactory = config.appComponentFactory; if (!Files.exists(Paths.get(cacheApkPath))) { - Log.i(TAG, "extract original apk"); + Log.i(TAG, "Extract original apk"); FileUtils.deleteFolderIfExists(Paths.get(originPath)); Files.createDirectories(Paths.get(originPath)); try (InputStream is = baseClassLoader.getResourceAsStream(ORIGINAL_APK_ASSET_PATH)) { @@ -182,31 +189,31 @@ public class LSPApplication extends ApplicationServiceClient { for (int i = codePaths.size() - 1; i >= 0; i--) { String splitName = i == 0 ? null : appInfo.splitNames[i - 1]; File curProfileFile = new File(profileDir, splitName == null ? "primary.prof" : splitName + ".split.prof").getAbsoluteFile(); - Log.d(TAG, "processing " + curProfileFile.getAbsolutePath()); + Log.d(TAG, "Processing " + curProfileFile.getAbsolutePath()); try { if (!curProfileFile.canWrite() && Files.size(curProfileFile.toPath()) == 0) { - Log.d(TAG, "skip profile " + curProfileFile.getAbsolutePath()); + Log.d(TAG, "Skip profile " + curProfileFile.getAbsolutePath()); continue; } if (curProfileFile.exists() && !curProfileFile.delete()) { try (var writer = new FileOutputStream(curProfileFile)) { - Log.d(TAG, "failed to delete, try to clear content " + curProfileFile.getAbsolutePath()); + Log.d(TAG, "Failed to delete, try to clear content " + curProfileFile.getAbsolutePath()); } catch (Throwable e) { - Log.e(TAG, "failed to delete and clear profile file " + curProfileFile.getAbsolutePath(), e); + Log.e(TAG, "Failed to delete and clear profile file " + curProfileFile.getAbsolutePath(), e); } Os.chmod(curProfileFile.getAbsolutePath(), 00400); } else { Files.createFile(curProfileFile.toPath(), attrs); } } catch (Throwable e) { - Log.e(TAG, "failed to disable profile file " + curProfileFile.getAbsolutePath(), e); + Log.e(TAG, "Failed to disable profile file " + curProfileFile.getAbsolutePath(), e); } } } public static void loadModules(Context context) { - if (useManager) { + if (config.useManager) { try { modules.addAll(managerResolver.getModules()); modules.forEach(m -> Log.i(TAG, "load module from manager: " + m.packageName)); @@ -215,19 +222,19 @@ public class LSPApplication extends ApplicationServiceClient { } } else { try { - for (var name : context.getAssets().list("modules")) { + for (var name : context.getAssets().list("lspatch/modules")) { String packageName = name.substring(0, name.length() - 4); String modulePath = context.getCacheDir() + "/lspatch/" + packageName + "/"; String cacheApkPath; try (ZipFile sourceFile = new ZipFile(context.getPackageResourcePath())) { - cacheApkPath = modulePath + sourceFile.getEntry("assets/modules/" + name).getCrc(); + cacheApkPath = modulePath + sourceFile.getEntry("assets/lspatch/modules/" + name).getCrc(); } if (!Files.exists(Paths.get(cacheApkPath))) { - Log.i(TAG, "extract module apk: " + packageName); + 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)) { + try (var is = context.getAssets().open("lspatch/modules/" + name)) { Files.copy(is, Paths.get(cacheApkPath)); } } @@ -236,7 +243,6 @@ public class LSPApplication extends ApplicationServiceClient { module.apkPath = cacheApkPath; module.packageName = packageName; module.file = loadModule(cacheApkPath); - if (module.file != null) module.file.hostApk = context.getPackageResourcePath(); modules.add(module); } } catch (Throwable ignored) { @@ -286,16 +292,16 @@ public class LSPApplication extends ApplicationServiceClient { PackageInfo packageInfo = PackageInfo.CREATOR.createFromParcel(out); if (packageInfo.packageName.equals(context.getApplicationInfo().packageName)) { if (packageInfo.signatures != null && packageInfo.signatures.length > 0) { - XLog.d(TAG, "replace signature info [0]"); - packageInfo.signatures[0] = new Signature(originalSignature); + XLog.d(TAG, "Replace signature info (method 1)"); + packageInfo.signatures[0] = new Signature(config.originalSignature); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (packageInfo.signingInfo != null) { - XLog.d(TAG, "replace signature info [1]"); + XLog.d(TAG, "Replace signature info (method 2)"); Signature[] signaturesArray = packageInfo.signingInfo.getApkContentsSigners(); if (signaturesArray != null && signaturesArray.length > 0) { - signaturesArray[0] = new Signature(originalSignature); + signaturesArray[0] = new Signature(config.originalSignature); } } } @@ -320,11 +326,11 @@ public class LSPApplication extends ApplicationServiceClient { } private static void doHook(Context context) throws IllegalAccessException, ClassNotFoundException, IOException, NoSuchFieldException { - int bypassLv = fetchSigbypassLv(context); - if (bypassLv >= Constants.SIGBYPASS_LV_PM) { + if (config.sigBypassLevel >= Constants.SIGBYPASS_LV_PM) { + XLog.d(TAG, "Original signature: " + config.originalSignature.substring(0, 16) + "..."); byPassSignature(context); } - if (bypassLv >= Constants.SIGBYPASS_LV_PM_OPENAT) { + if (config.sigBypassLevel >= Constants.SIGBYPASS_LV_PM_OPENAT) { String cacheApkPath; try (ZipFile sourceFile = new ZipFile(context.getPackageResourcePath())) { cacheApkPath = context.getCacheDir() + "/lspatch/origin/" + sourceFile.getEntry(ORIGINAL_APK_ASSET_PATH).getCrc(); @@ -333,22 +339,6 @@ public class LSPApplication extends ApplicationServiceClient { } } - private static int cacheSigbypassLv; - - private static int fetchSigbypassLv(Context context) { - if (cacheSigbypassLv != -1) { - return cacheSigbypassLv; - } - for (int i = Constants.SIGBYPASS_LV_DISABLE; i < Constants.SIGBYPASS_LV_MAX; i++) { - try (InputStream inputStream = context.getAssets().open(Constants.CONFIG_NAME_SIGBYPASSLV + i)) { - cacheSigbypassLv = i; - return i; - } catch (IOException ignore) { - } - } - return 0; - } - @Override public IBinder requestModuleBinder(String name) { return null; diff --git a/app/src/main/java/org/lsposed/lspatch/loader/util/FileUtils.java b/app/src/main/java/org/lsposed/lspatch/loader/util/FileUtils.java index d374a6c..06089b8 100644 --- a/app/src/main/java/org/lsposed/lspatch/loader/util/FileUtils.java +++ b/app/src/main/java/org/lsposed/lspatch/loader/util/FileUtils.java @@ -1,12 +1,6 @@ package org.lsposed.lspatch.loader.util; -import android.content.Context; - -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; @@ -14,18 +8,6 @@ import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; public class FileUtils { - public static String readTextFromInputStream(InputStream is) { - try (InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8); BufferedReader bufferedReader = new BufferedReader(reader)) { - StringBuilder builder = new StringBuilder(); - String str; - while ((str = bufferedReader.readLine()) != null) { - builder.append(str); - } - return builder.toString(); - } catch (Throwable ignored) { - } - return null; - } public static void deleteFolderIfExists(Path target) throws IOException { if (Files.notExists(target)) return; @@ -49,15 +31,4 @@ public class FileUtils { } }); } - - public static String readTextFromAssets(Context context, String assetsFileName) { - if (context == null) { - throw new IllegalStateException("context null"); - } - try (InputStream is = context.getAssets().open(assetsFileName)) { - return readTextFromInputStream(is); - } catch (Throwable ignored) { - } - return null; - } } diff --git a/appstub/src/main/java/org/lsposed/lspatch/appstub/LSPAppComponentFactoryStub.java b/appstub/src/main/java/org/lsposed/lspatch/appstub/LSPAppComponentFactoryStub.java index ae15de2..36511a8 100644 --- a/appstub/src/main/java/org/lsposed/lspatch/appstub/LSPAppComponentFactoryStub.java +++ b/appstub/src/main/java/org/lsposed/lspatch/appstub/LSPAppComponentFactoryStub.java @@ -14,7 +14,7 @@ public class LSPAppComponentFactoryStub extends AppComponentFactory { static { var cl = Objects.requireNonNull(LSPAppComponentFactoryStub.class.getClassLoader()); - try (var is = cl.getResourceAsStream("assets/lsp"); + try (var is = cl.getResourceAsStream("assets/lspatch/lsp.dex"); var os = new ByteArrayOutputStream()) { byte[] buffer = new byte[8192]; int n; @@ -34,7 +34,7 @@ public class LSPAppComponentFactoryStub extends AppComponentFactory { vmInstructionSet.setAccessible(true); String arch = (String) vmInstructionSet.invoke(getRuntime.invoke(null)); - String path = cl.getResource("assets/lib/lspd/" + arch + "/liblspd.so").getPath().substring(5); + String path = cl.getResource("assets/lspatch/lspd/" + arch + "/liblspd.so").getPath().substring(5); System.load(path); } catch (Throwable e) { Log.e("LSPatch", "load lspd error", e); diff --git a/build.gradle b/build.gradle index 4229e98..8d8763b 100644 --- a/build.gradle +++ b/build.gradle @@ -6,8 +6,8 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.1.0-alpha11' - classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.30' + classpath 'com.android.tools.build:gradle:7.1.0-alpha13' + classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31' } } diff --git a/core b/core index a6be001..c205f8a 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit a6be0018e02bbd0d581c8a5f7ee9310bb8a6a711 +Subproject commit c205f8a2c9d2f207b2ce6de14d0054f5220a0b10 diff --git a/patch/build.gradle b/patch/build.gradle index 63904ed..91fed56 100644 --- a/patch/build.gradle +++ b/patch/build.gradle @@ -16,6 +16,7 @@ dependencies { implementation project(':apkzlib') implementation 'commons-io:commons-io:2.11.0' implementation 'com.beust:jcommander:1.81' + implementation 'com.google.code.gson:gson:2.8.8' } jar { diff --git a/patch/src/main/java/org/lsposed/patch/LSPatch.java b/patch/src/main/java/org/lsposed/patch/LSPatch.java index e98ae0b..384e538 100644 --- a/patch/src/main/java/org/lsposed/patch/LSPatch.java +++ b/patch/src/main/java/org/lsposed/patch/LSPatch.java @@ -1,7 +1,8 @@ package org.lsposed.patch; +import static org.lsposed.lspatch.share.Constants.CONFIG_ASSET_PATH; +import static org.lsposed.lspatch.share.Constants.DEX_ASSET_PATH; import static org.lsposed.lspatch.share.Constants.ORIGINAL_APK_ASSET_PATH; -import static org.lsposed.lspatch.share.Constants.ORIGINAL_APP_COMPONENT_FACTORY_ASSET_PATH; import static org.lsposed.lspatch.share.Constants.PROXY_APP_COMPONENT_FACTORY; import com.android.tools.build.apkzlib.sign.SigningExtension; @@ -12,6 +13,7 @@ import com.android.tools.build.apkzlib.zip.ZFile; import com.android.tools.build.apkzlib.zip.ZFileOptions; import com.beust.jcommander.JCommander; import com.beust.jcommander.Parameter; +import com.google.gson.Gson; import com.wind.meditor.core.ManifestEditor; import com.wind.meditor.property.AttributeItem; import com.wind.meditor.property.ModificationProperty; @@ -19,13 +21,14 @@ import com.wind.meditor.utils.NodeValue; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; -import org.lsposed.lspatch.share.Constants; +import org.lsposed.lspatch.share.PatchConfig; import org.lsposed.patch.util.ApkSignatureHelper; import org.lsposed.patch.util.ManifestParser; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; @@ -36,6 +39,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; public class LSPatch { @@ -89,8 +93,6 @@ public class LSPatch { @Parameter(names = {"-m", "--embed"}, description = "Embed provided modules to apk") private List modules = new ArrayList<>(); - private static final String SIGNATURE_INFO_ASSET_PATH = "assets/original_signature_info.ini"; - private static final String USE_MANAGER_CONTROL_PATH = "assets/use_manager.ini"; private static final String ANDROID_MANIFEST_XML = "AndroidManifest.xml"; private static final HashSet ARCHES = new HashSet<>(Arrays.asList( "armeabi-v7a", @@ -107,7 +109,7 @@ public class LSPatch { private static final ZFileOptions Z_FILE_OPTIONS = new ZFileOptions().setAlignmentRule(AlignmentRules.compose( AlignmentRules.constantForSuffix(".so", 4096), - AlignmentRules.constantForSuffix(".bin", 4096) + AlignmentRules.constantForSuffix(ORIGINAL_APK_ASSET_PATH, 4096) )); private final JCommander jCommander; @@ -173,7 +175,8 @@ public class LSPatch { System.out.println("Parsing original apk..."); - try (ZFile dstZFile = ZFile.openReadWrite(tmpApk, Z_FILE_OPTIONS); var srcZFile = dstZFile.addNestedZip((ignore) -> ORIGINAL_APK_ASSET_PATH, srcApkFile, false)) { + try (var 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..."); @@ -193,19 +196,17 @@ public class LSPatch { } catch (Exception e) { throw new PatchError("Failed to register signer", e); } + + String originalSignature = null; if (sigbypassLevel > 0) { // save the apk original signature info, to support crack signature. - String originalSignature = ApkSignatureHelper.getApkSignInfo(srcApkFile.getAbsolutePath()); + originalSignature = ApkSignatureHelper.getApkSignInfo(srcApkFile.getAbsolutePath()); if (originalSignature == null || originalSignature.isEmpty()) { throw new PatchError("get original signature failed"); } + if (verbose) System.out.println("Original signature\n" + originalSignature); - try (var is = new ByteArrayInputStream(originalSignature.getBytes(StandardCharsets.UTF_8))) { - dstZFile.add(SIGNATURE_INFO_ASSET_PATH, is); - } catch (Throwable e) { - throw new PatchError("Error when saving signature", e); - } } // copy out manifest file from zlib @@ -213,14 +214,17 @@ public class LSPatch { if (manifestEntry == null) throw new PatchError("Provided file is not a valid apk"); - // parse the app main application full name from the manifest file - ManifestParser.Pair pair = ManifestParser.parseManifestFile(manifestEntry.open()); - if (pair == null) - throw new PatchError("Failed to parse AndroidManifest.xml"); - String appComponentFactory = pair.appComponentFactory == null ? "" : pair.appComponentFactory; + // parse the app appComponentFactory full name from the manifest file + String appComponentFactory; + try (var is = manifestEntry.open()) { + var pair =ManifestParser.parseManifestFile(is); + if (pair == null) + throw new PatchError("Failed to parse AndroidManifest.xml"); + appComponentFactory = pair.appComponentFactory == null ? "" : pair.appComponentFactory; - if (verbose) - System.out.println("original appComponentFactory class: " + appComponentFactory); + if (verbose) + System.out.println("original appComponentFactory class: " + appComponentFactory); + } System.out.println("Patching apk..."); // modify manifest @@ -230,18 +234,13 @@ public class LSPatch { throw new PatchError("Error when modifying manifest", e); } - // save original appComponentFactory name to asset file even its empty - try (var is = new ByteArrayInputStream(appComponentFactory.getBytes(StandardCharsets.UTF_8))) { - dstZFile.add(ORIGINAL_APP_COMPONENT_FACTORY_ASSET_PATH, is); - } - if (verbose) System.out.println("Adding native lib.."); // 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/lspd/" + arch + "/liblspd.so"; + String entryName = "assets/lspatch/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) { @@ -258,21 +257,23 @@ public class LSPatch { try (var is = getClass().getClassLoader().getResourceAsStream("assets/dex/loader.dex")) { dstZFile.add("classes.dex", is); } catch (Throwable e) { - throw new PatchError("Error when add dex", e); + throw new PatchError("Error when adding dex", e); } try (var is = getClass().getClassLoader().getResourceAsStream("assets/dex/lsp.dex")) { - dstZFile.add("assets/lsp", is); + dstZFile.add(DEX_ASSET_PATH, is); } catch (Throwable e) { - throw new PatchError("Error when add assets", e); + throw new PatchError("Error when adding assets", e); } // save lspatch config to asset.. - try (var is = new ByteArrayInputStream("42".getBytes(StandardCharsets.UTF_8))) { - dstZFile.add("assets/" + Constants.CONFIG_NAME_SIGBYPASSLV + sigbypassLevel, is); - } - try (var is = new ByteArrayInputStream(Boolean.toString(useManager).getBytes(StandardCharsets.UTF_8))) { - dstZFile.add(USE_MANAGER_CONTROL_PATH, is); + var config = new PatchConfig(useManager, sigbypassLevel, originalSignature, appComponentFactory); + var configBytes = new Gson().toJson(config).getBytes(StandardCharsets.UTF_8); + + try (var is = new ByteArrayInputStream(configBytes)) { + dstZFile.add(CONFIG_ASSET_PATH, is); + } catch (Throwable e) { + throw new PatchError("Error when saving config"); } Set apkArchs = new HashSet<>(); @@ -296,30 +297,12 @@ public class LSPatch { return false; }); + embedModules(dstZFile); + // create zip link if (verbose) System.out.println("Creating nested apk link..."); - for (var moduleFile : modules) { - final var moduleManifest = new ManifestParser.Pair[]{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 arch : apkArchs) { - dstZFile.addLink(nested.getEntry(), "lib/" + arch + "/" + packageName + ".so"); - } - } - } - for (StoredEntry entry : srcZFile.entries()) { String name = entry.getCentralDirectoryHeader().getName(); if (name.startsWith("classes") && name.endsWith(".dex")) continue; @@ -343,11 +326,27 @@ public class LSPatch { } } + private void embedModules(ZFile zFile) { + System.out.println("Embedding modules..."); + for (var module : modules) { + File file = new File(module); + try (var apk = ZFile.openReadOnly(new File(module)); + var fileIs = new FileInputStream(file); + var xmlIs = Objects.requireNonNull(apk.get(ANDROID_MANIFEST_XML)).open() + ) { + var manifest = Objects.requireNonNull(ManifestParser.parseManifestFile(xmlIs)); + var packageName = manifest.packageName; + System.out.println(" - " + packageName); + zFile.add("assets/lspatch/modules/" + packageName + ".bin", fileIs); + } catch (NullPointerException | IOException e) { + System.err.println(module + " does not exist or is not a valid apk file."); + } + } + } + private byte[] modifyManifestFile(InputStream is) throws IOException { ModificationProperty property = new ModificationProperty(); - if (!modules.isEmpty()) - property.addApplicationAttribute(new AttributeItem("extractNativeLibs", true)); if (overrideVersionCode) property.addManifestAttribute(new AttributeItem(NodeValue.Manifest.VERSION_CODE, 1)); property.addApplicationAttribute(new AttributeItem(NodeValue.Application.DEBUGGABLE, debuggableFlag)); diff --git a/share/src/main/java/org/lsposed/lspatch/share/Constants.java b/share/src/main/java/org/lsposed/lspatch/share/Constants.java index 7643712..5a15b3f 100644 --- a/share/src/main/java/org/lsposed/lspatch/share/Constants.java +++ b/share/src/main/java/org/lsposed/lspatch/share/Constants.java @@ -1,12 +1,13 @@ package org.lsposed.lspatch.share; public class Constants { - final static public String ORIGINAL_APK_ASSET_PATH = "assets/origin_apk.bin"; - final static public String ORIGINAL_APP_COMPONENT_FACTORY_ASSET_PATH = "assets/original_app_component_factory.ini"; + + final static public String DEX_ASSET_PATH = "assets/lspatch/lsp.dex"; + final static public String CONFIG_ASSET_PATH = "assets/lspatch/config.json"; + final static public String ORIGINAL_APK_ASSET_PATH = "assets/lspatch/origin_apk.bin"; final static public String PROXY_APP_COMPONENT_FACTORY = "org.lsposed.lspatch.appstub.LSPAppComponentFactoryStub"; final static public String MANAGER_PACKAGE_NAME = "org.lsposed.lspatch"; - final static public String CONFIG_NAME_SIGBYPASSLV = "lspatch_sigbypasslv"; final static public int SIGBYPASS_LV_DISABLE = 0; final static public int SIGBYPASS_LV_PM = 1; final static public int SIGBYPASS_LV_PM_OPENAT = 2; diff --git a/share/src/main/java/org/lsposed/lspatch/share/PatchConfig.java b/share/src/main/java/org/lsposed/lspatch/share/PatchConfig.java new file mode 100644 index 0000000..406d71e --- /dev/null +++ b/share/src/main/java/org/lsposed/lspatch/share/PatchConfig.java @@ -0,0 +1,16 @@ +package org.lsposed.lspatch.share; + +public class PatchConfig { + + public final boolean useManager; + public final int sigBypassLevel; + public final String originalSignature; + public final String appComponentFactory; + + public PatchConfig(boolean useManager, int sigBypassLevel, String originalSignature, String appComponentFactory) { + this.useManager = useManager; + this.sigBypassLevel = sigBypassLevel; + this.originalSignature = originalSignature; + this.appComponentFactory = appComponentFactory; + } +}