Skip to content

Commit

Permalink
Rewrite with libxposed
Browse files Browse the repository at this point in the history
  • Loading branch information
tehcneko committed Mar 11, 2024
1 parent d1275e4 commit 68ac14a
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 134 deletions.
7 changes: 4 additions & 3 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ android {
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}

lint {
Expand All @@ -61,5 +61,6 @@ android {
}

dependencies {
compileOnly 'de.robv.android.xposed:api:82'
compileOnly 'androidx.annotation:annotation:1.7.1'
compileOnly 'io.github.libxposed:api:100'
}
16 changes: 13 additions & 3 deletions app/proguard-rules.pro
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
16 changes: 2 additions & 14 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,8 @@
<application
android:allowBackup="true"
android:label="@string/app_name"
android:description="@string/xposed_description"
android:supportsRtl="true"
tools:ignore="AllowBackup,MissingApplicationIcon">
<meta-data
android:name="xposedmodule"
android:value="true" />
<meta-data
android:name="xposeddescription"
android:value="@string/xposed_description" />
<meta-data
android:name="xposedminversion"
android:value="53" />
<meta-data
android:name="xposedscope"
android:resource="@array/scope" />
</application>
tools:ignore="AllowBackup,MissingApplicationIcon" />

</manifest>
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();
}
}
}
5 changes: 0 additions & 5 deletions app/src/main/res/values/strings.xml
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.
3 changes: 3 additions & 0 deletions app/src/main/resources/META-INF/xposed/module.prop
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
minApiVersion=100
targetApiVersion=100
staticScope=true
Loading

0 comments on commit 68ac14a

Please sign in to comment.