Skip to content

Commit 9de5368

Browse files
committed
Inline hook interceptors, remove Hooker classes
Convert many Hooker implementations into inline lambda interceptors and remove the corresponding inner classes. Key changes: - Replace SecureLocked, CreateDisplay, CreateVirtualDisplayLocked, CheckPermission, OplusScreenCapture, ReturnTrue/False/Null and Toast hooker classes with direct lambda hooks. - Use StackWalker on newer Android SDKs (UPSIDE_DOWN_CAKE+) and fallback to Throwable stack traces for older versions when detecting specific caller frames. - Force create/display args to enable secure handling where needed (sets the display arg to true and injects VIRTUAL_DISPLAY_FLAG_SECURE for virtual displays when appropriate). - Inline simple constant-return hooks (false/true/null) for several system methods (screen capture/recording checks, containsSecureLayers, hasSecure, canBeScreenshotTarget, etc.). - Remap permission checks for CAPTURE_BLACKOUT_CONTENT to READ_FRAME_BUFFER inline. - Replace onResume hook with inline dialog display that alerts incorrect module usage and exits the process. These changes simplify the code by moving behavior next to hook registration and reduce boilerplate. Review the onResume dialog/exit behavior as it force-closes the app when triggered.
1 parent 01ac8d3 commit 9de5368

1 file changed

Lines changed: 82 additions & 142 deletions

File tree

app/src/main/java/io/github/lsposed/disableflagsecure/DisableFlagSecure.java

Lines changed: 82 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,27 @@ private void deoptimizeMethods(Class<?> clazz, String... names) {
224224
private void hookWindowState(ClassLoader classLoader) throws ClassNotFoundException, NoSuchMethodException {
225225
var windowStateClazz = classLoader.loadClass("com.android.server.wm.WindowState");
226226
var isSecureLockedMethod = windowStateClazz.getDeclaredMethod("isSecureLocked");
227-
hook(isSecureLockedMethod).intercept(new SecureLockedHooker());
227+
hook(isSecureLockedMethod).intercept(chain -> {
228+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
229+
var walker = StackWalker.getInstance();
230+
var match = walker.walk(frames -> frames
231+
.map(StackWalker.StackFrame::getMethodName)
232+
.limit(6)
233+
.skip(2)
234+
.anyMatch(s -> s.equals("setInitialSurfaceControlProperties") || s.equals("createSurfaceLocked")));
235+
if (match) return chain.proceed();
236+
} else {
237+
var stackTrace = new Throwable().getStackTrace();
238+
for (int i = 4; i < stackTrace.length && i < 8; i++) {
239+
var name = stackTrace[i].getMethodName();
240+
if (name.equals("setInitialSurfaceControlProperties") ||
241+
name.equals("createSurfaceLocked")) {
242+
return chain.proceed();
243+
}
244+
}
245+
}
246+
return false;
247+
});
228248
}
229249

230250
private static Field captureSecureLayersField;
@@ -257,12 +277,42 @@ private void hookDisplayControl(ClassLoader classLoader) throws ClassNotFoundExc
257277
Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM ?
258278
"createVirtualDisplay" :
259279
"createDisplay", String.class, boolean.class);
260-
hook(method).intercept(new CreateDisplayHooker());
280+
hook(method).intercept(chain -> {
281+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
282+
var stackTrace = new Throwable().getStackTrace();
283+
for (int i = 4; i < stackTrace.length && i < 8; i++) {
284+
var name = stackTrace[i].getMethodName();
285+
if (name.equals("createVirtualDisplayLocked")) {
286+
return chain.proceed();
287+
}
288+
}
289+
}
290+
var args = chain.getArgs().toArray();
291+
args[1] = true;
292+
return chain.proceed(args);
293+
});
261294
}
262295

263296
private void hookVirtualDisplayAdapter(ClassLoader classLoader) throws ClassNotFoundException {
264297
var displayControlClazz = classLoader.loadClass("com.android.server.display.VirtualDisplayAdapter");
265-
hookMethods(displayControlClazz, new CreateVirtualDisplayLockedHooker(), "createVirtualDisplayLocked");
298+
hookMethods(displayControlClazz, chain -> {
299+
var caller = (int) chain.getArg(2);
300+
if (caller >= 10000 && chain.getArg(1) == null) {
301+
// not os and not media projection
302+
return chain.proceed();
303+
}
304+
for (int i = 3; i < chain.getArgs().size(); i++) {
305+
var arg = chain.getArg(i);
306+
if (arg instanceof Integer flags) {
307+
flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
308+
var args = chain.getArgs().toArray();
309+
args[i] = flags;
310+
return chain.proceed(args);
311+
}
312+
}
313+
module.log(Log.WARN, TAG, "flag not found in CreateVirtualDisplayLockedHooker");
314+
return chain.proceed();
315+
}, "createVirtualDisplayLocked");
266316
}
267317

268318
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@@ -271,27 +321,35 @@ private void hookActivityTaskManagerService(ClassLoader classLoader) throws Clas
271321
var iBinderClazz = classLoader.loadClass("android.os.IBinder");
272322
var iScreenCaptureObserverClazz = classLoader.loadClass("android.app.IScreenCaptureObserver");
273323
var method = activityTaskManagerServiceClazz.getDeclaredMethod("registerScreenCaptureObserver", iBinderClazz, iScreenCaptureObserverClazz);
274-
hook(method).intercept(new ReturnNullHooker());
324+
hook(method).intercept(chain -> null);
275325
}
276326

277327
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
278328
private void hookWindowManagerService(ClassLoader classLoader) throws ClassNotFoundException, NoSuchMethodException {
279329
var windowManagerServiceClazz = classLoader.loadClass("com.android.server.wm.WindowManagerService");
280330
var iScreenRecordingCallbackClazz = classLoader.loadClass("android.window.IScreenRecordingCallback");
281331
var method = windowManagerServiceClazz.getDeclaredMethod("registerScreenRecordingCallback", iScreenRecordingCallbackClazz);
282-
hook(method).intercept(new ReturnFalseHooker());
332+
hook(method).intercept(chain -> false);
283333
}
284334

285335
private void hookActivityManagerService(ClassLoader classLoader) throws ClassNotFoundException, NoSuchMethodException {
286336
var activityTaskManagerServiceClazz = classLoader.loadClass("com.android.server.am.ActivityManagerService");
287337
var method = activityTaskManagerServiceClazz.getDeclaredMethod("checkPermission", String.class, int.class, int.class);
288-
hook(method).intercept(new CheckPermissionHooker());
338+
hook(method).intercept(chain -> {
339+
var permission = chain.getArg(0);
340+
if ("android.permission.CAPTURE_BLACKOUT_CONTENT".equals(permission)) {
341+
var args = chain.getArgs().toArray();
342+
args[0] = "android.permission.READ_FRAME_BUFFER";
343+
return chain.proceed(args);
344+
}
345+
return chain.proceed();
346+
});
289347
}
290348

291349
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
292350
private void hookHyperOS(ClassLoader classLoader) throws ClassNotFoundException {
293351
var windowManagerServiceImplClazz = classLoader.loadClass("com.android.server.wm.WindowManagerServiceImpl");
294-
hookMethods(windowManagerServiceImplClazz, new ReturnFalseHooker(), "notAllowCaptureDisplay");
352+
hookMethods(windowManagerServiceImplClazz, chain -> false, "notAllowCaptureDisplay");
295353
}
296354

297355
private void hookScreenshotHardwareBuffer(ClassLoader classLoader) throws ClassNotFoundException, NoSuchMethodException {
@@ -300,25 +358,29 @@ private void hookScreenshotHardwareBuffer(ClassLoader classLoader) throws ClassN
300358
"android.window.ScreenCapture$ScreenshotHardwareBuffer" :
301359
"android.view.SurfaceControl$ScreenshotHardwareBuffer");
302360
var method = screenshotHardwareBufferClazz.getDeclaredMethod("containsSecureLayers");
303-
hook(method).intercept(new ReturnFalseHooker());
361+
hook(method).intercept(chain -> false);
304362
}
305363

306364
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
307365
private void hookOplusScreenCapture(ClassLoader classLoader) throws ClassNotFoundException, NoSuchMethodException {
308366
var oplusScreenCaptureClazz = classLoader.loadClass("com.oplus.screenshot.OplusScreenCapture$CaptureArgs$Builder");
309367
var method = oplusScreenCaptureClazz.getDeclaredMethod("setUid", long.class);
310-
hook(method).intercept(new OplusScreenCaptureHooker());
368+
hook(method).intercept(chain -> {
369+
var args = chain.getArgs().toArray();
370+
args[0] = -1;
371+
return chain.proceed(args);
372+
});
311373
}
312374

313375
private void hookOplus(ClassLoader classLoader) throws ClassNotFoundException {
314376
// caller: com.android.server.wm.OplusLongshotWindowDump#dumpWindows
315377
var longshotMainClazz = classLoader.loadClass("com.android.server.wm.OplusLongshotMainWindow");
316-
hookMethods(longshotMainClazz, new ReturnFalseHooker(), "hasSecure");
378+
hookMethods(longshotMainClazz, chain -> false, "hasSecure");
317379
}
318380

319381
private void hookOneUI(ClassLoader classLoader) throws ClassNotFoundException {
320382
var wmScreenshotControllerClazz = classLoader.loadClass("com.android.server.wm.WmScreenshotController");
321-
hookMethods(wmScreenshotControllerClazz, new ReturnTrueHooker(), "canBeScreenshotTarget");
383+
hookMethods(wmScreenshotControllerClazz, chain -> true, "canBeScreenshotTarget");
322384
}
323385

324386
private void hookMethods(Class<?> clazz, Hooker hooker, String... names) {
@@ -330,50 +392,16 @@ private void hookMethods(Class<?> clazz, Hooker hooker, String... names) {
330392

331393
private void hookOnResume() throws NoSuchMethodException {
332394
var method = Activity.class.getDeclaredMethod("onResume");
333-
hook(method).intercept(new ToastHooker());
334-
}
335-
336-
private static class CreateDisplayHooker implements Hooker {
337-
338-
@Override
339-
public Object intercept(@NonNull Chain chain) throws Throwable {
340-
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
341-
var stackTrace = new Throwable().getStackTrace();
342-
for (int i = 4; i < stackTrace.length && i < 8; i++) {
343-
var name = stackTrace[i].getMethodName();
344-
if (name.equals("createVirtualDisplayLocked")) {
345-
return chain.proceed();
346-
}
347-
}
348-
}
349-
var args = chain.getArgs().toArray();
350-
args[1] = true;
351-
return chain.proceed(args);
352-
}
353-
}
354-
355-
private static class CheckPermissionHooker implements Hooker {
356-
357-
@Override
358-
public Object intercept(@NonNull Chain chain) throws Throwable {
359-
var permission = chain.getArg(0);
360-
if ("android.permission.CAPTURE_BLACKOUT_CONTENT".equals(permission)) {
361-
var args = chain.getArgs().toArray();
362-
args[0] = "android.permission.READ_FRAME_BUFFER";
363-
return chain.proceed(args);
364-
}
395+
hook(method).intercept(chain -> {
396+
var activity = (Activity) chain.getThisObject();
397+
new AlertDialog.Builder(activity)
398+
.setTitle("Enable Screenshot")
399+
.setMessage("Incorrect module usage, remove this app from scope.")
400+
.setCancelable(false)
401+
.setPositiveButton("OK", (dialog, which) -> System.exit(0))
402+
.show();
365403
return chain.proceed();
366-
}
367-
}
368-
369-
private static class OplusScreenCaptureHooker implements Hooker {
370-
371-
@Override
372-
public Object intercept(@NonNull Chain chain) throws Throwable {
373-
var args = chain.getArgs().toArray();
374-
args[0] = -1;
375-
return chain.proceed(args);
376-
}
404+
});
377405
}
378406

379407
private static class ScreenCaptureHooker implements Hooker {
@@ -394,92 +422,4 @@ public Object intercept(@NonNull Chain chain) throws Throwable {
394422
return chain.proceed();
395423
}
396424
}
397-
398-
private static class CreateVirtualDisplayLockedHooker implements Hooker {
399-
400-
@Override
401-
public Object intercept(@NonNull Chain chain) throws Throwable {
402-
var caller = (int) chain.getArg(2);
403-
if (caller >= 10000 && chain.getArg(1) == null) {
404-
// not os and not media projection
405-
return chain.proceed();
406-
}
407-
for (int i = 3; i < chain.getArgs().size(); i++) {
408-
var arg = chain.getArg(i);
409-
if (arg instanceof Integer flags) {
410-
flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
411-
var args = chain.getArgs().toArray();
412-
args[i] = flags;
413-
return chain.proceed(args);
414-
}
415-
}
416-
module.log(Log.WARN, TAG, "flag not found in CreateVirtualDisplayLockedHooker");
417-
return chain.proceed();
418-
}
419-
}
420-
421-
private static class SecureLockedHooker implements Hooker {
422-
423-
@Override
424-
public Object intercept(@NonNull Chain chain) throws Throwable {
425-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
426-
var walker = StackWalker.getInstance();
427-
var match = walker.walk(frames -> frames
428-
.map(StackWalker.StackFrame::getMethodName)
429-
.limit(6)
430-
.skip(2)
431-
.anyMatch(s -> s.equals("setInitialSurfaceControlProperties") || s.equals("createSurfaceLocked")));
432-
if (match) return chain.proceed();
433-
} else {
434-
var stackTrace = new Throwable().getStackTrace();
435-
for (int i = 4; i < stackTrace.length && i < 8; i++) {
436-
var name = stackTrace[i].getMethodName();
437-
if (name.equals("setInitialSurfaceControlProperties") ||
438-
name.equals("createSurfaceLocked")) {
439-
return chain.proceed();
440-
}
441-
}
442-
}
443-
return false;
444-
}
445-
}
446-
447-
private static class ReturnTrueHooker implements Hooker {
448-
449-
@Override
450-
public Object intercept(@NonNull Chain chain) {
451-
return true;
452-
}
453-
}
454-
455-
private static class ReturnFalseHooker implements Hooker {
456-
457-
@Override
458-
public Object intercept(@NonNull Chain chain) {
459-
return false;
460-
}
461-
}
462-
463-
private static class ReturnNullHooker implements Hooker {
464-
465-
@Override
466-
public Object intercept(@NonNull Chain chain) {
467-
return null;
468-
}
469-
}
470-
471-
private static class ToastHooker implements Hooker {
472-
473-
@Override
474-
public Object intercept(@NonNull Chain chain) throws Throwable {
475-
var activity = (Activity) chain.getThisObject();
476-
new AlertDialog.Builder(activity)
477-
.setTitle("Enable Screenshot")
478-
.setMessage("Incorrect module usage, remove this app from scope.")
479-
.setCancelable(false)
480-
.setPositiveButton("OK", (dialog, which) -> System.exit(0))
481-
.show();
482-
return chain.proceed();
483-
}
484-
}
485425
}

0 commit comments

Comments
 (0)