├── .gitignore ├── AKCore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── ak │ │ └── core │ │ ├── Main.java │ │ ├── NativeUtils.java │ │ ├── Xposed.java │ │ ├── XposedModules.java │ │ └── hooks │ │ └── BindApplication.java │ └── libs │ └── andhook-lib-3.5.8.jar ├── AKXposed ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── de │ │ └── robv │ │ └── android │ │ └── xposed │ │ ├── IXposedHookCmdInit.java │ │ ├── IXposedHookInitPackageResources.java │ │ ├── IXposedHookLoadPackage.java │ │ ├── IXposedHookZygoteInit.java │ │ ├── IXposedMod.java │ │ ├── SELinuxHelper.java │ │ ├── XC_MethodHook.java │ │ ├── XC_MethodReplacement.java │ │ ├── XSharedPreferences.java │ │ ├── XposedBridge.java │ │ ├── XposedHelpers.java │ │ ├── XposedInit.java │ │ ├── callbacks │ │ ├── IXUnhook.java │ │ ├── XC_InitPackageResources.java │ │ ├── XC_LayoutInflated.java │ │ ├── XC_LoadPackage.java │ │ └── XCallback.java │ │ └── services │ │ ├── BaseService.java │ │ ├── BinderService.java │ │ ├── DirectAccessService.java │ │ ├── FileResult.java │ │ └── ZygoteService.java │ ├── lib │ ├── XposedHiddenApi.jar │ └── andhook-lib-3.5.8.jar │ └── libs │ └── XposedExternal.jar ├── Binaries ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── jni │ ├── AK │ ├── include │ │ └── AndHook.h │ └── lib │ │ ├── arm64-v8a │ │ └── libAK.so │ │ ├── armeabi-v7a │ │ └── libAK.so │ │ ├── x86 │ │ └── libAK.so │ │ └── x86_64 │ │ └── libAK.so │ ├── AKCopyDependencies.cpp │ ├── AKGadget.cpp │ ├── AKInstaller.cpp │ ├── AKInterceptor.h │ ├── AKJniHelpers.h │ ├── AKLog.h │ ├── AKPlatform.h │ ├── AKSELinux.h │ ├── Android.mk │ ├── Application.mk │ ├── android_filesystem_config.h │ └── android_util_Log.h ├── LICENSE ├── README.md ├── build.gradle ├── screenshot.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Gradle files 2 | .gradle/ 3 | build/ 4 | */build/ 5 | */.externalNativeBuild/ 6 | gradlew 7 | gradlew.bat 8 | .idea/* 9 | 10 | # Intellij 11 | *.iml 12 | */*.iml 13 | 14 | #gradle wrapper 15 | gradle/ 16 | 17 | # Local configuration file (sdk path, etc) 18 | local.properties 19 | 20 | # Prerequisites 21 | *.d 22 | 23 | # Compiled Object files 24 | *.slo 25 | *.lo 26 | *.o 27 | *.obj 28 | 29 | # Precompiled Headers 30 | *.gch 31 | *.pch 32 | 33 | # Compiled Dynamic libraries 34 | *.dylib 35 | *.dll 36 | 37 | # Fortran module files 38 | *.mod 39 | *.smod 40 | 41 | # Compiled Static libraries 42 | *.lai 43 | *.la 44 | *.a 45 | *.lib 46 | 47 | # Executables 48 | *.exe 49 | *.out 50 | *.app 51 | -------------------------------------------------------------------------------- /AKCore/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 27 5 | buildToolsVersion "27.0.3" 6 | 7 | defaultConfig { 8 | applicationId "ak.core" 9 | minSdkVersion 16 10 | targetSdkVersion 27 11 | } 12 | compileOptions { 13 | sourceCompatibility JavaVersion.VERSION_1_8 14 | targetCompatibility JavaVersion.VERSION_1_8 15 | } 16 | } 17 | 18 | dependencies { 19 | implementation project(':xposed') 20 | implementation files('src/main/libs/andhook-lib-3.5.8.jar') 21 | } -------------------------------------------------------------------------------- /AKCore/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AKCore/src/main/java/ak/core/Main.java: -------------------------------------------------------------------------------- 1 | package ak.core; 2 | 3 | import ak.core.hooks.*; 4 | import andhook.lib.xposed.XposedHelpers; 5 | import android.app.ActivityThread; 6 | import android.util.Log; 7 | 8 | public final class Main { 9 | private static final String TAG = "AKMain"; 10 | 11 | /** 12 | * This gets called after the VM has been created, but before we run any 13 | * code. 14 | */ 15 | public static void onVmCreated() { 16 | Log.v(TAG, "onVmCreated called, classloader = " + Main.class.getClassLoader()); 17 | 18 | XposedHelpers.findAndHookMethod(ActivityThread.class, 19 | "handleBindApplication", 20 | "android.app.ActivityThread$AppBindData", new BindApplication()); 21 | } 22 | 23 | /** 24 | * Called by the zygote prior to every fork. Each call to {@code preFork} 25 | * is followed by a matching call to {@link #postForkChild()} on the child 26 | * process and {@link #postForkParent()} on the parent process. 27 | */ 28 | public static void preFork() { 29 | Log.v(TAG, "preFork called"); 30 | } 31 | 32 | /** 33 | * Called by the zygote in the parent process after every fork. 34 | */ 35 | public static void postForkParent() { 36 | Log.v(TAG, "postForkParent called"); 37 | } 38 | 39 | /** 40 | * Called by the zygote in the child process after every fork. 41 | */ 42 | public static void postForkChild() { 43 | Log.v(TAG, "postForkChild called"); 44 | } 45 | 46 | /** 47 | * Used to register native methods from the framework. 48 | */ 49 | public static native void registerFrameworkNatives(final ClassLoader loader); 50 | 51 | 52 | /** 53 | * Used to change the access permissions to file system objects (files and directories) to 665. 54 | */ 55 | public static native void makeWorldAccessible(final String path); 56 | } 57 | -------------------------------------------------------------------------------- /AKCore/src/main/java/ak/core/NativeUtils.java: -------------------------------------------------------------------------------- 1 | package ak.core; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.InputStream; 6 | import java.util.ArrayList; 7 | import java.util.Enumeration; 8 | import java.util.zip.ZipEntry; 9 | import java.util.zip.ZipFile; 10 | 11 | import android.os.Build; 12 | import android.util.Log; 13 | 14 | public final class NativeUtils { 15 | private static final String TAG = "AKUtils"; 16 | 17 | private static ArrayList selectAbi(final String arch, 18 | final ArrayList armeabi, 19 | final ArrayList armeabi_v7a, 20 | final ArrayList arm64_v8a, final ArrayList x86, 21 | final ArrayList x86_64) { 22 | switch (arch) { 23 | case "armeabi-v7a": 24 | case "armeabi": 25 | if (armeabi_v7a.size() > 0) 26 | return armeabi_v7a; 27 | else if (armeabi.size() > 0) 28 | return armeabi; 29 | break; 30 | case "arm64-v8a": 31 | if (arm64_v8a.size() > 0) 32 | return arm64_v8a; 33 | break; 34 | case "x86": 35 | if (x86.size() > 0) 36 | return x86; 37 | case "x86_64": 38 | if (x86_64.size() > 0) 39 | return x86_64; 40 | } 41 | return null; 42 | } 43 | 44 | /** 45 | * Copies native binaries to a shared library directory. 46 | * 47 | * @param path APK file to scan for native libraries 48 | * @param sharedLibraryDir directory for libraries to be copied to 49 | */ 50 | @SuppressWarnings("deprecation") 51 | public static void copyNativeBinaries(final String path, final File sharedLibraryDir) { 52 | final ArrayList armeabi = new ArrayList(); 53 | final ArrayList armeabi_v7a = new ArrayList(); 54 | final ArrayList arm64_v8a = new ArrayList(); 55 | final ArrayList x86 = new ArrayList(); 56 | final ArrayList x86_64 = new ArrayList(); 57 | try { 58 | final ZipFile zf = new ZipFile(path); 59 | final Enumeration es = zf.entries(); 60 | boolean hasLibraries = false; 61 | while (es.hasMoreElements()) { 62 | final ZipEntry e = es.nextElement(); 63 | final long s = e.getSize(); 64 | if (s > 0) { 65 | // lib/armeabi/libAK.so 66 | final String en = e.getName(); 67 | if (en.startsWith("lib/armeabi-v7a/")) { 68 | armeabi_v7a.add(e); 69 | hasLibraries = true; 70 | } else if (en.startsWith("lib/armeabi/")) { 71 | armeabi.add(e); 72 | hasLibraries = true; 73 | } else if (en.startsWith("lib/arm64-v8a/")) { 74 | arm64_v8a.add(e); 75 | hasLibraries = true; 76 | } else if (en.startsWith("lib/x86/")) { 77 | x86.add(e); 78 | hasLibraries = true; 79 | } else if (en.startsWith("lib/x86_64/")) { 80 | x86_64.add(e); 81 | hasLibraries = true; 82 | } 83 | } 84 | } 85 | if (!hasLibraries) { 86 | zf.close(); 87 | return; 88 | } 89 | 90 | ArrayList abi = selectAbi(Build.CPU_ABI, armeabi, 91 | armeabi_v7a, arm64_v8a, x86, x86_64); 92 | if (abi == null) { 93 | Log.w(TAG, Build.CPU_ABI + " abi not found or incompatible, trying " + Build.CPU_ABI2 + "..."); 94 | abi = selectAbi(Build.CPU_ABI2, armeabi, armeabi_v7a, 95 | arm64_v8a, x86, x86_64); 96 | } 97 | if (abi == null) { 98 | Log.e(TAG, "Not compatible abi found at" + path); 99 | zf.close(); 100 | return; 101 | } 102 | 103 | sharedLibraryDir.mkdirs(); 104 | Log.v(TAG, "extracting libraries to " + sharedLibraryDir.getAbsolutePath() + "..."); 105 | 106 | final byte[] buffer = new byte[1024 * 512]; 107 | for (final ZipEntry e : abi) { 108 | final File f = new File(sharedLibraryDir, new File(e.getName()).getName()); 109 | final long s = f.length(); 110 | if (s > 0 && s == e.getSize()) { 111 | Log.w(TAG, "Ignored unchanged file " + f.getCanonicalPath()); 112 | continue; 113 | } 114 | 115 | try { 116 | final InputStream src = zf.getInputStream(e); 117 | final FileOutputStream dest = new FileOutputStream(f); 118 | int len; 119 | while ((len = src.read(buffer)) > 0) { 120 | dest.write(buffer, 0, len); 121 | } 122 | dest.close(); 123 | src.close(); 124 | } catch (final Exception ex) { 125 | Log.e(TAG, f.getCanonicalPath(), ex); 126 | break; 127 | } 128 | 129 | f.setExecutable(true); 130 | } 131 | 132 | zf.close(); 133 | } catch (final Exception e) { 134 | Log.wtf(TAG, e); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /AKCore/src/main/java/ak/core/Xposed.java: -------------------------------------------------------------------------------- 1 | package ak.core; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | import java.lang.reflect.Field; 7 | import java.util.Properties; 8 | 9 | import andhook.lib.HookHelper; 10 | import andhook.lib.xposed.XC_MethodHook; 11 | import andhook.lib.xposed.XposedHelpers; 12 | import android.app.LoadedApk; 13 | import android.content.pm.ApplicationInfo; 14 | import android.os.Build.VERSION; 15 | import android.util.Log; 16 | 17 | public final class Xposed extends XC_MethodHook { 18 | private static final String TAG = "AKXposed"; 19 | static final int XPOSED_VERSION_INT = 100; 20 | public static final String XPOSED_INSTALLER_PKG_NAME = "de.robv.android.xposed.installer"; 21 | private static final String ANDROID_DATA_DIR = "/data/"; 22 | private static final String DEVICE_PROTECTED_DIR = ANDROID_DATA_DIR 23 | + "user_de/0/"; 24 | private static final String CREDENTIAL_PROTECTED_DIR = ANDROID_DATA_DIR 25 | + "data/"; 26 | static final String BASE_DIR = (VERSION.SDK_INT >= 24 ? DEVICE_PROTECTED_DIR 27 | : CREDENTIAL_PROTECTED_DIR) 28 | + XPOSED_INSTALLER_PKG_NAME + "/"; 29 | static final String MODULES_LIST_FILE = BASE_DIR + "conf/modules.list"; 30 | static final String ENABLED_MODULES_LIST_FILE = BASE_DIR 31 | + "conf/enabled_modules.list"; 32 | static final String DISABLED_FILE = BASE_DIR + "conf/disabled"; 33 | static final String LOG_FILE = BASE_DIR + "log/error.log"; 34 | 35 | @SuppressWarnings("deprecation") 36 | private static void writeXposedProperties(final File propertyFile, 37 | final boolean retry) { 38 | final Properties properties = new Properties(); 39 | properties.put("version", String.valueOf(XPOSED_VERSION_INT)); 40 | properties.put("minsdk", "50"); 41 | properties.put("arch", android.os.Build.CPU_ABI); 42 | properties.put("maxsdk", String.valueOf(XPOSED_VERSION_INT)); 43 | 44 | FileOutputStream f = null; 45 | try { 46 | f = new FileOutputStream(propertyFile); 47 | properties.store(f, null); 48 | } catch (final IOException e) { 49 | propertyFile.delete(); 50 | if (retry) 51 | writeXposedProperties(propertyFile, false); 52 | } finally { 53 | if (f != null) 54 | try { 55 | f.close(); 56 | } catch (final IOException ignored) { 57 | } 58 | } 59 | } 60 | 61 | public static void init() { 62 | de.robv.android.xposed.XposedInit.init(XPOSED_VERSION_INT, LOG_FILE, 63 | new File(BASE_DIR, "shared_prefs/").getAbsolutePath()); 64 | } 65 | 66 | static ClassLoader getXposedClassLoader() { 67 | return de.robv.android.xposed.XposedInit.class.getClassLoader(); 68 | } 69 | 70 | public static void handleModule(final LoadedApk loadedApk) { 71 | Log.v(TAG, "handleModule " + loadedApk); 72 | try { 73 | final ClassLoader cl = loadedApk.getClassLoader(); 74 | final Field parent = HookHelper.findFieldHierarchically(cl.getClass(), "parent"); 75 | parent.setAccessible(true); 76 | parent.set(cl, getXposedClassLoader()); 77 | Log.v(TAG, "handleModule " + parent.get(cl)); 78 | } catch (final Throwable t) { 79 | Log.wtf(TAG, t); 80 | } 81 | } 82 | 83 | public static void handleInstaller(final LoadedApk loadedApk) { 84 | final ClassLoader cl = loadedApk.getClassLoader(); 85 | final ApplicationInfo appInfo = loadedApk.getApplicationInfo(); 86 | final File xposedProp = new File(appInfo.dataDir, "xposed_prop"); 87 | if (!xposedProp.exists()) { 88 | writeXposedProperties(xposedProp, true); 89 | } 90 | 91 | final Class xposedApp = XposedHelpers.findClass( 92 | XPOSED_INSTALLER_PKG_NAME + ".XposedApp", cl); 93 | try { 94 | final String[] xposed_prop_files = (String[]) XposedHelpers 95 | .getStaticObjectField(xposedApp, "XPOSED_PROP_FILES"); 96 | final String xposedPropPath = xposedProp.getPath(); 97 | for (int i = 0; i < xposed_prop_files.length; ++i) { 98 | xposed_prop_files[i] = xposedPropPath; 99 | } 100 | } catch (final Throwable ignored) { 101 | } 102 | 103 | XposedHelpers.findAndHookMethod(xposedApp, "getActiveXposedVersion", 104 | new Xposed()); 105 | XposedHelpers.findAndHookMethod(xposedApp, "getInstalledXposedVersion", 106 | new Xposed()); 107 | 108 | // unnecessary, just for test 109 | try { 110 | XposedHelpers.findAndHookMethod( 111 | "android/support/design/widget/Snackbar", cl, "setText", 112 | CharSequence.class, new XC_MethodHook() { 113 | @Override 114 | protected void beforeHookedMethod( 115 | final MethodHookParam param) { 116 | final String text = param.args[0].toString(); 117 | if (text.equalsIgnoreCase("Xposed Framework will be enabled on next reboot")) { 118 | param.args[0] = (CharSequence) "Xposed Framework is enabled"; 119 | } else if (text 120 | .equalsIgnoreCase("Xposed Framework will be disabled on next reboot")) { 121 | param.args[0] = (CharSequence) "Xposed Framework is disabled"; 122 | } 123 | } 124 | }); 125 | } catch (final Throwable ignored) { 126 | } 127 | } 128 | 129 | @Override 130 | protected void beforeHookedMethod(final MethodHookParam param) { 131 | param.setResult(XPOSED_VERSION_INT); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /AKCore/src/main/java/ak/core/XposedModules.java: -------------------------------------------------------------------------------- 1 | package ak.core; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.FileReader; 6 | import java.io.InputStream; 7 | import java.io.InputStreamReader; 8 | import java.util.ArrayList; 9 | 10 | import dalvik.system.PathClassLoader; 11 | import andhook.lib.AndHook; 12 | import android.app.LoadedApk; 13 | import android.util.Log; 14 | 15 | public final class XposedModules { 16 | private static final String TAG = "AKXposed"; 17 | 18 | // /data/app/andhook.test-1/base.apk 19 | private ArrayList modules = null; 20 | // andhook.test 21 | private ArrayList enabled_modules = null; 22 | 23 | private static void logXposed(final String text) { 24 | de.robv.android.xposed.XposedBridge.log(text); 25 | } 26 | 27 | private static void logXposed(final Throwable t) { 28 | de.robv.android.xposed.XposedBridge.log(t); 29 | } 30 | 31 | private static ArrayList readList(final String path) { 32 | final File f = new File(path); 33 | if (!f.exists()) { 34 | Log.e(TAG, "No such file or directory " + path); 35 | return null; 36 | } 37 | final ArrayList s = new ArrayList<>(); 38 | try { 39 | final BufferedReader reader = new BufferedReader(new FileReader(f)); 40 | String line; 41 | while ((line = reader.readLine()) != null) { 42 | line = line.trim(); 43 | if (!line.startsWith("#") && !line.isEmpty()) { 44 | s.add(line); 45 | } 46 | } 47 | reader.close(); 48 | } catch (final Exception e) { 49 | Log.e(TAG, "Error reading " + path, e); 50 | } 51 | return s.isEmpty() ? null : s; 52 | } 53 | 54 | public void preload() { 55 | if (new File(Xposed.BASE_DIR).exists()) { 56 | if (new File(Xposed.DISABLED_FILE).exists()) { 57 | Log.w(TAG, "Xposed is disabled, skipping..."); 58 | } else { 59 | this.modules = readList(Xposed.MODULES_LIST_FILE); 60 | this.enabled_modules = readList(Xposed.ENABLED_MODULES_LIST_FILE); 61 | if (this.modules == null || this.enabled_modules == null) { 62 | Log.w(TAG, "No enabled xposed modules found!"); 63 | this.modules = null; 64 | this.enabled_modules = null; 65 | } else if (this.modules.size() != this.enabled_modules.size()) { 66 | Log.e(TAG, "Unexpected number of modules!"); 67 | this.modules = null; 68 | this.enabled_modules = null; 69 | } 70 | } 71 | } else { 72 | Log.w(TAG, 73 | "Xposed installer not found, all modules are disabled by default!"); 74 | } 75 | } 76 | 77 | private static void makeDirWorldAccessible(final File sharedLibraryDir) { 78 | Log.v(TAG, 79 | "sharedLibraryDir = " + sharedLibraryDir.getAbsolutePath()); 80 | Log.v(TAG, 81 | "sharedLibraryDir.canRead = " + sharedLibraryDir.canRead()); 82 | 83 | Main.makeWorldAccessible(sharedLibraryDir.getAbsolutePath()); 84 | final File[] files = sharedLibraryDir.listFiles(); 85 | if (files != null) { 86 | for (final File file : files) { 87 | if (file.isFile()) 88 | Main.makeWorldAccessible(file.getAbsolutePath()); 89 | } 90 | } 91 | } 92 | 93 | public void load(final LoadedApk loadedApk) { 94 | if (this.modules == null || this.enabled_modules == null) 95 | return; 96 | 97 | for (int i = 0; i < enabled_modules.size(); ++i) { 98 | String path = modules.get(i); 99 | if (!new File(path).exists()) { 100 | // package updated? 101 | logXposed("Module apk " + path + " not exists!"); 102 | if (path.contains("-1/")) 103 | path = path.replace("-1/", "-2/"); 104 | else 105 | path = path.replace("-2/", "-1/"); 106 | } 107 | 108 | /* 109 | * final File sharedLibraryDir = new File(loadedApk.getDataDir(), "xlibs"); 110 | * NativeUtils.copyNativeBinaries(path, sharedLibraryDir); 111 | */ 112 | final File sharedLibraryDir = new File(loadedApk.getDataDirFile() 113 | .getParent(), enabled_modules.get(i) + "/lib"); 114 | makeDirWorldAccessible(sharedLibraryDir); 115 | 116 | final PathClassLoader cl = new PathClassLoader(path, 117 | sharedLibraryDir.getAbsolutePath(), 118 | Xposed.getXposedClassLoader()); 119 | final InputStream xposed_init = cl 120 | .getResourceAsStream("assets/xposed_init"); 121 | if (xposed_init == null) { 122 | logXposed("module apk " + path 123 | + " does not contain xposed_init, skipped!"); 124 | continue; 125 | } 126 | 127 | final BufferedReader xposed_init_reader = new BufferedReader( 128 | new InputStreamReader(xposed_init)); 129 | try { 130 | AndHook.stopDaemons(); 131 | String entry_class; 132 | while ((entry_class = xposed_init_reader.readLine()) != null) { 133 | entry_class = entry_class.trim(); 134 | if (entry_class.isEmpty() || entry_class.startsWith("#")) 135 | continue; 136 | 137 | Log.v(TAG, "Loading xposed module entry " + entry_class 138 | + "..."); 139 | try { 140 | de.robv.android.xposed.XposedInit.call(loadedApk, 141 | cl.loadClass(entry_class), path); 142 | } catch (final Throwable t) { 143 | logXposed(t); 144 | } 145 | } 146 | AndHook.startDaemons(); 147 | } catch (final Throwable t) { 148 | logXposed(t); 149 | } finally { 150 | try { 151 | xposed_init_reader.close(); 152 | xposed_init.close(); 153 | } catch (final Throwable ignored) { 154 | } 155 | } 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /AKCore/src/main/java/ak/core/hooks/BindApplication.java: -------------------------------------------------------------------------------- 1 | package ak.core.hooks; 2 | 3 | import ak.core.Xposed; 4 | import ak.core.XposedModules; 5 | import andhook.lib.xposed.XC_MethodHook; 6 | import andhook.lib.xposed.XposedHelpers; 7 | import android.app.ActivityThread; 8 | import android.app.LoadedApk; 9 | import android.content.Context; 10 | import android.content.pm.ApplicationInfo; 11 | import android.content.pm.PackageManager; 12 | import android.content.res.CompatibilityInfo; 13 | import android.os.UserHandle; 14 | import android.util.Log; 15 | 16 | public final class BindApplication extends XC_MethodHook { 17 | private static final String TAG = "AKBindApp"; 18 | 19 | @Override 20 | protected void beforeHookedMethod(final MethodHookParam param) { 21 | try { 22 | final ApplicationInfo appInfo = (ApplicationInfo) XposedHelpers.getObjectField(param.args[0], "appInfo"); 23 | final String processName = (String) XposedHelpers.getObjectField(param.args[0], "processName"); 24 | Log.i(TAG, "onBindApplication called, processName = " + processName); 25 | Log.i(TAG, "myUserId = " + UserHandle.myUserId() + 26 | ", uid = " + appInfo.uid + 27 | ", processName = " + appInfo.processName + 28 | ", packageName = " + appInfo.packageName + 29 | ", className = " + appInfo.className + 30 | ", nativeLibraryDir = " + appInfo.nativeLibraryDir + 31 | ", dataDir = " + appInfo.dataDir); 32 | 33 | final ActivityThread activityThread = (ActivityThread) param.thisObject; 34 | XposedHelpers.setObjectField(activityThread, "mBoundApplication", param.args[0]); 35 | 36 | final CompatibilityInfo compatInfo = (CompatibilityInfo) XposedHelpers.getObjectField(param.args[0], "compatInfo"); 37 | final LoadedApk loadedApk = activityThread.getPackageInfoNoCheck(appInfo, compatInfo); 38 | Xposed.init(); 39 | if (appInfo.packageName.equalsIgnoreCase(Xposed.XPOSED_INSTALLER_PKG_NAME)) { 40 | Xposed.handleInstaller(loadedApk); 41 | } else { 42 | // ActivityThread.currentApplication() == null 43 | try { 44 | final Context context = activityThread.getSystemContext(); 45 | final PackageManager pkgMgr = context.getPackageManager(); 46 | final ApplicationInfo appMetaInfo = pkgMgr.getApplicationInfo(appInfo.packageName, 47 | PackageManager.GET_META_DATA); 48 | if (appMetaInfo.metaData != null && appMetaInfo.metaData.containsKey("xposedmodule")) { 49 | Xposed.handleModule(loadedApk); 50 | } 51 | } catch (final Throwable t) { 52 | Log.wtf(TAG, t); 53 | } 54 | } 55 | 56 | final XposedModules xp = new XposedModules(); 57 | xp.preload(); 58 | xp.load(loadedApk); 59 | } catch (final Throwable tr) { 60 | Log.wtf(TAG, tr); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /AKCore/src/main/libs/andhook-lib-3.5.8.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rprop/AKFramework/a551b1d71640b24163308afe6b5094c47dbc3ce0/AKCore/src/main/libs/andhook-lib-3.5.8.jar -------------------------------------------------------------------------------- /AKXposed/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 27 5 | buildToolsVersion "27.0.3" 6 | 7 | defaultConfig { 8 | minSdkVersion 16 9 | targetSdkVersion 27 10 | } 11 | compileOptions { 12 | sourceCompatibility JavaVersion.VERSION_1_8 13 | targetCompatibility JavaVersion.VERSION_1_8 14 | } 15 | } 16 | 17 | dependencies { 18 | compileOnly files('src/main/lib/XposedHiddenApi.jar') 19 | compileOnly files('src/main/lib/andhook-lib-3.5.8.jar') 20 | implementation files('src/main/libs/XposedExternal.jar') 21 | } -------------------------------------------------------------------------------- /AKXposed/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AKXposed/src/main/java/de/robv/android/xposed/IXposedHookCmdInit.java: -------------------------------------------------------------------------------- 1 | package de.robv.android.xposed; 2 | 3 | 4 | /** 5 | * Hook the initialization of Java-based command-line tools (like pm). 6 | * 7 | * @hide Xposed no longer hooks command-line tools, therefore this interface shouldn't be 8 | * implemented anymore. 9 | */ 10 | public interface IXposedHookCmdInit extends IXposedMod { 11 | /** 12 | * Called very early during startup of a command-line tool. 13 | * @param startupParam Details about the module itself and the started process. 14 | * @throws Throwable Everything is caught, but it will prevent further initialization of the module. 15 | */ 16 | void initCmdApp(StartupParam startupParam) throws Throwable; 17 | 18 | /** Data holder for {@link #initCmdApp}. */ 19 | final class StartupParam { 20 | /*package*/ StartupParam() {} 21 | 22 | /** The path to the module's APK. */ 23 | public String modulePath; 24 | 25 | /** The class name of the tools that the hook was invoked for. */ 26 | public String startClassName; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /AKXposed/src/main/java/de/robv/android/xposed/IXposedHookInitPackageResources.java: -------------------------------------------------------------------------------- 1 | package de.robv.android.xposed; 2 | 3 | import android.content.res.XResources; 4 | 5 | import de.robv.android.xposed.callbacks.XC_InitPackageResources; 6 | import de.robv.android.xposed.callbacks.XC_InitPackageResources.InitPackageResourcesParam; 7 | 8 | /** 9 | * Get notified when the resources for an app are initialized. 10 | * In {@link #handleInitPackageResources}, resource replacements can be created. 11 | * 12 | *

This interface should be implemented by the module's main class. Xposed will take care of 13 | * registering it as a callback automatically. 14 | */ 15 | public interface IXposedHookInitPackageResources extends IXposedMod { 16 | /** 17 | * This method is called when resources for an app are being initialized. 18 | * Modules can call special methods of the {@link XResources} class in order to replace resources. 19 | * 20 | * @param resparam Information about the resources. 21 | * @throws Throwable Everything the callback throws is caught and logged. 22 | */ 23 | void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable; 24 | 25 | /** @hide */ 26 | final class Wrapper extends XC_InitPackageResources { 27 | private final IXposedHookInitPackageResources instance; 28 | public Wrapper(IXposedHookInitPackageResources instance) { 29 | this.instance = instance; 30 | } 31 | @Override 32 | public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable { 33 | instance.handleInitPackageResources(resparam); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /AKXposed/src/main/java/de/robv/android/xposed/IXposedHookLoadPackage.java: -------------------------------------------------------------------------------- 1 | package de.robv.android.xposed; 2 | 3 | import android.app.Application; 4 | 5 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 6 | import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; 7 | 8 | /** 9 | * Get notified when an app ("Android package") is loaded. 10 | * This is especially useful to hook some app-specific methods. 11 | * 12 | *

This interface should be implemented by the module's main class. Xposed will take care of 13 | * registering it as a callback automatically. 14 | */ 15 | public interface IXposedHookLoadPackage extends IXposedMod { 16 | /** 17 | * This method is called when an app is loaded. It's called very early, even before 18 | * {@link Application#onCreate} is called. 19 | * Modules can set up their app-specific hooks here. 20 | * 21 | * @param lpparam Information about the app. 22 | * @throws Throwable Everything the callback throws is caught and logged. 23 | */ 24 | void handleLoadPackage(LoadPackageParam lpparam) throws Throwable; 25 | 26 | /** @hide */ 27 | final class Wrapper extends XC_LoadPackage { 28 | private final IXposedHookLoadPackage instance; 29 | public Wrapper(IXposedHookLoadPackage instance) { 30 | this.instance = instance; 31 | } 32 | @Override 33 | public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable { 34 | instance.handleLoadPackage(lpparam); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /AKXposed/src/main/java/de/robv/android/xposed/IXposedHookZygoteInit.java: -------------------------------------------------------------------------------- 1 | package de.robv.android.xposed; 2 | 3 | /** 4 | * Hook the initialization of Zygote process(es), from which all the apps are forked. 5 | * 6 | *

Implement this interface in your module's main class in order to be notified when Android is 7 | * starting up. In {@link IXposedHookZygoteInit}, you can modify objects and place hooks that should 8 | * be applied for every app. Only the Android framework/system classes are available at that point 9 | * in time. Use {@code null} as class loader for {@link XposedHelpers#findAndHookMethod(String, ClassLoader, String, Object...)} 10 | * and its variants. 11 | * 12 | *

If you want to hook one/multiple specific apps, use {@link IXposedHookLoadPackage} instead. 13 | */ 14 | public interface IXposedHookZygoteInit extends IXposedMod { 15 | /** 16 | * Called very early during startup of Zygote. 17 | * @param startupParam Details about the module itself and the started process. 18 | * @throws Throwable everything is caught, but will prevent further initialization of the module. 19 | */ 20 | void initZygote(StartupParam startupParam) throws Throwable; 21 | 22 | /** Data holder for {@link #initZygote}. */ 23 | final class StartupParam { 24 | /*package*/ StartupParam() {} 25 | 26 | /** The path to the module's APK. */ 27 | public String modulePath; 28 | 29 | /** 30 | * Always {@code true} on 32-bit ROMs. On 64-bit, it's only {@code true} for the primary 31 | * process that starts the system_server. 32 | */ 33 | public boolean startsSystemServer; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /AKXposed/src/main/java/de/robv/android/xposed/IXposedMod.java: -------------------------------------------------------------------------------- 1 | package de.robv.android.xposed; 2 | 3 | /** Marker interface for Xposed modules. Cannot be implemented directly. */ 4 | /* package */ interface IXposedMod {} 5 | -------------------------------------------------------------------------------- /AKXposed/src/main/java/de/robv/android/xposed/SELinuxHelper.java: -------------------------------------------------------------------------------- 1 | package de.robv.android.xposed; 2 | 3 | import android.os.SELinux; 4 | 5 | import de.robv.android.xposed.services.BaseService; 6 | import de.robv.android.xposed.services.BinderService; 7 | import de.robv.android.xposed.services.DirectAccessService; 8 | import de.robv.android.xposed.services.ZygoteService; 9 | 10 | /** 11 | * A helper to work with (or without) SELinux, abstracting much of its big 12 | * complexity. 13 | */ 14 | public final class SELinuxHelper { 15 | private SELinuxHelper() { 16 | } 17 | 18 | /** 19 | * Determines whether SELinux is disabled or enabled. 20 | * 21 | * @return A boolean indicating whether SELinux is enabled. 22 | */ 23 | public static boolean isSELinuxEnabled() { 24 | return sIsSELinuxEnabled; 25 | } 26 | 27 | /** 28 | * Determines whether SELinux is permissive or enforcing. 29 | * 30 | * @return A boolean indicating whether SELinux is enforcing. 31 | */ 32 | public static boolean isSELinuxEnforced() { 33 | return sIsSELinuxEnabled && SELinux.isSELinuxEnforced(); 34 | } 35 | 36 | /** 37 | * Gets the security context of the current process. 38 | * 39 | * @return A String representing the security context of the current 40 | * process. 41 | */ 42 | public static String getContext() { 43 | return sIsSELinuxEnabled ? SELinux.getContext() : null; 44 | } 45 | 46 | /** 47 | * Retrieve the service to be used when accessing files in 48 | * {@code /data/data/*}. 49 | * 50 | *

51 | * IMPORTANT: If you call this from the Zygote process, 52 | * don't re-use the result in different process! 53 | * 54 | * @return An instance of the service. 55 | */ 56 | public static BaseService getAppDataFileService() { 57 | if (sServiceAppDataFile != null) 58 | return sServiceAppDataFile; 59 | throw new UnsupportedOperationException(); 60 | } 61 | 62 | // ---------------------------------------------------------------------------- 63 | private static boolean sIsSELinuxEnabled = false; 64 | private static BaseService sServiceAppDataFile = null; 65 | 66 | /* package */static void initOnce() { 67 | try { 68 | sIsSELinuxEnabled = SELinux.isSELinuxEnabled(); 69 | } catch (NoClassDefFoundError ignored) { 70 | } 71 | } 72 | 73 | /* package */static void initForProcess(String packageName) { 74 | if (sIsSELinuxEnabled) { 75 | if (packageName == null) { // Zygote 76 | sServiceAppDataFile = new ZygoteService(); 77 | } else if (packageName.equals("android")) { // system_server 78 | sServiceAppDataFile = BinderService 79 | .getService(BinderService.TARGET_APP); 80 | } else { // app 81 | sServiceAppDataFile = new DirectAccessService(); 82 | } 83 | } else { 84 | sServiceAppDataFile = new DirectAccessService(); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /AKXposed/src/main/java/de/robv/android/xposed/XC_MethodHook.java: -------------------------------------------------------------------------------- 1 | package de.robv.android.xposed; 2 | 3 | import java.lang.reflect.Member; 4 | 5 | import de.robv.android.xposed.callbacks.IXUnhook; 6 | import de.robv.android.xposed.callbacks.XCallback; 7 | 8 | /** 9 | * Callback class for method hooks. 10 | * 11 | *

12 | * Usually, anonymous subclasses of this class are created which override 13 | * {@link #beforeHookedMethod} and/or {@link #afterHookedMethod}. 14 | */ 15 | public abstract class XC_MethodHook extends XCallback { 16 | andhook.lib.xposed.XC_MethodHook ak_callback = null; 17 | 18 | @SuppressWarnings("all") 19 | static final class AK extends andhook.lib.xposed.XC_MethodHook { 20 | private final de.robv.android.xposed.XC_MethodHook callback; 21 | 22 | AK(final de.robv.android.xposed.XC_MethodHook callback) { 23 | this.callback = callback; 24 | callback.ak_callback = this; 25 | } 26 | 27 | @Override 28 | protected final void beforeHookedMethod( 29 | final andhook.lib.xposed.XC_MethodHook.MethodHookParam param) 30 | throws Throwable { 31 | final de.robv.android.xposed.XC_MethodHook.MethodHookParam xparam = new de.robv.android.xposed.XC_MethodHook.MethodHookParam( 32 | param); 33 | this.callback.beforeHookedMethod(xparam); 34 | xparam.writeback(); 35 | } 36 | 37 | @Override 38 | protected final void afterHookedMethod( 39 | final andhook.lib.xposed.XC_MethodHook.MethodHookParam param) 40 | throws Throwable { 41 | final de.robv.android.xposed.XC_MethodHook.MethodHookParam xparam = new de.robv.android.xposed.XC_MethodHook.MethodHookParam( 42 | param); 43 | this.callback.afterHookedMethod(xparam); 44 | xparam.writeback(); 45 | } 46 | } 47 | 48 | /** 49 | * Creates a new callback with default priority. 50 | */ 51 | @SuppressWarnings("deprecation") 52 | public XC_MethodHook() { 53 | super(); 54 | } 55 | 56 | /** 57 | * Creates a new callback with a specific priority. 58 | * 59 | *

60 | * Note that {@link #afterHookedMethod} will be called in reversed order, 61 | * i.e. the callback with the highest priority will be called last. This 62 | * way, the callback has the final control over the return value. 63 | * {@link #beforeHookedMethod} is called as usual, i.e. highest priority 64 | * first. 65 | * 66 | * @param priority 67 | * See {@link XCallback#priority}. 68 | */ 69 | public XC_MethodHook(int priority) { 70 | super(priority); 71 | } 72 | 73 | /** 74 | * Called before the invocation of the method. 75 | * 76 | *

77 | * You can use {@link MethodHookParam#setResult} and 78 | * {@link MethodHookParam#setThrowable} to prevent the original method from 79 | * being called. 80 | * 81 | *

82 | * Note that implementations shouldn't call {@code super(param)}, it's not 83 | * necessary. 84 | * 85 | * @param param 86 | * Information about the method call. 87 | * @throws Throwable 88 | * Everything the callback throws is caught and logged. 89 | */ 90 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 91 | } 92 | 93 | /** 94 | * Called after the invocation of the method. 95 | * 96 | *

97 | * You can use {@link MethodHookParam#setResult} and 98 | * {@link MethodHookParam#setThrowable} to modify the return value of the 99 | * original method. 100 | * 101 | *

102 | * Note that implementations shouldn't call {@code super(param)}, it's not 103 | * necessary. 104 | * 105 | * @param param 106 | * Information about the method call. 107 | * @throws Throwable 108 | * Everything the callback throws is caught and logged. 109 | */ 110 | protected void afterHookedMethod(MethodHookParam param) throws Throwable { 111 | } 112 | 113 | /** 114 | * Wraps information about the method call and allows to influence it. 115 | */ 116 | public static final class MethodHookParam extends XCallback.Param { 117 | /** @hide */ 118 | @SuppressWarnings("deprecation") 119 | public MethodHookParam() { 120 | super(); 121 | } 122 | 123 | /** The hooked method/constructor. */ 124 | public Member method; 125 | 126 | /** 127 | * The {@code this} reference for an instance method, or {@code null} 128 | * for static methods. 129 | */ 130 | public Object thisObject; 131 | 132 | /** Arguments to the method call. */ 133 | public Object[] args; 134 | 135 | // private final andhook.lib.xposed.XC_MethodHook.MethodHookParam 136 | // ak_param; 137 | private Object result = null; 138 | private Throwable throwable = null; 139 | /* package */boolean returnEarly = false; 140 | 141 | /** Returns the result of the method call. */ 142 | public Object getResult() { 143 | return z_ak_param().getResult(); 144 | } 145 | 146 | /** 147 | * Modify the result of the method call. 148 | * 149 | *

150 | * If called from {@link #beforeHookedMethod}, it prevents the call to 151 | * the original method. 152 | */ 153 | public void setResult(Object result) { 154 | z_ak_param().setResult(result); 155 | } 156 | 157 | /** Returns the {@link Throwable} thrown by the method, or {@code null}. */ 158 | public Throwable getThrowable() { 159 | return z_ak_param().getThrowable(); 160 | } 161 | 162 | /** Returns true if an exception was thrown by the method. */ 163 | public boolean hasThrowable() { 164 | return z_ak_param().hasThrowable(); 165 | } 166 | 167 | /** 168 | * Modify the exception thrown of the method call. 169 | * 170 | *

171 | * If called from {@link #beforeHookedMethod}, it prevents the call to 172 | * the original method. 173 | */ 174 | public void setThrowable(Throwable throwable) { 175 | z_ak_param().setThrowable(throwable); 176 | } 177 | 178 | /** 179 | * Returns the result of the method call, or throws the Throwable caused 180 | * by it. 181 | */ 182 | public Object getResultOrThrowable() throws Throwable { 183 | return z_ak_param().getResultOrThrowable(); 184 | } 185 | 186 | private andhook.lib.xposed.XC_MethodHook.MethodHookParam z_ak_param() { 187 | return (andhook.lib.xposed.XC_MethodHook.MethodHookParam) result; 188 | } 189 | 190 | MethodHookParam( 191 | final andhook.lib.xposed.XC_MethodHook.MethodHookParam param) { 192 | super(); 193 | this.result = param; 194 | this.method = param.method; 195 | this.thisObject = param.thisObject; 196 | this.args = param.args; 197 | } 198 | 199 | void writeback() { 200 | final andhook.lib.xposed.XC_MethodHook.MethodHookParam ak_param = z_ak_param(); 201 | ak_param.method = this.method; 202 | ak_param.thisObject = this.thisObject; 203 | ak_param.args = this.args; 204 | this.result = null; 205 | } 206 | } 207 | 208 | /** 209 | * An object with which the method/constructor can be unhooked. 210 | */ 211 | public class Unhook implements IXUnhook { 212 | private final Member hookMethod; 213 | 214 | /* package */Unhook(Member hookMethod) { 215 | this.hookMethod = hookMethod; 216 | } 217 | 218 | /** 219 | * Returns the method/constructor that has been hooked. 220 | */ 221 | public Member getHookedMethod() { 222 | return hookMethod; 223 | } 224 | 225 | @Override 226 | public XC_MethodHook getCallback() { 227 | return XC_MethodHook.this; 228 | } 229 | 230 | @SuppressWarnings("deprecation") 231 | @Override 232 | public void unhook() { 233 | XposedBridge.unhookMethod(hookMethod, XC_MethodHook.this); 234 | } 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /AKXposed/src/main/java/de/robv/android/xposed/XC_MethodReplacement.java: -------------------------------------------------------------------------------- 1 | package de.robv.android.xposed; 2 | 3 | import de.robv.android.xposed.callbacks.XCallback; 4 | 5 | /** 6 | * A special case of {@link XC_MethodHook} which completely replaces the 7 | * original method. 8 | */ 9 | public abstract class XC_MethodReplacement extends XC_MethodHook { 10 | /** 11 | * Creates a new callback with default priority. 12 | */ 13 | public XC_MethodReplacement() { 14 | super(); 15 | } 16 | 17 | /** 18 | * Creates a new callback with a specific priority. 19 | * 20 | * @param priority 21 | * See {@link XCallback#priority}. 22 | */ 23 | public XC_MethodReplacement(int priority) { 24 | super(priority); 25 | } 26 | 27 | /** @hide */ 28 | @Override 29 | protected final void beforeHookedMethod(MethodHookParam param) 30 | throws Throwable { 31 | try { 32 | Object result = replaceHookedMethod(param); 33 | param.setResult(result); 34 | } catch (Throwable t) { 35 | param.setThrowable(t); 36 | } 37 | } 38 | 39 | /** @hide */ 40 | @Override 41 | @SuppressWarnings("EmptyMethod") 42 | protected final void afterHookedMethod(MethodHookParam param) 43 | throws Throwable { 44 | } 45 | 46 | /** 47 | * Shortcut for replacing a method completely. Whatever is returned/thrown 48 | * here is taken instead of the result of the original method (which will 49 | * not be called). 50 | * 51 | *

52 | * Note that implementations shouldn't call {@code super(param)}, it's not 53 | * necessary. 54 | * 55 | * @param param 56 | * Information about the method call. 57 | * @throws Throwable 58 | * Anything that is thrown by the callback will be passed on to 59 | * the original caller. 60 | */ 61 | @SuppressWarnings("UnusedParameters") 62 | protected abstract Object replaceHookedMethod(MethodHookParam param) 63 | throws Throwable; 64 | 65 | /** 66 | * Predefined callback that skips the method without replacements. 67 | */ 68 | public static final XC_MethodReplacement DO_NOTHING = new XC_MethodReplacement( 69 | PRIORITY_HIGHEST * 2) { 70 | @Override 71 | protected Object replaceHookedMethod(MethodHookParam param) 72 | throws Throwable { 73 | return null; 74 | } 75 | }; 76 | 77 | /** 78 | * Creates a callback which always returns a specific value. 79 | * 80 | * @param result 81 | * The value that should be returned to callers of the hooked 82 | * method. 83 | */ 84 | public static XC_MethodReplacement returnConstant(final Object result) { 85 | return returnConstant(PRIORITY_DEFAULT, result); 86 | } 87 | 88 | /** 89 | * Like {@link #returnConstant(Object)}, but allows to specify a priority 90 | * for the callback. 91 | * 92 | * @param priority 93 | * See {@link XCallback#priority}. 94 | * @param result 95 | * The value that should be returned to callers of the hooked 96 | * method. 97 | */ 98 | public static XC_MethodReplacement returnConstant(int priority, 99 | final Object result) { 100 | return new XC_MethodReplacement(priority) { 101 | @Override 102 | protected Object replaceHookedMethod(MethodHookParam param) 103 | throws Throwable { 104 | return result; 105 | } 106 | }; 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /AKXposed/src/main/java/de/robv/android/xposed/XSharedPreferences.java: -------------------------------------------------------------------------------- 1 | package de.robv.android.xposed; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.content.SharedPreferences; 6 | import android.preference.PreferenceManager; 7 | import android.util.Log; 8 | 9 | import com.android.internal.util.XmlUtils; 10 | 11 | import org.xmlpull.v1.XmlPullParserException; 12 | 13 | import java.io.File; 14 | import java.io.FileNotFoundException; 15 | import java.io.IOException; 16 | import java.util.HashMap; 17 | import java.util.Map; 18 | import java.util.Set; 19 | 20 | import de.robv.android.xposed.services.FileResult; 21 | 22 | /** 23 | * This class is basically the same as SharedPreferencesImpl from AOSP, but 24 | * read-only and without listeners support. Instead, it is made to be compatible 25 | * with all ROMs. 26 | */ 27 | public final class XSharedPreferences implements SharedPreferences { 28 | private static final String TAG = "XSharedPreferences"; 29 | private final File mFile; 30 | private final String mFilename; 31 | private Map mMap; 32 | private boolean mLoaded = false; 33 | private long mLastModified; 34 | private long mFileSize; 35 | 36 | /** 37 | * Read settings from the specified file. 38 | * 39 | * @param prefFile 40 | * The file to read the preferences from. 41 | */ 42 | public XSharedPreferences(File prefFile) { 43 | mFile = prefFile; 44 | mFilename = mFile.getAbsolutePath(); 45 | startLoadFromDisk(); 46 | } 47 | 48 | /** 49 | * Read settings from the default preferences for a package. These 50 | * preferences are returned by 51 | * {@link PreferenceManager#getDefaultSharedPreferences}. 52 | * 53 | * @param packageName 54 | * The package name. 55 | */ 56 | public XSharedPreferences(String packageName) { 57 | this(packageName, packageName + "_preferences"); 58 | } 59 | 60 | /** 61 | * Read settings from a custom preferences file for a package. These 62 | * preferences are returned by 63 | * {@link Context#getSharedPreferences(String, int)}. 64 | * 65 | * @param packageName 66 | * The package name. 67 | * @param prefFileName 68 | * The file name without ".xml". 69 | */ 70 | public XSharedPreferences(String packageName, String prefFileName) { 71 | mFile = XposedInit.getSharedPreferences(packageName, prefFileName); 72 | mFilename = mFile.getAbsolutePath(); 73 | startLoadFromDisk(); 74 | } 75 | 76 | /** 77 | * Tries to make the preferences file world-readable. 78 | * 79 | *

80 | * Warning: This is only meant to work around permission 81 | * "fix" functions that are part of some recoveries. It doesn't replace the 82 | * need to open preferences with {@code MODE_WORLD_READABLE} in the module's 83 | * UI code. Otherwise, Android will set stricter permissions again during 84 | * the next save. 85 | * 86 | *

87 | * This will only work if executed as root (e.g. {@code initZygote()}) and 88 | * only if SELinux is disabled. 89 | * 90 | * @return {@code true} in case the file could be made world-readable. 91 | */ 92 | @SuppressLint("SetWorldReadable") 93 | public boolean makeWorldReadable() { 94 | if (!SELinuxHelper.getAppDataFileService().hasDirectFileAccess()) 95 | return false; // It doesn't make much sense to make the file 96 | // readable if we wouldn't be able to access it 97 | // anyway. 98 | 99 | if (!mFile.exists()) // Just in case - the file should never be created 100 | // if it doesn't exist. 101 | return false; 102 | 103 | return mFile.setReadable(true, false); 104 | } 105 | 106 | /** 107 | * Returns the file that is backing these preferences. 108 | * 109 | *

110 | * Warning: The file might not be accessible directly. 111 | */ 112 | public File getFile() { 113 | return mFile; 114 | } 115 | 116 | private void startLoadFromDisk() { 117 | synchronized (this) { 118 | mLoaded = false; 119 | } 120 | new Thread("XSharedPreferences-load") { 121 | @Override 122 | public void run() { 123 | synchronized (XSharedPreferences.this) { 124 | loadFromDiskLocked(); 125 | } 126 | } 127 | }.start(); 128 | } 129 | 130 | @SuppressWarnings({ "rawtypes", "unchecked" }) 131 | private void loadFromDiskLocked() { 132 | if (mLoaded) { 133 | return; 134 | } 135 | 136 | Map map = null; 137 | FileResult result = null; 138 | try { 139 | result = SELinuxHelper.getAppDataFileService().getFileInputStream( 140 | mFilename, mFileSize, mLastModified); 141 | if (result.stream != null) { 142 | map = XmlUtils.readMapXml(result.stream); 143 | result.stream.close(); 144 | } else { 145 | // The file is unchanged, keep the current values 146 | map = mMap; 147 | } 148 | } catch (XmlPullParserException e) { 149 | Log.w(TAG, "getSharedPreferences", e); 150 | } catch (FileNotFoundException ignored) { 151 | // SharedPreferencesImpl has a canRead() check, so it doesn't log 152 | // anything in case the file doesn't exist 153 | } catch (IOException e) { 154 | Log.w(TAG, "getSharedPreferences", e); 155 | } finally { 156 | if (result != null && result.stream != null) { 157 | try { 158 | result.stream.close(); 159 | } catch (RuntimeException rethrown) { 160 | throw rethrown; 161 | } catch (Exception ignored) { 162 | } 163 | } 164 | } 165 | 166 | mLoaded = true; 167 | if (map != null) { 168 | mMap = map; 169 | mLastModified = result.mtime; 170 | mFileSize = result.size; 171 | } else { 172 | mMap = new HashMap<>(); 173 | } 174 | notifyAll(); 175 | } 176 | 177 | /** 178 | * Reload the settings from file if they have changed. 179 | * 180 | *

181 | * Warning: With enforcing SELinux, this call might be 182 | * quite expensive. 183 | */ 184 | public synchronized void reload() { 185 | if (hasFileChanged()) 186 | startLoadFromDisk(); 187 | } 188 | 189 | /** 190 | * Check whether the file has changed since the last time it has been 191 | * loaded. 192 | * 193 | *

194 | * Warning: With enforcing SELinux, this call might be 195 | * quite expensive. 196 | */ 197 | public synchronized boolean hasFileChanged() { 198 | try { 199 | FileResult result = SELinuxHelper.getAppDataFileService().statFile( 200 | mFilename); 201 | return mLastModified != result.mtime || mFileSize != result.size; 202 | } catch (FileNotFoundException ignored) { 203 | // SharedPreferencesImpl doesn't log anything in case the file 204 | // doesn't exist 205 | return true; 206 | } catch (IOException e) { 207 | Log.w(TAG, "hasFileChanged", e); 208 | return true; 209 | } 210 | } 211 | 212 | private void awaitLoadedLocked() { 213 | while (!mLoaded) { 214 | try { 215 | wait(); 216 | } catch (InterruptedException unused) { 217 | } 218 | } 219 | } 220 | 221 | /** @hide */ 222 | @Override 223 | public Map getAll() { 224 | synchronized (this) { 225 | awaitLoadedLocked(); 226 | return new HashMap<>(mMap); 227 | } 228 | } 229 | 230 | /** @hide */ 231 | @Override 232 | public String getString(String key, String defValue) { 233 | synchronized (this) { 234 | awaitLoadedLocked(); 235 | String v = (String) mMap.get(key); 236 | return v != null ? v : defValue; 237 | } 238 | } 239 | 240 | /** @hide */ 241 | @Override 242 | @SuppressWarnings("unchecked") 243 | public Set getStringSet(String key, Set defValues) { 244 | synchronized (this) { 245 | awaitLoadedLocked(); 246 | Set v = (Set) mMap.get(key); 247 | return v != null ? v : defValues; 248 | } 249 | } 250 | 251 | /** @hide */ 252 | @Override 253 | public int getInt(String key, int defValue) { 254 | synchronized (this) { 255 | awaitLoadedLocked(); 256 | Integer v = (Integer) mMap.get(key); 257 | return v != null ? v : defValue; 258 | } 259 | } 260 | 261 | /** @hide */ 262 | @Override 263 | public long getLong(String key, long defValue) { 264 | synchronized (this) { 265 | awaitLoadedLocked(); 266 | Long v = (Long) mMap.get(key); 267 | return v != null ? v : defValue; 268 | } 269 | } 270 | 271 | /** @hide */ 272 | @Override 273 | public float getFloat(String key, float defValue) { 274 | synchronized (this) { 275 | awaitLoadedLocked(); 276 | Float v = (Float) mMap.get(key); 277 | return v != null ? v : defValue; 278 | } 279 | } 280 | 281 | /** @hide */ 282 | @Override 283 | public boolean getBoolean(String key, boolean defValue) { 284 | synchronized (this) { 285 | awaitLoadedLocked(); 286 | Boolean v = (Boolean) mMap.get(key); 287 | return v != null ? v : defValue; 288 | } 289 | } 290 | 291 | /** @hide */ 292 | @Override 293 | public boolean contains(String key) { 294 | synchronized (this) { 295 | awaitLoadedLocked(); 296 | return mMap.containsKey(key); 297 | } 298 | } 299 | 300 | /** @deprecated Not supported by this implementation. */ 301 | @Deprecated 302 | @Override 303 | public Editor edit() { 304 | throw new UnsupportedOperationException("read-only implementation"); 305 | } 306 | 307 | /** @deprecated Not supported by this implementation. */ 308 | @Deprecated 309 | @Override 310 | public void registerOnSharedPreferenceChangeListener( 311 | OnSharedPreferenceChangeListener listener) { 312 | throw new UnsupportedOperationException( 313 | "listeners are not supported in this implementation"); 314 | } 315 | 316 | /** @deprecated Not supported by this implementation. */ 317 | @Deprecated 318 | @Override 319 | public void unregisterOnSharedPreferenceChangeListener( 320 | OnSharedPreferenceChangeListener listener) { 321 | throw new UnsupportedOperationException( 322 | "listeners are not supported in this implementation"); 323 | } 324 | 325 | } 326 | -------------------------------------------------------------------------------- /AKXposed/src/main/java/de/robv/android/xposed/XposedBridge.java: -------------------------------------------------------------------------------- 1 | package de.robv.android.xposed; 2 | 3 | import android.os.Build; 4 | import android.util.Log; 5 | 6 | import java.lang.reflect.AccessibleObject; 7 | import java.lang.reflect.InvocationTargetException; 8 | import java.lang.reflect.Member; 9 | import java.lang.reflect.Method; 10 | import java.util.Arrays; 11 | import java.util.HashMap; 12 | import java.util.HashSet; 13 | import java.util.Map; 14 | import java.util.Set; 15 | 16 | import de.robv.android.xposed.callbacks.XC_InitPackageResources; 17 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 18 | 19 | /** 20 | * This class contains most of Xposed's central logic, such as initialization 21 | * and callbacks used by the native side. It also includes methods to add new 22 | * hooks. 23 | */ 24 | @SuppressWarnings("JniMissingFunction") 25 | public final class XposedBridge { 26 | /** 27 | * The system class loader which can be used to locate Android framework 28 | * classes. Application classes cannot be retrieved from it. 29 | * 30 | * @see ClassLoader#getSystemClassLoader 31 | */ 32 | public static final ClassLoader BOOTCLASSLOADER = ClassLoader 33 | .getSystemClassLoader(); 34 | 35 | /** @hide */ 36 | public static final String TAG = "AKXposed"; 37 | 38 | /** Use {@link #getXposedVersion()} instead. */ 39 | public static int XPOSED_BRIDGE_VERSION = 100; 40 | 41 | /* package */static boolean isZygote = true; 42 | 43 | private static int runtime = 0; 44 | private static final int RUNTIME_DALVIK = 1; 45 | private static final int RUNTIME_ART = 2; 46 | 47 | /* package */static boolean disableHooks = false; 48 | 49 | // This field is set "magically" on MIUI. 50 | /* package */static long BOOT_START_TIME; 51 | 52 | private static final Object[] EMPTY_ARRAY = new Object[0]; 53 | 54 | // built-in handlers 55 | private static final Map> sHookedMethodCallbacks = new HashMap<>(); 56 | /* package */static final CopyOnWriteSortedSet sLoadedPackageCallbacks = new CopyOnWriteSortedSet<>(); 57 | /* package */static final CopyOnWriteSortedSet sInitPackageResourcesCallbacks = new CopyOnWriteSortedSet<>(); 58 | 59 | private native static boolean hadInitErrors(); 60 | 61 | private static native int getRuntime(); 62 | 63 | /* package */static native boolean startsSystemServer(); 64 | 65 | /* package */static native String getStartClassName(); 66 | 67 | /* package */native static boolean initXResourcesNative(); 68 | 69 | /** 70 | * Returns the currently installed version of the Xposed framework. 71 | */ 72 | public static int getXposedVersion() { 73 | return XPOSED_BRIDGE_VERSION; 74 | } 75 | 76 | /** 77 | * Writes a message to the Xposed error log. 78 | * 79 | *

80 | * DON'T FLOOD THE LOG!!! This is only meant for error logging. If 81 | * you want to write information/debug messages, use logcat. 82 | * 83 | * @param text 84 | * The log message. 85 | */ 86 | public static void log(String text) { 87 | Log.e(TAG, text); 88 | XposedInit.log(text); 89 | } 90 | 91 | /** 92 | * Logs a stack trace to the Xposed error log. 93 | * 94 | *

95 | * DON'T FLOOD THE LOG!!! This is only meant for error logging. If 96 | * you want to write information/debug messages, use logcat. 97 | * 98 | * @param t 99 | * The Throwable object for the stack trace. 100 | */ 101 | public static void log(Throwable t) { 102 | Log.wtf(TAG, t); 103 | XposedInit.log(Log.getStackTraceString(t)); 104 | } 105 | 106 | public static XC_MethodHook.Unhook hookMethod(Member hookMethod, 107 | XC_MethodHook callback) { 108 | final andhook.lib.xposed.XC_MethodHook.Unhook uk = andhook.lib.xposed.XposedBridge 109 | .hookMethod(hookMethod, new XC_MethodHook.AK(callback)); 110 | if (uk != null) { 111 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH) 112 | Log.v(TAG, "hook " + hookMethod.getDeclaringClass().getName() 113 | + "@" + hookMethod.getName() + " successfully"); 114 | return callback.new Unhook(uk.getHookedMethod()); 115 | } else { 116 | Log.e(TAG, "failed to hook " 117 | + hookMethod.getDeclaringClass().getName() + "@" 118 | + hookMethod.getName()); 119 | return null; 120 | } 121 | } 122 | 123 | @Deprecated 124 | public static void unhookMethod(Member hookMethod, XC_MethodHook callback) { 125 | andhook.lib.xposed.XposedBridge.unhookMethod(hookMethod, 126 | callback.ak_callback); 127 | } 128 | 129 | /** 130 | * Hooks all methods with a certain name that were declared in the specified 131 | * class. Inherited methods and constructors are not considered. For 132 | * constructors, use {@link #hookAllConstructors} instead. 133 | * 134 | * @param hookClass 135 | * The class to check for declared methods. 136 | * @param methodName 137 | * The name of the method(s) to hook. 138 | * @param callback 139 | * The callback to be executed when the hooked methods are 140 | * called. 141 | * @return A set containing one object for each found method which can be 142 | * used to unhook it. 143 | */ 144 | @SuppressWarnings("UnusedReturnValue") 145 | public static Set hookAllMethods(Class hookClass, 146 | String methodName, XC_MethodHook callback) { 147 | HashSet unhooks = new HashSet<>(); 148 | for (Member method : hookClass.getDeclaredMethods()) 149 | if (method.getName().equals(methodName)) 150 | unhooks.add(hookMethod(method, callback)); 151 | return unhooks; 152 | } 153 | 154 | /** 155 | * Hook all constructors of the specified class. 156 | * 157 | * @param hookClass 158 | * The class to check for constructors. 159 | * @param callback 160 | * The callback to be executed when the hooked constructors are 161 | * called. 162 | * @return A set containing one object for each found constructor which can 163 | * be used to unhook it. 164 | */ 165 | @SuppressWarnings("UnusedReturnValue") 166 | public static Set hookAllConstructors( 167 | Class hookClass, XC_MethodHook callback) { 168 | HashSet unhooks = new HashSet<>(); 169 | for (Member constructor : hookClass.getDeclaredConstructors()) 170 | unhooks.add(hookMethod(constructor, callback)); 171 | return unhooks; 172 | } 173 | 174 | private static Object handleHookedMethod(Member method, 175 | int originalMethodId, Object additionalInfoObj, Object thisObject, 176 | Object[] args) throws Throwable { 177 | return null; 178 | } 179 | 180 | /** 181 | * Adds a callback to be executed when an app ("Android package") is loaded. 182 | * 183 | *

184 | * You probably don't need to call this. Simply implement 185 | * {@link IXposedHookLoadPackage} in your module class and Xposed will take 186 | * care of registering it as a callback. 187 | * 188 | * @param callback 189 | * The callback to be executed. 190 | * @hide 191 | */ 192 | public static void hookLoadPackage(XC_LoadPackage callback) { 193 | synchronized (sLoadedPackageCallbacks) { 194 | sLoadedPackageCallbacks.add(callback); 195 | } 196 | } 197 | 198 | /** 199 | * Adds a callback to be executed when the resources for an app are 200 | * initialized. 201 | * 202 | *

203 | * You probably don't need to call this. Simply implement 204 | * {@link IXposedHookInitPackageResources} in your module class and Xposed 205 | * will take care of registering it as a callback. 206 | * 207 | * @param callback 208 | * The callback to be executed. 209 | * @hide 210 | */ 211 | public static void hookInitPackageResources(XC_InitPackageResources callback) { 212 | synchronized (sInitPackageResourcesCallbacks) { 213 | sInitPackageResourcesCallbacks.add(callback); 214 | } 215 | } 216 | 217 | /** 218 | * Intercept every call to the specified method and call a handler function 219 | * instead. 220 | * 221 | * @param method 222 | * The method to intercept 223 | */ 224 | private native synchronized static void hookMethodNative(Member method, 225 | Class declaringClass, int slot, Object additionalInfo); 226 | 227 | private native static Object invokeOriginalMethodNative(Member method, 228 | int methodId, Class[] parameterTypes, Class returnType, 229 | Object thisObject, Object[] args) throws IllegalAccessException, 230 | IllegalArgumentException, InvocationTargetException; 231 | 232 | /** 233 | * Basically the same as {@link Method#invoke}, but calls the original 234 | * method as it was before the interception by Xposed. Also, access 235 | * permissions are not checked. 236 | * 237 | *

238 | * There are very few cases where this method is needed. A common mistake is 239 | * to replace a method and then invoke the original one based on dynamic 240 | * conditions. This creates overhead and skips further hooks by other 241 | * modules. Instead, just hook (don't replace) the method and call 242 | * {@code param.setResult(null)} in {@link XC_MethodHook#beforeHookedMethod} 243 | * if the original method should be skipped. 244 | * 245 | * @param method 246 | * The method to be called. 247 | * @param thisObject 248 | * For non-static calls, the "this" pointer, otherwise 249 | * {@code null}. 250 | * @param args 251 | * Arguments for the method call as Object[] array. 252 | * @return The result returned from the invoked method. 253 | * @throws NullPointerException 254 | * if {@code receiver == null} for a non-static method 255 | * @throws IllegalAccessException 256 | * if this method is not accessible (see 257 | * {@link AccessibleObject}) 258 | * @throws IllegalArgumentException 259 | * if the number of arguments doesn't match the number of 260 | * parameters, the receiver is incompatible with the declaring 261 | * class, or an argument could not be unboxed or converted by a 262 | * widening conversion to the corresponding parameter type 263 | * @throws InvocationTargetException 264 | * if an exception was thrown by the invoked method 265 | */ 266 | public static Object invokeOriginalMethod(Member method, Object thisObject, 267 | Object[] args) { 268 | return andhook.lib.xposed.XposedBridge.invokeOriginalMethod(method, 269 | thisObject, args); 270 | } 271 | 272 | /* package */static void setObjectClass(Object obj, Class clazz) { 273 | if (clazz.isAssignableFrom(obj.getClass())) { 274 | throw new IllegalArgumentException("Cannot transfer object from " 275 | + obj.getClass() + " to " + clazz); 276 | } 277 | setObjectClassNative(obj, clazz); 278 | } 279 | 280 | private static native void setObjectClassNative(Object obj, Class clazz); 281 | 282 | /* package */static native void dumpObjectNative(Object obj); 283 | 284 | /* package */static Object cloneToSubclass(Object obj, Class targetClazz) { 285 | if (obj == null) 286 | return null; 287 | 288 | if (!obj.getClass().isAssignableFrom(targetClazz)) 289 | throw new ClassCastException(targetClazz + " doesn't extend " 290 | + obj.getClass()); 291 | 292 | return cloneToSubclassNative(obj, targetClazz); 293 | } 294 | 295 | private static native Object cloneToSubclassNative(Object obj, 296 | Class targetClazz); 297 | 298 | private static native void removeFinalFlagNative(Class clazz); 299 | 300 | /* package */static native void closeFilesBeforeForkNative(); 301 | 302 | /* package */static native void reopenFilesAfterForkNative(); 303 | 304 | /* package */static native void invalidateCallersNative(Member[] methods); 305 | 306 | /** @hide */ 307 | public static final class CopyOnWriteSortedSet { 308 | private transient volatile Object[] elements = EMPTY_ARRAY; 309 | 310 | @SuppressWarnings("UnusedReturnValue") 311 | public synchronized boolean add(E e) { 312 | int index = indexOf(e); 313 | if (index >= 0) 314 | return false; 315 | 316 | Object[] newElements = new Object[elements.length + 1]; 317 | System.arraycopy(elements, 0, newElements, 0, elements.length); 318 | newElements[elements.length] = e; 319 | Arrays.sort(newElements); 320 | elements = newElements; 321 | return true; 322 | } 323 | 324 | @SuppressWarnings("UnusedReturnValue") 325 | public synchronized boolean remove(E e) { 326 | int index = indexOf(e); 327 | if (index == -1) 328 | return false; 329 | 330 | Object[] newElements = new Object[elements.length - 1]; 331 | System.arraycopy(elements, 0, newElements, 0, index); 332 | System.arraycopy(elements, index + 1, newElements, index, 333 | elements.length - index - 1); 334 | elements = newElements; 335 | return true; 336 | } 337 | 338 | private int indexOf(Object o) { 339 | for (int i = 0; i < elements.length; i++) { 340 | if (o.equals(elements[i])) 341 | return i; 342 | } 343 | return -1; 344 | } 345 | 346 | public Object[] getSnapshot() { 347 | return elements; 348 | } 349 | } 350 | 351 | private static class AdditionalHookInfo { 352 | final CopyOnWriteSortedSet callbacks; 353 | final Class[] parameterTypes; 354 | final Class returnType; 355 | 356 | private AdditionalHookInfo( 357 | CopyOnWriteSortedSet callbacks, 358 | Class[] parameterTypes, Class returnType) { 359 | this.callbacks = callbacks; 360 | this.parameterTypes = parameterTypes; 361 | this.returnType = returnType; 362 | } 363 | } 364 | } 365 | -------------------------------------------------------------------------------- /AKXposed/src/main/java/de/robv/android/xposed/XposedInit.java: -------------------------------------------------------------------------------- 1 | package de.robv.android.xposed; 2 | 3 | import java.io.BufferedWriter; 4 | import java.io.File; 5 | import java.io.FileOutputStream; 6 | import java.io.OutputStreamWriter; 7 | 8 | import android.app.LoadedApk; 9 | import android.os.Environment; 10 | import android.os.Build.VERSION; 11 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 12 | 13 | public final class XposedInit { 14 | static String XPOSED_PREF_DATA_PATH = null; 15 | private static String XPOSED_LOG_PATH = null; 16 | private static BufferedWriter writer = null; 17 | 18 | // @Keep 19 | static synchronized void log(final String text) { 20 | if (XPOSED_LOG_PATH != null) { 21 | if (writer == null) { 22 | try { 23 | writer = new BufferedWriter(new OutputStreamWriter( 24 | new FileOutputStream(XPOSED_LOG_PATH, true))); 25 | } catch (final Exception ignored) { 26 | return; 27 | } 28 | } 29 | try { 30 | writer.write(text.trim() + "\n"); 31 | writer.flush(); 32 | } catch (final Exception ignored) { 33 | try { 34 | writer.close(); 35 | } catch (final Exception e) { 36 | } 37 | writer = null; 38 | } 39 | } 40 | } 41 | 42 | // @Keep 43 | public static void init(final int xposed_version, final String log_path, final String prefs_path) { 44 | XposedBridge.XPOSED_BRIDGE_VERSION = xposed_version; 45 | try { 46 | SELinuxHelper.initOnce(); 47 | SELinuxHelper.initForProcess("app"); 48 | } catch (final Throwable ignored) { 49 | } 50 | 51 | XPOSED_LOG_PATH = log_path; 52 | XPOSED_PREF_DATA_PATH = prefs_path; 53 | } 54 | 55 | private static native void makeWorldAccessible(final String path); 56 | 57 | private static File makeWorldAccessible(final File file) { 58 | makeWorldAccessible(file.getParentFile().getAbsolutePath()); 59 | makeWorldAccessible(file.getAbsolutePath()); 60 | return file; 61 | } 62 | 63 | static File getSharedPreferences(final String packageName, final String prefFileName) { 64 | final String path = packageName + "/shared_prefs/" + prefFileName + ".xml"; 65 | final File file = makeWorldAccessible(new File(Environment.getDataDirectory(), "data/" + path)); 66 | if (!file.canRead() && VERSION.SDK_INT >= 24) { 67 | return makeWorldAccessible(new File(Environment.getDataDirectory(), "user_de/0/" + path)); 68 | } 69 | return file; 70 | } 71 | 72 | // @Keep 73 | public static void call(final LoadedApk loadedApk, 74 | final Class entry_class, final String modulePath) 75 | throws Throwable { 76 | if (!IXposedMod.class.isAssignableFrom(entry_class)) { 77 | final String e = "class " + entry_class + " cannot be cast to " 78 | + IXposedMod.class; 79 | log(e); 80 | throw new ClassCastException(e); 81 | } 82 | 83 | final Object inst = entry_class.newInstance(); 84 | if (inst instanceof IXposedHookZygoteInit) { 85 | final IXposedHookZygoteInit.StartupParam param = new IXposedHookZygoteInit.StartupParam(); 86 | param.modulePath = modulePath; 87 | param.startsSystemServer = false; 88 | ((IXposedHookZygoteInit) inst).initZygote(param); 89 | } 90 | 91 | if (inst instanceof IXposedHookLoadPackage) { 92 | final XposedBridge.CopyOnWriteSortedSet xc_callbacks = new XposedBridge.CopyOnWriteSortedSet<>(); 93 | xc_callbacks.add(new IXposedHookLoadPackage.Wrapper( 94 | (IXposedHookLoadPackage) inst)); 95 | try { 96 | final Object[] existingCallbacks = XposedBridge.sLoadedPackageCallbacks 97 | .getSnapshot(); 98 | for (final Object cb : existingCallbacks) { 99 | xc_callbacks.add((XC_LoadPackage) cb); 100 | } 101 | } catch (final Exception ignored) { 102 | } 103 | 104 | final XC_LoadPackage.LoadPackageParam param = new XC_LoadPackage.LoadPackageParam(xc_callbacks); 105 | param.appInfo = loadedApk.getApplicationInfo(); 106 | param.packageName = param.appInfo.packageName; 107 | param.classLoader = loadedApk.getClassLoader(); 108 | param.isFirstApplication = true; 109 | param.processName = param.appInfo.processName; 110 | XC_LoadPackage.callAll(param); 111 | } 112 | 113 | // @TODO: support resource hook 114 | /* 115 | * if (inst instanceof IXposedHookInitPackageResources) { 116 | * 117 | * } 118 | */ 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /AKXposed/src/main/java/de/robv/android/xposed/callbacks/IXUnhook.java: -------------------------------------------------------------------------------- 1 | package de.robv.android.xposed.callbacks; 2 | 3 | import de.robv.android.xposed.IXposedHookZygoteInit; 4 | 5 | /** 6 | * Interface for objects that can be used to remove callbacks. 7 | * 8 | *

Just like hooking methods etc., unhooking applies only to the current process. 9 | * In other process (or when the app is removed from memory and then restarted), the hook will still 10 | * be active. The Zygote process (see {@link IXposedHookZygoteInit}) is an exception, the hook won't 11 | * be inherited by any future processes forked from it in the future. 12 | * 13 | * @param The class of the callback. 14 | */ 15 | public interface IXUnhook { 16 | /** 17 | * Returns the callback that has been registered. 18 | */ 19 | T getCallback(); 20 | 21 | /** 22 | * Removes the callback. 23 | */ 24 | void unhook(); 25 | } 26 | -------------------------------------------------------------------------------- /AKXposed/src/main/java/de/robv/android/xposed/callbacks/XC_InitPackageResources.java: -------------------------------------------------------------------------------- 1 | package de.robv.android.xposed.callbacks; 2 | 3 | import android.content.res.XResources; 4 | 5 | import de.robv.android.xposed.IXposedHookInitPackageResources; 6 | import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet; 7 | 8 | /** 9 | * This class is only used for internal purposes, except for the {@link InitPackageResourcesParam} 10 | * subclass. 11 | */ 12 | public abstract class XC_InitPackageResources extends XCallback implements IXposedHookInitPackageResources { 13 | /** 14 | * Creates a new callback with default priority. 15 | * @hide 16 | */ 17 | @SuppressWarnings("deprecation") 18 | public XC_InitPackageResources() { 19 | super(); 20 | } 21 | 22 | /** 23 | * Creates a new callback with a specific priority. 24 | * 25 | * @param priority See {@link XCallback#priority}. 26 | * @hide 27 | */ 28 | public XC_InitPackageResources(int priority) { 29 | super(priority); 30 | } 31 | 32 | /** 33 | * Wraps information about the resources being initialized. 34 | */ 35 | public static final class InitPackageResourcesParam extends XCallback.Param { 36 | /** @hide */ 37 | public InitPackageResourcesParam(CopyOnWriteSortedSet callbacks) { 38 | super(callbacks); 39 | } 40 | 41 | /** The name of the package for which resources are being loaded. */ 42 | public String packageName; 43 | 44 | /** 45 | * Reference to the resources that can be used for calls to 46 | * {@link XResources#setReplacement(String, String, String, Object)}. 47 | */ 48 | public XResources res; 49 | } 50 | 51 | /** @hide */ 52 | @Override 53 | protected void call(Param param) throws Throwable { 54 | if (param instanceof InitPackageResourcesParam) 55 | handleInitPackageResources((InitPackageResourcesParam) param); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /AKXposed/src/main/java/de/robv/android/xposed/callbacks/XC_LayoutInflated.java: -------------------------------------------------------------------------------- 1 | package de.robv.android.xposed.callbacks; 2 | 3 | import android.content.res.XResources; 4 | import android.content.res.XResources.ResourceNames; 5 | import android.view.View; 6 | 7 | import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet; 8 | 9 | /** 10 | * Callback for hooking layouts. Such callbacks can be passed to {@link XResources#hookLayout} 11 | * and its variants. 12 | */ 13 | public abstract class XC_LayoutInflated extends XCallback { 14 | /** 15 | * Creates a new callback with default priority. 16 | */ 17 | @SuppressWarnings("deprecation") 18 | public XC_LayoutInflated() { 19 | super(); 20 | } 21 | 22 | /** 23 | * Creates a new callback with a specific priority. 24 | * 25 | * @param priority See {@link XCallback#priority}. 26 | */ 27 | public XC_LayoutInflated(int priority) { 28 | super(priority); 29 | } 30 | 31 | /** 32 | * Wraps information about the inflated layout. 33 | */ 34 | public static final class LayoutInflatedParam extends XCallback.Param { 35 | /** @hide */ 36 | public LayoutInflatedParam(CopyOnWriteSortedSet callbacks) { 37 | super(callbacks); 38 | } 39 | 40 | /** The view that has been created from the layout. */ 41 | public View view; 42 | 43 | /** Container with the ID and name of the underlying resource. */ 44 | public ResourceNames resNames; 45 | 46 | /** Directory from which the layout was actually loaded (e.g. "layout-sw600dp"). */ 47 | public String variant; 48 | 49 | /** Resources containing the layout. */ 50 | public XResources res; 51 | } 52 | 53 | /** @hide */ 54 | @Override 55 | protected void call(Param param) throws Throwable { 56 | if (param instanceof LayoutInflatedParam) 57 | handleLayoutInflated((LayoutInflatedParam) param); 58 | } 59 | 60 | /** 61 | * This method is called when the hooked layout has been inflated. 62 | * 63 | * @param liparam Information about the layout and the inflated view. 64 | * @throws Throwable Everything the callback throws is caught and logged. 65 | */ 66 | public abstract void handleLayoutInflated(LayoutInflatedParam liparam) throws Throwable; 67 | 68 | /** 69 | * An object with which the callback can be removed. 70 | */ 71 | public class Unhook implements IXUnhook { 72 | private final String resDir; 73 | private final int id; 74 | 75 | /** @hide */ 76 | public Unhook(String resDir, int id) { 77 | this.resDir = resDir; 78 | this.id = id; 79 | } 80 | 81 | /** 82 | * Returns the resource ID of the hooked layout. 83 | */ 84 | public int getId() { 85 | return id; 86 | } 87 | 88 | @Override 89 | public XC_LayoutInflated getCallback() { 90 | return XC_LayoutInflated.this; 91 | } 92 | 93 | @Override 94 | public void unhook() { 95 | XResources.unhookLayout(resDir, id, XC_LayoutInflated.this); 96 | } 97 | 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /AKXposed/src/main/java/de/robv/android/xposed/callbacks/XC_LoadPackage.java: -------------------------------------------------------------------------------- 1 | package de.robv.android.xposed.callbacks; 2 | 3 | import android.content.pm.ApplicationInfo; 4 | 5 | import de.robv.android.xposed.IXposedHookLoadPackage; 6 | import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet; 7 | 8 | /** 9 | * This class is only used for internal purposes, except for the {@link LoadPackageParam} 10 | * subclass. 11 | */ 12 | public abstract class XC_LoadPackage extends XCallback implements IXposedHookLoadPackage { 13 | /** 14 | * Creates a new callback with default priority. 15 | * @hide 16 | */ 17 | @SuppressWarnings("deprecation") 18 | public XC_LoadPackage() { 19 | super(); 20 | } 21 | 22 | /** 23 | * Creates a new callback with a specific priority. 24 | * 25 | * @param priority See {@link XCallback#priority}. 26 | * @hide 27 | */ 28 | public XC_LoadPackage(int priority) { 29 | super(priority); 30 | } 31 | 32 | /** 33 | * Wraps information about the app being loaded. 34 | */ 35 | public static final class LoadPackageParam extends XCallback.Param { 36 | /** @hide */ 37 | public LoadPackageParam(CopyOnWriteSortedSet callbacks) { 38 | super(callbacks); 39 | } 40 | 41 | /** The name of the package being loaded. */ 42 | public String packageName; 43 | 44 | /** The process in which the package is executed. */ 45 | public String processName; 46 | 47 | /** The ClassLoader used for this package. */ 48 | public ClassLoader classLoader; 49 | 50 | /** More information about the application being loaded. */ 51 | public ApplicationInfo appInfo; 52 | 53 | /** Set to {@code true} if this is the first (and main) application for this process. */ 54 | public boolean isFirstApplication; 55 | } 56 | 57 | /** @hide */ 58 | @Override 59 | protected void call(Param param) throws Throwable { 60 | if (param instanceof LoadPackageParam) 61 | handleLoadPackage((LoadPackageParam) param); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /AKXposed/src/main/java/de/robv/android/xposed/callbacks/XCallback.java: -------------------------------------------------------------------------------- 1 | package de.robv.android.xposed.callbacks; 2 | 3 | import android.os.Bundle; 4 | 5 | import java.io.Serializable; 6 | 7 | import de.robv.android.xposed.XposedBridge; 8 | import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet; 9 | 10 | /** 11 | * Base class for Xposed callbacks. 12 | * 13 | * This class only keeps a priority for ordering multiple callbacks. 14 | * The actual (abstract) callback methods are added by subclasses. 15 | */ 16 | public abstract class XCallback implements Comparable { 17 | /** 18 | * Callback priority, higher number means earlier execution. 19 | * 20 | *

This is usually set to {@link #PRIORITY_DEFAULT}. However, in case a certain callback should 21 | * be executed earlier or later a value between {@link #PRIORITY_HIGHEST} and {@link #PRIORITY_LOWEST} 22 | * can be set instead. The values are just for orientation though, Xposed doesn't enforce any 23 | * boundaries on the priority values. 24 | */ 25 | public final int priority; 26 | 27 | /** @deprecated This constructor can't be hidden for technical reasons. Nevertheless, don't use it! */ 28 | @Deprecated 29 | public XCallback() { 30 | this.priority = PRIORITY_DEFAULT; 31 | } 32 | 33 | /** @hide */ 34 | public XCallback(int priority) { 35 | this.priority = priority; 36 | } 37 | 38 | /** 39 | * Base class for Xposed callback parameters. 40 | */ 41 | public static abstract class Param { 42 | /** @hide */ 43 | public final Object[] callbacks; 44 | private Bundle extra; 45 | 46 | /** @deprecated This constructor can't be hidden for technical reasons. Nevertheless, don't use it! */ 47 | @Deprecated 48 | protected Param() { 49 | callbacks = null; 50 | } 51 | 52 | /** @hide */ 53 | protected Param(CopyOnWriteSortedSet callbacks) { 54 | this.callbacks = callbacks.getSnapshot(); 55 | } 56 | 57 | /** 58 | * This can be used to store any data for the scope of the callback. 59 | * 60 | *

Use this instead of instance variables, as it has a clear reference to e.g. each 61 | * separate call to a method, even when the same method is called recursively. 62 | * 63 | * @see #setObjectExtra 64 | * @see #getObjectExtra 65 | */ 66 | public synchronized Bundle getExtra() { 67 | if (extra == null) 68 | extra = new Bundle(); 69 | return extra; 70 | } 71 | 72 | /** 73 | * Returns an object stored with {@link #setObjectExtra}. 74 | */ 75 | public Object getObjectExtra(String key) { 76 | Serializable o = getExtra().getSerializable(key); 77 | if (o instanceof SerializeWrapper) 78 | return ((SerializeWrapper) o).object; 79 | return null; 80 | } 81 | 82 | /** 83 | * Stores any object for the scope of the callback. For data types that support it, use 84 | * the {@link Bundle} returned by {@link #getExtra} instead. 85 | */ 86 | public void setObjectExtra(String key, Object o) { 87 | getExtra().putSerializable(key, new SerializeWrapper(o)); 88 | } 89 | 90 | private static class SerializeWrapper implements Serializable { 91 | private static final long serialVersionUID = 1L; 92 | private final Object object; 93 | public SerializeWrapper(Object o) { 94 | object = o; 95 | } 96 | } 97 | } 98 | 99 | /** @hide */ 100 | public static void callAll(Param param) { 101 | if (param.callbacks == null) 102 | throw new IllegalStateException("This object was not created for use with callAll"); 103 | 104 | for (int i = 0; i < param.callbacks.length; i++) { 105 | try { 106 | ((XCallback) param.callbacks[i]).call(param); 107 | } catch (Throwable t) { XposedBridge.log(t); } 108 | } 109 | } 110 | 111 | /** @hide */ 112 | protected void call(Param param) throws Throwable {} 113 | 114 | /** @hide */ 115 | @Override 116 | public int compareTo(XCallback other) { 117 | if (this == other) 118 | return 0; 119 | 120 | // order descending by priority 121 | if (other.priority != this.priority) 122 | return other.priority - this.priority; 123 | // then randomly 124 | else if (System.identityHashCode(this) < System.identityHashCode(other)) 125 | return -1; 126 | else 127 | return 1; 128 | } 129 | 130 | /** The default priority, see {@link #priority}. */ 131 | public static final int PRIORITY_DEFAULT = 50; 132 | 133 | /** Execute this callback late, see {@link #priority}. */ 134 | public static final int PRIORITY_LOWEST = -10000; 135 | 136 | /** Execute this callback early, see {@link #priority}. */ 137 | public static final int PRIORITY_HIGHEST = 10000; 138 | } 139 | -------------------------------------------------------------------------------- /AKXposed/src/main/java/de/robv/android/xposed/services/BaseService.java: -------------------------------------------------------------------------------- 1 | package de.robv.android.xposed.services; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.FileNotFoundException; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | 8 | import de.robv.android.xposed.SELinuxHelper; 9 | 10 | /** 11 | * General definition of a file access service provided by the Xposed framework. 12 | * 13 | *

14 | * References to a concrete subclass should generally be retrieved from 15 | * {@link SELinuxHelper}. 16 | */ 17 | public abstract class BaseService { 18 | /** Flag for {@link #checkFileAccess}: Read access. */ 19 | public static final int R_OK = 4; 20 | /** Flag for {@link #checkFileAccess}: Write access. */ 21 | public static final int W_OK = 2; 22 | /** Flag for {@link #checkFileAccess}: Executable access. */ 23 | public static final int X_OK = 1; 24 | /** Flag for {@link #checkFileAccess}: File/directory exists. */ 25 | public static final int F_OK = 0; 26 | 27 | /** 28 | * Checks whether the services accesses files directly (instead of using 29 | * IPC). 30 | * 31 | * @return {@code true} in case direct access is possible. 32 | */ 33 | public boolean hasDirectFileAccess() { 34 | return false; 35 | } 36 | 37 | /** 38 | * Check whether a file is accessible. SELinux might enforce stricter 39 | * checks. 40 | * 41 | * @param filename 42 | * The absolute path of the file to check. 43 | * @param mode 44 | * The mode for POSIX's {@code access()} function. 45 | * @return The result of the {@code access()} function. 46 | */ 47 | public abstract boolean checkFileAccess(String filename, int mode); 48 | 49 | /** 50 | * Check whether a file exists. 51 | * 52 | * @param filename 53 | * The absolute path of the file to check. 54 | * @return The result of the {@code access()} function. 55 | */ 56 | @SuppressWarnings("BooleanMethodIsAlwaysInverted") 57 | public boolean checkFileExists(String filename) { 58 | return checkFileAccess(filename, F_OK); 59 | } 60 | 61 | /** 62 | * Determine the size and modification time of a file. 63 | * 64 | * @param filename 65 | * The absolute path of the file to check. 66 | * @return A {@link FileResult} object holding the result. 67 | * @throws IOException 68 | * In case an error occurred while retrieving the information. 69 | */ 70 | public abstract FileResult statFile(String filename) throws IOException; 71 | 72 | /** 73 | * Determine the size time of a file. 74 | * 75 | * @param filename 76 | * The absolute path of the file to check. 77 | * @return The file size. 78 | * @throws IOException 79 | * In case an error occurred while retrieving the information. 80 | */ 81 | public long getFileSize(String filename) throws IOException { 82 | return statFile(filename).size; 83 | } 84 | 85 | /** 86 | * Determine the size time of a file. 87 | * 88 | * @param filename 89 | * The absolute path of the file to check. 90 | * @return The file modification time. 91 | * @throws IOException 92 | * In case an error occurred while retrieving the information. 93 | */ 94 | public long getFileModificationTime(String filename) throws IOException { 95 | return statFile(filename).mtime; 96 | } 97 | 98 | /** 99 | * Read a file into memory. 100 | * 101 | * @param filename 102 | * The absolute path of the file to read. 103 | * @return A {@code byte} array with the file content. 104 | * @throws IOException 105 | * In case an error occurred while reading the file. 106 | */ 107 | public abstract byte[] readFile(String filename) throws IOException; 108 | 109 | /** 110 | * Read a file into memory, but only if it has changed since the last time. 111 | * 112 | * @param filename 113 | * The absolute path of the file to read. 114 | * @param previousSize 115 | * File size of last read. 116 | * @param previousTime 117 | * File modification time of last read. 118 | * @return A {@link FileResult} object holding the result. 119 | *

120 | * The {@link FileResult#content} field might be {@code null} if the 121 | * file is unmodified ({@code previousSize} and {@code previousTime} 122 | * are still valid). 123 | * @throws IOException 124 | * In case an error occurred while reading the file. 125 | */ 126 | public abstract FileResult readFile(String filename, long previousSize, 127 | long previousTime) throws IOException; 128 | 129 | /** 130 | * Read a file into memory, optionally only if it has changed since the last 131 | * time. 132 | * 133 | * @param filename 134 | * The absolute path of the file to read. 135 | * @param offset 136 | * Number of bytes to skip at the beginning of the file. 137 | * @param length 138 | * Number of bytes to read (0 means read to end of file). 139 | * @param previousSize 140 | * Optional: File size of last read. 141 | * @param previousTime 142 | * Optional: File modification time of last read. 143 | * @return A {@link FileResult} object holding the result. 144 | *

145 | * The {@link FileResult#content} field might be {@code null} if the 146 | * file is unmodified ({@code previousSize} and {@code previousTime} 147 | * are still valid). 148 | * @throws IOException 149 | * In case an error occurred while reading the file. 150 | */ 151 | public abstract FileResult readFile(String filename, int offset, 152 | int length, long previousSize, long previousTime) 153 | throws IOException; 154 | 155 | /** 156 | * Get a stream to the file content. Depending on the service, it may or may 157 | * not be read completely into memory. 158 | * 159 | * @param filename 160 | * The absolute path of the file to read. 161 | * @return An {@link InputStream} to the file content. 162 | * @throws IOException 163 | * In case an error occurred while reading the file. 164 | */ 165 | public InputStream getFileInputStream(String filename) throws IOException { 166 | return new ByteArrayInputStream(readFile(filename)); 167 | } 168 | 169 | /** 170 | * Get a stream to the file content, but only if it has changed since the 171 | * last time. Depending on the service, it may or may not be read completely 172 | * into memory. 173 | * 174 | * @param filename 175 | * The absolute path of the file to read. 176 | * @param previousSize 177 | * Optional: File size of last read. 178 | * @param previousTime 179 | * Optional: File modification time of last read. 180 | * @return A {@link FileResult} object holding the result. 181 | *

182 | * The {@link FileResult#stream} field might be {@code null} if the 183 | * file is unmodified ({@code previousSize} and {@code previousTime} 184 | * are still valid). 185 | * @throws IOException 186 | * In case an error occurred while reading the file. 187 | */ 188 | public FileResult getFileInputStream(String filename, long previousSize, 189 | long previousTime) throws IOException { 190 | FileResult result = readFile(filename, previousSize, previousTime); 191 | if (result.content == null) 192 | return result; 193 | return new FileResult(new ByteArrayInputStream(result.content), 194 | result.size, result.mtime); 195 | } 196 | 197 | // ---------------------------------------------------------------------------- 198 | /* package */BaseService() { 199 | } 200 | 201 | /* package */static void ensureAbsolutePath(String filename) { 202 | if (!filename.startsWith("/")) { 203 | throw new IllegalArgumentException( 204 | "Only absolute filenames are allowed: " + filename); 205 | } 206 | } 207 | 208 | /* package */static void throwCommonIOException(int errno, String errorMsg, 209 | String filename, String defaultText) throws IOException { 210 | switch (errno) { 211 | case 1: // EPERM 212 | case 13: // EACCES 213 | throw new FileNotFoundException(errorMsg != null ? errorMsg 214 | : "Permission denied: " + filename); 215 | case 2: // ENOENT 216 | throw new FileNotFoundException(errorMsg != null ? errorMsg 217 | : "No such file or directory: " + filename); 218 | case 12: // ENOMEM 219 | throw new OutOfMemoryError(errorMsg); 220 | case 21: // EISDIR 221 | throw new FileNotFoundException(errorMsg != null ? errorMsg 222 | : "Is a directory: " + filename); 223 | default: 224 | throw new IOException(errorMsg != null ? errorMsg : "Error " 225 | + errno + defaultText + filename); 226 | } 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /AKXposed/src/main/java/de/robv/android/xposed/services/BinderService.java: -------------------------------------------------------------------------------- 1 | package de.robv.android.xposed.services; 2 | 3 | import android.os.IBinder; 4 | import android.os.Parcel; 5 | import android.os.RemoteException; 6 | import android.os.ServiceManager; 7 | 8 | import java.io.IOException; 9 | 10 | /** @hide */ 11 | public final class BinderService extends BaseService { 12 | public static final int TARGET_APP = 0; 13 | public static final int TARGET_SYSTEM = 1; 14 | 15 | /** 16 | * Retrieve the binder service running in the specified context. 17 | * @param target Either {@link #TARGET_APP} or {@link #TARGET_SYSTEM}. 18 | * @return A reference to the service. 19 | * @throws IllegalStateException In case the service doesn't exist (should never happen). 20 | */ 21 | public static BinderService getService(int target) { 22 | if (target < 0 || target > sServices.length) { 23 | throw new IllegalArgumentException("Invalid service target " + target); 24 | } 25 | synchronized (sServices) { 26 | if (sServices[target] == null) { 27 | sServices[target] = new BinderService(target); 28 | } 29 | return sServices[target]; 30 | } 31 | } 32 | 33 | @Override 34 | public boolean checkFileAccess(String filename, int mode) { 35 | ensureAbsolutePath(filename); 36 | 37 | Parcel data = Parcel.obtain(); 38 | Parcel reply = Parcel.obtain(); 39 | data.writeInterfaceToken(INTERFACE_TOKEN); 40 | data.writeString(filename); 41 | data.writeInt(mode); 42 | 43 | try { 44 | mRemote.transact(ACCESS_FILE_TRANSACTION, data, reply, 0); 45 | } catch (RemoteException e) { 46 | data.recycle(); 47 | reply.recycle(); 48 | return false; 49 | } 50 | 51 | reply.readException(); 52 | int result = reply.readInt(); 53 | reply.recycle(); 54 | data.recycle(); 55 | return result == 0; 56 | } 57 | 58 | @Override 59 | public FileResult statFile(String filename) throws IOException { 60 | ensureAbsolutePath(filename); 61 | 62 | Parcel data = Parcel.obtain(); 63 | Parcel reply = Parcel.obtain(); 64 | data.writeInterfaceToken(INTERFACE_TOKEN); 65 | data.writeString(filename); 66 | 67 | try { 68 | mRemote.transact(STAT_FILE_TRANSACTION, data, reply, 0); 69 | } catch (RemoteException e) { 70 | data.recycle(); 71 | reply.recycle(); 72 | throw new IOException(e); 73 | } 74 | 75 | reply.readException(); 76 | int errno = reply.readInt(); 77 | if (errno != 0) 78 | throwCommonIOException(errno, null, filename, " while retrieving attributes for "); 79 | 80 | long size = reply.readLong(); 81 | long time = reply.readLong(); 82 | reply.recycle(); 83 | data.recycle(); 84 | return new FileResult(size, time); 85 | } 86 | 87 | @Override 88 | public byte[] readFile(String filename) throws IOException { 89 | return readFile(filename, 0, 0, 0, 0).content; 90 | } 91 | 92 | @Override 93 | public FileResult readFile(String filename, long previousSize, long previousTime) throws IOException { 94 | return readFile(filename, 0, 0, previousSize, previousTime); 95 | } 96 | 97 | @Override 98 | public FileResult readFile(String filename, int offset, int length, 99 | long previousSize, long previousTime) throws IOException { 100 | ensureAbsolutePath(filename); 101 | 102 | Parcel data = Parcel.obtain(); 103 | Parcel reply = Parcel.obtain(); 104 | data.writeInterfaceToken(INTERFACE_TOKEN); 105 | data.writeString(filename); 106 | data.writeInt(offset); 107 | data.writeInt(length); 108 | data.writeLong(previousSize); 109 | data.writeLong(previousTime); 110 | 111 | try { 112 | mRemote.transact(READ_FILE_TRANSACTION, data, reply, 0); 113 | } catch (RemoteException e) { 114 | data.recycle(); 115 | reply.recycle(); 116 | throw new IOException(e); 117 | } 118 | 119 | reply.readException(); 120 | int errno = reply.readInt(); 121 | String errorMsg = reply.readString(); 122 | long size = reply.readLong(); 123 | long time = reply.readLong(); 124 | byte[] content = reply.createByteArray(); 125 | reply.recycle(); 126 | data.recycle(); 127 | 128 | switch (errno) { 129 | case 0: 130 | return new FileResult(content, size, time); 131 | case 22: // EINVAL 132 | if (errorMsg != null) { 133 | IllegalArgumentException iae = new IllegalArgumentException(errorMsg); 134 | if (offset == 0 && length == 0) 135 | throw new IOException(iae); 136 | else 137 | throw iae; 138 | } else { 139 | throw new IllegalArgumentException("Offset " + offset + " / Length " + length 140 | + " is out of range for " + filename + " with size " + size); 141 | } 142 | default: 143 | throwCommonIOException(errno, errorMsg, filename, " while reading "); 144 | throw new IllegalStateException(); // not reached 145 | } 146 | } 147 | 148 | 149 | // ---------------------------------------------------------------------------- 150 | private static final String INTERFACE_TOKEN = "de.robv.android.xposed.IXposedService"; 151 | 152 | private static final int ACCESS_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 2; 153 | private static final int STAT_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 3; 154 | private static final int READ_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 4; 155 | 156 | private static final String[] SERVICE_NAMES = { "user.xposed.app", "user.xposed.system" }; 157 | private static final BinderService[] sServices = new BinderService[2]; 158 | private final IBinder mRemote; 159 | 160 | private BinderService(int target) { 161 | IBinder binder = ServiceManager.getService(SERVICE_NAMES[target]); 162 | if (binder == null) 163 | throw new IllegalStateException("Service " + SERVICE_NAMES[target] + " does not exist"); 164 | this.mRemote = binder; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /AKXposed/src/main/java/de/robv/android/xposed/services/DirectAccessService.java: -------------------------------------------------------------------------------- 1 | package de.robv.android.xposed.services; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | 9 | /** @hide */ 10 | public final class DirectAccessService extends BaseService { 11 | @Override 12 | public boolean hasDirectFileAccess() { 13 | return true; 14 | } 15 | 16 | @SuppressWarnings("RedundantIfStatement") 17 | @Override 18 | public boolean checkFileAccess(String filename, int mode) { 19 | File file = new File(filename); 20 | if (mode == F_OK && !file.exists()) 21 | return false; 22 | if ((mode & R_OK) != 0 && !file.canRead()) 23 | return false; 24 | if ((mode & W_OK) != 0 && !file.canWrite()) 25 | return false; 26 | if ((mode & X_OK) != 0 && !file.canExecute()) 27 | return false; 28 | return true; 29 | } 30 | 31 | @Override 32 | public boolean checkFileExists(String filename) { 33 | return new File(filename).exists(); 34 | } 35 | 36 | @Override 37 | public FileResult statFile(String filename) throws IOException { 38 | File file = new File(filename); 39 | return new FileResult(file.length(), file.lastModified()); 40 | } 41 | 42 | @Override 43 | public byte[] readFile(String filename) throws IOException { 44 | File file = new File(filename); 45 | byte content[] = new byte[(int) file.length()]; 46 | FileInputStream fis = new FileInputStream(file); 47 | fis.read(content); 48 | fis.close(); 49 | return content; 50 | } 51 | 52 | @Override 53 | public FileResult readFile(String filename, long previousSize, 54 | long previousTime) throws IOException { 55 | File file = new File(filename); 56 | long size = file.length(); 57 | long time = file.lastModified(); 58 | if (previousSize == size && previousTime == time) 59 | return new FileResult(size, time); 60 | return new FileResult(readFile(filename), size, time); 61 | } 62 | 63 | @Override 64 | public FileResult readFile(String filename, int offset, int length, 65 | long previousSize, long previousTime) throws IOException { 66 | File file = new File(filename); 67 | long size = file.length(); 68 | long time = file.lastModified(); 69 | if (previousSize == size && previousTime == time) 70 | return new FileResult(size, time); 71 | 72 | // Shortcut for the simple case 73 | if (offset <= 0 && length <= 0) 74 | return new FileResult(readFile(filename), size, time); 75 | 76 | // Check range 77 | if (offset > 0 && offset >= size) { 78 | throw new IllegalArgumentException("Offset " + offset 79 | + " is out of range for " + filename); 80 | } else if (offset < 0) { 81 | offset = 0; 82 | } 83 | 84 | if (length > 0 && (offset + length) > size) { 85 | throw new IllegalArgumentException("Length " + length 86 | + " is out of range for " + filename); 87 | } else if (length <= 0) { 88 | length = (int) (size - offset); 89 | } 90 | 91 | byte content[] = new byte[length]; 92 | FileInputStream fis = new FileInputStream(file); 93 | fis.skip(offset); 94 | fis.read(content); 95 | fis.close(); 96 | return new FileResult(content, size, time); 97 | } 98 | 99 | /** 100 | * {@inheritDoc} 101 | *

102 | * This implementation returns a BufferedInputStream instead of loading the 103 | * file into memory. 104 | */ 105 | @Override 106 | public InputStream getFileInputStream(String filename) throws IOException { 107 | return new BufferedInputStream(new FileInputStream(filename), 16 * 1024); 108 | } 109 | 110 | /** 111 | * {@inheritDoc} 112 | *

113 | * This implementation returns a BufferedInputStream instead of loading the 114 | * file into memory. 115 | */ 116 | @Override 117 | public FileResult getFileInputStream(String filename, long previousSize, 118 | long previousTime) throws IOException { 119 | File file = new File(filename); 120 | long size = file.length(); 121 | long time = file.lastModified(); 122 | if (previousSize == size && previousTime == time) 123 | return new FileResult(size, time); 124 | return new FileResult(new BufferedInputStream(new FileInputStream( 125 | filename), 16 * 1024), size, time); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /AKXposed/src/main/java/de/robv/android/xposed/services/FileResult.java: -------------------------------------------------------------------------------- 1 | package de.robv.android.xposed.services; 2 | 3 | import java.io.InputStream; 4 | 5 | /** 6 | * Holder for the result of a {@link BaseService#readFile} or {@link BaseService#statFile} call. 7 | */ 8 | public final class FileResult { 9 | /** File content, might be {@code null} if the file wasn't read. */ 10 | public final byte[] content; 11 | /** File input stream, might be {@code null} if the file wasn't read. */ 12 | public final InputStream stream; 13 | /** File size. */ 14 | public final long size; 15 | /** File last modification time. */ 16 | public final long mtime; 17 | 18 | /*package*/ FileResult(long size, long mtime) { 19 | this.content = null; 20 | this.stream = null; 21 | this.size = size; 22 | this.mtime = mtime; 23 | } 24 | 25 | /*package*/ FileResult(byte[] content, long size, long mtime) { 26 | this.content = content; 27 | this.stream = null; 28 | this.size = size; 29 | this.mtime = mtime; 30 | } 31 | 32 | /*package*/ FileResult(InputStream stream, long size, long mtime) { 33 | this.content = null; 34 | this.stream = stream; 35 | this.size = size; 36 | this.mtime = mtime; 37 | } 38 | 39 | /** @hide */ 40 | @Override 41 | public String toString() { 42 | StringBuilder sb = new StringBuilder("{"); 43 | if (content != null) { 44 | sb.append("content.length: "); 45 | sb.append(content.length); 46 | sb.append(", "); 47 | } 48 | if (stream != null) { 49 | sb.append("stream: "); 50 | sb.append(stream.toString()); 51 | sb.append(", "); 52 | } 53 | sb.append("size: "); 54 | sb.append(size); 55 | sb.append(", mtime: "); 56 | sb.append(mtime); 57 | sb.append("}"); 58 | return sb.toString(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /AKXposed/src/main/java/de/robv/android/xposed/services/ZygoteService.java: -------------------------------------------------------------------------------- 1 | package de.robv.android.xposed.services; 2 | 3 | import java.io.IOException; 4 | import java.util.Arrays; 5 | 6 | /** @hide */ 7 | @SuppressWarnings("JniMissingFunction") 8 | public final class ZygoteService extends BaseService { 9 | @Override 10 | public native boolean checkFileAccess(String filename, int mode); 11 | 12 | @Override 13 | public native FileResult statFile(String filename) throws IOException; 14 | 15 | @Override 16 | public native byte[] readFile(String filename) throws IOException; 17 | 18 | @Override 19 | // Just for completeness, we don't expect this to be called often in Zygote. 20 | public FileResult readFile(String filename, long previousSize, 21 | long previousTime) throws IOException { 22 | FileResult stat = statFile(filename); 23 | if (previousSize == stat.size && previousTime == stat.mtime) 24 | return stat; 25 | return new FileResult(readFile(filename), stat.size, stat.mtime); 26 | } 27 | 28 | @Override 29 | // Just for completeness, we don't expect this to be called often in Zygote. 30 | public FileResult readFile(String filename, int offset, int length, 31 | long previousSize, long previousTime) throws IOException { 32 | FileResult stat = statFile(filename); 33 | if (previousSize == stat.size && previousTime == stat.mtime) 34 | return stat; 35 | 36 | // Shortcut for the simple case 37 | if (offset <= 0 && length <= 0) 38 | return new FileResult(readFile(filename), stat.size, stat.mtime); 39 | 40 | // Check range 41 | if (offset > 0 && offset >= stat.size) { 42 | throw new IllegalArgumentException("offset " + offset + " >= size " 43 | + stat.size + " for " + filename); 44 | } else if (offset < 0) { 45 | offset = 0; 46 | } 47 | 48 | if (length > 0 && (offset + length) > stat.size) { 49 | throw new IllegalArgumentException("offset " + offset 50 | + " + length " + length + " > size " + stat.size + " for " 51 | + filename); 52 | } else if (length <= 0) { 53 | length = (int) (stat.size - offset); 54 | } 55 | 56 | byte[] content = readFile(filename); 57 | return new FileResult(Arrays.copyOfRange(content, offset, offset 58 | + length), stat.size, stat.mtime); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /AKXposed/src/main/lib/XposedHiddenApi.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rprop/AKFramework/a551b1d71640b24163308afe6b5094c47dbc3ce0/AKXposed/src/main/lib/XposedHiddenApi.jar -------------------------------------------------------------------------------- /AKXposed/src/main/lib/andhook-lib-3.5.8.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rprop/AKFramework/a551b1d71640b24163308afe6b5094c47dbc3ce0/AKXposed/src/main/lib/andhook-lib-3.5.8.jar -------------------------------------------------------------------------------- /AKXposed/src/main/libs/XposedExternal.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rprop/AKFramework/a551b1d71640b24163308afe6b5094c47dbc3ce0/AKXposed/src/main/libs/XposedExternal.jar -------------------------------------------------------------------------------- /Binaries/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 27 5 | buildToolsVersion "27.0.3" 6 | 7 | defaultConfig { 8 | minSdkVersion 16 9 | targetSdkVersion 27 10 | externalNativeBuild { 11 | ndkBuild { 12 | abiFilters "x86" 13 | } 14 | } 15 | } 16 | 17 | externalNativeBuild { 18 | ndkBuild { 19 | path file("src/main/jni/Android.mk") 20 | } 21 | } 22 | 23 | compileOptions { 24 | sourceCompatibility JavaVersion.VERSION_1_8 25 | targetCompatibility JavaVersion.VERSION_1_8 26 | } 27 | } -------------------------------------------------------------------------------- /Binaries/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Binaries/src/main/jni/AK/include/AndHook.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * @author : Rprop (r_prop@outlook.com) 4 | * @date : 2018/05/05 5 | * https://github.com/Rprop/AndHook 6 | * 7 | */ 8 | #pragma once 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define AK_ANDROID_RUNTIME NULL 15 | #define AK_RWX (PROT_READ | PROT_WRITE | PROT_EXEC) 16 | #ifdef __cplusplus 17 | # define AK_BOOL bool 18 | # define AK_DEFAULT(v) = v 19 | extern "C" { 20 | #else 21 | # define AK_BOOL char 22 | # define AK_DEFAULT(v) 23 | #endif 24 | 25 | ///

26 | /// Gets handle to specified module or NULL if the target is not currently loaded 27 | /// 28 | const void *AKGetImageByName(const char *name AK_DEFAULT(AK_ANDROID_RUNTIME)); 29 | /// 30 | /// Gets the address of defined symbols or NULL 31 | /// 32 | void *AKFindSymbol(const void *handle, const char *symbol); 33 | /// 34 | /// Gets the base address of defined symbols or NULL 35 | /// 36 | void *AKGetBaseAddress(const void *handle); 37 | /// 38 | /// Releases internal memory (without affecting the module state) 39 | /// 40 | void AKCloseImage(const void *handle); 41 | /// 42 | /// Intercepts native method 43 | /// 44 | void AKHookFunction(const void *symbol, const void *replace, void **result AK_DEFAULT(NULL)); 45 | /// 46 | /// Intercepts native method and writes trampoline to rwx. 47 | /// @warning `rwx` should be aligned properly and large enough to hold the trampoline 48 | /// 49 | void *AKHookFunctionV(const void *symbol, const void *replace, void *rwx, const uintptr_t size AK_DEFAULT(64)); 50 | /// 51 | /// Intercepts native method if possible and writes trampoline to rwx. 52 | /// @warning `rwx` should be aligned properly and large enough to hold the trampoline 53 | /// 54 | void *AKHookFunctionEx(const void *symbol, const uintptr_t overwritable, const void *replace, 55 | void *rwx, const uintptr_t size AK_DEFAULT(64)); 56 | /// 57 | /// Creates a wrapper function of `func` to preserve the contents of the registers. 58 | /// @warning For x86 and x86_64, this function does nothing and simply returns the original `func` 59 | /// 60 | void *AKWrapFunction(const void *func); 61 | /// 62 | /// Prints the specified region of memory to the log 63 | /// 64 | void AKPrintHexBinary(const void *addr, uintptr_t len, const char *name AK_DEFAULT(NULL)); 65 | /// 66 | /// Sets protection on the specified region of memory. 67 | /// `addr` is NOT necessary to be aligned to a page boundary 68 | /// 69 | AK_BOOL AKProtectMemory(const void *addr, uintptr_t len, int prot AK_DEFAULT(AK_RWX)); 70 | /// 71 | /// Sets protection and patches the specified region of memory 72 | /// 73 | AK_BOOL AKPatchMemory(const void *addr, const void *data, uintptr_t len); 74 | /// 75 | /// Suspends all threads in the current process except the calling one. 76 | /// This function works by spawning a Linux task which then attaches to every 77 | /// thread in the caller process with ptrace. 78 | /// @warning Calling a library function while threads are suspended could cause a 79 | /// deadlock, if one of the treads happens to be holding a libc lock 80 | /// 81 | AK_BOOL AKSuspendAllThreads(); 82 | /// 83 | /// Resumes all threads suspended by AKSuspendAllThreads 84 | /// 85 | void AKResumeAllThreads(); 86 | /// 87 | /// Enables or disables dex fast-loading mechanism [ART only] 88 | /// 89 | void AKEnableFastDexLoad(AK_BOOL enable); 90 | /// 91 | /// Retrieves the last build date 92 | /// 93 | const char *AKLastBuildDate(); 94 | 95 | /// 96 | /// Performs the basic initialization for java-related api. 97 | /// The function ensures that this initialization occurs only once, 98 | /// even when multiple threads may attempt the initialization 99 | /// 100 | jint AKInitializeOnce(JNIEnv *env AK_DEFAULT(NULL), JavaVM *jvm AK_DEFAULT(NULL)); 101 | /// 102 | /// Registers internal native methods with the specified classloader that loads the library 103 | /// 104 | void AKRegisterLibrary(JNIEnv *env, jobject classloader); 105 | /// 106 | /// Intercepts java method using native code 107 | /// 108 | void AKJavaHookMethod(JNIEnv *env, jclass clazz, const char *method, const char *signature, 109 | const void *replace, jmethodID *result AK_DEFAULT(NULL)); 110 | /// 111 | /// Intercepts java method using native code 112 | /// 113 | void AKJavaHookMethodV(JNIEnv *env, jmethodID methodId, const void *replace, jmethodID *result AK_DEFAULT(NULL)); 114 | /// 115 | /// Marks the specified java method as native (if not) and backups original method if `result` != NULL 116 | /// 117 | AK_BOOL AKForceNativeMethod(JNIEnv *env, jmethodID methodId, const void *jni_entrypoint, AK_BOOL fast_native AK_DEFAULT(0), 118 | jmethodID *result AK_DEFAULT(NULL)); 119 | /// 120 | /// Copies the specified java method. 121 | /// @warning The result method's access flags may be changed if the original method is virtual 122 | /// 123 | AK_BOOL AKShadowCopyMethod(JNIEnv *env, jmethodID methodId, jmethodID *result); 124 | /// 125 | /// Restores java method from backup 126 | /// 127 | AK_BOOL AKRestoreMethod(jmethodID j_backup, jmethodID j_dest); 128 | /// 129 | /// Registers the native method and returns the new entry point. 130 | /// The returned entry point might be different from the native_method argument 131 | /// if some MethodCallback modifies it. 132 | /// @warning This function provided is intended only for Android O or later, 133 | /// as there are possible runtime callbacks that we should notify 134 | /// 135 | const void *AKRegisterNative(jmethodID methodId, const void *native_method, AK_BOOL fast_native AK_DEFAULT(0)); 136 | /// 137 | /// Gets the entry of the specified native method or NULL if the method is not native or has not yet been registered 138 | /// 139 | void *AKGetNativeEntry(jmethodID methodId, AK_BOOL *critical_native AK_DEFAULT(NULL)); 140 | /// 141 | /// Creates a DexClassLoader that loads classes from .jar and .apk files containing a classes.dex entry. 142 | /// This function never throws exceptions. 143 | /// @warning parameter `cache_dir` is deprecated and has no effect since Android O 144 | /// 145 | jobject AKLoadFileDex(JNIEnv *env, const char *dex_path, const char *cache_dir AK_DEFAULT(NULL), 146 | const char *lib_path AK_DEFAULT(NULL), jobject parent AK_DEFAULT(NULL)); 147 | /// 148 | /// Creates a PathClassLoader that operates on two given lists of files and directories. 149 | /// This function never throws exceptions 150 | /// 151 | jobject AKLoadPathDex(JNIEnv *env, const char *dex_path, 152 | const char *lib_path AK_DEFAULT(NULL), jobject parent AK_DEFAULT(NULL)); 153 | /// 154 | /// Creates a InMemoryClassLoader that loads classes from a buffer containing a DEX file [ART only, since API 26]. 155 | /// This function never throws exceptions 156 | /// 157 | jobject AKLoadMemoryDex(JNIEnv *env, const void *buffer, jlong capacity, jobject parent AK_DEFAULT(NULL)); 158 | /// 159 | /// Loads a locally-defined class with the specified classloader or the system ClassLoader 160 | /// This function never throws exceptions 161 | /// 162 | jclass AKLoadClass(JNIEnv *env, jobject classloader, const char *name, jthrowable *exception AK_DEFAULT(NULL)); 163 | /// 164 | /// Returns the system ClassLoader which represents the CLASSPATH 165 | /// 166 | jobject AKGetSystemClassLoader(JNIEnv *env); 167 | /// 168 | /// Returns the ClassLoader which loads the specified class 169 | /// 170 | jobject AKGetClassLoader(JNIEnv *env, jclass clazz); 171 | /// 172 | /// Returns the context ClassLoader associated with the specified Context instance 173 | /// 174 | jobject AKGetContextClassLoader(JNIEnv *env, jobject context); 175 | /// 176 | /// Returns the user-visible SDK version of Android framework 177 | /// 178 | int AKGetSdkVersion(); 179 | /// 180 | /// Returns the absolute path to the application specific cache directory with trailing slash 181 | /// 182 | const char *AKGetCacheDirectory(); 183 | /// 184 | /// Sets the absolute path to the cache directory 185 | /// 186 | const char *AKSetCacheDirectory(const char *path); 187 | /// 188 | /// Returns the absolute path to the application specific libs directory with trailing slash 189 | /// 190 | const char *AKGetNativeLibsDirectory(); 191 | /// 192 | /// Sets the specified java method's entrypoint to its OAT code or forces JIT compilation if available [ART only] 193 | /// 194 | void AKOptimizeMethod(jmethodID method); 195 | /// 196 | /// Triggers JIT compilation if available [ART only, since API 24]. 197 | /// @warning Before Android P, the JIT compiler cannot compile native jni method 198 | /// 199 | AK_BOOL AKForceJitCompile(jmethodID method); 200 | /// 201 | /// Forces the specified java method to be executed in the interpreter [ART only] 202 | /// 203 | void AKDeoptimizeMethod(jmethodID method); 204 | /// 205 | /// Dumps all the virtual and direct methods of the specified class 206 | /// 207 | void AKDumpClassMethods(JNIEnv *env, jclass clazz AK_DEFAULT(NULL), const char *clsname AK_DEFAULT(NULL)); 208 | /// 209 | /// Restarts daemons stopped by AKStopJavaDaemons 210 | /// 211 | AK_BOOL AKStartJavaDaemons(JNIEnv *env); 212 | /// 213 | /// Stops daemons so that the zygote can be a single-threaded process 214 | /// 215 | AK_BOOL AKStopJavaDaemons(JNIEnv *env); 216 | /// 217 | /// Ensures all threads running Java suspend and that those not running Java don't start 218 | /// 219 | AK_BOOL AKLockJavaThreads(); 220 | /// 221 | /// Unlocks and broadcasts a notification to all threads interrupted by AKLockJavaThreads 222 | /// 223 | void AKUnlockJavaThreads(); 224 | 225 | /// 226 | /// Table of interface function pointers 227 | /// 228 | struct AKInvokeInterface { 229 | intptr_t version; 230 | const void *(*GetImageByName)(const char *name /* = AK_ANDROID_RUNTIME */); 231 | void *(*FindSymbol)(const void *handle, const char *symbol); 232 | void *(*GetBaseAddress)(const void *handle); 233 | void(*CloseImage)(const void *handle); 234 | void(*HookFunction)(const void *symbol, const void *replace, void **result /* = NULL */); 235 | void *(*HookFunctionV)(const void *symbol, const void *replace, void *rwx, const uintptr_t size /* = 64 */); 236 | void *(*HookFunctionEx)(const void *symbol, const uintptr_t overwritable, const void *replace, void *rwx, const uintptr_t size /* = 64 */); 237 | void *(*WrapFunction)(const void *func); 238 | void(*PrintHexBinary)(const void *addr, uintptr_t len, const char *name /* = NULL */); 239 | AK_BOOL(*ProtectMemory)(const void *addr, uintptr_t len, int prot /* = AK_RWX */); 240 | AK_BOOL(*PatchMemory)(const void *addr, const void *data, uintptr_t len); 241 | AK_BOOL(*SuspendAllThreads)(); 242 | void(*ResumeAllThreads)(); 243 | void(*EnableFastDexLoad)(AK_BOOL enable); 244 | const char *(*LastBuildDate)(); 245 | jint(*InitializeOnce)(JNIEnv *env /* = NULL */, JavaVM *jvm /* = NULL */); 246 | void(*RegisterLibrary)(JNIEnv *env, jobject classloader); 247 | void(*JavaHookMethod)(JNIEnv *env, jclass clazz, const char *method, const char *signature, const void *replace, jmethodID *result /* = NULL */); 248 | void(*JavaHookMethodV)(JNIEnv *env, jmethodID methodId, const void *replace, jmethodID *result /* = NULL */); 249 | AK_BOOL(*ForceNativeMethod)(JNIEnv *env, jmethodID methodId, const void *jni_entrypoint, AK_BOOL fast_native /* = 0 */, jmethodID *result /* = NULL */); 250 | AK_BOOL(*ShadowCopyMethod)(JNIEnv *env, jmethodID methodId, jmethodID *result); 251 | AK_BOOL(*RestoreMethod)(jmethodID j_backup, jmethodID j_dest); 252 | const void *(*RegisterNative)(jmethodID methodId, const void *native_method, AK_BOOL fast_native /* = 0 */); 253 | void *(*GetNativeEntry)(jmethodID methodId, AK_BOOL *critical_native /* = NULL */); 254 | jobject(*LoadFileDex)(JNIEnv *env, const char *dex_path, const char *cache_dir /* = NULL */, const char *lib_path /* = NULL */, jobject parent /* = NULL */); 255 | jobject(*LoadPathDex)(JNIEnv *env, const char *dex_path, const char *lib_path /* = NULL */, jobject parent /* = NULL */); 256 | jobject(*LoadMemoryDex)(JNIEnv *env, const void *buffer, jlong capacity, jobject parent /* = NULL */); 257 | jclass(*LoadClass)(JNIEnv *env, jobject classloader, const char *name, jthrowable *exception); 258 | jobject(*GetSystemClassLoader)(JNIEnv *env); 259 | jobject(*GetClassLoader)(JNIEnv *env, jclass clazz); 260 | jobject(*GetContextClassLoader)(JNIEnv *env, jobject context); 261 | int(*GetSdkVersion)(); 262 | const char *(*GetCacheDirectory)(); 263 | const char *(*SetCacheDirectory)(const char *path); 264 | const char *(*GetNativeLibsDirectory)(); 265 | void(*OptimizeMethod)(jmethodID method); 266 | AK_BOOL(*ForceJitCompile)(jmethodID method); 267 | void(*DeoptimizeMethod)(jmethodID method); 268 | void(*DumpClassMethods)(JNIEnv *env, jclass clazz /* = NULL */, const char *clsname /* = NULL */); 269 | AK_BOOL(*StartJavaDaemons)(JNIEnv *env); 270 | AK_BOOL(*StopJavaDaemons)(JNIEnv *env); 271 | AK_BOOL(*LockJavaThreads)(); 272 | void(*UnlockJavaThreads)(); 273 | }; 274 | /// 275 | /// Retrieves invocation interfaces 276 | /// 277 | struct AKInvokeInterface *AKGetInvokeInterface(); 278 | 279 | #ifdef __cplusplus 280 | } 281 | #endif 282 | -------------------------------------------------------------------------------- /Binaries/src/main/jni/AK/lib/arm64-v8a/libAK.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rprop/AKFramework/a551b1d71640b24163308afe6b5094c47dbc3ce0/Binaries/src/main/jni/AK/lib/arm64-v8a/libAK.so -------------------------------------------------------------------------------- /Binaries/src/main/jni/AK/lib/armeabi-v7a/libAK.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rprop/AKFramework/a551b1d71640b24163308afe6b5094c47dbc3ce0/Binaries/src/main/jni/AK/lib/armeabi-v7a/libAK.so -------------------------------------------------------------------------------- /Binaries/src/main/jni/AK/lib/x86/libAK.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rprop/AKFramework/a551b1d71640b24163308afe6b5094c47dbc3ce0/Binaries/src/main/jni/AK/lib/x86/libAK.so -------------------------------------------------------------------------------- /Binaries/src/main/jni/AK/lib/x86_64/libAK.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rprop/AKFramework/a551b1d71640b24163308afe6b5094c47dbc3ce0/Binaries/src/main/jni/AK/lib/x86_64/libAK.so -------------------------------------------------------------------------------- /Binaries/src/main/jni/AKCopyDependencies.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * @author : Rprop (r_prop@outlook.com) 4 | * https://github.com/Rprop/AKFramework 5 | * 6 | */ 7 | int main(int argc, char *argv[]) 8 | { 9 | return 0; 10 | } -------------------------------------------------------------------------------- /Binaries/src/main/jni/AKGadget.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * @author : Rprop (r_prop@outlook.com) 4 | * https://github.com/Rprop/AKFramework 5 | * 6 | */ 7 | #define AK_TAG "AKFramework" 8 | #define AK_CONSOLE 0 9 | #define AK_MAX_RETRIES 5 10 | #define AK_TIME_LIMIT 160 // seconds 11 | #define AK_FILE_PREFIX ".__ak" 12 | #define AK_TMP_DIR "/data/local/tmp/" 13 | #define AK_PRIVATE_DIR AK_TMP_DIR AK_FILE_PREFIX "/" 14 | #define AK_GUARD_FILE AK_TMP_DIR AK_FILE_PREFIX "_guard" 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "AKPlatform.h" 26 | #include "AKSELinux.h" 27 | #include "AKLog.h" 28 | #include "AKJniHelpers.h" 29 | #include "android_util_Log.h" 30 | #include "android_filesystem_config.h" 31 | 32 | //------------------------------------------------------------------------- 33 | 34 | static AKInvokeInterface *ak_functions; 35 | 36 | //------------------------------------------------------------------------- 37 | 38 | static void disable_inline_optimizing(JavaVMInitArgs *vm_args) 39 | { 40 | // Note: 41 | // -Xcompiler-option, used for runtime compilation(DexClassLoader) 42 | // -Ximage-compiler-option, used for boot-image compilation on device 43 | // 44 | // --inline-max-code-units= 45 | // --inline-depth-limit= Android_M 46 | if (vm_args != NULL && vm_args->version >= JNI_VERSION_1_4) { 47 | vm_args->ignoreUnrecognized = JNI_TRUE; 48 | 49 | static JavaVMOption def_options[128] = {}; 50 | if (vm_args->nOptions < jint(sizeof(def_options) / sizeof(def_options[0]) - 4u)) { 51 | bool has_any = false; 52 | for (jint i = 0; i < vm_args->nOptions; ++i) { 53 | if (strstr(vm_args->options[i].optionString, "--inline-max-code-units=") != NULL) { 54 | vm_args->options[i].optionString = "--inline-max-code-units=0"; 55 | has_any = true; 56 | } //if 57 | } 58 | if (!has_any) { 59 | AKINFO("disabling any inline optimizations..."); 60 | 61 | const jint i = vm_args->nOptions; 62 | memcpy(def_options, vm_args->options, sizeof(def_options[0]) * i); 63 | def_options[i + 0].optionString = "-Xcompiler-option"; 64 | def_options[i + 1].optionString = "--inline-max-code-units=0"; 65 | def_options[i + 2].optionString = "-Ximage-compiler-option"; 66 | def_options[i + 3].optionString = "--inline-max-code-units=0"; 67 | vm_args->nOptions += 4; 68 | vm_args->options = def_options; 69 | } //if 70 | } //if 71 | } //if 72 | } 73 | 74 | //------------------------------------------------------------------------- 75 | 76 | static void register_native_methods(JNIEnv *env, jclass clazz, jobject loader) 77 | { 78 | static void(JNICALL *registerFrameworkNatives)(JNIEnv *, jclass, jobject) = [](JNIEnv *env, jclass, jobject classloader) { 79 | ak_functions->RegisterLibrary(env, classloader); 80 | }; 81 | static void(JNICALL *makeWorldAccessible)(JNIEnv *, jclass, jstring) = [](JNIEnv *env, jclass, jstring path) { 82 | ScopedUtfChars cp(env, path); 83 | if (cp.c_str() == NULL) return; 84 | 85 | AKSELinux::setcon(cp.c_str(), "u:object_r:system_data_file:s0"); 86 | chmod(cp.c_str(), S_IRWXU | S_IRWXG | S_IRWXO); 87 | if (AKPlatform::sdk_version() > 20) { 88 | chown(cp.c_str(), AID_EVERYBODY, AID_EVERYBODY); 89 | } else { 90 | chown(cp.c_str(), AID_SDCARD_R, AID_INET); 91 | } //if 92 | }; 93 | static const JNINativeMethod methods[] = { 94 | { "makeWorldAccessible", "(Ljava/lang/String;)V", reinterpret_cast(makeWorldAccessible) }, // MUST be first 95 | { "registerFrameworkNatives", "(Ljava/lang/ClassLoader;)V", reinterpret_cast(registerFrameworkNatives) }, 96 | }; 97 | if (env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0])) < 0 || env->ExceptionCheck()) { 98 | env->ExceptionDescribe(); 99 | env->ExceptionClear(); 100 | } //if 101 | 102 | clazz = ak_functions->LoadClass(env, loader, "de/robv/android/xposed/XposedInit", NULL); 103 | if (clazz != NULL) { 104 | if (env->RegisterNatives(clazz, methods, 1) < 0 || env->ExceptionCheck()) { 105 | env->ExceptionDescribe(); 106 | env->ExceptionClear(); 107 | } //if 108 | } //if 109 | } 110 | 111 | //------------------------------------------------------------------------- 112 | 113 | static jmethodID find_method_nothrow(JNIEnv *env, jclass clazz, const char *name, const char *sig) 114 | { 115 | jmethodID method = env->GetStaticMethodID(clazz, name, sig); 116 | if (env->ExceptionCheck()) { 117 | env->ExceptionDescribe(); 118 | env->ExceptionClear(); 119 | 120 | method = env->GetMethodID(clazz, name, sig); 121 | if (env->ExceptionCheck()) { 122 | env->ExceptionDescribe(); 123 | env->ExceptionClear(); 124 | return NULL; 125 | } //if 126 | } //if 127 | 128 | return method; 129 | } 130 | 131 | //------------------------------------------------------------------------- 132 | 133 | static void register_fork_events(JavaVM *vm, JNIEnv *env, jclass clazz) 134 | { 135 | static JavaVM *s_vm; 136 | static jclass s_clazz; 137 | static jmethodID s_callbacks[3]; 138 | 139 | s_vm = vm; 140 | s_clazz = static_cast(env->NewGlobalRef(clazz)); 141 | s_callbacks[0] = find_method_nothrow(env, clazz, "preFork", "()V"); 142 | s_callbacks[1] = find_method_nothrow(env, clazz, "postForkParent", "()V"); 143 | s_callbacks[2] = find_method_nothrow(env, clazz, "postForkChild", "()V"); 144 | 145 | static auto invoke_callback = [](intptr_t k) { 146 | JNIEnv *env; 147 | if (s_callbacks[k] != NULL && 148 | s_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) == JNI_OK) { 149 | env->CallStaticVoidMethod(s_clazz, s_callbacks[k]); 150 | if (env->ExceptionCheck()) { 151 | env->ExceptionDescribe(); 152 | env->ExceptionClear(); 153 | } //if 154 | } //if 155 | }; 156 | 157 | pthread_atfork([]() { // prepare 158 | invoke_callback(0); 159 | }, []() { // parent 160 | invoke_callback(1); 161 | }, []() { // child 162 | invoke_callback(2); 163 | }); 164 | } 165 | 166 | //------------------------------------------------------------------------- 167 | 168 | static jint(JNICALL *JNI_CreateJavaVM_System)(JavaVM **p_vm, JNIEnv **p_env, JavaVMInitArgs *vm_args); 169 | extern "C" jint JNICALL JNI_CreateJavaVM_Impl(JavaVM **p_vm, JNIEnv **p_env, JavaVMInitArgs *vm_args) 170 | { 171 | AKINFO("p_vm = %p, p_env = %p, vm_args = %p", p_vm, p_env, vm_args); 172 | 173 | disable_inline_optimizing(vm_args); 174 | jint r = JNI_CreateJavaVM_System(p_vm, p_env, vm_args); 175 | 176 | AKINFO("JavaVM created, vm = %p, env = %p, ret = %d", *p_vm, *p_env, r); 177 | 178 | while (r >= JNI_OK) { 179 | mkdir(AK_PRIVATE_DIR, S_IRWXU); 180 | ak_functions->SetCacheDirectory(AK_PRIVATE_DIR); 181 | if (ak_functions->InitializeOnce(*p_env, *p_vm) != JNI_VERSION_1_6) 182 | break; 183 | 184 | AKINFO("cache_dir = %s", ak_functions->GetCacheDirectory()); 185 | AKINFO("lib_dir = %s", ak_functions->GetNativeLibsDirectory()); 186 | 187 | ScopedLocalFrame frame(*p_env); 188 | 189 | jobject loader = ak_functions->LoadFileDex(*p_env, AK_TMP_DIR "AKCore.apk", NULL, NULL, NULL); 190 | if (loader == NULL) break; 191 | 192 | jclass clazz = ak_functions->LoadClass(*p_env, loader, "ak/core/Main", NULL); 193 | if (clazz == NULL) break; 194 | 195 | jmethodID method = find_method_nothrow(*p_env, clazz, "onVmCreated", "()V"); 196 | if (method == NULL) break; 197 | 198 | ak_functions->RegisterLibrary(*p_env, loader); 199 | register_native_methods(*p_env, clazz, loader); 200 | register_android_util_Log(*p_env); 201 | 202 | (*p_env)->CallStaticVoidMethod(clazz, method); 203 | if ((*p_env)->ExceptionCheck()) { 204 | (*p_env)->ExceptionDescribe(); 205 | (*p_env)->ExceptionClear(); 206 | } //if 207 | 208 | register_fork_events(*p_vm, *p_env, clazz); 209 | break; 210 | } //if 211 | 212 | return r; 213 | } 214 | 215 | //------------------------------------------------------------------------- 216 | 217 | static bool should_disable_framewrok() 218 | { 219 | // If we are booting without the real /data, don't load framework. 220 | char vold_decrypt[PROP_VALUE_MAX] = {}; 221 | if ((__system_property_get("vold.decrypt", vold_decrypt) > 0) && 222 | (strcmp(vold_decrypt, "trigger_restart_min_framework") == 0 || strcmp(vold_decrypt, "1") == 0)) { 223 | AKINFO("deferring framework loading until after vold decrypt"); 224 | return true; 225 | } //if 226 | 227 | mkdir(AK_TMP_DIR, S_IRWXU | S_IRWXG | S_IRWXO); 228 | 229 | int fd = TEMP_FAILURE_RETRY(open(AK_GUARD_FILE, 230 | O_RDWR | O_CREAT | O_CLOEXEC | O_SYNC, 231 | S_IRWXU | S_IRWXG | S_IRWXO)); 232 | if (fd == -1) { 233 | AKERR("error opening %s, errno = %d", AK_GUARD_FILE, errno); 234 | return true; 235 | } //if 236 | 237 | flock(fd, LOCK_EX); 238 | 239 | bool should_disable = false; 240 | 241 | int64_t ts[2] = {}; 242 | TEMP_FAILURE_RETRY(read(fd, ts, sizeof(ts))); 243 | 244 | if (static_cast(time(NULL)) - ts[0] <= AK_TIME_LIMIT) { 245 | should_disable = (++ts[1] >= AK_MAX_RETRIES); 246 | AKINFO("last = %" PRId64 ", retry = %" PRId64, ts[0], ts[1]); 247 | } else { 248 | ts[0] = time(NULL); 249 | ts[1] = 0; 250 | } //if 251 | 252 | lseek(fd, 0, SEEK_SET); 253 | TEMP_FAILURE_RETRY(write(fd, ts, sizeof(ts))); 254 | 255 | // fdatasync(fd); 256 | flock(fd, LOCK_UN); 257 | close(fd); 258 | 259 | return should_disable; 260 | } 261 | 262 | //------------------------------------------------------------------------- 263 | 264 | class static_initializer 265 | { 266 | public: 267 | static_initializer() { 268 | AKINFO("-->Starting framework..."); 269 | 270 | if (should_disable_framewrok()) { 271 | AKWARN("framework is temporarily disabled, skipping..."); 272 | return; 273 | } //if 274 | 275 | void *h = dlopen(__AK_LIB__, RTLD_GLOBAL | RTLD_NOW); 276 | if (h == NULL) { 277 | AKERR("dlopen %s failed, error = %s!", __AK_LIB__, dlerror()); 278 | return; 279 | } //if 280 | 281 | void *q = dlsym(h, __STRING(AKGetInvokeInterface)); 282 | if (q == NULL) { 283 | AKERR("dlsym %s failed, error = %s!", __STRING(AKGetInvokeInterface), dlerror()); 284 | return; 285 | } //if 286 | 287 | ak_functions = reinterpret_cast<__typeof__(&AKGetInvokeInterface)>(q)(); 288 | if (ak_functions == NULL) { 289 | AKERR("ak_functions = %p!", ak_functions); 290 | return; 291 | } //if 292 | 293 | AKINFO("ak_functions = %p, version = %" PRIdPTR, ak_functions, ak_functions->version); 294 | if (ak_functions->version != __AK_VER__) { 295 | AKERR("ak_functions version mismatched, current = %d!", __AK_VER__); 296 | return; 297 | } //if 298 | 299 | void *p = dlsym(RTLD_DEFAULT, __STRING(JNI_CreateJavaVM)); 300 | if (p == NULL) { 301 | AKERR("dlsym %s failed, error = %s!", __STRING(JNI_CreateJavaVM), dlerror()); 302 | return; 303 | } //if 304 | 305 | AKINFO("JNI_CreateJavaVM = %p", p); 306 | ak_functions->HookFunction(p, 307 | reinterpret_cast(&JNI_CreateJavaVM_Impl), 308 | reinterpret_cast(&JNI_CreateJavaVM_System)); 309 | 310 | AKINFO("-->Framework started."); 311 | } 312 | }; 313 | static static_initializer __s; 314 | // extern "C" void __attribute__((constructor)) __init(void) {} -------------------------------------------------------------------------------- /Binaries/src/main/jni/AKInstaller.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * @author : Rprop (r_prop@outlook.com) 4 | * https://github.com/Rprop/AKFramework 5 | * 6 | */ 7 | #define AK_TAG "AKInstaller" 8 | #include 9 | #include "AKInterceptor.h" 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | if (argc == 2) { 14 | if (strcmp(argv[1], "install") == 0) { 15 | int rt = AKInterceptor::preverify(); 16 | if (rt == 0) 17 | rt = AKInterceptor::install(); 18 | return rt; 19 | } else if (strcmp(argv[1], "uninstall") == 0) { 20 | int rt = AKInterceptor::preverify(); 21 | if (rt == 0) 22 | rt = AKInterceptor::uninstall(); 23 | return rt; 24 | } else if (strcmp(argv[1], "reinstall") == 0) { 25 | int rt = AKInterceptor::preverify(); 26 | if (rt == 0) { 27 | AKInterceptor::uninstall(); 28 | rt = AKInterceptor::install(); 29 | } //if 30 | return rt; 31 | } else if (strcmp(argv[1], "preverify") == 0) { 32 | return AKInterceptor::preverify(); 33 | } //if 34 | } //if 35 | 36 | AKINFO("AKFramework installer v1.0.%d\n\nUsage: %s [install|uninstall|reinstall|preverify]\n", 37 | __AK_VER__, argv[0]); 38 | 39 | return 0; 40 | } -------------------------------------------------------------------------------- /Binaries/src/main/jni/AKInterceptor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * @author : Rprop (r_prop@outlook.com) 4 | * https://github.com/Rprop/AKFramework 5 | * 6 | */ 7 | #pragma once 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "AKPlatform.h" 22 | #include "AKSELinux.h" 23 | #include "AKLog.h" 24 | 25 | #define __BIN_DIR__ "/system/bin/" 26 | #ifdef __LP64__ 27 | # define __LIB_DIR__ "/system/lib64/" 28 | # define __TARGET__ __BIN_DIR__ "app_process64" 29 | # define __ELFCLASS__ ELFCLASS64 30 | #else 31 | # define __LIB_DIR__ "/system/lib/" 32 | # define __TARGET__ __BIN_DIR__ "app_process32" 33 | # define __ELFCLASS__ ELFCLASS32 34 | #endif // __LP64__ 35 | #define __TMP_SUFFIX__ "_tmp" 36 | #define __BAK_SUFFIX__ "_bak" 37 | #define __TARGET_TMP__ __TARGET__ __TMP_SUFFIX__ 38 | #define __TARGET_BAK__ __TARGET__ __BAK_SUFFIX__ 39 | #define __GADGET_PATH__ __LIB_DIR__ __GADGET__ 40 | #if !defined(__GADGET__) || !defined(__AK_LIB__) 41 | # error __GADGET__ __AK_LIB__ 42 | # define __GADGET__ "android_runtime.so" 43 | # define __AK_LIB__ "libAK.so" 44 | #else 45 | static_assert(__GADGET__[0] == 'a' && __GADGET__[1] == 'n', __GADGET__); 46 | #endif //__GADGET__ 47 | 48 | /* 49 | * Static injection based on modifying the ELF format 50 | */ 51 | class AKInterceptor 52 | { 53 | private: 54 | #ifdef LIEF_ELF_SUPPORT 55 | static int inject_elf(LIEF::ELF::Binary *binary_) { 56 | // @TODO 57 | binary_->add_library(__GADGET__); 58 | binary_->write(__TARGET_TMP__); 59 | 60 | return 0; 61 | } 62 | #else 63 | static int inject_elf(bool inst = true) { 64 | static char buffer[256U * 1024U] = {}; // 256kb 65 | 66 | int fd = TEMP_FAILURE_RETRY(open(__TARGET__, O_RDONLY, NULL)); 67 | int sz = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); 68 | close(fd); 69 | 70 | auto ehdr = reinterpret_cast(buffer); 71 | auto phdr = reinterpret_cast(buffer + ehdr->e_phoff); 72 | if (ehdr->e_ident[EI_CLASS] != __ELFCLASS__ || 73 | ehdr->e_ident[EI_DATA] != ELFDATA2LSB) { 74 | AKERR("unexpected elf format! target = %s, size = %d", __TARGET__, sz); 75 | return ELIBBAD; 76 | } //if 77 | 78 | int pn = 0; 79 | while (phdr->p_type != PT_DYNAMIC) { 80 | ++phdr; 81 | if (++pn >= ehdr->e_phnum) { 82 | AKERR("failed to locate PT_DYNAMIC segment! target = %s, size = %d", __TARGET__, sz); 83 | return ELIBBAD; 84 | } //if 85 | } 86 | 87 | auto dyn = reinterpret_cast(buffer + phdr->p_offset); 88 | do { 89 | if (dyn->d_tag == DT_STRTAB) { 90 | auto strtab = buffer + dyn->d_un.d_ptr; 91 | auto liboff = UINTPTR_MAX; 92 | auto dbgdyn = dyn - 1; 93 | auto gaddyn = dyn - 1; 94 | dyn = reinterpret_cast(buffer + phdr->p_offset); 95 | do { 96 | switch (dyn->d_tag) { 97 | case DT_NEEDED: { 98 | auto so_name = strtab + dyn->d_un.d_val; 99 | if (strcmp(so_name, __GADGET__) == 0) { 100 | if (inst) { 101 | AKERR("framework is already installed!"); 102 | return EEXIST; 103 | } //if 104 | gaddyn = dyn; 105 | } else if (strcmp(so_name + 3U, __GADGET__) == 0) { 106 | liboff = dyn->d_un.d_val + 3U; 107 | } //if 108 | } 109 | break; 110 | case DT_DEBUG: 111 | dbgdyn = dyn; 112 | break; 113 | } 114 | } while ((++dyn)->d_tag != DT_NULL); 115 | 116 | // modify ELF in place 117 | if (inst) { 118 | if (liboff == UINTPTR_MAX || dbgdyn == dyn - 1) { 119 | AKERR("no proper injection point found! gadget = %s", __GADGET__); 120 | return ENOTSUP; 121 | } //if 122 | 123 | dbgdyn->d_tag = DT_NEEDED; 124 | dbgdyn->d_un.d_val = liboff; 125 | } else { 126 | if (gaddyn == dyn - 1) { 127 | AKERR("framework was not installed!"); 128 | return EINVAL; 129 | } //if 130 | 131 | gaddyn->d_tag = DT_DEBUG; 132 | gaddyn->d_un.d_val = 0u; 133 | } //if 134 | 135 | dyn = NULL; 136 | break; 137 | } //if 138 | } while ((++dyn)->d_tag != DT_NULL); 139 | if (dyn != NULL) { 140 | AKERR("failed to locate DT_STRTAB entry! target = %s, size = %d", __TARGET__, sz); 141 | return ENOTSUP; 142 | } //if 143 | 144 | fd = TEMP_FAILURE_RETRY(open(__TARGET_TMP__, 145 | O_CREAT | O_CLOEXEC | O_WRONLY | O_TRUNC, 146 | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)); 147 | if (fd == -1) { 148 | AKERR("error writing %s! errno = %d", __TARGET_TMP__, errno); 149 | return errno; 150 | } //if 151 | 152 | TEMP_FAILURE_RETRY(write(fd, buffer, sz)); 153 | close(fd); 154 | 155 | return 0; 156 | } 157 | #endif // LIEF_ELF_SUPPORT 158 | 159 | private: 160 | static int elf_check(const char *const path) { 161 | char command[PATH_MAX]; 162 | strcpy(command, path); 163 | strcat(command, AKPlatform::sdk_version() > 20 ? " -showversion" : " -help / /"); 164 | 165 | int rt = system(command); 166 | return WIFEXITED(rt) ? WEXITSTATUS(rt) : rt; 167 | } 168 | static int copy_file(const char *src, const char *dst) { 169 | int sd = TEMP_FAILURE_RETRY(open(src, O_RDONLY | O_CLOEXEC, 0)); 170 | if (sd == -1) { 171 | AKERR("error opening %s! errno = %d", src, errno); 172 | return errno; 173 | } //if 174 | 175 | // shall fail if the file exists 176 | int dd = TEMP_FAILURE_RETRY(open(dst, O_CREAT | O_CLOEXEC | O_WRONLY | O_EXCL, 177 | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)); 178 | if (dd == -1) { 179 | AKERR("error creating %s! errno = %d", dst, errno); 180 | return errno; 181 | } //if 182 | 183 | char buffer[1024 * 1024]; 184 | int r; 185 | while ((r = TEMP_FAILURE_RETRY(read(sd, buffer, sizeof(buffer)))) > 0) { 186 | r = TEMP_FAILURE_RETRY(write(dd, buffer, r)); 187 | if (r < 0) break; 188 | } 189 | if (r < 0) { 190 | r = errno; 191 | AKERR("error reading %s or writing %s! errno = %d", src, dst, r); 192 | } else { 193 | r = 0; 194 | } //if 195 | 196 | close(dd); 197 | close(sd); 198 | return r; 199 | } 200 | 201 | public: 202 | static int preverify() { 203 | AKINFO("-->Verifying environment and performing prerequisite checks..."); 204 | #ifndef __LP64__ 205 | if (AKPlatform::sdk_version() <= 20) { 206 | // that's ok as if newpath exists it will not be overwritten 207 | symlink(__BIN_DIR__ "app_process", __TARGET__); 208 | } //if 209 | #endif // __LP64__ 210 | 211 | if (getuid() != 0) { 212 | AKERR("failed to detect root privileges! uid = %u", getuid()); 213 | return EACCES; 214 | } //if 215 | 216 | // test file system 217 | int fd, rt = 0; 218 | do { 219 | fd = TEMP_FAILURE_RETRY(open(__TARGET_TMP__, 220 | O_CREAT | O_CLOEXEC | O_WRONLY | O_TRUNC, 221 | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)); 222 | if (fd == -1) { 223 | if (rt != 0) { 224 | AKERR("error writing %s: read-only filesystem? errno = %d", __TARGET_TMP__, errno); 225 | return EROFS; 226 | } //if 227 | 228 | if (errno != EROFS) { 229 | AKERR("unexpected errno %d, should be %d", errno, EROFS); 230 | } //if 231 | 232 | // mount("/dev/block/sda6", "/system", "ext4", O_RDWR | MS_REMOUNT, "") 233 | rt = system("mount -o rw,remount /system"); 234 | if (rt != -1) rt = WEXITSTATUS(rt); 235 | if (rt != 0) { 236 | AKERR("failed to remount /system! exit = %d, errno = %d", rt, errno); 237 | } //if 238 | 239 | // try again 240 | rt = -1; 241 | } else { 242 | close(fd); 243 | unlink(__TARGET_TMP__); 244 | break; 245 | } //if 246 | } while (true); 247 | assert(fd != -1); 248 | 249 | AKINFO("environment is successfully preverified!"); 250 | return 0; 251 | } 252 | static int install() { 253 | AKINFO("-->Installing and configuring framework..."); 254 | 255 | typedef struct stat stat_t; 256 | stat_t statbuf; 257 | if (stat(__TARGET__, &statbuf) == -1) { 258 | AKERR("stat %s failed! errno = %d", __TARGET__, errno); 259 | return errno; 260 | } //if 261 | 262 | int rt = elf_check(__TARGET__); 263 | if (rt != 0) { 264 | AKERR("failed to execute %s! exit = %d, errno = %d", __TARGET__, rt, errno); 265 | return ENOTSUP; 266 | } //if 267 | 268 | #ifdef LIEF_ELF_SUPPORT 269 | LIEF::ELF::Binary *binary_ = NULL; 270 | try { 271 | binary_ = LIEF::ELF::Parser::parse(__TARGET__); 272 | if (binary_->has_library(__GADGET__)) { 273 | AKERR("framework is already installed!"); 274 | rt = EEXIST; 275 | } else { 276 | rt = inject_elf(binary_); 277 | } //if 278 | } catch (const LIEF::exception &e) { 279 | AKERR("error: %s", e.what()); 280 | rt = ELIBBAD; 281 | } 282 | if (binary_ != NULL) binary_->release(); 283 | #else 284 | rt = inject_elf(); 285 | #endif // LIEF_ELF_SUPPORT 286 | if (rt != 0) return rt; 287 | 288 | // setting proper ownership 289 | if (chown(__TARGET_TMP__, statbuf.st_uid, statbuf.st_gid) == -1) { 290 | AKERR("chown %s failed! errno = %d", __TARGET_TMP__, errno); 291 | return errno; 292 | } //if 293 | 294 | // setting proper permissions 295 | if (chmod(__TARGET_TMP__, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1) { 296 | AKERR("chmod %s failed! errno = %d", __TARGET_TMP__, errno); 297 | return errno; 298 | } //if 299 | 300 | // removing old gadget and its dependencies 301 | unlink(__GADGET_PATH__); 302 | unlink(__LIB_DIR__ __AK_LIB__); 303 | 304 | // pre check 305 | rt = elf_check(__TARGET_TMP__); 306 | if (rt == 0) { // should be something like `library __GADGET__ not found` 307 | AKERR("%s may be corrupted! exit = %d, errno = %d", __TARGET_TMP__, rt, errno); 308 | return ELIBBAD; 309 | } //if 310 | 311 | // copying new files 312 | chdir("/data/local/tmp"); 313 | if ((rt = copy_file(__GADGET__, __GADGET_PATH__)) != 0 || 314 | (rt = copy_file(__AK_LIB__, __LIB_DIR__ __AK_LIB__)) != 0) { 315 | char pat[1234]; 316 | AKINFO("%s", getcwd(pat, 1234)); 317 | return rt; 318 | } //if 319 | 320 | // post check 321 | rt = elf_check(__TARGET_TMP__); 322 | if (rt != 0) { 323 | AKERR("%s was corrupted! exit = %d, errno = %d", __TARGET_TMP__, rt, errno); 324 | return ELIBBAD; 325 | } //if 326 | 327 | // expand all symbolic links 328 | char __REAL_TARGET__[PATH_MAX] = __TARGET__; 329 | if (realpath(__TARGET__, __REAL_TARGET__) == NULL) { 330 | AKERR("realpath %s! errno = %d", __TARGET__, errno); 331 | return errno; 332 | } //if 333 | 334 | // backup 335 | unlink(__TARGET_BAK__); 336 | if (link(__REAL_TARGET__, __TARGET_BAK__) == -1) { 337 | AKERR("link %s to %s failed! errno = %d", __REAL_TARGET__, __TARGET_BAK__, errno); 338 | return errno; 339 | } //if 340 | 341 | // critical operations 342 | unlink(__REAL_TARGET__); 343 | if (rename(__TARGET_TMP__, __REAL_TARGET__) == -1) { 344 | // recovery 345 | if (rename(__TARGET_BAK__, __REAL_TARGET__) == -1) { 346 | link(__TARGET_BAK__, __REAL_TARGET__); 347 | } //if 348 | 349 | AKERR("rename %s to %s failed! errno = %d", __TARGET_TMP__, __REAL_TARGET__, errno); 350 | return errno; 351 | } //if 352 | 353 | // selinux 354 | AKSELinux::setcon(__REAL_TARGET__, "u:object_r:zygote_exec:s0"); 355 | 356 | AKINFO("framework installed successfully!"); 357 | return 0; 358 | } 359 | static int uninstall() { 360 | AKINFO("-->Preparing for removal of framework..."); 361 | 362 | int rt = elf_check(__TARGET_BAK__); 363 | if (rt != 0) { 364 | AKERR("failed to execute %s! exit = %d, errno = %d", __TARGET_BAK__, rt, errno); 365 | return ENOTSUP; 366 | } //if 367 | 368 | // expand all symbolic links 369 | char __REAL_TARGET__[PATH_MAX] = __TARGET__; 370 | if (realpath(__TARGET__, __REAL_TARGET__) == NULL) { 371 | AKERR("realpath %s! errno = %d", __TARGET__, errno); 372 | return errno; 373 | } //if 374 | 375 | // critical operations 376 | if (rename(__TARGET_BAK__, __REAL_TARGET__) != -1) { 377 | unlink(__GADGET_PATH__); 378 | unlink(__LIB_DIR__ __AK_LIB__); 379 | } else { 380 | AKERR("rename %s to %s failed! errno = %d", __TARGET_BAK__, __REAL_TARGET__, errno); 381 | return errno; 382 | } //if 383 | 384 | // selinux 385 | AKSELinux::setcon(__REAL_TARGET__, "u:object_r:zygote_exec:s0"); 386 | 387 | AKINFO("framework has been successfully uninstalled!"); 388 | return 0; 389 | } 390 | }; -------------------------------------------------------------------------------- /Binaries/src/main/jni/AKJniHelpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #if !defined(DISALLOW_COPY_AND_ASSIGN) 5 | // DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. 6 | // It goes in the private: declarations in a class. 7 | #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ 8 | TypeName(const TypeName&) = delete; \ 9 | void operator=(const TypeName&) = delete 10 | #endif // !defined(DISALLOW_COPY_AND_ASSIGN) 11 | 12 | class ScopedLocalFrame 13 | { 14 | public: 15 | // Number of local references in the indirect reference table. The value is arbitrary but 16 | // low enough that it forces sanity checks. 17 | static constexpr size_t kLocalsInitial = 512u; 18 | 19 | public: 20 | ScopedLocalFrame(JNIEnv *env, jint capacity = kLocalsInitial / 2u) : mEnv(env) { 21 | env->PushLocalFrame(capacity); 22 | } 23 | 24 | ~ScopedLocalFrame() { 25 | this->Pop(NULL); 26 | } 27 | 28 | jobject Pop(jobject java_survivor) { 29 | if (this->mEnv != NULL) { 30 | java_survivor = this->mEnv->PopLocalFrame(java_survivor); 31 | this->mEnv = NULL; 32 | return java_survivor; 33 | } //if 34 | 35 | return NULL; 36 | } 37 | 38 | private: 39 | JNIEnv *mEnv; 40 | DISALLOW_COPY_AND_ASSIGN(ScopedLocalFrame); 41 | }; 42 | 43 | template 44 | class ScopedLocalRef 45 | { 46 | public: 47 | ScopedLocalRef(JNIEnv *env, T localRef) : mEnv(env), mLocalRef(localRef) { 48 | this->ensureNoThrow(); 49 | } 50 | 51 | ScopedLocalRef(JNIEnv *env, const void *localRef) : ScopedLocalRef(env, static_cast(const_cast(localRef))) {} 52 | 53 | ~ScopedLocalRef() { 54 | this->reset(); 55 | } 56 | 57 | void ensureNoThrow() { 58 | if (this->mEnv->ExceptionCheck()) { 59 | this->mEnv->ExceptionDescribe(); 60 | this->mEnv->ExceptionClear(); 61 | this->mLocalRef = NULL; 62 | } //if 63 | } 64 | 65 | void reset(T ptr = nullptr) { 66 | if (ptr != this->mLocalRef) { 67 | if (this->mLocalRef != nullptr) { 68 | this->mEnv->DeleteLocalRef(this->mLocalRef); 69 | } //if 70 | this->mLocalRef = ptr; 71 | this->ensureNoThrow(); 72 | } //if 73 | } 74 | 75 | T release() __attribute__((warn_unused_result)) { 76 | T localRef = this->mLocalRef; 77 | this->mLocalRef = NULL; 78 | return localRef; 79 | } 80 | 81 | T get() const { 82 | return this->mLocalRef; 83 | } 84 | 85 | operator T() const { 86 | return this->mLocalRef; 87 | } 88 | 89 | private: 90 | JNIEnv *const mEnv; 91 | T mLocalRef; 92 | DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef); 93 | }; 94 | 95 | class ScopedUtfChars 96 | { 97 | public: 98 | ScopedUtfChars(JNIEnv *env, jstring js) : mEnv(env), mString(js) { 99 | if (js == NULL) { 100 | this->mUtfChars = NULL; 101 | } else { 102 | this->mUtfChars = env->GetStringUTFChars(this->mString, NULL); 103 | } //if 104 | } 105 | 106 | ScopedUtfChars(ScopedUtfChars &&rhs) : mEnv(rhs.mEnv), mString(rhs.mString), mUtfChars(rhs.mUtfChars) { 107 | rhs.mEnv = NULL; 108 | rhs.mString = NULL; 109 | rhs.mUtfChars = NULL; 110 | } 111 | 112 | ~ScopedUtfChars() { 113 | if (this->mUtfChars != NULL) { 114 | this->mEnv->ReleaseStringUTFChars(this->mString, this->mUtfChars); 115 | } //if 116 | } 117 | 118 | ScopedUtfChars &operator = (ScopedUtfChars&& rhs) { 119 | if (this != &rhs) { 120 | // Delete the currently owned UTF chars. 121 | this->~ScopedUtfChars(); 122 | 123 | // Move the rhs ScopedUtfChars and zero it out. 124 | this->mEnv = rhs.mEnv; 125 | this->mString = rhs.mString; 126 | this->mUtfChars = rhs.mUtfChars; 127 | rhs.mEnv = NULL; 128 | rhs.mString = NULL; 129 | rhs.mUtfChars = NULL; 130 | } //if 131 | return *this; 132 | } 133 | 134 | const char *c_str() const { 135 | return this->mUtfChars; 136 | } 137 | 138 | size_t c_size() const { 139 | return strlen(this->mUtfChars); 140 | } 141 | 142 | jsize size() const { 143 | return this->mEnv->GetStringUTFLength(this->mString); 144 | } 145 | 146 | operator const char *() const { 147 | return this->mUtfChars; 148 | } 149 | 150 | const char &operator [] (const size_t n) const { 151 | return this->mUtfChars[n]; 152 | } 153 | 154 | private: 155 | JNIEnv *mEnv; 156 | jstring mString; 157 | const char *mUtfChars; 158 | DISALLOW_COPY_AND_ASSIGN(ScopedUtfChars); 159 | }; -------------------------------------------------------------------------------- /Binaries/src/main/jni/AKLog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * @author : Rprop (r_prop@outlook.com) 4 | * https://github.com/Rprop/AKFramework 5 | * 6 | */ 7 | #pragma once 8 | #include 9 | #include 10 | 11 | #ifndef AK_TAG 12 | # define AK_TAG __FUNCTION__ 13 | #endif // AK_TAG 14 | 15 | #ifndef AK_CONSOLE 16 | # define AK_CONSOLE 1 17 | #endif // AK_CONSOLE 18 | 19 | #if AK_CONSOLE 20 | # define AKINFO(...) AKLog::__printf(ANDROID_LOG_INFO, AK_TAG, __VA_ARGS__) 21 | # define AKWARN(...) AKLog::__printf(ANDROID_LOG_WARN, AK_TAG, __VA_ARGS__) 22 | # define AKERR(...) AKLog::__printf(ANDROID_LOG_ERROR, AK_TAG, __VA_ARGS__) 23 | class AKLog 24 | { 25 | public: 26 | static int __printf(const int _Priority, char const *const _Tag, char const *const _Format, ...) { 27 | va_list _Args; 28 | va_start(_Args, _Format); 29 | 30 | char _Buffer[8192]; 31 | int _Ret = vsprintf(_Buffer, _Format, _Args); 32 | 33 | __android_log_write(_Priority, _Tag, _Buffer); 34 | fprintf(_Priority >= ANDROID_LOG_ERROR ? stderr : stdout, 35 | "%s\n", _Buffer); 36 | 37 | va_end(_Args); 38 | return _Ret; 39 | } 40 | }; 41 | #else 42 | # define AKINFO(...) __android_log_print(ANDROID_LOG_INFO, AK_TAG, __VA_ARGS__) 43 | # define AKWARN(...) __android_log_print(ANDROID_LOG_WARN, AK_TAG, __VA_ARGS__) 44 | # define AKERR(...) __android_log_print(ANDROID_LOG_ERROR, AK_TAG, __VA_ARGS__) 45 | #endif // AK_CONSOLE 46 | 47 | -------------------------------------------------------------------------------- /Binaries/src/main/jni/AKPlatform.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * @author : Rprop (r_prop@outlook.com) 4 | * https://github.com/Rprop/AKFramework 5 | * 6 | */ 7 | #pragma once 8 | #include 9 | #include 10 | 11 | class AKPlatform 12 | { 13 | public: 14 | static int sdk_version() { 15 | static int SDK_INT = -1; 16 | if (SDK_INT <= 0) { 17 | char s[PROP_VALUE_MAX]; 18 | __system_property_get("ro.build.version.sdk", s); 19 | SDK_INT = atoi(s); 20 | } //if 21 | 22 | return SDK_INT; 23 | } 24 | }; -------------------------------------------------------------------------------- /Binaries/src/main/jni/AKSELinux.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * @author : Rprop (r_prop@outlook.com) 4 | * https://github.com/Rprop/AKFramework 5 | * 6 | */ 7 | #pragma once 8 | #include 9 | #include 10 | #include 11 | #include 12 | #ifndef XATTR_NAME_SELINUX 13 | # define XATTR_NAME_SELINUX "security.selinux" 14 | #endif // !XATTR_NAME_SELINUX 15 | #define INITCONTEXTLEN 255 16 | 17 | class AKSELinux 18 | { 19 | public: 20 | static int getcon(const char *const path, char context[INITCONTEXTLEN + 1]) { 21 | return getxattr(path, XATTR_NAME_SELINUX, context, INITCONTEXTLEN); 22 | } 23 | static int setcon(const char *const path, const char *const context) { 24 | int rt = setxattr(path, XATTR_NAME_SELINUX, context, strlen(context) + 1, 0); 25 | if (rt == -1) { 26 | char command[PATH_MAX]; 27 | snprintf(command, sizeof(command), "chcon %s %s", context, path); 28 | system(command); 29 | } //if 30 | return rt; 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /Binaries/src/main/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | AK_GADGET := android_runtime 3 | AK_VERSION := 20180505 4 | 5 | include $(CLEAR_VARS) 6 | LOCAL_MODULE := ak_installer 7 | LOCAL_SRC_FILES := AKInstaller.cpp 8 | LOCAL_C_INCLUDES := $(LOCAL_PATH) 9 | LOCAL_C_INCLUDES += $(LOCAL_PATH)/AK/include 10 | LOCAL_CFLAGS := -D__GADGET__="""$(AK_GADGET)$(TARGET_SONAME_EXTENSION)""" 11 | LOCAL_CFLAGS += -D__AK_LIB__="""libAK$(TARGET_SONAME_EXTENSION)""" -D__AK_VER__=$(AK_VERSION) 12 | LOCAL_LDLIBS := -llog 13 | include $(BUILD_EXECUTABLE) 14 | 15 | include $(CLEAR_VARS) 16 | LOCAL_MODULE := $(AK_GADGET) 17 | LOCAL_SRC_FILES := AKGadget.cpp 18 | LOCAL_C_INCLUDES := $(LOCAL_PATH) 19 | LOCAL_C_INCLUDES += $(LOCAL_PATH)/AK/include 20 | LOCAL_CFLAGS := -D__AK_LIB__="""libAK$(TARGET_SONAME_EXTENSION)""" -D__AK_VER__=$(AK_VERSION) 21 | LOCAL_LDFLAGS := -Wl,-soname,$(AK_GADGET).""so 22 | LOCAL_MODULE_FILENAME := $(AK_GADGET) 23 | LOCAL_LDLIBS := -llog 24 | include $(BUILD_SHARED_LIBRARY) 25 | 26 | include $(CLEAR_VARS) 27 | LOCAL_MODULE := copy_dependencies 28 | LOCAL_SRC_FILES := AKCopyDependencies.cpp 29 | LOCAL_SHARED_LIBRARIES := AK 30 | include $(BUILD_EXECUTABLE) 31 | 32 | include $(CLEAR_VARS) 33 | LOCAL_MODULE := AK 34 | LOCAL_SRC_FILES := $(LOCAL_PATH)/AK/lib/$(TARGET_ARCH_ABI)/lib$(LOCAL_MODULE)$(TARGET_SONAME_EXTENSION) 35 | include $(PREBUILT_SHARED_LIBRARY) -------------------------------------------------------------------------------- /Binaries/src/main/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := x86 2 | APP_STL := system 3 | APP_CPPFLAGS := -std=c++14 4 | APP_THIN_ARCHIVE := true -------------------------------------------------------------------------------- /Binaries/src/main/jni/android_filesystem_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /* This file is used to define the properties of the filesystem 18 | ** images generated by build tools (mkbootfs and mkyaffs2image) and 19 | ** by the device side of adb. 20 | */ 21 | 22 | /* 23 | * This file is consumed by build/tools/fs_config and is used 24 | * for generating various files. Anything #define AID_ 25 | * becomes the mapping for getpwnam/getpwuid, etc. The 26 | * field is lowercased. 27 | * For example: 28 | * #define AID_FOO_BAR 6666 becomes a friendly name of "foo_bar" 29 | * 30 | * The above holds true with the exception of: 31 | * mediacodec 32 | * mediaex 33 | * mediadrm 34 | * Whose friendly names do not match the #define statements. 35 | * 36 | * Additionally, AID_OEM_RESERVED_START and AID_OEM_RESERVED_END 37 | * can be used to define reserved OEM ranges used for sanity checks 38 | * during the build process. The rules are, they must end with START/END 39 | * The proper convention is incrementing a number like so: 40 | * AID_OEM_RESERVED_START 41 | * AID_OEM_RESERVED_1_START 42 | * AID_OEM_RESERVED_2_START 43 | * ... 44 | * The same applies to the END. 45 | * They are not required to be in order, but must not overlap each other and 46 | * must define a START and END'ing range. START must be smaller than END. 47 | */ 48 | 49 | #ifndef _ANDROID_FILESYSTEM_CONFIG_H_ 50 | #define _ANDROID_FILESYSTEM_CONFIG_H_ 51 | 52 | #include 53 | #include 54 | #include 55 | 56 | #define CAP_MASK_LONG(cap_name) (1ULL << (cap_name)) 57 | 58 | /* This is the master Users and Groups config for the platform. 59 | * DO NOT EVER RENUMBER 60 | */ 61 | 62 | #define AID_ROOT 0 /* traditional unix root user */ 63 | 64 | #define AID_SYSTEM 1000 /* system server */ 65 | 66 | #define AID_RADIO 1001 /* telephony subsystem, RIL */ 67 | #define AID_BLUETOOTH 1002 /* bluetooth subsystem */ 68 | #define AID_GRAPHICS 1003 /* graphics devices */ 69 | #define AID_INPUT 1004 /* input devices */ 70 | #define AID_AUDIO 1005 /* audio devices */ 71 | #define AID_CAMERA 1006 /* camera devices */ 72 | #define AID_LOG 1007 /* log devices */ 73 | #define AID_COMPASS 1008 /* compass device */ 74 | #define AID_MOUNT 1009 /* mountd socket */ 75 | #define AID_WIFI 1010 /* wifi subsystem */ 76 | #define AID_ADB 1011 /* android debug bridge (adbd) */ 77 | #define AID_INSTALL 1012 /* group for installing packages */ 78 | #define AID_MEDIA 1013 /* mediaserver process */ 79 | #define AID_DHCP 1014 /* dhcp client */ 80 | #define AID_SDCARD_RW 1015 /* external storage write access */ 81 | #define AID_VPN 1016 /* vpn system */ 82 | #define AID_KEYSTORE 1017 /* keystore subsystem */ 83 | #define AID_USB 1018 /* USB devices */ 84 | #define AID_DRM 1019 /* DRM server */ 85 | #define AID_MDNSR 1020 /* MulticastDNSResponder (service discovery) */ 86 | #define AID_GPS 1021 /* GPS daemon */ 87 | #define AID_UNUSED1 1022 /* deprecated, DO NOT USE */ 88 | #define AID_MEDIA_RW 1023 /* internal media storage write access */ 89 | #define AID_MTP 1024 /* MTP USB driver access */ 90 | #define AID_UNUSED2 1025 /* deprecated, DO NOT USE */ 91 | #define AID_DRMRPC 1026 /* group for drm rpc */ 92 | #define AID_NFC 1027 /* nfc subsystem */ 93 | #define AID_SDCARD_R 1028 /* external storage read access */ 94 | #define AID_CLAT 1029 /* clat part of nat464 */ 95 | #define AID_LOOP_RADIO 1030 /* loop radio devices */ 96 | #define AID_MEDIA_DRM 1031 /* MediaDrm plugins */ 97 | #define AID_PACKAGE_INFO 1032 /* access to installed package details */ 98 | #define AID_SDCARD_PICS 1033 /* external storage photos access */ 99 | #define AID_SDCARD_AV 1034 /* external storage audio/video access */ 100 | #define AID_SDCARD_ALL 1035 /* access all users external storage */ 101 | #define AID_LOGD 1036 /* log daemon */ 102 | #define AID_SHARED_RELRO 1037 /* creator of shared GNU RELRO files */ 103 | #define AID_DBUS 1038 /* dbus-daemon IPC broker process */ 104 | #define AID_TLSDATE 1039 /* tlsdate unprivileged user */ 105 | #define AID_MEDIA_EX 1040 /* mediaextractor process */ 106 | #define AID_AUDIOSERVER 1041 /* audioserver process */ 107 | #define AID_METRICS_COLL 1042 /* metrics_collector process */ 108 | #define AID_METRICSD 1043 /* metricsd process */ 109 | #define AID_WEBSERV 1044 /* webservd process */ 110 | #define AID_DEBUGGERD 1045 /* debuggerd unprivileged user */ 111 | #define AID_MEDIA_CODEC 1046 /* mediacodec process */ 112 | #define AID_CAMERASERVER 1047 /* cameraserver process */ 113 | #define AID_FIREWALL 1048 /* firewalld process */ 114 | #define AID_TRUNKS 1049 /* trunksd process (TPM daemon) */ 115 | #define AID_NVRAM 1050 /* Access-controlled NVRAM */ 116 | #define AID_DNS 1051 /* DNS resolution daemon (system: netd) */ 117 | #define AID_DNS_TETHER 1052 /* DNS resolution daemon (tether: dnsmasq) */ 118 | #define AID_WEBVIEW_ZYGOTE 1053 /* WebView zygote process */ 119 | #define AID_VEHICLE_NETWORK 1054 /* Vehicle network service */ 120 | #define AID_MEDIA_AUDIO 1055 /* GID for audio files on internal media storage */ 121 | #define AID_MEDIA_VIDEO 1056 /* GID for video files on internal media storage */ 122 | #define AID_MEDIA_IMAGE 1057 /* GID for image files on internal media storage */ 123 | #define AID_TOMBSTONED 1058 /* tombstoned user */ 124 | #define AID_MEDIA_OBB 1059 /* GID for OBB files on internal media storage */ 125 | #define AID_ESE 1060 /* embedded secure element (eSE) subsystem */ 126 | #define AID_OTA_UPDATE 1061 /* resource tracking UID for OTA updates */ 127 | /* Changes to this file must be made in AOSP, *not* in internal branches. */ 128 | 129 | #define AID_SHELL 2000 /* adb and debug shell user */ 130 | #define AID_CACHE 2001 /* cache access */ 131 | #define AID_DIAG 2002 /* access to diagnostic resources */ 132 | 133 | /* The range 2900-2999 is reserved for OEM, and must never be 134 | * used here */ 135 | #define AID_OEM_RESERVED_START 2900 136 | #define AID_OEM_RESERVED_END 2999 137 | 138 | /* The 3000 series are intended for use as supplemental group id's only. 139 | * They indicate special Android capabilities that the kernel is aware of. */ 140 | #define AID_NET_BT_ADMIN 3001 /* bluetooth: create any socket */ 141 | #define AID_NET_BT 3002 /* bluetooth: create sco, rfcomm or l2cap sockets */ 142 | #define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */ 143 | #define AID_NET_RAW 3004 /* can create raw INET sockets */ 144 | #define AID_NET_ADMIN 3005 /* can configure interfaces and routing tables. */ 145 | #define AID_NET_BW_STATS 3006 /* read bandwidth statistics */ 146 | #define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */ 147 | #define AID_READPROC 3009 /* Allow /proc read access */ 148 | #define AID_WAKELOCK 3010 /* Allow system wakelock read/write access */ 149 | 150 | /* The range 5000-5999 is also reserved for OEM, and must never be used here. */ 151 | #define AID_OEM_RESERVED_2_START 5000 152 | #define AID_OEM_RESERVED_2_END 5999 153 | 154 | #define AID_EVERYBODY 9997 /* shared between all apps in the same profile */ 155 | #define AID_MISC 9998 /* access to misc storage */ 156 | #define AID_NOBODY 9999 157 | 158 | #define AID_APP 10000 /* TODO: switch users over to AID_APP_START */ 159 | #define AID_APP_START 10000 /* first app user */ 160 | #define AID_APP_END 19999 /* last app user */ 161 | 162 | #define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */ 163 | #define AID_CACHE_GID_END 29999 /* end of gids for apps to mark cached data */ 164 | 165 | #define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */ 166 | #define AID_EXT_GID_END 39999 /* end of gids for apps to mark external data */ 167 | 168 | #define AID_EXT_CACHE_GID_START 40000 /* start of gids for apps to mark external cached data */ 169 | #define AID_EXT_CACHE_GID_END 49999 /* end of gids for apps to mark external cached data */ 170 | 171 | #define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */ 172 | #define AID_SHARED_GID_END 59999 /* end of gids for apps in each user to share */ 173 | 174 | #define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */ 175 | #define AID_ISOLATED_END 99999 /* end of uids for fully isolated sandboxed processes */ 176 | 177 | #define AID_USER 100000 /* TODO: switch users over to AID_USER_OFFSET */ 178 | #define AID_USER_OFFSET 100000 /* offset for uid ranges for each user */ 179 | 180 | /* 181 | * android_ids has moved to pwd/grp functionality. 182 | * If you need to add one, the structure is now 183 | * auto-generated based on the AID_ constraints 184 | * documented at the top of this header file. 185 | * Also see build/tools/fs_config for more details. 186 | */ 187 | 188 | #endif 189 | -------------------------------------------------------------------------------- /Binaries/src/main/jni/android_util_Log.h: -------------------------------------------------------------------------------- 1 | /* //device/libs/android_runtime/android_util_Log.cpp 2 | ** 3 | ** Copyright 2006, The Android Open Source Project 4 | ** 5 | ** Licensed under the Apache License, Version 2.0 (the "License"); 6 | ** you may not use this file except in compliance with the License. 7 | ** You may obtain a copy of the License at 8 | ** 9 | ** http://www.apache.org/licenses/LICENSE-2.0 10 | ** 11 | ** Unless required by applicable law or agreed to in writing, software 12 | ** distributed under the License is distributed on an "AS IS" BASIS, 13 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | ** See the License for the specific language governing permissions and 15 | ** limitations under the License. 16 | */ 17 | #pragma once 18 | #include 19 | #define LOGGER_ENTRY_MAX_PAYLOAD 4068 20 | 21 | enum { 22 | LOG_ID_MIN = 0, 23 | 24 | LOG_ID_MAIN = 0, 25 | LOG_ID_RADIO = 1, 26 | LOG_ID_EVENTS = 2, 27 | LOG_ID_SYSTEM = 3, 28 | LOG_ID_CRASH = 4, 29 | 30 | LOG_ID_MAX 31 | }; 32 | 33 | static jboolean isLoggable(const char* tag, jint level) { 34 | return JNI_TRUE; 35 | } 36 | 37 | static jboolean android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring tag, jint level) { 38 | if (tag == NULL) { 39 | return false; 40 | } 41 | 42 | const char* chars = env->GetStringUTFChars(tag, NULL); 43 | if (!chars) { 44 | return false; 45 | } 46 | 47 | jboolean result = isLoggable(chars, level); 48 | 49 | env->ReleaseStringUTFChars(tag, chars); 50 | return result; 51 | } 52 | 53 | /* 54 | * In class android.util.Log: 55 | * public static native int println_native(int buffer, int priority, String tag, String msg) 56 | */ 57 | static jint android_util_Log_println_native(JNIEnv* env, jobject clazz, 58 | jint bufID, jint priority, jstring tagObj, jstring msgObj) { 59 | const char* tag = NULL; 60 | const char* msg = NULL; 61 | 62 | if (msgObj == NULL) { 63 | // println needs a message 64 | return -1; 65 | } 66 | 67 | if (bufID < 0 || bufID >= LOG_ID_MAX) { 68 | // bad bufID 69 | return -1; 70 | } 71 | 72 | if (tagObj != NULL) 73 | tag = env->GetStringUTFChars(tagObj, NULL); 74 | msg = env->GetStringUTFChars(msgObj, NULL); 75 | 76 | int res = __android_log_write(/*bufID, */(android_LogPriority)priority, tag, msg); 77 | 78 | if (tag != NULL) 79 | env->ReleaseStringUTFChars(tagObj, tag); 80 | env->ReleaseStringUTFChars(msgObj, msg); 81 | 82 | return res; 83 | } 84 | 85 | /* 86 | * In class android.util.Log: 87 | * private static native int logger_entry_max_payload_native() 88 | */ 89 | static jint android_util_Log_logger_entry_max_payload_native(JNIEnv *, jobject) 90 | { 91 | return static_cast(LOGGER_ENTRY_MAX_PAYLOAD); 92 | } 93 | 94 | /* 95 | * JNI registration. 96 | */ 97 | static const JNINativeMethod gMethods[] = { 98 | /* name, signature, funcPtr */ 99 | { "isLoggable", "(Ljava/lang/String;I)Z", (void*) android_util_Log_isLoggable }, 100 | { "println_native", "(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println_native } 101 | }; 102 | static const JNINativeMethod gMethods_N[] = { 103 | /* name, signature, funcPtr */ 104 | { "logger_entry_max_payload_native", "()I", (void*) android_util_Log_logger_entry_max_payload_native } 105 | }; 106 | static void register_android_util_Log(JNIEnv *env) 107 | { 108 | jclass clazz = env->FindClass("android/util/Log"); 109 | if (env->RegisterNatives(clazz, gMethods, 2) >= 0) { 110 | env->RegisterNatives(clazz, gMethods_N, 1); 111 | } //if 112 | 113 | env->ExceptionClear(); 114 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 RLib 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Warning 2 | ***Install framework at your own risk. It may damage your device and avoid the warranty.*** 3 | *If the system failed to boot within 160 seconds, the framework will disable itself.* 4 | 5 | # Usage 6 | adb push AKCore.apk /data/local/tmp/ 7 | adb push libAK.so /data/local/tmp/ 8 | adb push android_runtime.so /data/local/tmp/ 9 | adb push ak_installer /data/local/tmp/ 10 | adb root 11 | adb shell chmod 555 /data/local/tmp/ak_installer 12 | 13 | # Preverify 14 | ./ak_installer preverify 15 | 16 | # Install 17 | ./ak_installer install 18 | 19 | # Uninstall 20 | ./ak_installer uninstall 21 | 22 | # Reinstall 23 | ./ak_installer reinstall 24 | 25 | # Screenshots 26 | After successful installation (***zygote restart required***), xposed installer and most of the xposed modules should be working properly. 27 | ![Xposed](https://github.com/Rprop/AKFramework/raw/master/screenshot.png) 28 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | repositories { 4 | jcenter() 5 | google() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.1.1' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | jcenter() 15 | google() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rprop/AKFramework/a551b1d71640b24163308afe6b5094c47dbc3ce0/screenshot.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':Binaries', ':AKCore', ':AKXposed' 2 | --------------------------------------------------------------------------------