just copy loader
This commit is contained in:
parent
64a9076931
commit
cb7b40cbd3
|
|
@ -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
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
[submodule "mmp"]
|
||||||
|
path = mmp
|
||||||
|
url = https://github.com/MMPosed/MMPosed.git
|
||||||
|
branch = mmpatch
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
/build
|
||||||
|
/target
|
||||||
|
|
@ -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')
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
palceholder
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 |
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="sample_app_title">Xposed Module Loader</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
/build
|
||||||
|
|
@ -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')
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">library</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
Binary file not shown.
|
|
@ -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
|
||||||
|
|
@ -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" "$@"
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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')
|
||||||
Loading…
Reference in New Issue