Skip to content

Commit 9a25867

Browse files
committed
init
0 parents  commit 9a25867

11 files changed

Lines changed: 162 additions & 0 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.theos/
2+
packages/
3+
.DS_Store

Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
TARGET := iphone:clang:latest:latest
2+
DEBUG = 0
3+
4+
include $(THEOS)/makefiles/common.mk
5+
6+
TWEAK_NAME = ScreenProtectorKit-Spoof
7+
8+
ScreenProtectorKit-Spoof_FILES = Tweak.x
9+
ScreenProtectorKit-Spoof_CFLAGS = -fobjc-arc
10+
11+
include $(THEOS_MAKE_PATH)/tweak.mk

README.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# ScreenProtectorKit Spoof
2+
3+
A Logos tweak to spoof screenshot and screen recording protections in [ScreenProtectorKit](https://github.com/prongbang/ScreenProtectorKit).
4+
5+
## Analysis
6+
7+
To understand how the library blocks screen capture, inspect the compiled Mach-O binary with a disassembler. Then search for the `ScreenProtectorKit` namespace to identify its core API.
8+
9+
![image0](img/img0.png)
10+
11+
By looking at related iOS system APIs, we can identify three defense layers used by the library:
12+
13+
1. **The Visual Layer (`UITextField`):** The library uses the common `setSecureTextEntry:` trick to hide the app UI during screenshots or screen recording.
14+
15+
![image1](img/img1.png)
16+
17+
2. **The Active Check Layer (`UIScreen`):** The `screenIsRecording()` function checks `[UIScreen mainScreen].isCaptured` directly to detect whether the screen is being recorded.
18+
19+
![image2](img/img2.png)
20+
21+
3. **The Event Detection Layer (`NSNotificationCenter`):** The `screenshotObserver(using:)` and `screenRecordObserver(using:)` functions show that the library listens for screenshot and screen recording notifications through `NSNotificationCenter` and forwards them to the Flutter layer.
22+
23+
![image3](img/img3.png)
24+
25+
## The Spoof
26+
27+
To disable these protections, all three layers need to be handled separately. Hooking the usual `addObserver:selector:` is not enough here, because the library uses block-based observers instead.
28+
29+
Below is a breakdown of the Logos tweak (`Tweak.x`) and how it targets each layer.
30+
31+
### 1. Visual Layer (`UITextField`)
32+
33+
The library hides the app UI behind a secure text field so iOS will black out the screen. We spoof this by forcing `setSecureTextEntry:` to always use `NO`. We also spoof the getter in case the app checks the UI state.
34+
35+
```objective-c
36+
%hook UITextField
37+
38+
- (void)setSecureTextEntry:(BOOL)secure {
39+
%orig(NO);
40+
}
41+
42+
- (BOOL)isSecureTextEntry {
43+
return NO;
44+
}
45+
46+
%end
47+
```
48+
49+
### 2. Active Check Layer (`UIScreen`)
50+
51+
The app also checks whether screen recording is active. We spoof `isCaptured` to always return `NO`, so the app always thinks screen recording is inactive.
52+
53+
```objective-c
54+
%hook UIScreen
55+
56+
- (BOOL)isCaptured {
57+
return NO;
58+
}
59+
60+
%end
61+
```
62+
63+
### 3. Event Detection Layer (`NSNotificationCenter`)
64+
65+
The library reports screenshot and screen recording events to the Flutter layer. We spoof this by returning `nil` during observer registration, so the event handler cannot run.
66+
67+
```objective-c
68+
%hook NSNotificationCenter
69+
70+
- (id)addObserverForName:(NSNotificationName)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block {
71+
72+
if ([name isEqualToString:UIApplicationUserDidTakeScreenshotNotification] ||
73+
[name isEqualToString:UIScreenCapturedDidChangeNotification]) {
74+
return nil;
75+
}
76+
77+
return %orig;
78+
}
79+
80+
%end
81+
```
82+
83+
## Example with `CLICK-Ed`
84+
85+
Spoof successful, with screenshots and screen recording working normally and no black screen.
86+
87+
![image4](img/img4.png)
88+
89+
## Usage
90+
91+
Use [Sideloadly](https://sideloadly.io/) to inject the dylib into the `.ipa`.
92+
93+
## Disclaimer
94+
95+
This project is for educational and research purposes only.

ScreenProtectorKit-Spoof.plist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{ Filter = { Bundles = ( "realjobscomltd.clickqa" ); }; }

Tweak.x

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#import <UIKit/UIKit.h>
2+
3+
%hook UITextField
4+
5+
// 1. Bypass the Visual Layer: Prevent iOS from applying the black/blur privacy screen
6+
- (void)setSecureTextEntry:(BOOL)secure {
7+
%orig(NO);
8+
}
9+
10+
// Spoof the getter just in case the app validates the UI state
11+
- (BOOL)isSecureTextEntry {
12+
return NO;
13+
}
14+
15+
%end
16+
17+
%hook UIScreen
18+
19+
// 2. Bypass the Active Polling Layer: Force the system to report that no recording is active
20+
- (BOOL)isCaptured {
21+
return NO;
22+
}
23+
24+
%end
25+
26+
%hook NSNotificationCenter
27+
28+
// 3. Bypass the Event Layer: Intercept block-based observer registrations
29+
- (id)addObserverForName:(NSNotificationName)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block {
30+
31+
// If the app tries to listen for screenshot or screen recording events, drop it
32+
if ([name isEqualToString:UIApplicationUserDidTakeScreenshotNotification] ||
33+
[name isEqualToString:UIScreenCapturedDidChangeNotification]) {
34+
35+
// Return nil to provide an invalid observer token. The closure will never execute.
36+
return nil;
37+
}
38+
39+
return %orig;
40+
}
41+
42+
%end
43+

control

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Package: com.fl0rk.screenprotectorkit-spoof
2+
Name: ScreenProtectorKit-Spoof
3+
Version: 0.0.1
4+
Architecture: iphoneos-arm
5+
Description: Spoof screen recording detection!
6+
Maintainer: Fl0rk
7+
Author: Fl0rk
8+
Section: Tweaks
9+
Depends: mobilesubstrate (>= 0.9.5000)

img/img0.png

288 KB
Loading

img/img1.png

42.4 KB
Loading

img/img2.png

87.1 KB
Loading

img/img3.png

278 KB
Loading

0 commit comments

Comments
 (0)