New inject way

TODO: we should make a standalone app for test
This commit is contained in:
LoveSy 2021-06-20 11:42:17 +08:00
parent c4cc5ab563
commit e8ccca40a9
14 changed files with 195 additions and 265 deletions

View File

@ -21,6 +21,12 @@ android {
}
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
}
buildTypes {
debug {

View File

@ -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>

View File

@ -1 +1 @@
org.lsposed.lspatch.tester.Hook
org.lsposed.org.lsposed.org.lsposed.lspatch.tester.Hook

View File

@ -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 {

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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()) {

View File

@ -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();
}
}

View File

@ -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"

View File

@ -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

@ -1 +1 @@
Subproject commit 90b37a68aa32e79ea2dc58048f7657ba66abed89
Subproject commit 9e47e9027c4244317085cce2a95ddcbb7b579ee0

View File

@ -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);
}