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 {
|
buildTypes {
|
||||||
debug {
|
debug {
|
||||||
|
|
|
||||||
|
|
@ -6,19 +6,22 @@
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name="org.lsposed.lspatch.tester.XposedTestApplication"
|
android:name="org.lsposed.lspatch.appstub.LSPApplicationStub"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/sample_app_title"
|
android:label="@string/sample_app_title"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true">
|
android:supportsRtl="true">
|
||||||
<activity android:name="org.lsposed.lspatch.tester.MainActivity">
|
<activity android:name="org.lsposed.lspatch.tester.MainActivity"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
<meta-data android:name="xposedmodule" android:value="true"/>
|
||||||
|
<meta-data android:name="xposedminversion" android:value="53"/>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</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;
|
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;
|
import java.lang.reflect.Member;
|
||||||
|
|
||||||
public class XposedHelper {
|
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.XLog;
|
||||||
import org.lsposed.lspatch.loader.util.XpatchUtils;
|
import org.lsposed.lspatch.loader.util.XpatchUtils;
|
||||||
import org.lsposed.lspatch.share.Constants;
|
import org.lsposed.lspatch.share.Constants;
|
||||||
|
import org.lsposed.lspd.deopt.PrebuiltMethodsDeopter;
|
||||||
import org.lsposed.lspd.nativebridge.SigBypass;
|
import org.lsposed.lspd.nativebridge.SigBypass;
|
||||||
import org.lsposed.lspd.yahfa.hooker.YahfaHooker;
|
import org.lsposed.lspd.yahfa.hooker.YahfaHooker;
|
||||||
|
|
||||||
|
|
@ -40,7 +41,8 @@ import de.robv.android.xposed.XposedInit;
|
||||||
public class LSPApplication {
|
public class LSPApplication {
|
||||||
private static final String ORIGINAL_APPLICATION_NAME_ASSET_PATH = "original_application_name.ini";
|
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 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 originalApplicationName = null;
|
||||||
private static String originalSignature = null;
|
private static String originalSignature = null;
|
||||||
private static Application sOriginalApplication = null;
|
private static Application sOriginalApplication = null;
|
||||||
|
|
@ -49,51 +51,43 @@ public class LSPApplication {
|
||||||
|
|
||||||
private static int TRANSACTION_getPackageInfo_ID = -1;
|
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 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;
|
final static public int PER_USER_RANGE = 100000;
|
||||||
|
|
||||||
static Context context;
|
final static private LSPApplication instance = new LSPApplication();
|
||||||
|
|
||||||
static public boolean isIsolated() {
|
static public boolean isIsolated() {
|
||||||
int uid = android.os.Process.myUid();
|
return (android.os.Process.myUid() % PER_USER_RANGE) >= FIRST_APP_ZYGOTE_ISOLATED_UID;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static {
|
static public void onLoad() {
|
||||||
cacheSigbypassLv = -1;
|
cacheSigbypassLv = -1;
|
||||||
|
|
||||||
if (isIsolated()) {
|
if (isIsolated()) {
|
||||||
XLog.d(TAG, "skip isolated process");
|
XLog.d(TAG, "skip isolated process");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else {
|
Context context = XpatchUtils.createAppContext();
|
||||||
context = XpatchUtils.createAppContext();
|
if (context == null) {
|
||||||
if (context == null) {
|
XLog.e(TAG, "create context err");
|
||||||
XLog.e(TAG, "create context err");
|
return;
|
||||||
}
|
}
|
||||||
else {
|
YahfaHooker.init();
|
||||||
System.load(context.getApplicationInfo().nativeLibraryDir + "/liblspd.so");
|
XposedBridge.initXResources();
|
||||||
YahfaHooker.init();
|
PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote
|
||||||
XposedInit.startsSystemServer = false;
|
XposedInit.startsSystemServer = false;
|
||||||
|
|
||||||
originalApplicationName = FileUtils.readTextFromAssets(context, ORIGINAL_APPLICATION_NAME_ASSET_PATH);
|
originalApplicationName = FileUtils.readTextFromAssets(context, ORIGINAL_APPLICATION_NAME_ASSET_PATH);
|
||||||
originalSignature = FileUtils.readTextFromAssets(context, ORIGINAL_SIGNATURE_ASSET_PATH);
|
originalSignature = FileUtils.readTextFromAssets(context, ORIGINAL_SIGNATURE_ASSET_PATH);
|
||||||
|
|
||||||
XLog.d(TAG, "original application class " + originalApplicationName);
|
XLog.d(TAG, "original application class " + originalApplicationName);
|
||||||
XLog.d(TAG, "original signature info " + originalSignature);
|
XLog.d(TAG, "original signature info " + originalSignature);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
doHook();
|
doHook(context);
|
||||||
initAndLoadModules(context);
|
initAndLoadModules(context);
|
||||||
}
|
} catch (Throwable e) {
|
||||||
catch (Exception e) {
|
Log.e(TAG, "Do hook", e);
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,12 +100,7 @@ public class LSPApplication {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isApplicationProxied() {
|
private static boolean isApplicationProxied() {
|
||||||
if (originalApplicationName != null && !originalApplicationName.isEmpty() && !("android.app.Application").equals(originalApplicationName)) {
|
return originalApplicationName != null && !originalApplicationName.isEmpty() && !("android.app.Application").equals(originalApplicationName);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ClassLoader getAppClassLoader() {
|
private static ClassLoader getAppClassLoader() {
|
||||||
|
|
@ -122,14 +111,13 @@ public class LSPApplication {
|
||||||
Object mBoundApplication = XposedHelpers.getObjectField(getActivityThread(), "mBoundApplication");
|
Object mBoundApplication = XposedHelpers.getObjectField(getActivityThread(), "mBoundApplication");
|
||||||
Object loadedApkObj = XposedHelpers.getObjectField(mBoundApplication, "info");
|
Object loadedApkObj = XposedHelpers.getObjectField(mBoundApplication, "info");
|
||||||
appClassLoader = (ClassLoader) XposedHelpers.callMethod(loadedApkObj, "getClassLoader");
|
appClassLoader = (ClassLoader) XposedHelpers.callMethod(loadedApkObj, "getClassLoader");
|
||||||
}
|
} catch (Throwable e) {
|
||||||
catch (Exception e) {
|
Log.e(TAG, "getAppClassLoader", e);
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
return appClassLoader;
|
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();
|
Field[] pmStubFields = Class.forName("android.content.pm.IPackageManager$Stub").getDeclaredFields();
|
||||||
for (Field field : pmStubFields) {
|
for (Field field : pmStubFields) {
|
||||||
if (!Modifier.isStatic(field.getModifiers()) || field.getType() != int.class) {
|
if (!Modifier.isStatic(field.getModifiers()) || field.getType() != int.class) {
|
||||||
|
|
@ -203,25 +191,26 @@ public class LSPApplication {
|
||||||
// reset pos
|
// reset pos
|
||||||
out.setDataPosition(0);
|
out.setDataPosition(0);
|
||||||
}
|
}
|
||||||
}
|
} catch (Throwable err) {
|
||||||
catch (Throwable err) {
|
|
||||||
err.printStackTrace();
|
err.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void doHook() throws IllegalAccessException, ClassNotFoundException, IOException {
|
private static void doHook(Context context) throws IllegalAccessException, ClassNotFoundException, IOException {
|
||||||
if (isApplicationProxied()) {
|
if (isApplicationProxied()) {
|
||||||
hookContextImplSetOuterContext();
|
hookContextImplSetOuterContext();
|
||||||
hookInstallContentProviders();
|
hookInstallContentProviders();
|
||||||
hookActivityAttach();
|
hookActivityAttach();
|
||||||
hookServiceAttach();
|
hookServiceAttach();
|
||||||
}
|
}
|
||||||
if (fetchSigbypassLv() >= Constants.SIGBYPASS_LV_PM) {
|
hookApplicationStub();
|
||||||
byPassSignature();
|
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");
|
File apk = new File(context.getCacheDir(), "lspatchapk.so");
|
||||||
if (!apk.exists()) {
|
if (!apk.exists()) {
|
||||||
try (InputStream inputStream = context.getAssets().open("origin_apk.bin");
|
try (InputStream inputStream = context.getAssets().open("origin_apk.bin");
|
||||||
|
|
@ -241,7 +230,7 @@ public class LSPApplication {
|
||||||
|
|
||||||
private static int cacheSigbypassLv;
|
private static int cacheSigbypassLv;
|
||||||
|
|
||||||
private static int fetchSigbypassLv() {
|
private static int fetchSigbypassLv(Context context) {
|
||||||
if (cacheSigbypassLv != -1) {
|
if (cacheSigbypassLv != -1) {
|
||||||
return cacheSigbypassLv;
|
return cacheSigbypassLv;
|
||||||
}
|
}
|
||||||
|
|
@ -249,48 +238,82 @@ public class LSPApplication {
|
||||||
try (InputStream inputStream = context.getAssets().open(Constants.CONFIG_NAME_SIGBYPASSLV + i)) {
|
try (InputStream inputStream = context.getAssets().open(Constants.CONFIG_NAME_SIGBYPASSLV + i)) {
|
||||||
cacheSigbypassLv = i;
|
cacheSigbypassLv = i;
|
||||||
return 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() {
|
private static void hookContextImplSetOuterContext() {
|
||||||
XposedHelpers.findAndHookMethod("android.app.ContextImpl", getAppClassLoader(), "setOuterContext", Context.class, new XC_MethodHook() {
|
try {
|
||||||
@Override
|
XposedHelpers.findAndHookMethod("android.app.ContextImpl", getAppClassLoader(), "setOuterContext", Context.class, new XC_MethodHook() {
|
||||||
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
@Override
|
||||||
replaceApplicationParam(param.args);
|
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||||
// XposedHelpers.setObjectField(param.thisObject, "mOuterContext", sOriginalApplication);
|
replaceApplicationParam(param.args);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Log.e(TAG, "hookContextImplSetOuterContext", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void hookInstallContentProviders() {
|
private static void hookInstallContentProviders() {
|
||||||
XposedBridge.hookAllMethods(XposedHelpers.findClass("android.app.ActivityThread", getAppClassLoader()), "installContentProviders", new XC_MethodHook() {
|
try {
|
||||||
@Override
|
XposedBridge.hookAllMethods(XposedHelpers.findClass("android.app.ActivityThread", getAppClassLoader()), "installContentProviders", new XC_MethodHook() {
|
||||||
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
@Override
|
||||||
replaceApplicationParam(param.args);
|
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||||
}
|
replaceApplicationParam(param.args);
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Log.e(TAG, "hookInstallContextProviders", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void hookActivityAttach() {
|
private static void hookActivityAttach() {
|
||||||
XposedBridge.hookAllMethods(XposedHelpers.findClass("android.app.Activity", getAppClassLoader()), "attach", new XC_MethodHook() {
|
try {
|
||||||
@Override
|
XposedBridge.hookAllMethods(XposedHelpers.findClass("android.app.Activity", getAppClassLoader()), "attach", new XC_MethodHook() {
|
||||||
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
@Override
|
||||||
replaceApplicationParam(param.args);
|
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||||
}
|
replaceApplicationParam(param.args);
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Log.e(TAG, "hookActivityAttach", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void hookServiceAttach() {
|
private static void hookServiceAttach() {
|
||||||
XposedBridge.hookAllMethods(XposedHelpers.findClass("android.app.Service", getAppClassLoader()), "attach", new XC_MethodHook() {
|
try {
|
||||||
@Override
|
XposedBridge.hookAllMethods(XposedHelpers.findClass("android.app.Service", getAppClassLoader()), "attach", new XC_MethodHook() {
|
||||||
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
@Override
|
||||||
replaceApplicationParam(param.args);
|
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||||
}
|
replaceApplicationParam(param.args);
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Log.e(TAG, "hookServiceAttach", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void replaceApplicationParam(Object[] args) {
|
private static void replaceApplicationParam(Object[] args) {
|
||||||
|
|
@ -309,9 +332,8 @@ public class LSPApplication {
|
||||||
try {
|
try {
|
||||||
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
|
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
|
||||||
activityThread = XposedHelpers.callStaticMethod(activityThreadClass, "currentActivityThread");
|
activityThread = XposedHelpers.callStaticMethod(activityThreadClass, "currentActivityThread");
|
||||||
}
|
} catch (Throwable e) {
|
||||||
catch (Exception e) {
|
Log.e(TAG, "getActivityThread", e);
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return activityThread;
|
return activityThread;
|
||||||
|
|
@ -328,29 +350,23 @@ public class LSPApplication {
|
||||||
private void attachOrignalBaseContext(Context base) {
|
private void attachOrignalBaseContext(Context base) {
|
||||||
try {
|
try {
|
||||||
XposedHelpers.callMethod(sOriginalApplication, "attachBaseContext", base);
|
XposedHelpers.callMethod(sOriginalApplication, "attachBaseContext", base);
|
||||||
}
|
} catch (Throwable e) {
|
||||||
catch (Exception e) {
|
Log.e(TAG, "attachOriginalBaseContext", e);
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setLoadedApkField(Context base) {
|
private void setLoadedApkField(Context base) {
|
||||||
// mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
|
|
||||||
try {
|
try {
|
||||||
Class<?> contextImplClass = Class.forName("android.app.ContextImpl");
|
Class<?> contextImplClass = Class.forName("android.app.ContextImpl");
|
||||||
Object contextImpl = XposedHelpers.callStaticMethod(contextImplClass, "getImpl", base);
|
Object contextImpl = XposedHelpers.callStaticMethod(contextImplClass, "getImpl", base);
|
||||||
Object loadedApk = XposedHelpers.getObjectField(contextImpl, "mPackageInfo");
|
Object loadedApk = XposedHelpers.getObjectField(contextImpl, "mPackageInfo");
|
||||||
XposedHelpers.setObjectField(sOriginalApplication, "mLoadedApk", loadedApk);
|
XposedHelpers.setObjectField(sOriginalApplication, "mLoadedApk", loadedApk);
|
||||||
}
|
} catch (Throwable e) {
|
||||||
catch (Exception e) {
|
Log.e(TAG, "setLoadedApkField", e);
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
// setLoadedApkField(sOriginalApplication);
|
|
||||||
// XposedHelpers.setObjectField(sOriginalApplication, "mLoadedApk", XposedHelpers.getObjectField(this, "mLoadedApk"));
|
|
||||||
|
|
||||||
if (isApplicationProxied()) {
|
if (isApplicationProxied()) {
|
||||||
// replaceApplication();
|
// replaceApplication();
|
||||||
replaceLoadedApkApplication();
|
replaceLoadedApkApplication();
|
||||||
|
|
@ -371,18 +387,16 @@ public class LSPApplication {
|
||||||
|
|
||||||
// replace LoadedApk.java makeApplication() mApplication = app;
|
// replace LoadedApk.java makeApplication() mApplication = app;
|
||||||
XposedHelpers.setObjectField(loadedApkObj, "mApplication", sOriginalApplication);
|
XposedHelpers.setObjectField(loadedApkObj, "mApplication", sOriginalApplication);
|
||||||
}
|
} catch (Throwable e) {
|
||||||
catch (Exception e) {
|
Log.e(TAG, "replaceLoadedApkApplication", e);
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void replaceActivityThreadApplication() {
|
private void replaceActivityThreadApplication() {
|
||||||
try {
|
try {
|
||||||
XposedHelpers.setObjectField(getActivityThread(), "mInitialApplication", sOriginalApplication);
|
XposedHelpers.setObjectField(getActivityThread(), "mInitialApplication", sOriginalApplication);
|
||||||
}
|
} catch (Throwable e) {
|
||||||
catch (Exception e) {
|
Log.e(TAG, "replaceActivityThreadApplication", e);
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -390,9 +404,8 @@ public class LSPApplication {
|
||||||
if (sOriginalApplication == null) {
|
if (sOriginalApplication == null) {
|
||||||
try {
|
try {
|
||||||
sOriginalApplication = (Application) getAppClassLoader().loadClass(originalApplicationName).newInstance();
|
sOriginalApplication = (Application) getAppClassLoader().loadClass(originalApplicationName).newInstance();
|
||||||
}
|
} catch (Throwable e) {
|
||||||
catch (InstantiationException | ClassNotFoundException | IllegalAccessException e) {
|
Log.e(TAG, "createOriginalApplication", e);
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sOriginalApplication;
|
return sOriginalApplication;
|
||||||
|
|
@ -404,9 +417,8 @@ public class LSPApplication {
|
||||||
Object applicationInfoObj = XposedHelpers.getObjectField(mBoundApplication, "appInfo"); // info
|
Object applicationInfoObj = XposedHelpers.getObjectField(mBoundApplication, "appInfo"); // info
|
||||||
|
|
||||||
XposedHelpers.setObjectField(applicationInfoObj, "className", originalApplicationName);
|
XposedHelpers.setObjectField(applicationInfoObj, "className", originalApplicationName);
|
||||||
}
|
} catch (Throwable e) {
|
||||||
catch (Exception e) {
|
Log.e(TAG, "modifyApplicationInfoClassName", e);
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -158,12 +158,6 @@ public class LSPLoader {
|
||||||
|
|
||||||
appContext = context;
|
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);
|
initSELinux(context);
|
||||||
|
|
||||||
ClassLoader originClassLoader = context.getClassLoader();
|
ClassLoader originClassLoader = context.getClassLoader();
|
||||||
|
|
@ -195,7 +189,7 @@ public class LSPLoader {
|
||||||
if (!app.enabled) {
|
if (!app.enabled) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (app.metaData != null && (app.metaData.containsKey("xposedmodule"))) {
|
if (app.metaData != null && app.metaData.containsKey("xposedminversion")) {
|
||||||
String apkPath = pkg.applicationInfo.publicSourceDir;
|
String apkPath = pkg.applicationInfo.publicSourceDir;
|
||||||
String apkName = context.getPackageManager().getApplicationLabel(pkg.applicationInfo).toString();
|
String apkName = context.getPackageManager().getApplicationLabel(pkg.applicationInfo).toString();
|
||||||
if (TextUtils.isEmpty(apkPath)) {
|
if (TextUtils.isEmpty(apkPath)) {
|
||||||
|
|
@ -211,27 +205,24 @@ public class LSPLoader {
|
||||||
|
|
||||||
final List<Pair<String, String>> installedModuleListFinal = installedModuleList;
|
final List<Pair<String, String>> installedModuleListFinal = installedModuleList;
|
||||||
|
|
||||||
new Thread(new Runnable() {
|
new Thread(() -> {
|
||||||
@Override
|
List<String> savedPackageNameList = loadPackageNameListFromFile(false);
|
||||||
public void run() {
|
if (savedPackageNameList == null) {
|
||||||
List<String> savedPackageNameList = loadPackageNameListFromFile(false);
|
savedPackageNameList = new ArrayList<>();
|
||||||
if (savedPackageNameList == null) {
|
}
|
||||||
savedPackageNameList = new ArrayList<>();
|
List<Pair<String, String>> addPackageList = new ArrayList<>();
|
||||||
}
|
for (Pair<String, String> packgagePair : installedModuleListFinal) {
|
||||||
List<Pair<String, String>> addPackageList = new ArrayList<>();
|
if (!savedPackageNameList.contains(packgagePair.first)) {
|
||||||
for (Pair<String, String> packgagePair : installedModuleListFinal) {
|
XLog.d(TAG, "append " + packgagePair + " to " + XPOSED_MODULE_FILE_PATH);
|
||||||
if (!savedPackageNameList.contains(packgagePair.first)) {
|
addPackageList.add(packgagePair);
|
||||||
XLog.d(TAG, "append " + packgagePair + " to " + XPOSED_MODULE_FILE_PATH);
|
|
||||||
addPackageList.add(packgagePair);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
appendPackageNameToFile(addPackageList);
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
appendPackageNameToFile(addPackageList);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}).start();
|
}).start();
|
||||||
return modulePathList;
|
return modulePathList;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
||||||
public class Hook implements IXposedHookLoadPackage {
|
public class Hook implements IXposedHookLoadPackage {
|
||||||
@Override
|
@Override
|
||||||
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
|
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
|
@Override
|
||||||
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||||
param.setResult(true);
|
param.setResult(true);
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,6 @@ public class MainActivity extends Activity {
|
||||||
param.setResult(true);
|
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);
|
TextView textView = findViewById(R.id.msg);
|
||||||
if (checkXposed() && checkXposed2()) {
|
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"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context="org.lsposed.lspatch.tester.MainActivity">
|
tools:context="org.lsposed.org.lsposed.tester.MainActivity">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/msg"
|
android:id="@+id/msg"
|
||||||
|
|
@ -11,4 +11,4 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Hello World!" />
|
android:text="Hello World!" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
|
||||||
|
|
@ -1,120 +1,36 @@
|
||||||
package org.lsposed.lspatch.appstub;
|
package org.lsposed.lspatch.appstub;
|
||||||
|
|
||||||
import android.app.ActivityThread;
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
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 {
|
public class LSPApplicationStub extends Application {
|
||||||
final static String TAG = LSPApplicationStub.class.getSimpleName();
|
|
||||||
|
|
||||||
static Object realLSPApplication = null;
|
private static byte[] dex = null;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// load real lsp loader from asset
|
try (var is = LSPApplicationStub.class.getClassLoader().getResourceAsStream("assets/lsp");
|
||||||
Context context = createAppContext();
|
var os = new ByteArrayOutputStream()) {
|
||||||
if (context == null) {
|
byte[] buffer = new byte[8192];
|
||||||
throw new IllegalStateException("create context err");
|
int n;
|
||||||
}
|
while (-1 != (n = is.read(buffer))) {
|
||||||
else {
|
os.write(buffer, 0, n);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
dex = os.toByteArray();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
android.util.Log.e("LSPatch", "load dex error", e);
|
||||||
}
|
}
|
||||||
|
System.loadLibrary("lspd");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
try {
|
|
||||||
realLSPApplication.getClass().getDeclaredMethod("onCreate").invoke(realLSPApplication);
|
|
||||||
}
|
|
||||||
catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
|
|
||||||
throw new IllegalStateException("wtf", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void attachBaseContext(Context base) {
|
protected void attachBaseContext(Context base) {
|
||||||
super.attachBaseContext(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")) {
|
try (var is = getClass().getClassLoader().getResourceAsStream("assets/dex/lsp.dex")) {
|
||||||
zFile.add("assets/lsp.dex", is);
|
zFile.add("assets/lsp", is);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
throw new PatchError("Error when add assets: " + e);
|
throw new PatchError("Error when add assets: " + e);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue