just copy loader

This commit is contained in:
327135569 2021-04-07 00:53:54 +08:00
parent 64a9076931
commit cb7b40cbd3
36 changed files with 1619 additions and 0 deletions

16
loader/.gitignore vendored Normal file
View File

@ -0,0 +1,16 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
/out
/.idea

4
loader/.gitmodules vendored Normal file
View File

@ -0,0 +1,4 @@
[submodule "mmp"]
path = mmp
url = https://github.com/MMPosed/MMPosed.git
branch = mmpatch

17
loader/README.md Normal file
View File

@ -0,0 +1,17 @@
# Readme
LSPosed as hook framework
There some major change since xpatch
1. use LSPosed as hook framework
1. keep loader simple, clear not nesseacry things, like bypass signature. let developer do this part
`Maybe perform force push if some private data leak in project. Sorry for the confusion.`
# Useage
1. You need do signature bypass by yourself
1. Orignal signature saved to assets/original_signature_info.ini
1. Orignal apk saved to assets/original_apk.bin
For example, you may need to replace signatures from getPackageInfo and redirect `/data/app/{your apk}/base.apk` to `original_apk.bin` to bypass normally signature check.

2
loader/app/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/build
/target

71
loader/app/build.gradle Normal file
View File

@ -0,0 +1,71 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 30
defaultConfig {
applicationId "com.storm.wind.xposed"
minSdkVersion 27
targetSdkVersion 28
versionCode version_code as Integer
versionName version_name
multiDexEnabled false
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a'
}
}
buildTypes {
debug {
debuggable true
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
release {
debuggable false
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
lintOptions {
abortOnError false
}
applicationVariants.all { variant ->
def buildType = variant.name.capitalize()
def variantLowered = variant.name.toLowerCase()
variant.outputs.all {
outputFileName = "${variant.getFlavorName()}-${variant.versionName}.apk"
}
task "copyDex$buildType"(type: Copy) {
dependsOn("assemble$buildType")
def dexFilePath = "$buildDir/intermediates/dex/${variantLowered}/mergeDex${buildType}/classes.dex"
from dexFilePath
rename "(.*).dex", "classes-${version_name}.dex"
into "$rootProject.projectDir/out/list-dex"
}
task "copySo$buildType"(type: Copy) {
dependsOn("assemble$buildType")
from "$buildDir/intermediates/merged_native_libs/${variantLowered}/out/lib"
into "$rootProject.projectDir/out/list-so"
}
task "copy$buildType"() {
dependsOn("copySo$buildType")
dependsOn("copyDex$buildType")
doLast {
System.out.println("Dex and so files has been copy to ${rootProject.projectDir}${File.separator}out")
}
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':xpatchcore')
implementation("androidx.core:core:1.3.2")
implementation project(path: ':lspcore')
}

26
loader/app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,26 @@
-keep class com.wind.xposed.entry.MMPEntry {
public <init>();
public void initAndLoadModules();
}
-keep class com.wind.xpatch.proxy.**{*;}
-keep class de.robv.android.xposed.**{*;}
-keep class android.app.**{*;}
-keep class android.content.**{*;}
-keep class android.os.**{*;}
-keep class android.view.**{*;}
-keep class com.lody.whale.**{*;}
-keep class com.android.internal.**{*;}
-keep class xposed.dummy.**{*;}
-keep class com.wind.xposed.entry.util.**{*;}
-keep class com.swift.sandhook.**{*;}
-keep class com.swift.sandhook.xposedcompat.**{*;}
-dontwarn android.content.res.Resources
-dontwarn android.content.res.Resources$Theme
-dontwarn android.content.res.AssetManager
-dontwarn android.content.res.TypedArray

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.storm.wind.xposed">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:name=".XposedApplication"
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=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1 @@
palceholder

View File

@ -0,0 +1,61 @@
package com.storm.wind.xposed;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import androidx.core.app.ActivityCompat;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedHelpers;
//import android.support.v4.app.ActivityCompat;
public class MainActivity extends Activity {
private static final int REQUEST_PERMISSION_CODE = 1;
private static String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
@SuppressLint("SetTextI18n")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE, REQUEST_PERMISSION_CODE);
}
}
XposedHelpers.findAndHookMethod(this.getClass(), "checkXposed", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.setResult(true);
}
});
TextView textView = findViewById(R.id.msg);
if (checkXposed()) {
textView.setText("ok");
}
else {
textView.setText("fail");
}
}
public void onClick(View view) {
}
public boolean checkXposed() {
return false;
}
}

View File

@ -0,0 +1,24 @@
package com.storm.wind.xposed;
import static com.wind.xposed.entry.MMPLoader.initAndLoadModules;
import android.app.Application;
import android.content.Context;
import org.lsposed.lspd.yahfa.hooker.YahfaHooker;
import de.robv.android.xposed.XposedInit;
public class XposedApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
}
static {
System.loadLibrary("lspd");
YahfaHooker.init();
XposedInit.startsSystemServer = false;
initAndLoadModules();
}
}

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="Hello World!" />
</RelativeLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sample_app_title">Xposed Module Loader</string>
</resources>

42
loader/build.gradle Normal file
View File

@ -0,0 +1,42 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
maven { url "https://jcenter.bintray.com" }
maven { url "https://jitpack.io" }
}
dependencies {
classpath 'com.android.tools.build:gradle:7.0.0-alpha12'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.30"
}
}
ext {
androidCompileSdkVersion = 30
androidCompileNdkVersion = "22.1.7171670"
androidBuildToolsVersion = "30.0.3"
androidMinSdkVersion = 27
androidTargetSdkVersion = 28
verCode = 1
verName = "mmpatch"
apiCode = 93
defaultManagerPackageName = "org.github.mmpatch"
androidSourceCompatibility = JavaVersion.VERSION_11
androidTargetCompatibility = JavaVersion.VERSION_11
zipPathMagiskReleasePath = project(":lspcore").projectDir.path + "/build/tmp/release/magisk/"
}
allprojects {
repositories {
google()
mavenCentral()
maven { url "https://jcenter.bintray.com" }
maven { url "https://jitpack.io" }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

1
loader/core/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

29
loader/core/build.gradle Normal file
View File

@ -0,0 +1,29 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 30
defaultConfig {
minSdkVersion rootProject.ext.androidMinSdkVersion
targetSdkVersion rootProject.ext.androidTargetSdkVersion
versionCode 1
versionName "1.0"
}
buildTypes {
debug {
debuggable true
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
release {
debuggable false
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation project(':lspcore')
}

16
loader/core/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,16 @@
#-keep class com.wind.xposed.entry.XposedModuleEntry {
# public <init>();
# public void init();
#}
#-keep class de.robv.android.xposed.**{*;}
#-keep class com.swift.sandhook.**{*;}
#-keep class com.swift.sandhook.xposedcompat.**{*;}
#
#-dontwarn de.robv.android.xposed.XposedHelper
-keep class com.wind.xposed.entry.MMPEntry {
public <init>();
public void initAndLoadModules();
}
-keep class de.robv.android.xposed.**{*;}
-dontwarn de.robv.android.xposed.XposedHelper

View File

@ -0,0 +1,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.wind.xposed.entry">
<uses-sdk tools:overrideLibrary="de.robv.android.xposed" />
<application
android:allowBackup="true"
android:supportsRtl="true"></application>
</manifest>

View File

@ -0,0 +1,22 @@
package android.app;
import android.content.pm.ApplicationInfo;
import android.content.res.CompatibilityInfo;
public final class ActivityThread {
public static ActivityThread currentActivityThread() {
throw new UnsupportedOperationException("STUB");
}
public static Application currentApplication() {
throw new UnsupportedOperationException("STUB");
}
public static String currentPackageName() {
throw new UnsupportedOperationException("STUB");
}
public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, CompatibilityInfo compatInfo) {
throw new UnsupportedOperationException("STUB");
}
}

View File

@ -0,0 +1,21 @@
package android.app;
import android.content.pm.ApplicationInfo;
public final class LoadedApk {
public ApplicationInfo getApplicationInfo() {
throw new UnsupportedOperationException("STUB");
}
public ClassLoader getClassLoader() {
throw new UnsupportedOperationException("STUB");
}
public String getPackageName() {
throw new UnsupportedOperationException("STUB");
}
public String getResDir() {
throw new UnsupportedOperationException("STUB");
}
}

View File

@ -0,0 +1,18 @@
package android.content.res;
import android.os.Parcel;
import android.os.Parcelable;
public class CompatibilityInfo implements Parcelable {
public static final Parcelable.Creator<CompatibilityInfo> CREATOR = null;
@Override
public int describeContents() {
throw new UnsupportedOperationException("STUB");
}
@Override
public void writeToParcel(Parcel dest, int flags) {
throw new UnsupportedOperationException("STUB");
}
}

View File

@ -0,0 +1,275 @@
package com.wind.xposed.entry;
import static com.wind.xposed.entry.MMPLoader.initAndLoadModules;
import android.app.Application;
import android.content.Context;
import com.wind.xposed.entry.util.FileUtils;
import com.wind.xposed.entry.util.ReflectionApiCheck;
import com.wind.xposed.entry.util.XLog;
import com.wind.xposed.entry.util.XpatchUtils;
import org.lsposed.lspd.yahfa.hooker.YahfaHooker;
import java.util.ArrayList;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.XposedInit;
/**
* Created by Windysha
*/
public class MMPApplication extends Application {
private static final String ORIGINAL_APPLICATION_NAME_ASSET_PATH = "original_application_name.ini";
private static final String TAG = "XpatchProxyApplication";
private static String originalApplicationName = null;
private static Application sOriginalApplication = null;
private static ClassLoader appClassLoader;
private static Object activityThread;
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 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);
}
static {
ReflectionApiCheck.unseal();
System.loadLibrary("lspd");
YahfaHooker.init();
XposedInit.startsSystemServer = false;
Context context = XpatchUtils.createAppContext();
originalApplicationName = FileUtils.readTextFromAssets(context, ORIGINAL_APPLICATION_NAME_ASSET_PATH);
XLog.d(TAG, "original application name " + originalApplicationName);
if (isIsolated()) {
XLog.d(TAG, "skip isolated process");
}
else {
if (isApplicationProxied()) {
doHook();
initAndLoadModules(context);
}
else {
XLog.e(TAG, "something wrong");
}
}
}
public MMPApplication() {
super();
if (isApplicationProxied()) {
createOriginalApplication();
}
}
private static boolean isApplicationProxied() {
if (originalApplicationName != null && !originalApplicationName.isEmpty() && !("android.app.Application").equals(originalApplicationName)) {
return true;
}
else {
return false;
}
}
private static ClassLoader getAppClassLoader() {
if (appClassLoader != null) {
return appClassLoader;
}
try {
Object mBoundApplication = XposedHelpers.getObjectField(getActivityThread(), "mBoundApplication");
Object loadedApkObj = XposedHelpers.getObjectField(mBoundApplication, "info");
appClassLoader = (ClassLoader) XposedHelpers.callMethod(loadedApkObj, "getClassLoader");
}
catch (Exception e) {
e.printStackTrace();
}
return appClassLoader;
}
private static void doHook() {
hookContextImplSetOuterContext();
hookInstallContentProviders();
hookActivityAttach();
hookServiceAttach();
}
private static void hookContextImplSetOuterContext() {
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);
}
});
}
private static void hookInstallContentProviders() {
XposedBridge.hookAllMethods(XposedHelpers.findClass("android.app.ActivityThread", getAppClassLoader()), "installContentProviders", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
replaceApplicationParam(param.args);
}
});
}
private static void hookActivityAttach() {
XposedBridge.hookAllMethods(XposedHelpers.findClass("android.app.Activity", getAppClassLoader()), "attach", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
replaceApplicationParam(param.args);
}
});
}
private static void hookServiceAttach() {
XposedBridge.hookAllMethods(XposedHelpers.findClass("android.app.Service", getAppClassLoader()), "attach", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
replaceApplicationParam(param.args);
}
});
}
private static void replaceApplicationParam(Object[] args) {
if (args == null || args.length == 0) {
return;
}
for (Object para : args) {
if (para instanceof MMPApplication) {
para = sOriginalApplication;
}
}
}
private static Object getActivityThread() {
if (activityThread == null) {
try {
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
activityThread = XposedHelpers.callStaticMethod(activityThreadClass, "currentActivityThread");
}
catch (Exception e) {
e.printStackTrace();
}
}
return activityThread;
}
@Override
protected void attachBaseContext(Context base) {
// 将applicationInfo中保存的applcation class name还原为真实的application class name
if (isApplicationProxied()) {
modifyApplicationInfoClassName();
}
super.attachBaseContext(base);
if (isApplicationProxied()) {
attachOrignalBaseContext(base);
setLoadedApkField(base);
}
// setApplicationLoadedApk(base);
}
private void attachOrignalBaseContext(Context base) {
try {
XposedHelpers.callMethod(sOriginalApplication, "attachBaseContext", base);
}
catch (Exception e) {
e.printStackTrace();
}
}
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();
}
}
@Override
public void onCreate() {
// setLoadedApkField(sOriginalApplication);
// XposedHelpers.setObjectField(sOriginalApplication, "mLoadedApk", XposedHelpers.getObjectField(this, "mLoadedApk"));
super.onCreate();
if (isApplicationProxied()) {
// replaceApplication();
replaceLoadedApkApplication();
replaceActivityThreadApplication();
sOriginalApplication.onCreate();
}
}
private void replaceLoadedApkApplication() {
try {
// replace LoadedApk.java makeApplication() mActivityThread.mAllApplications.add(app);
ArrayList<Application> list = (ArrayList<Application>) XposedHelpers.getObjectField(getActivityThread(), "mAllApplications");
list.add(sOriginalApplication);
Object mBoundApplication = XposedHelpers.getObjectField(getActivityThread(), "mBoundApplication"); // AppBindData
Object loadedApkObj = XposedHelpers.getObjectField(mBoundApplication, "info"); // info
// replace LoadedApk.java makeApplication() mApplication = app;
XposedHelpers.setObjectField(loadedApkObj, "mApplication", sOriginalApplication);
}
catch (Exception e) {
e.printStackTrace();
}
}
private void replaceActivityThreadApplication() {
try {
XposedHelpers.setObjectField(getActivityThread(), "mInitialApplication", sOriginalApplication);
}
catch (Exception e) {
e.printStackTrace();
}
}
private Application createOriginalApplication() {
if (sOriginalApplication == null) {
try {
sOriginalApplication = (Application) getAppClassLoader().loadClass(originalApplicationName).newInstance();
}
catch (InstantiationException | ClassNotFoundException | IllegalAccessException e) {
e.printStackTrace();
}
}
return sOriginalApplication;
}
private void modifyApplicationInfoClassName() {
try {
Object mBoundApplication = XposedHelpers.getObjectField(getActivityThread(), "mBoundApplication"); // AppBindData
Object applicationInfoObj = XposedHelpers.getObjectField(mBoundApplication, "appInfo"); // info
XposedHelpers.setObjectField(applicationInfoObj, "className", originalApplicationName);
}
catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,335 @@
package com.wind.xposed.entry;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import com.wind.xposed.entry.util.FileUtils;
import com.wind.xposed.entry.util.XLog;
import com.wind.xposed.entry.util.XpatchUtils;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import dalvik.system.DelegateLastClassLoader;
import de.robv.android.xposed.IXposedHookInitPackageResources;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.IXposedHookZygoteInit;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelper;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
public class MMPLoader {
private static final String TAG = MMPLoader.class.getSimpleName();
private static final String DIR_BASE = Environment.getExternalStorageDirectory().getAbsolutePath();
private static final String XPOSED_MODULE_FILE_PATH = "xpmodules.list";
private static AtomicBoolean hasInited = new AtomicBoolean(false);
private static Context appContext;
@SuppressLint("DiscouragedPrivateApi")
public static boolean loadModule(final String moduleApkPath, String moduleOdexDir, String moduleLibPath, final ApplicationInfo currentApplicationInfo, ClassLoader appClassLoader) {
XLog.i(TAG, "Loading modules from " + moduleApkPath);
if (!new File(moduleApkPath).exists()) {
XLog.e(TAG, moduleApkPath + " does not exist");
return false;
}
ClassLoader mcl = new DelegateLastClassLoader(moduleApkPath, null, appClassLoader);
try {
if (mcl.loadClass(XposedBridge.class.getName()).getClassLoader() != appClassLoader) {
Log.e(TAG, " Cannot load module:");
Log.e(TAG, " The Xposed API classes are compiled into the module's APK.");
Log.e(TAG, " This may cause strange issues and must be fixed by the module developer.");
Log.e(TAG, " For details, see: http://api.xposed.info/using.html");
return false;
}
}
catch (ClassNotFoundException ignored) {
}
try (InputStream is = mcl.getResourceAsStream("assets/xposed_init")) {
if (is == null) {
XLog.e(TAG, "assets/xposed_init not found in the APK");
return false;
}
BufferedReader moduleClassesReader = new BufferedReader(new InputStreamReader(is));
String moduleClassName;
while ((moduleClassName = moduleClassesReader.readLine()) != null) {
moduleClassName = moduleClassName.trim();
if (moduleClassName.isEmpty() || moduleClassName.startsWith("#")) {
continue;
}
try {
XLog.i(TAG, "Loading class " + moduleClassName);
Class<?> moduleClass = mcl.loadClass(moduleClassName);
if (!XposedHelper.isIXposedMod(moduleClass)) {
Log.w(TAG, "This class doesn't implement any sub-interface of IXposedMod, skipping it");
continue;
}
else if (IXposedHookInitPackageResources.class.isAssignableFrom(moduleClass)) {
Log.w(TAG, "This class requires resource-related hooks (which are disabled), skipping it.");
continue;
}
final Object moduleInstance = moduleClass.newInstance();
if (moduleInstance instanceof IXposedHookZygoteInit) {
XposedHelper.callInitZygote(moduleApkPath, moduleInstance);
}
if (moduleInstance instanceof IXposedHookLoadPackage) {
// hookLoadPackage(new IXposedHookLoadPackage.Wrapper((IXposedHookLoadPackage) moduleInstance));
IXposedHookLoadPackage.Wrapper wrapper = new IXposedHookLoadPackage.Wrapper((IXposedHookLoadPackage) moduleInstance, moduleApkPath);
XposedBridge.CopyOnWriteSortedSet<XC_LoadPackage> xc_loadPackageCopyOnWriteSortedSet = new XposedBridge.CopyOnWriteSortedSet<>();
xc_loadPackageCopyOnWriteSortedSet.add(wrapper);
XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(xc_loadPackageCopyOnWriteSortedSet);
lpparam.packageName = currentApplicationInfo.packageName;
lpparam.processName = (String) Class.forName("android.app.ActivityThread").getDeclaredMethod("currentProcessName").invoke(null);
lpparam.classLoader = appClassLoader;
lpparam.appInfo = currentApplicationInfo;
lpparam.isFirstApplication = true;
XC_LoadPackage.callAll(lpparam);
}
if (moduleInstance instanceof IXposedHookInitPackageResources) {
XLog.w(TAG, "unsupport resource hook");
}
}
catch (Throwable t) {
XLog.e(TAG, "", t);
}
}
}
catch (IOException e) {
XLog.e(TAG, "", e);
}
return true;
}
public static void initAndLoadModules() {
Context context = XpatchUtils.createAppContext();
initAndLoadModules(context);
}
public static void initAndLoadModules(Context context) {
if (!hasInited.compareAndSet(false, true)) {
XLog.w(TAG, "has been init");
return;
}
if (context == null) {
XLog.e(TAG, "try to init with context null");
return;
}
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();
List<String> modulePathList = loadAllInstalledModule(context);
for (String modulePath : modulePathList) {
String dexPath = context.getDir("xposed_plugin_dex", Context.MODE_PRIVATE).getAbsolutePath();
if (!TextUtils.isEmpty(modulePath)) {
MMPLoader.loadModule(modulePath, dexPath, null, context.getApplicationInfo(), originClassLoader);
}
}
}
private static void initSELinux(Context context) {
XposedHelper.initSeLinux(context.getApplicationInfo().processName);
}
private static List<String> loadAllInstalledModule(Context context) {
PackageManager pm = context.getPackageManager();
List<String> modulePathList = new ArrayList<>();
List<String> packageNameList = loadPackageNameListFromFile(true);
List<Pair<String, String>> installedModuleList = new ArrayList<>();
boolean configFileExist = configFileExist();
for (PackageInfo pkg : pm.getInstalledPackages(PackageManager.GET_META_DATA)) {
ApplicationInfo app = pkg.applicationInfo;
if (!app.enabled) {
continue;
}
if (app.metaData != null && (app.metaData.containsKey("xposedmodule"))) {
String apkPath = pkg.applicationInfo.publicSourceDir;
String apkName = context.getPackageManager().getApplicationLabel(pkg.applicationInfo).toString();
if (TextUtils.isEmpty(apkPath)) {
apkPath = pkg.applicationInfo.sourceDir;
}
if (!TextUtils.isEmpty(apkPath) && (!configFileExist || packageNameList == null || packageNameList.contains(app.packageName))) {
XLog.d(TAG, "query installed module path " + apkPath);
modulePathList.add(apkPath);
}
installedModuleList.add(Pair.create(pkg.applicationInfo.packageName, apkName));
}
}
final List<Pair<String, String>> installedModuleListFinal = installedModuleList;
new Thread(new Runnable() {
@Override
public void run() {
List<String> savedPackageNameList = loadPackageNameListFromFile(false);
if (savedPackageNameList == null) {
savedPackageNameList = new ArrayList<>();
}
List<Pair<String, String>> addPackageList = new ArrayList<>();
for (Pair<String, String> packgagePair : installedModuleListFinal) {
if (!savedPackageNameList.contains(packgagePair.first)) {
XLog.d(TAG, "append " + packgagePair + " to " + XPOSED_MODULE_FILE_PATH);
addPackageList.add(packgagePair);
}
}
try {
appendPackageNameToFile(addPackageList);
}
catch (IOException e) {
e.printStackTrace();
}
}
}).start();
return modulePathList;
}
// 从sd卡中加载指定文件以加载指定的xposed module
private static List<String> loadPackageNameListFromFile(boolean loadActivedPackages) {
File moduleFile = new File(DIR_BASE, XPOSED_MODULE_FILE_PATH);
if (!moduleFile.exists()) {
return null;
}
List<String> modulePackageList = new ArrayList<>();
FileInputStream fileInputStream = null;
BufferedReader bufferedReader = null;
try {
fileInputStream = new FileInputStream(moduleFile);
bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));
String modulePackageName;
while ((modulePackageName = bufferedReader.readLine()) != null) {
modulePackageName = modulePackageName.trim();
if (modulePackageName.isEmpty() || (modulePackageName.startsWith("#") && loadActivedPackages)) {
continue;
}
if (modulePackageName.startsWith("#")) {
modulePackageName = modulePackageName.substring(1);
}
int index = modulePackageName.indexOf("#");
if (index > 0) {
modulePackageName = modulePackageName.substring(0, index);
}
XLog.d(TAG, "load " + XPOSED_MODULE_FILE_PATH + " file result, modulePackageName " + modulePackageName);
modulePackageList.add(modulePackageName);
}
}
catch (IOException e) {
e.printStackTrace();
return null;
}
finally {
closeStream(fileInputStream);
closeStream(bufferedReader);
}
return modulePackageList;
}
private static void appendPackageNameToFile(List<Pair<String, String>> packageNameList) throws IOException {
if (isEmpty(packageNameList)) {
return;
}
File moduleFile = new File(DIR_BASE, XPOSED_MODULE_FILE_PATH);
if (!moduleFile.exists()) {
if (!moduleFile.createNewFile()) {
throw new IllegalStateException("create " + XPOSED_MODULE_FILE_PATH + " err");
}
}
FileOutputStream outputStream = null;
BufferedWriter writer = null;
try {
outputStream = new FileOutputStream(moduleFile, true);
writer = new BufferedWriter(new OutputStreamWriter(outputStream));
for (Pair<String, String> packageInfo : packageNameList) {
String packageName = packageInfo.first;
String appName = packageInfo.second;
writer.write(packageName + "#" + appName);
writer.write("\n");
XLog.d(TAG, "append new pkg to " + XPOSED_MODULE_FILE_PATH);
}
writer.flush();
}
catch (Exception e) {
e.printStackTrace();
}
finally {
closeStream(outputStream);
closeStream(writer);
}
}
private static boolean configFileExist() {
File moduleConfigFile = new File(DIR_BASE, XPOSED_MODULE_FILE_PATH);
return moduleConfigFile.exists();
}
private static void closeStream(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
private static boolean isEmpty(Collection<?> collection) {
if (collection == null || collection.size() == 0) {
return true;
}
return false;
}
public static Context getAppContext() {
return appContext;
}
}

View File

@ -0,0 +1,55 @@
package com.wind.xposed.entry.util;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Process;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
public class FileUtils {
//读写权限
private static String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
public static boolean isSdcardPermissionGranted(Context context) {
int pid = android.os.Process.myPid();
int uid = Process.myUid();
return context.checkPermission(PERMISSIONS_STORAGE[0], pid, uid) == PackageManager.PERMISSION_GRANTED && context.checkPermission(PERMISSIONS_STORAGE[1], pid, uid) == PackageManager.PERMISSION_GRANTED;
}
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 (Exception e) {
e.printStackTrace();
}
return null;
}
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 (Exception e) {
e.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,93 @@
package com.wind.xposed.entry.util;
import static android.os.Build.VERSION.SDK_INT;
import android.os.Build;
import android.util.Log;
import java.lang.reflect.Method;
/**
* @author Windysha
*/
public class ReflectionApiCheck {
private static final String TAG = ReflectionApiCheck.class.getSimpleName();
private static final int ERROR_EXEMPT_FAILED = -21;
private static Object sVmRuntime;
private static Method setHiddenApiExemptions;
static {
if (SDK_INT >= Build.VERSION_CODES.P) {
try {
Method forName = Class.class.getDeclaredMethod("forName", String.class);
Method getDeclaredMethod = Class.class.getDeclaredMethod("getDeclaredMethod", String.class, Class[].class);
Class<?> vmRuntimeClass = (Class<?>) forName.invoke(null, "dalvik.system.VMRuntime");
Method getRuntime = (Method) getDeclaredMethod.invoke(vmRuntimeClass, "getRuntime", null);
if (getRuntime == null) {
throw new IllegalStateException("getRuntime method null");
}
setHiddenApiExemptions = (Method) getDeclaredMethod.invoke(vmRuntimeClass, "setHiddenApiExemptions", new Class[]{String[].class});
sVmRuntime = getRuntime.invoke(null);
}
catch (Throwable e) {
Log.e(TAG, "reflect bootstrap failed:", e);
}
}
}
public static int unseal() {
if (SDK_INT < 28) {
// Below Android P, ignore
return 0;
}
// try exempt API first.
if (exemptAll()) {
return 0;
}
else {
return ERROR_EXEMPT_FAILED;
}
}
/**
* make the method exempted from hidden API check.
*
* @param method the method signature prefix.
* @return true if success.
*/
public static boolean exempt(String method) {
return exempt(new String[]{method});
}
/**
* make specific methods exempted from hidden API check.
*
* @param methods the method signature prefix, such as "Ldalvik/system", "Landroid" or even "L"
* @return true if success
*/
public static boolean exempt(String... methods) {
if (sVmRuntime == null || setHiddenApiExemptions == null) {
return false;
}
try {
setHiddenApiExemptions.invoke(sVmRuntime, new Object[]{methods});
return true;
}
catch (Throwable e) {
return false;
}
}
/**
* Make all hidden API exempted.
*
* @return true if success.
*/
public static boolean exemptAll() {
return exempt(new String[]{"L"});
}
}

View File

@ -0,0 +1,44 @@
package com.wind.xposed.entry.util;
import com.wind.xposed.entry.BuildConfig;
public class XLog {
private static boolean enableLog = BuildConfig.DEBUG;
public static void d(String tag, String msg) {
if (enableLog) {
android.util.Log.d(tag, msg);
}
}
public static void v(String tag, String msg) {
if (enableLog) {
android.util.Log.v(tag, msg);
}
}
public static void w(String tag, String msg) {
if (enableLog) {
android.util.Log.w(tag, msg);
}
}
public static void i(String tag, String msg) {
if (enableLog) {
android.util.Log.i(tag, msg);
}
}
public static void e(String tag, String msg) {
if (enableLog) {
android.util.Log.e(tag, msg);
}
}
public static void e(String tag, String msg, Throwable tr) {
if (enableLog) {
android.util.Log.e(tag, msg, tr);
}
}
}

View File

@ -0,0 +1,62 @@
package com.wind.xposed.entry.util;
import android.app.ActivityThread;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.util.Log;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class XpatchUtils {
final static String TAG = "MMP" + XpatchUtils.class.getSimpleName();
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) {
e.printStackTrace();
}
return null;
}
public static boolean isApkDebugable(Context context) {
try {
ApplicationInfo info = context.getApplicationInfo();
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
}
catch (Exception ignore) {
}
return false;
}
}

View File

@ -0,0 +1,33 @@
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 {
private static final String TAG = "XposedHelper";
public static void initSeLinux(String processName) {
// SELinuxHelper.initOnce();
// SELinuxHelper.initForProcess(processName);
}
public static boolean isIXposedMod(Class<?> moduleClass) {
return IXposedMod.class.isAssignableFrom(moduleClass);
}
public static XC_MethodHook.Unhook newUnHook(XC_MethodHook XC_MethodHook, Member member) {
return XC_MethodHook.new Unhook(member);
}
public static void callInitZygote(String modulePath, Object moduleInstance) throws Throwable {
IXposedHookZygoteInit.StartupParam param = new IXposedHookZygoteInit.StartupParam();
param.modulePath = modulePath;
param.startsSystemServer = false;
((IXposedHookZygoteInit) moduleInstance).initZygote(param);
}
}

View File

@ -0,0 +1,3 @@
<resources>
<string name="app_name">library</string>
</resources>

20
loader/gradle.properties Normal file
View File

@ -0,0 +1,20 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
version_name=1.0
version_code=1
android.useAndroidX=true

BIN
loader/gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,6 @@
#Thu Mar 25 19:56:14 CST 2021
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-all.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

172
loader/gradlew vendored Normal file
View File

@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
loader/gradlew.bat vendored Normal file
View File

@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

14
loader/settings.gradle Normal file
View File

@ -0,0 +1,14 @@
include ':app'
rootProject.name='MMPLoader'
include ':xpatchcore'
project(':xpatchcore').projectDir = new File('core')
include ':lspcore'
project(':lspcore').projectDir = new File('mmp/core')
include ':hiddenapi-stubs'
project(':hiddenapi-stubs').projectDir = new File('mmp/hiddenapi-stubs')
include ':interface'
project(':interface').projectDir = new File('mmp/service/interface')
include ':hiddenapi-bridge'
project(':hiddenapi-bridge').projectDir = new File('mmp/hiddenapi-bridge')
include ':manager-service'
project(':manager-service').projectDir = new File('mmp/manager-service')