-
Notifications
You must be signed in to change notification settings - Fork 57
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
206 additions
and
134 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,15 @@ | ||
-keep class io.github.lsposed.disableflagsecure.DisableFlagSecure | ||
-adaptresourcefilecontents META-INF/xposed/java_init.list | ||
-keepattributes RuntimeVisibleAnnotations | ||
|
||
-keep,allowobfuscation,allowoptimization public class * extends io.github.libxposed.api.XposedModule { | ||
public <init>(...); | ||
public void onPackageLoaded(...); | ||
public void onSystemServerLoaded(...); | ||
} | ||
-keep,allowoptimization,allowobfuscation @io.github.libxposed.api.annotations.* class * { | ||
@io.github.libxposed.api.annotations.BeforeInvocation <methods>; | ||
@io.github.libxposed.api.annotations.AfterInvocation <methods>; | ||
} | ||
|
||
-repackageclasses | ||
-allowaccessmodification | ||
-overloadaggressively | ||
-allowaccessmodification |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
284 changes: 176 additions & 108 deletions
284
app/src/main/java/io/github/lsposed/disableflagsecure/DisableFlagSecure.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,144 +1,212 @@ | ||
package io.github.lsposed.disableflagsecure; | ||
|
||
import android.annotation.SuppressLint; | ||
import android.annotation.TargetApi; | ||
import android.app.Activity; | ||
import android.os.Build; | ||
import android.util.Log; | ||
import android.widget.Toast; | ||
|
||
import java.lang.reflect.InvocationTargetException; | ||
import java.lang.reflect.Member; | ||
import androidx.annotation.NonNull; | ||
|
||
import java.lang.reflect.Method; | ||
import java.util.Arrays; | ||
import java.util.function.BiConsumer; | ||
import java.util.function.BiPredicate; | ||
|
||
import de.robv.android.xposed.IXposedHookLoadPackage; | ||
import de.robv.android.xposed.XC_MethodHook; | ||
import de.robv.android.xposed.XC_MethodReplacement; | ||
import de.robv.android.xposed.XposedBridge; | ||
import de.robv.android.xposed.XposedHelpers; | ||
import de.robv.android.xposed.callbacks.XC_LoadPackage; | ||
import io.github.libxposed.api.XposedInterface; | ||
import io.github.libxposed.api.XposedModule; | ||
import io.github.libxposed.api.annotations.BeforeInvocation; | ||
import io.github.libxposed.api.annotations.XposedHooker; | ||
|
||
@SuppressLint({"PrivateApi", "BlockedPrivateApi"}) | ||
public class DisableFlagSecure extends XposedModule { | ||
|
||
public class DisableFlagSecure implements IXposedHookLoadPackage { | ||
private final static Method deoptimizeMethod; | ||
public DisableFlagSecure(XposedInterface base, ModuleLoadedParam param) { | ||
super(base, param); | ||
} | ||
|
||
@Override | ||
public void onSystemServerLoaded(@NonNull SystemServerLoadedParam param) { | ||
var classLoader = param.getClassLoader(); | ||
|
||
static { | ||
Method m = null; | ||
try { | ||
//noinspection JavaReflectionMemberAccess | ||
m = XposedBridge.class.getDeclaredMethod("deoptimizeMethod", Member.class); | ||
deoptimizeSystemServer(classLoader); | ||
} catch (Throwable t) { | ||
XposedBridge.log(t); | ||
log("deoptimize system server failed", t); | ||
} | ||
try { | ||
hookWindowState(classLoader); | ||
} catch (Throwable t) { | ||
log("hook WindowState failed", t); | ||
} | ||
deoptimizeMethod = m; | ||
} | ||
|
||
static void deoptimizeMethod(Class<?> c, String n) throws InvocationTargetException, IllegalAccessException { | ||
for (Method m : c.getDeclaredMethods()) { | ||
if (deoptimizeMethod != null && m.getName().equals(n)) { | ||
deoptimizeMethod.invoke(null, m); | ||
Log.d("DisableFlagSecure", "Deoptimized " + m); | ||
} | ||
try { | ||
hookHyperOS(classLoader); | ||
} catch (ClassNotFoundException ignored) { | ||
} catch (Throwable t) { | ||
log("hook HyperOS failed", t); | ||
} | ||
} | ||
|
||
@Override | ||
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) { | ||
if (loadPackageParam.packageName.equals("android")) { | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { | ||
try { | ||
Class<?> windowsState = XposedHelpers.findClass("com.android.server.wm.WindowState", loadPackageParam.classLoader); | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { | ||
XposedHelpers.findAndHookMethod( | ||
windowsState, | ||
"isSecureLocked", | ||
XC_MethodReplacement.returnConstant(false)); | ||
} else { | ||
XposedHelpers.findAndHookMethod( | ||
"com.android.server.wm.WindowManagerService", | ||
loadPackageParam.classLoader, | ||
"isSecureLocked", | ||
windowsState, | ||
XC_MethodReplacement.returnConstant(false)); | ||
} | ||
hookActivityTaskManagerService(classLoader); | ||
} catch (Throwable t) { | ||
XposedBridge.log(t); | ||
log("hook ActivityTaskManagerService failed", t); | ||
} | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { | ||
} | ||
} | ||
|
||
@SuppressLint("PrivateApi") | ||
@Override | ||
public void onPackageLoaded(@NonNull PackageLoadedParam param) { | ||
if (!param.isFirstPackage()) return; | ||
|
||
var classLoader = param.getClassLoader(); | ||
switch (param.getPackageName()) { | ||
case "com.flyme.systemuiex": | ||
try { | ||
XposedHelpers.findAndHookMethod( | ||
"com.android.server.wm.ActivityTaskManagerService", | ||
loadPackageParam.classLoader, | ||
"registerScreenCaptureObserver", | ||
"android.os.IBinder", | ||
"android.app.IScreenCaptureObserver", | ||
XC_MethodReplacement.DO_NOTHING); | ||
hookFlyme(classLoader); | ||
} catch (Throwable t) { | ||
XposedBridge.log(t); | ||
} | ||
} | ||
try { | ||
deoptimizeMethod(XposedHelpers.findClass("com.android.server.wm.WindowStateAnimator", loadPackageParam.classLoader), "createSurfaceLocked"); | ||
var c = XposedHelpers.findClass("com.android.server.display.DisplayManagerService", loadPackageParam.classLoader); | ||
deoptimizeMethod(c, "setUserPreferredModeForDisplayLocked"); | ||
deoptimizeMethod(c, "setUserPreferredDisplayModeInternal"); | ||
c = XposedHelpers.findClass("com.android.server.wm.InsetsPolicy$InsetsPolicyAnimationControlListener", loadPackageParam.classLoader); | ||
for (var m : c.getDeclaredConstructors()) { | ||
deoptimizeMethod.invoke(null, m); | ||
log("hook Flyme failed", t); | ||
} | ||
c = XposedHelpers.findClass("com.android.server.wm.InsetsPolicy", loadPackageParam.classLoader); | ||
deoptimizeMethod(c, "startAnimation"); | ||
deoptimizeMethod(c, "controlAnimationUnchecked"); | ||
for (int i = 0; i < 20; i++) { | ||
c = XposedHelpers.findClassIfExists("com.android.server.wm.DisplayContent$$ExternalSyntheticLambda" + i, loadPackageParam.classLoader); | ||
if (c != null && BiPredicate.class.isAssignableFrom(c)) { | ||
deoptimizeMethod(c, "test"); | ||
} | ||
break; | ||
case "com.oplus.screenshot": | ||
try { | ||
hookOplus(classLoader); | ||
} catch (Throwable t) { | ||
log("hook OPlus failed", t); | ||
} | ||
c = XposedHelpers.findClass("com.android.server.wm.WindowManagerService", loadPackageParam.classLoader); | ||
deoptimizeMethod(c, "relayoutWindow"); | ||
for (int i = 0; i < 20; i++) { | ||
c = XposedHelpers.findClassIfExists("com.android.server.wm.RootWindowContainer$$ExternalSyntheticLambda" + i, loadPackageParam.classLoader); | ||
if (c != null && BiConsumer.class.isAssignableFrom(c)) { | ||
deoptimizeMethod(c, "accept"); | ||
} | ||
break; | ||
default: | ||
try { | ||
hookOnResume(); | ||
} catch (Throwable ignored) { | ||
} | ||
} catch (Throwable t) { | ||
XposedBridge.log(t); | ||
} | ||
} | ||
} | ||
|
||
private void deoptimizeSystemServer(ClassLoader classLoader) throws ClassNotFoundException { | ||
deoptimizeMethods( | ||
classLoader.loadClass("com.android.server.wm.WindowStateAnimator"), | ||
"createSurfaceLocked"); | ||
|
||
deoptimizeMethods( | ||
classLoader.loadClass("com.android.server.display.DisplayManagerService"), | ||
"setUserPreferredModeForDisplayLocked", | ||
"setUserPreferredDisplayModeInternal"); | ||
|
||
Arrays.stream(classLoader | ||
.loadClass("com.android.server.wm.InsetsPolicy$InsetsPolicyAnimationControlListener") | ||
.getDeclaredConstructors()) | ||
.forEach(this::deoptimize); | ||
|
||
deoptimizeMethods( | ||
classLoader.loadClass("com.android.server.wm.InsetsPolicy"), | ||
"startAnimation", | ||
"controlAnimationUnchecked"); | ||
|
||
deoptimizeMethods( | ||
classLoader.loadClass("com.android.server.wm.WindowManagerService"), | ||
"relayoutWindow"); | ||
|
||
for (int i = 0; i < 20; i++) { | ||
try { | ||
Class<?> windowsManagerServiceImpl = XposedHelpers.findClassIfExists("com.android.server.wm.WindowManagerServiceImpl", loadPackageParam.classLoader); | ||
if (windowsManagerServiceImpl != null) { | ||
XposedBridge.hookAllMethods( | ||
windowsManagerServiceImpl, | ||
"notAllowCaptureDisplay", | ||
XC_MethodReplacement.returnConstant(false)); | ||
var clazz = classLoader.loadClass("com.android.server.wm.RootWindowContainer$$ExternalSyntheticLambda" + i); | ||
if (BiConsumer.class.isAssignableFrom(clazz)) { | ||
deoptimizeMethods(clazz, "accept"); | ||
} | ||
} catch (Throwable t) { | ||
XposedBridge.log(t); | ||
} catch (ClassNotFoundException ignored) { | ||
} | ||
} else if (loadPackageParam.packageName.equals("com.flyme.systemuiex")) { | ||
try { | ||
XposedHelpers.findAndHookMethod("android.view.SurfaceControl$ScreenshotHardwareBuffer", loadPackageParam.classLoader, "containsSecureLayers", XC_MethodReplacement.returnConstant(false)); | ||
} catch (Throwable t) { | ||
XposedBridge.log(t); | ||
var clazz = classLoader.loadClass("com.android.server.wm.DisplayContent$$ExternalSyntheticLambda" + i); | ||
if (BiPredicate.class.isAssignableFrom(clazz)) { | ||
deoptimizeMethods(clazz, "test"); | ||
} | ||
} catch (ClassNotFoundException ignored) { | ||
} | ||
} else if (loadPackageParam.packageName.equals("com.oplus.screenshot")) { | ||
try { | ||
Class<?> screenshotContext = XposedHelpers.findClassIfExists("com.oplus.screenshot.screenshot.core.ScreenshotContext", loadPackageParam.classLoader); | ||
XposedBridge.hookAllMethods(screenshotContext, "setScreenshotReject", XC_MethodReplacement.DO_NOTHING); | ||
XposedBridge.hookAllMethods(screenshotContext, "setLongshotReject", XC_MethodReplacement.DO_NOTHING); | ||
} catch (Throwable t) { | ||
XposedBridge.log(t); | ||
} | ||
} | ||
|
||
private void deoptimizeMethods(Class<?> clazz, String... names) { | ||
var list = Arrays.asList(names); | ||
Arrays.stream(clazz.getDeclaredMethods()) | ||
.filter(method -> list.contains(method.getName())) | ||
.forEach(this::deoptimize); | ||
} | ||
|
||
private void hookWindowState(ClassLoader classLoader) throws ClassNotFoundException, NoSuchMethodException { | ||
var windowStateClazz = classLoader.loadClass("com.android.server.wm.WindowState"); | ||
Method isSecureLockedMethod; | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { | ||
isSecureLockedMethod = windowStateClazz.getDeclaredMethod("isSecureLocked"); | ||
} else { | ||
var windowManagerServiceClazz = classLoader.loadClass("com.android.server.wm.WindowManagerService"); | ||
isSecureLockedMethod = windowManagerServiceClazz.getDeclaredMethod("isSecureLocked", windowStateClazz); | ||
} | ||
hook(isSecureLockedMethod, ReturnFalseHooker.class); | ||
} | ||
|
||
@TargetApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) | ||
private void hookActivityTaskManagerService(ClassLoader classLoader) throws ClassNotFoundException, NoSuchMethodException { | ||
var activityTaskManagerServiceClazz = classLoader.loadClass("com.android.server.wm.ActivityTaskManagerService"); | ||
var iBinderClazz = classLoader.loadClass("android.os.IBinder"); | ||
var iScreenCaptureObserverClazz = classLoader.loadClass("android.app.IScreenCaptureObserver"); | ||
var method = activityTaskManagerServiceClazz.getDeclaredMethod("registerScreenCaptureObserver", iBinderClazz, iScreenCaptureObserverClazz); | ||
hook(method, ReturnNullHooker.class); | ||
} | ||
|
||
private void hookHyperOS(ClassLoader classLoader) throws ClassNotFoundException { | ||
var windowManagerServiceImplClazz = classLoader.loadClass("com.android.server.wm.WindowManagerServiceImpl"); | ||
for (var method : windowManagerServiceImplClazz.getDeclaredMethods()) { | ||
if (method.getName().equals("notAllowCaptureDisplay")) { | ||
hook(method, ReturnFalseHooker.class); | ||
} | ||
} else if (loadPackageParam.isFirstApplication) { | ||
XposedHelpers.findAndHookMethod(Activity.class, "onResume", new XC_MethodHook() { | ||
@Override | ||
protected void afterHookedMethod(MethodHookParam param) { | ||
Activity activity = (Activity) param.thisObject; | ||
Toast.makeText(activity, "DFS: Incorrect module usage, remove this app from scope.", Toast.LENGTH_LONG).show(); | ||
activity.finish(); | ||
} | ||
}); | ||
} | ||
} | ||
|
||
private void hookFlyme(ClassLoader classLoader) throws ClassNotFoundException, NoSuchMethodException { | ||
var screenshotHardwareBufferClazz = classLoader.loadClass("android.view.SurfaceControl$ScreenshotHardwareBuffer"); | ||
var method = screenshotHardwareBufferClazz.getDeclaredMethod("containsSecureLayers"); | ||
hook(method, ReturnFalseHooker.class); | ||
} | ||
|
||
private void hookOplus(ClassLoader classLoader) throws ClassNotFoundException, NoSuchMethodException { | ||
var screenshotContextClazz = classLoader.loadClass("com.oplus.screenshot.screenshot.core.ScreenshotContext"); | ||
var method = screenshotContextClazz.getDeclaredMethod("setScreenshotReject"); | ||
hook(method, ReturnNullHooker.class); | ||
method = screenshotContextClazz.getDeclaredMethod("setLongshotReject"); | ||
hook(method, ReturnNullHooker.class); | ||
} | ||
|
||
private void hookOnResume() throws NoSuchMethodException { | ||
var method = Activity.class.getDeclaredMethod("onResume"); | ||
hook(method, ToastHooker.class); | ||
} | ||
|
||
@XposedHooker | ||
private static class ReturnFalseHooker implements Hooker { | ||
@BeforeInvocation | ||
public static void before(@NonNull BeforeHookCallback callback) { | ||
callback.returnAndSkip(false); | ||
} | ||
} | ||
|
||
@XposedHooker | ||
private static class ReturnNullHooker implements Hooker { | ||
@BeforeInvocation | ||
public static void before(@NonNull BeforeHookCallback callback) { | ||
callback.returnAndSkip(null); | ||
} | ||
} | ||
|
||
@XposedHooker | ||
private static class ToastHooker implements Hooker { | ||
@BeforeInvocation | ||
public static void before(@NonNull BeforeHookCallback callback) { | ||
var activity = (Activity) callback.getThisObject(); | ||
assert activity != null; | ||
Toast.makeText(activity, "DFS: Incorrect module usage, remove this app from scope.", Toast.LENGTH_LONG).show(); | ||
activity.finish(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,4 @@ | ||
<resources> | ||
<string name="app_name">Disable FLAG_SECURE</string> | ||
<string name="xposed_description">Disable FLAG_SECURE on all windows, enabling screenshots in apps that normally wouldn\'t allow it.</string> | ||
<string-array name="scope"> | ||
<item>android</item> | ||
<item>com.flyme.systemuiex</item> | ||
<item>com.oplus.screenshot</item> | ||
</string-array> | ||
</resources> |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
minApiVersion=100 | ||
targetApiVersion=100 | ||
staticScope=true |
Oops, something went wrong.