New inject way
TODO: we should make a standalone app for test
This commit is contained in:
parent
c4cc5ab563
commit
e8ccca40a9
|
|
@ -21,6 +21,12 @@ android {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
}
|
||||
buildTypes {
|
||||
debug {
|
||||
|
|
|
|||
|
|
@ -6,19 +6,22 @@
|
|||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<application
|
||||
android:name="org.lsposed.lspatch.tester.XposedTestApplication"
|
||||
android:name="org.lsposed.lspatch.appstub.LSPApplicationStub"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/sample_app_title"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true">
|
||||
<activity android:name="org.lsposed.lspatch.tester.MainActivity">
|
||||
<activity android:name="org.lsposed.lspatch.tester.MainActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<meta-data android:name="xposedmodule" android:value="true"/>
|
||||
<meta-data android:name="xposedminversion" android:value="53"/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
@ -1 +1 @@
|
|||
org.lsposed.lspatch.tester.Hook
|
||||
org.lsposed.org.lsposed.org.lsposed.lspatch.tester.Hook
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
package de.robv.android.xposed;
|
||||
|
||||
import de.robv.android.xposed.IXposedHookZygoteInit;
|
||||
import de.robv.android.xposed.IXposedMod;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
|
||||
import java.lang.reflect.Member;
|
||||
|
||||
public class XposedHelper {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
package org.lsposed.lspatch.appstub;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
public class LSPApplicationStub extends Application {
|
||||
|
||||
private static byte[] dex = null;
|
||||
|
||||
static {
|
||||
try (var is = LSPApplicationStub.class.getClassLoader().getResourceAsStream("assets/lsp");
|
||||
var os = new ByteArrayOutputStream()) {
|
||||
byte[] buffer = new byte[8192];
|
||||
int n;
|
||||
while (-1 != (n = is.read(buffer))) {
|
||||
os.write(buffer, 0, n);
|
||||
}
|
||||
dex = os.toByteArray();
|
||||
} catch (Throwable e) {
|
||||
android.util.Log.e("LSPatch", "load dex error", e);
|
||||
}
|
||||
System.loadLibrary("lspd");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
super.attachBaseContext(base);
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ import org.lsposed.lspatch.loader.util.FileUtils;
|
|||
import org.lsposed.lspatch.loader.util.XLog;
|
||||
import org.lsposed.lspatch.loader.util.XpatchUtils;
|
||||
import org.lsposed.lspatch.share.Constants;
|
||||
import org.lsposed.lspd.deopt.PrebuiltMethodsDeopter;
|
||||
import org.lsposed.lspd.nativebridge.SigBypass;
|
||||
import org.lsposed.lspd.yahfa.hooker.YahfaHooker;
|
||||
|
||||
|
|
@ -40,7 +41,8 @@ import de.robv.android.xposed.XposedInit;
|
|||
public class LSPApplication {
|
||||
private static final String ORIGINAL_APPLICATION_NAME_ASSET_PATH = "original_application_name.ini";
|
||||
private static final String ORIGINAL_SIGNATURE_ASSET_PATH = "original_signature_info.ini";
|
||||
private static final String TAG = LSPApplication.class.getSimpleName();
|
||||
private static final String TAG = "LSPatch";
|
||||
|
||||
private static String originalApplicationName = null;
|
||||
private static String originalSignature = null;
|
||||
private static Application sOriginalApplication = null;
|
||||
|
|
@ -49,35 +51,30 @@ public class LSPApplication {
|
|||
|
||||
private static int TRANSACTION_getPackageInfo_ID = -1;
|
||||
|
||||
final static public int FIRST_ISOLATED_UID = 99000;
|
||||
final static public int LAST_ISOLATED_UID = 99999;
|
||||
final static public int FIRST_APP_ZYGOTE_ISOLATED_UID = 90000;
|
||||
final static public int LAST_APP_ZYGOTE_ISOLATED_UID = 98999;
|
||||
final static public int SHARED_RELRO_UID = 1037;
|
||||
final static public int PER_USER_RANGE = 100000;
|
||||
|
||||
static Context context;
|
||||
final static private LSPApplication instance = new LSPApplication();
|
||||
|
||||
static public boolean isIsolated() {
|
||||
int uid = android.os.Process.myUid();
|
||||
uid = uid % PER_USER_RANGE;
|
||||
return (uid >= FIRST_ISOLATED_UID && uid <= LAST_ISOLATED_UID) || (uid >= FIRST_APP_ZYGOTE_ISOLATED_UID && uid <= LAST_APP_ZYGOTE_ISOLATED_UID);
|
||||
return (android.os.Process.myUid() % PER_USER_RANGE) >= FIRST_APP_ZYGOTE_ISOLATED_UID;
|
||||
}
|
||||
|
||||
static {
|
||||
static public void onLoad() {
|
||||
cacheSigbypassLv = -1;
|
||||
|
||||
if (isIsolated()) {
|
||||
XLog.d(TAG, "skip isolated process");
|
||||
return;
|
||||
}
|
||||
else {
|
||||
context = XpatchUtils.createAppContext();
|
||||
Context context = XpatchUtils.createAppContext();
|
||||
if (context == null) {
|
||||
XLog.e(TAG, "create context err");
|
||||
return;
|
||||
}
|
||||
else {
|
||||
System.load(context.getApplicationInfo().nativeLibraryDir + "/liblspd.so");
|
||||
YahfaHooker.init();
|
||||
XposedBridge.initXResources();
|
||||
PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote
|
||||
XposedInit.startsSystemServer = false;
|
||||
|
||||
originalApplicationName = FileUtils.readTextFromAssets(context, ORIGINAL_APPLICATION_NAME_ASSET_PATH);
|
||||
|
|
@ -87,13 +84,10 @@ public class LSPApplication {
|
|||
XLog.d(TAG, "original signature info " + originalSignature);
|
||||
|
||||
try {
|
||||
doHook();
|
||||
doHook(context);
|
||||
initAndLoadModules(context);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "Do hook", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -106,12 +100,7 @@ public class LSPApplication {
|
|||
}
|
||||
|
||||
private static boolean isApplicationProxied() {
|
||||
if (originalApplicationName != null && !originalApplicationName.isEmpty() && !("android.app.Application").equals(originalApplicationName)) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
return originalApplicationName != null && !originalApplicationName.isEmpty() && !("android.app.Application").equals(originalApplicationName);
|
||||
}
|
||||
|
||||
private static ClassLoader getAppClassLoader() {
|
||||
|
|
@ -122,14 +111,13 @@ public class LSPApplication {
|
|||
Object mBoundApplication = XposedHelpers.getObjectField(getActivityThread(), "mBoundApplication");
|
||||
Object loadedApkObj = XposedHelpers.getObjectField(mBoundApplication, "info");
|
||||
appClassLoader = (ClassLoader) XposedHelpers.callMethod(loadedApkObj, "getClassLoader");
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "getAppClassLoader", e);
|
||||
}
|
||||
return appClassLoader;
|
||||
}
|
||||
|
||||
private static void byPassSignature() throws ClassNotFoundException, IllegalAccessException {
|
||||
private static void byPassSignature(Context context) throws ClassNotFoundException, IllegalAccessException {
|
||||
Field[] pmStubFields = Class.forName("android.content.pm.IPackageManager$Stub").getDeclaredFields();
|
||||
for (Field field : pmStubFields) {
|
||||
if (!Modifier.isStatic(field.getModifiers()) || field.getType() != int.class) {
|
||||
|
|
@ -203,25 +191,26 @@ public class LSPApplication {
|
|||
// reset pos
|
||||
out.setDataPosition(0);
|
||||
}
|
||||
}
|
||||
catch (Throwable err) {
|
||||
} catch (Throwable err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void doHook() throws IllegalAccessException, ClassNotFoundException, IOException {
|
||||
private static void doHook(Context context) throws IllegalAccessException, ClassNotFoundException, IOException {
|
||||
if (isApplicationProxied()) {
|
||||
hookContextImplSetOuterContext();
|
||||
hookInstallContentProviders();
|
||||
hookActivityAttach();
|
||||
hookServiceAttach();
|
||||
}
|
||||
if (fetchSigbypassLv() >= Constants.SIGBYPASS_LV_PM) {
|
||||
byPassSignature();
|
||||
hookApplicationStub();
|
||||
int bypassLv = fetchSigbypassLv(context);
|
||||
if (bypassLv >= Constants.SIGBYPASS_LV_PM) {
|
||||
byPassSignature(context);
|
||||
}
|
||||
if (fetchSigbypassLv() >= Constants.SIGBYPASS_LV_PM_OPENAT) {
|
||||
if (bypassLv >= Constants.SIGBYPASS_LV_PM_OPENAT) {
|
||||
File apk = new File(context.getCacheDir(), "lspatchapk.so");
|
||||
if (!apk.exists()) {
|
||||
try (InputStream inputStream = context.getAssets().open("origin_apk.bin");
|
||||
|
|
@ -241,7 +230,7 @@ public class LSPApplication {
|
|||
|
||||
private static int cacheSigbypassLv;
|
||||
|
||||
private static int fetchSigbypassLv() {
|
||||
private static int fetchSigbypassLv(Context context) {
|
||||
if (cacheSigbypassLv != -1) {
|
||||
return cacheSigbypassLv;
|
||||
}
|
||||
|
|
@ -249,48 +238,82 @@ public class LSPApplication {
|
|||
try (InputStream inputStream = context.getAssets().open(Constants.CONFIG_NAME_SIGBYPASSLV + i)) {
|
||||
cacheSigbypassLv = i;
|
||||
return i;
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
} catch (IOException ignore) {
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException(Constants.CONFIG_NAME_SIGBYPASSLV + " err");
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void hookApplicationStub() {
|
||||
try {
|
||||
Class<?> appStub = XposedHelpers.findClass("org.lsposed.lspatch.appstub.LSPApplicationStub", getAppClassLoader());
|
||||
XposedHelpers.findAndHookMethod(appStub, "onCreate", new XC_MethodHook() {
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||
instance.onCreate();
|
||||
}
|
||||
});
|
||||
XposedHelpers.findAndHookMethod(appStub, "attachBaseContext", Context.class, new XC_MethodHook() {
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||
instance.attachBaseContext((Context) param.args[0]);
|
||||
}
|
||||
});
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "hookApplicationStub");
|
||||
}
|
||||
}
|
||||
|
||||
private static void hookContextImplSetOuterContext() {
|
||||
try {
|
||||
XposedHelpers.findAndHookMethod("android.app.ContextImpl", getAppClassLoader(), "setOuterContext", Context.class, new XC_MethodHook() {
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||
replaceApplicationParam(param.args);
|
||||
// XposedHelpers.setObjectField(param.thisObject, "mOuterContext", sOriginalApplication);
|
||||
}
|
||||
});
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "hookContextImplSetOuterContext", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void hookInstallContentProviders() {
|
||||
try {
|
||||
XposedBridge.hookAllMethods(XposedHelpers.findClass("android.app.ActivityThread", getAppClassLoader()), "installContentProviders", new XC_MethodHook() {
|
||||
@Override
|
||||
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||
replaceApplicationParam(param.args);
|
||||
}
|
||||
});
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "hookInstallContextProviders", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void hookActivityAttach() {
|
||||
try {
|
||||
XposedBridge.hookAllMethods(XposedHelpers.findClass("android.app.Activity", getAppClassLoader()), "attach", new XC_MethodHook() {
|
||||
@Override
|
||||
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||
replaceApplicationParam(param.args);
|
||||
}
|
||||
});
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "hookActivityAttach", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void hookServiceAttach() {
|
||||
try {
|
||||
XposedBridge.hookAllMethods(XposedHelpers.findClass("android.app.Service", getAppClassLoader()), "attach", new XC_MethodHook() {
|
||||
@Override
|
||||
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||
replaceApplicationParam(param.args);
|
||||
}
|
||||
});
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "hookServiceAttach", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void replaceApplicationParam(Object[] args) {
|
||||
|
|
@ -309,9 +332,8 @@ public class LSPApplication {
|
|||
try {
|
||||
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
|
||||
activityThread = XposedHelpers.callStaticMethod(activityThreadClass, "currentActivityThread");
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "getActivityThread", e);
|
||||
}
|
||||
}
|
||||
return activityThread;
|
||||
|
|
@ -328,29 +350,23 @@ public class LSPApplication {
|
|||
private void attachOrignalBaseContext(Context base) {
|
||||
try {
|
||||
XposedHelpers.callMethod(sOriginalApplication, "attachBaseContext", base);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "attachOriginalBaseContext", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void setLoadedApkField(Context base) {
|
||||
// mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
|
||||
try {
|
||||
Class<?> contextImplClass = Class.forName("android.app.ContextImpl");
|
||||
Object contextImpl = XposedHelpers.callStaticMethod(contextImplClass, "getImpl", base);
|
||||
Object loadedApk = XposedHelpers.getObjectField(contextImpl, "mPackageInfo");
|
||||
XposedHelpers.setObjectField(sOriginalApplication, "mLoadedApk", loadedApk);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "setLoadedApkField", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void onCreate() {
|
||||
// setLoadedApkField(sOriginalApplication);
|
||||
// XposedHelpers.setObjectField(sOriginalApplication, "mLoadedApk", XposedHelpers.getObjectField(this, "mLoadedApk"));
|
||||
|
||||
if (isApplicationProxied()) {
|
||||
// replaceApplication();
|
||||
replaceLoadedApkApplication();
|
||||
|
|
@ -371,18 +387,16 @@ public class LSPApplication {
|
|||
|
||||
// replace LoadedApk.java makeApplication() mApplication = app;
|
||||
XposedHelpers.setObjectField(loadedApkObj, "mApplication", sOriginalApplication);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "replaceLoadedApkApplication", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void replaceActivityThreadApplication() {
|
||||
try {
|
||||
XposedHelpers.setObjectField(getActivityThread(), "mInitialApplication", sOriginalApplication);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "replaceActivityThreadApplication", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -390,9 +404,8 @@ public class LSPApplication {
|
|||
if (sOriginalApplication == null) {
|
||||
try {
|
||||
sOriginalApplication = (Application) getAppClassLoader().loadClass(originalApplicationName).newInstance();
|
||||
}
|
||||
catch (InstantiationException | ClassNotFoundException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "createOriginalApplication", e);
|
||||
}
|
||||
}
|
||||
return sOriginalApplication;
|
||||
|
|
@ -404,9 +417,8 @@ public class LSPApplication {
|
|||
Object applicationInfoObj = XposedHelpers.getObjectField(mBoundApplication, "appInfo"); // info
|
||||
|
||||
XposedHelpers.setObjectField(applicationInfoObj, "className", originalApplicationName);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "modifyApplicationInfoClassName", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,12 +158,6 @@ public class LSPLoader {
|
|||
|
||||
appContext = context;
|
||||
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
|
||||
if (!FileUtils.isSdcardPermissionGranted(context)) {
|
||||
XLog.e(TAG, "File permission is not granted, can not control xposed module by file " + XPOSED_MODULE_FILE_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
initSELinux(context);
|
||||
|
||||
ClassLoader originClassLoader = context.getClassLoader();
|
||||
|
|
@ -195,7 +189,7 @@ public class LSPLoader {
|
|||
if (!app.enabled) {
|
||||
continue;
|
||||
}
|
||||
if (app.metaData != null && (app.metaData.containsKey("xposedmodule"))) {
|
||||
if (app.metaData != null && app.metaData.containsKey("xposedminversion")) {
|
||||
String apkPath = pkg.applicationInfo.publicSourceDir;
|
||||
String apkName = context.getPackageManager().getApplicationLabel(pkg.applicationInfo).toString();
|
||||
if (TextUtils.isEmpty(apkPath)) {
|
||||
|
|
@ -211,9 +205,7 @@ public class LSPLoader {
|
|||
|
||||
final List<Pair<String, String>> installedModuleListFinal = installedModuleList;
|
||||
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
new Thread(() -> {
|
||||
List<String> savedPackageNameList = loadPackageNameListFromFile(false);
|
||||
if (savedPackageNameList == null) {
|
||||
savedPackageNameList = new ArrayList<>();
|
||||
|
|
@ -231,7 +223,6 @@ public class LSPLoader {
|
|||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
return modulePathList;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
|||
public class Hook implements IXposedHookLoadPackage {
|
||||
@Override
|
||||
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
|
||||
XposedHelpers.findAndHookMethod("org.lsposed.lspatch.tester.MainActivity", lpparam.classLoader, "checkXposed2", new XC_MethodHook() {
|
||||
XposedHelpers.findAndHookMethod("org.lsposed.org.lsposed.org.lsposed.lspatch.tester.MainActivity", lpparam.classLoader, "checkXposed2", new XC_MethodHook() {
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||
param.setResult(true);
|
||||
|
|
|
|||
|
|
@ -26,12 +26,6 @@ public class MainActivity extends Activity {
|
|||
param.setResult(true);
|
||||
}
|
||||
});
|
||||
XposedHelpers.findAndHookMethod(this.getClass(), "checkXposed2", new XC_MethodHook() {
|
||||
@Override
|
||||
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||
param.setResult(true);
|
||||
}
|
||||
});
|
||||
|
||||
TextView textView = findViewById(R.id.msg);
|
||||
if (checkXposed() && checkXposed2()) {
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
package org.lsposed.lspatch.tester;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
|
||||
import org.lsposed.lspatch.loader.LSPLoader;
|
||||
import org.lsposed.lspd.yahfa.hooker.YahfaHooker;
|
||||
|
||||
import de.robv.android.xposed.XposedInit;
|
||||
|
||||
// you can run this app to test hook framework
|
||||
public class XposedTestApplication extends Application {
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
super.attachBaseContext(base);
|
||||
}
|
||||
|
||||
static {
|
||||
System.loadLibrary("lspd");
|
||||
YahfaHooker.init();
|
||||
XposedInit.startsSystemServer = false;
|
||||
LSPLoader.initAndLoadModules();
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="org.lsposed.lspatch.tester.MainActivity">
|
||||
tools:context="org.lsposed.org.lsposed.tester.MainActivity">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/msg"
|
||||
|
|
|
|||
|
|
@ -1,120 +1,36 @@
|
|||
package org.lsposed.lspatch.appstub;
|
||||
|
||||
import android.app.ActivityThread;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import dalvik.system.InMemoryDexClassLoader;
|
||||
|
||||
public class LSPApplicationStub extends Application {
|
||||
final static String TAG = LSPApplicationStub.class.getSimpleName();
|
||||
|
||||
static Object realLSPApplication = null;
|
||||
private static byte[] dex = null;
|
||||
|
||||
static {
|
||||
// load real lsp loader from asset
|
||||
Context context = createAppContext();
|
||||
if (context == null) {
|
||||
throw new IllegalStateException("create context err");
|
||||
}
|
||||
else {
|
||||
try (InputStream inputStream = context.getAssets().open("lsp.dex");
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
|
||||
|
||||
int nRead;
|
||||
byte[] data = new byte[16384];
|
||||
|
||||
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
|
||||
buffer.write(data, 0, nRead);
|
||||
}
|
||||
|
||||
// loader can load it's own so from app native library dir
|
||||
String libraryDir = context.getApplicationInfo().nativeLibraryDir;
|
||||
|
||||
Log.d(TAG, "LSPApplicationStub cl: " + LSPApplicationStub.class.getClassLoader());
|
||||
Log.d(TAG, "NativePath : " + libraryDir);
|
||||
|
||||
InMemoryDexClassLoader loaderClassLoader = new InMemoryDexClassLoader(ByteBuffer.wrap(buffer.toByteArray()),
|
||||
LSPApplicationStub.class.getClassLoader());
|
||||
Class<?> lspa = loaderClassLoader.loadClass("org.lsposed.lspatch.loader.LSPApplication");
|
||||
realLSPApplication = lspa.newInstance();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("wtf", e);
|
||||
try (var is = LSPApplicationStub.class.getClassLoader().getResourceAsStream("assets/lsp");
|
||||
var os = new ByteArrayOutputStream()) {
|
||||
byte[] buffer = new byte[8192];
|
||||
int n;
|
||||
while (-1 != (n = is.read(buffer))) {
|
||||
os.write(buffer, 0, n);
|
||||
}
|
||||
dex = os.toByteArray();
|
||||
} catch (Throwable e) {
|
||||
android.util.Log.e("LSPatch", "load dex error", e);
|
||||
}
|
||||
System.loadLibrary("lspd");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
try {
|
||||
realLSPApplication.getClass().getDeclaredMethod("onCreate").invoke(realLSPApplication);
|
||||
}
|
||||
catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
|
||||
throw new IllegalStateException("wtf", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
super.attachBaseContext(base);
|
||||
|
||||
try {
|
||||
Method method = realLSPApplication.getClass().getDeclaredMethod("attachBaseContext", Context.class);
|
||||
method.setAccessible(true);
|
||||
method.invoke(realLSPApplication, base);
|
||||
}
|
||||
catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
|
||||
throw new IllegalStateException("wtf", e);
|
||||
}
|
||||
}
|
||||
|
||||
// copy from app project
|
||||
public static Context createAppContext() {
|
||||
try {
|
||||
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
|
||||
Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
|
||||
currentActivityThreadMethod.setAccessible(true);
|
||||
|
||||
Object activityThreadObj = currentActivityThreadMethod.invoke(null);
|
||||
|
||||
Field boundApplicationField = activityThreadClass.getDeclaredField("mBoundApplication");
|
||||
boundApplicationField.setAccessible(true);
|
||||
Object mBoundApplication = boundApplicationField.get(activityThreadObj); // AppBindData
|
||||
if (mBoundApplication == null) {
|
||||
Log.e(TAG, "mBoundApplication null");
|
||||
return null;
|
||||
}
|
||||
Field infoField = mBoundApplication.getClass().getDeclaredField("info"); // info
|
||||
infoField.setAccessible(true);
|
||||
Object loadedApkObj = infoField.get(mBoundApplication); // LoadedApk
|
||||
if (loadedApkObj == null) {
|
||||
Log.e(TAG, "loadedApkObj null");
|
||||
return null;
|
||||
}
|
||||
Class<?> contextImplClass = Class.forName("android.app.ContextImpl");
|
||||
Method createAppContextMethod = contextImplClass.getDeclaredMethod("createAppContext", activityThreadClass, loadedApkObj.getClass());
|
||||
createAppContextMethod.setAccessible(true);
|
||||
|
||||
Object context = createAppContextMethod.invoke(null, (ActivityThread) activityThreadObj, loadedApkObj);
|
||||
|
||||
if (context instanceof Context) {
|
||||
return (Context) context;
|
||||
}
|
||||
}
|
||||
catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | NoSuchFieldException e) {
|
||||
throw new IllegalStateException("wtf", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
2
core
2
core
|
|
@ -1 +1 @@
|
|||
Subproject commit 90b37a68aa32e79ea2dc58048f7657ba66abed89
|
||||
Subproject commit 9e47e9027c4244317085cce2a95ddcbb7b579ee0
|
||||
|
|
@ -254,7 +254,7 @@ public class LSPatch {
|
|||
}
|
||||
|
||||
try (var is = getClass().getClassLoader().getResourceAsStream("assets/dex/lsp.dex")) {
|
||||
zFile.add("assets/lsp.dex", is);
|
||||
zFile.add("assets/lsp", is);
|
||||
} catch (Throwable e) {
|
||||
throw new PatchError("Error when add assets: " + e);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue