Use AppComponentFactoryStub to instantiate LSPApplication and original application

This commit is contained in:
Nullptr 2021-07-18 13:26:38 +08:00
parent ed6eaec32c
commit 8cbf468d59
4 changed files with 31 additions and 35 deletions

View File

@ -116,9 +116,6 @@ public class LSPApplication extends ApplicationServiceClient {
} catch (Throwable e) {
Log.e(TAG, "Do hook", e);
}
if (isApplicationProxied()) {
instance.createOriginalApplication();
}
}
public static void disableProfile(Context context) {
@ -265,17 +262,7 @@ public class LSPApplication extends ApplicationServiceClient {
try {
Object mBoundApplication = XposedHelpers.getObjectField(getActivityThread(), "mBoundApplication");
Object loadedApkObj = XposedHelpers.getObjectField(mBoundApplication, "info");
var mClassLoader = (ClassLoader) XposedHelpers.getObjectField(loadedApkObj, "mClassLoader");
final String cacheApkPath = context.getCacheDir().getAbsolutePath() + "/origin_apk.bin";
try (InputStream inputStream = context.getAssets().open("origin_apk.bin")) {
Files.copy(inputStream, Paths.get(cacheApkPath));
} catch (FileAlreadyExistsException ignored) {
}
appClassLoader = new PathClassLoader(cacheApkPath, mClassLoader.getParent());
XposedHelpers.setObjectField(loadedApkObj, "mClassLoader", appClassLoader);
Log.d(TAG, "appClassLoader is now switched to " + appClassLoader);
appClassLoader = (ClassLoader) XposedHelpers.getObjectField(loadedApkObj, "mClassLoader");
} catch (Throwable e) {
Log.e(TAG, "initAppClassLoader", e);
}

View File

@ -23,13 +23,21 @@ import dalvik.system.PathClassLoader;
@SuppressLint("NewApi")
public class LSPAppComponentFactoryStub extends AppComponentFactory {
private static final String TAG = "LSPatch";
private static final String PROXY_APPLICATION = "org.lsposed.lspatch.appstub.LSPApplicationStub";
private static final String ORIGINAL_APK_ASSET_PATH = "assets/origin_apk.bin";
private static final String ORIGINAL_APP_COMPONENT_FACTORY_ASSET_PATH = "assets/original_app_component_factory.ini";
private ClassLoader appClassLoader = null;
private ClassLoader baseClassLoader = null;
private AppComponentFactory originalAppComponentFactory = null;
// Proxy appComponentFactory to load the original one
private void initOrigin(ClassLoader cl, ApplicationInfo aInfo) {
/**
* Instantiate original AppComponentFactory<br/>
* This method will be called at <b>instantiateClassLoader</b> by <b>createOrUpdateClassLoaderLocked</b>
*
* @param cl PathClassLoader(originalApk)
**/
private void initOriginalAppComponentFactory(ClassLoader cl, ApplicationInfo aInfo) {
final String cacheApkPath = aInfo.dataDir + "/cache/origin_apk.bin";
final String originalAppComponentFactoryClass = FileUtils.readTextFromInputStream(cl.getResourceAsStream(ORIGINAL_APP_COMPONENT_FACTORY_ASSET_PATH));
@ -38,22 +46,27 @@ public class LSPAppComponentFactoryStub extends AppComponentFactory {
Files.copy(inputStream, Paths.get(cacheApkPath));
} catch (FileAlreadyExistsException ignored) {
}
ClassLoader appClassLoader = new PathClassLoader(cacheApkPath, cl.getParent());
originalAppComponentFactory = (AppComponentFactory) appClassLoader.loadClass(originalAppComponentFactoryClass).newInstance();
Log.d(TAG, "appComponentFactory is now switched to " + originalAppComponentFactory);
appClassLoader = new PathClassLoader(cacheApkPath, cl.getParent());
if (originalAppComponentFactoryClass == null || originalAppComponentFactoryClass.isEmpty())
originalAppComponentFactory = new AppComponentFactory();
else
originalAppComponentFactory = (AppComponentFactory) appClassLoader.loadClass(originalAppComponentFactoryClass).newInstance();
Log.d(TAG, "Instantiate original AppComponentFactory: " + originalAppComponentFactory);
} catch (Throwable e) {
Log.e(TAG, "initOrigin", e);
Log.e(TAG, "initOriginalAppComponentFactory", e);
}
}
@Override
public ClassLoader instantiateClassLoader(ClassLoader cl, ApplicationInfo aInfo) {
if (originalAppComponentFactory == null) initOrigin(cl, aInfo);
return originalAppComponentFactory.instantiateClassLoader(cl, aInfo);
baseClassLoader = cl;
initOriginalAppComponentFactory(cl, aInfo);
return originalAppComponentFactory.instantiateClassLoader(appClassLoader, aInfo);
}
@Override
public Application instantiateApplication(ClassLoader cl, String className) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
baseClassLoader.loadClass(PROXY_APPLICATION).newInstance();
return originalAppComponentFactory.instantiateApplication(cl, className);
}

View File

@ -8,7 +8,7 @@ buildscript {
maven { url "https://jitpack.io" }
}
dependencies {
classpath 'com.android.tools.build:gradle:7.0.0-beta04'
classpath 'com.android.tools.build:gradle:7.0.0-beta05'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.32"
}
}

View File

@ -94,7 +94,6 @@ public class LSPatch {
));
private static JCommander jCommander;
private boolean hasAppComponentFactory;
public static void main(String... args) throws IOException {
LSPatch lsPatch = new LSPatch();
@ -193,8 +192,7 @@ public class LSPatch {
if (triple == null)
throw new PatchError("Failed to parse AndroidManifest.xml");
String applicationName = triple.applicationName == null ? "" : triple.applicationName;
String appComponentFactory = triple.appComponentFactory;
hasAppComponentFactory = appComponentFactory != null;
String appComponentFactory = triple.appComponentFactory == null ? "" : triple.appComponentFactory;
if (verbose) {
System.out.println("original application name: " + applicationName);
@ -210,12 +208,11 @@ public class LSPatch {
}
// save original appComponentFactory name to asset file even its empty
if (appComponentFactory != null)
try (var is = new ByteArrayInputStream(appComponentFactory.getBytes(StandardCharsets.UTF_8))) {
zFile.add(APP_COMPONENT_FACTORY_ASSET_PATH, is);
} catch (Throwable e) {
throw new PatchError("Error when saving appComponentFactory class: " + e);
}
try (var is = new ByteArrayInputStream(appComponentFactory.getBytes(StandardCharsets.UTF_8))) {
zFile.add(APP_COMPONENT_FACTORY_ASSET_PATH, is);
} catch (Throwable e) {
throw new PatchError("Error when saving appComponentFactory class: " + e);
}
// save original main application name to asset file even its empty
try (var is = new ByteArrayInputStream(applicationName.getBytes(StandardCharsets.UTF_8))) {
@ -350,9 +347,8 @@ public class LSPatch {
ModificationProperty property = new ModificationProperty();
property.addApplicationAttribute(new AttributeItem(NodeValue.Application.DEBUGGABLE, debuggableFlag));
property.addApplicationAttribute(new AttributeItem(NodeValue.Application.NAME, PROXY_APPLICATION));
if (hasAppComponentFactory)
property.addApplicationAttribute(new AttributeItem("appComponentFactory", PROXY_APP_COMPONENT_FACTORY));
//property.addApplicationAttribute(new AttributeItem(NodeValue.Application.NAME, PROXY_APPLICATION));
property.addApplicationAttribute(new AttributeItem("appComponentFactory", PROXY_APP_COMPONENT_FACTORY));
var os = new ByteArrayOutputStream();
(new ManifestEditor(is, os, property)).processManifest();