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 extends XCallback> 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 | 
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 |
--------------------------------------------------------------------------------