Rewrite with libxposed

This commit is contained in:
tehcneko 2024-03-11 21:08:15 +08:00
parent d1275e4cf6
commit 68ac14a2b8
9 changed files with 220 additions and 148 deletions

View File

@ -48,8 +48,8 @@ android {
} }
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_11 sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_17
} }
lint { lint {
@ -61,5 +61,6 @@ android {
} }
dependencies { dependencies {
compileOnly 'de.robv.android.xposed:api:82' compileOnly 'androidx.annotation:annotation:1.7.1'
compileOnly 'io.github.libxposed:api:100'
} }

View File

@ -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 -repackageclasses
-allowaccessmodification -allowaccessmodification
-overloadaggressively

View File

@ -5,20 +5,8 @@
<application <application
android:allowBackup="true" android:allowBackup="true"
android:label="@string/app_name" android:label="@string/app_name"
android:description="@string/xposed_description"
android:supportsRtl="true" android:supportsRtl="true"
tools:ignore="AllowBackup,MissingApplicationIcon"> 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>
</manifest> </manifest>

View File

@ -1,144 +1,212 @@
package io.github.lsposed.disableflagsecure; package io.github.lsposed.disableflagsecure;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity; import android.app.Activity;
import android.os.Build; import android.os.Build;
import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
import java.lang.reflect.InvocationTargetException; import androidx.annotation.NonNull;
import java.lang.reflect.Member;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.BiPredicate; import java.util.function.BiPredicate;
import de.robv.android.xposed.IXposedHookLoadPackage; import io.github.libxposed.api.XposedInterface;
import de.robv.android.xposed.XC_MethodHook; import io.github.libxposed.api.XposedModule;
import de.robv.android.xposed.XC_MethodReplacement; import io.github.libxposed.api.annotations.BeforeInvocation;
import de.robv.android.xposed.XposedBridge; import io.github.libxposed.api.annotations.XposedHooker;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
public class DisableFlagSecure implements IXposedHookLoadPackage { @SuppressLint({"PrivateApi", "BlockedPrivateApi"})
private final static Method deoptimizeMethod; public class DisableFlagSecure extends XposedModule {
static { public DisableFlagSecure(XposedInterface base, ModuleLoadedParam param) {
Method m = null; super(base, param);
try {
//noinspection JavaReflectionMemberAccess
m = XposedBridge.class.getDeclaredMethod("deoptimizeMethod", Member.class);
} catch (Throwable t) {
XposedBridge.log(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);
}
}
} }
@Override @Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) { public void onSystemServerLoaded(@NonNull SystemServerLoadedParam param) {
if (loadPackageParam.packageName.equals("android")) { var classLoader = param.getClassLoader();
try {
deoptimizeSystemServer(classLoader);
} catch (Throwable t) {
log("deoptimize system server failed", t);
}
try {
hookWindowState(classLoader);
} catch (Throwable t) {
log("hook WindowState failed", t);
}
try {
hookHyperOS(classLoader);
} catch (ClassNotFoundException ignored) {
} catch (Throwable t) {
log("hook HyperOS failed", t);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
try { try {
Class<?> windowsState = XposedHelpers.findClass("com.android.server.wm.WindowState", loadPackageParam.classLoader); hookActivityTaskManagerService(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));
}
} catch (Throwable t) { } catch (Throwable t) {
XposedBridge.log(t); log("hook ActivityTaskManagerService failed", t);
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
try {
XposedHelpers.findAndHookMethod(
"com.android.server.wm.ActivityTaskManagerService",
loadPackageParam.classLoader,
"registerScreenCaptureObserver",
"android.os.IBinder",
"android.app.IScreenCaptureObserver",
XC_MethodReplacement.DO_NOTHING);
} 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);
}
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");
}
}
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");
}
}
} catch (Throwable t) {
XposedBridge.log(t);
}
try {
Class<?> windowsManagerServiceImpl = XposedHelpers.findClassIfExists("com.android.server.wm.WindowManagerServiceImpl", loadPackageParam.classLoader);
if (windowsManagerServiceImpl != null) {
XposedBridge.hookAllMethods(
windowsManagerServiceImpl,
"notAllowCaptureDisplay",
XC_MethodReplacement.returnConstant(false));
}
} catch (Throwable t) {
XposedBridge.log(t);
}
} 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);
}
} 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);
}
} 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();
}
});
} }
} }
@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 {
hookFlyme(classLoader);
} catch (Throwable t) {
log("hook Flyme failed", t);
}
break;
case "com.oplus.screenshot":
try {
hookOplus(classLoader);
} catch (Throwable t) {
log("hook OPlus failed", t);
}
break;
default:
try {
hookOnResume();
} catch (Throwable ignored) {
}
}
}
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 {
var clazz = classLoader.loadClass("com.android.server.wm.RootWindowContainer$$ExternalSyntheticLambda" + i);
if (BiConsumer.class.isAssignableFrom(clazz)) {
deoptimizeMethods(clazz, "accept");
}
} catch (ClassNotFoundException ignored) {
}
try {
var clazz = classLoader.loadClass("com.android.server.wm.DisplayContent$$ExternalSyntheticLambda" + i);
if (BiPredicate.class.isAssignableFrom(clazz)) {
deoptimizeMethods(clazz, "test");
}
} catch (ClassNotFoundException ignored) {
}
}
}
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);
}
}
}
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();
}
}
} }

View File

@ -1,9 +1,4 @@
<resources> <resources>
<string name="app_name">Disable FLAG_SECURE</string> <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 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> </resources>

View File

@ -0,0 +1,3 @@
minApiVersion=100
targetApiVersion=100
staticScope=true

View File

@ -0,0 +1,3 @@
system
com.flyme.systemuiex
com.oplus.screenshot

View File

@ -16,7 +16,11 @@ allprojects {
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
maven { url = "https://api.xposed.info" } mavenLocal {
content {
includeGroup("io.github.libxposed")
}
}
} }
} }