Skip to content

Commit 6ca482a

Browse files
authored
Tweak
1 parent f9150d4 commit 6ca482a

14 files changed

Lines changed: 624 additions & 0 deletions

BioLock.plist

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

Makefile

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
ARCHS = arm64 arm64e
2+
TARGET = iphone:clang:15.6:15.0
3+
4+
THEOS_PACKAGE_SCHEME = rootless
5+
6+
INSTALL_TARGET_PROCESSES = SpringBoard
7+
8+
-include device.mk
9+
include $(THEOS)/makefiles/common.mk
10+
11+
TWEAK_NAME = BioLock
12+
13+
BioLock_FILES = Tweak.x
14+
BioLock_FRAMEWORKS = UIKit LocalAuthentication AudioToolbox
15+
BioLock_PRIVATE_FRAMEWORKS = SpringBoardServices FrontBoardServices
16+
BioLock_CFLAGS = -fobjc-arc -Wno-deprecated-declarations
17+
18+
include $(THEOS_MAKE_PATH)/tweak.mk
19+
20+
SUBPROJECTS += biolockprefs
21+
include $(THEOS_MAKE_PATH)/aggregate.mk
22+
23+
after-install::
24+
install.exec "killall -9 SpringBoard"

Tweak.x

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
2+
3+
4+
#import <UIKit/UIKit.h>
5+
#import <LocalAuthentication/LocalAuthentication.h>
6+
#import <AudioToolbox/AudioServices.h>
7+
8+
@interface SBApplication : NSObject
9+
- (NSString *)bundleIdentifier;
10+
- (NSString *)displayName;
11+
@end
12+
13+
@interface SBUIController : NSObject
14+
+ (instancetype)sharedInstance;
15+
- (void)activateApplication:(id)app fromIcon:(id)icon location:(long long)location activationSettings:(id)settings actions:(id)actions;
16+
@end
17+
18+
19+
static NSString * const kPrefsID = @"com.batues.biolock";
20+
static BOOL gEnabled = YES;
21+
static BOOL gAllowPasscode = YES;
22+
static BOOL gVibrateOnFail = YES;
23+
static NSInteger gAuthCacheDuration = 0;
24+
static NSString *gCustomPrompt = nil;
25+
static NSSet<NSString *> *gProtectedApps = nil;
26+
27+
static NSMutableDictionary<NSString *, NSDate *> *gAuthCache = nil;
28+
static NSMutableSet<NSString *> *gInTransition = nil;
29+
static dispatch_queue_t gAuthQueue = nil;
30+
31+
#pragma mark - Logic
32+
33+
static void LoadPrefs() {
34+
@autoreleasepool {
35+
36+
NSString *prefsPath = @"/var/mobile/Library/Preferences/com.batues.biolock.plist";
37+
NSDictionary *prefs = [NSDictionary dictionaryWithContentsOfFile:prefsPath];
38+
39+
40+
if (!prefs) {
41+
CFStringRef appID = (__bridge CFStringRef)kPrefsID;
42+
CFPreferencesAppSynchronize(appID);
43+
CFArrayRef keyList = CFPreferencesCopyKeyList(appID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
44+
if (keyList) {
45+
prefs = (__bridge_transfer NSDictionary *)CFPreferencesCopyMultiple(keyList, appID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
46+
CFRelease(keyList);
47+
}
48+
}
49+
50+
gEnabled = prefs[@"Enabled"] ? [prefs[@"Enabled"] boolValue] : YES;
51+
gAllowPasscode = prefs[@"AllowPasscode"] ? [prefs[@"AllowPasscode"] boolValue] : YES;
52+
gVibrateOnFail = prefs[@"VibrateOnFail"] ? [prefs[@"VibrateOnFail"] boolValue] : YES;
53+
gAuthCacheDuration = [prefs[@"AuthCacheDuration"] integerValue];
54+
gCustomPrompt = [prefs[@"CustomPrompt"] copy];
55+
56+
57+
id protected = prefs[@"ProtectedApps"];
58+
if ([protected isKindOfClass:[NSArray class]]) {
59+
gProtectedApps = [NSSet setWithArray:protected];
60+
} else if ([protected isKindOfClass:[NSDictionary class]]) {
61+
62+
gProtectedApps = [NSSet setWithArray:[protected allKeys]];
63+
}
64+
65+
NSLog(@"[BioLock] Loaded %lu protected apps. Enabled: %d", (unsigned long)gProtectedApps.count, gEnabled);
66+
}
67+
}
68+
69+
static void HandlePrefsChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
70+
LoadPrefs();
71+
}
72+
73+
static void HandleClearCache(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
74+
if (gAuthQueue) {
75+
dispatch_async(gAuthQueue, ^{
76+
[gAuthCache removeAllObjects];
77+
});
78+
}
79+
}
80+
81+
static BOOL IsAuthCached(NSString *bundleID) {
82+
if (!bundleID) return NO;
83+
__block BOOL valid = NO;
84+
dispatch_sync(gAuthQueue, ^{
85+
NSDate *authDate = gAuthCache[bundleID];
86+
if (authDate && [[NSDate date] timeIntervalSinceDate:authDate] < gAuthCacheDuration) {
87+
valid = YES;
88+
}
89+
});
90+
return valid;
91+
}
92+
93+
#pragma mark - Hooks
94+
95+
%hook SBUIController
96+
97+
- (void)activateApplication:(id)app fromIcon:(id)icon location:(long long)location activationSettings:(id)settings actions:(id)actions {
98+
SBApplication *sbApp = (SBApplication *)app;
99+
NSString *bundleID = [sbApp bundleIdentifier];
100+
101+
102+
103+
104+
105+
if (!gEnabled || !bundleID || ![gProtectedApps containsObject:bundleID]) {
106+
%orig;
107+
return;
108+
}
109+
110+
111+
__block BOOL isBypassing = NO;
112+
dispatch_sync(gAuthQueue, ^{
113+
if ([gInTransition containsObject:bundleID]) {
114+
isBypassing = YES;
115+
}
116+
});
117+
118+
if (isBypassing) {
119+
%orig;
120+
return;
121+
}
122+
123+
124+
if (gAuthCacheDuration > 0 && IsAuthCached(bundleID)) {
125+
%orig;
126+
return;
127+
}
128+
129+
130+
NSString *appName = [sbApp respondsToSelector:@selector(displayName)] ? [sbApp displayName] : bundleID;
131+
NSString *reason = (gCustomPrompt && gCustomPrompt.length > 0) ?
132+
[gCustomPrompt stringByReplacingOccurrencesOfString:@"%app%" withString:appName] :
133+
[NSString stringWithFormat:@"Unlock %@", appName];
134+
135+
LAContext *context = [[LAContext alloc] init];
136+
LAPolicy policy = gAllowPasscode ? LAPolicyDeviceOwnerAuthentication : LAPolicyDeviceOwnerAuthenticationWithBiometrics;
137+
138+
[context evaluatePolicy:policy localizedReason:reason reply:^(BOOL success, NSError *error) {
139+
dispatch_async(dispatch_get_main_queue(), ^{
140+
if (success) {
141+
dispatch_sync(gAuthQueue, ^{
142+
[gInTransition addObject:bundleID];
143+
if (gAuthCacheDuration > 0) gAuthCache[bundleID] = [NSDate date];
144+
});
145+
146+
147+
[self activateApplication:app fromIcon:icon location:location activationSettings:settings actions:actions];
148+
149+
150+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), gAuthQueue, ^{
151+
[gInTransition removeObject:bundleID];
152+
});
153+
} else {
154+
if (gVibrateOnFail) AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
155+
NSLog(@"[BioLock] Auth failed for %@", bundleID);
156+
}
157+
});
158+
}];
159+
}
160+
161+
%end
162+
163+
#pragma mark - Constructor
164+
165+
%ctor {
166+
gAuthQueue = dispatch_queue_create("com.batues.biolock.queue", DISPATCH_QUEUE_SERIAL);
167+
gAuthCache = [[NSMutableDictionary alloc] init];
168+
gInTransition = [[NSMutableSet alloc] init];
169+
170+
LoadPrefs();
171+
172+
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, HandlePrefsChanged, CFSTR("com.batues.biolock/ReloadPrefs"), NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
173+
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, HandleClearCache, CFSTR("com.batues.biolock/ClearCache"), NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
174+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#import <Preferences/PSListController.h>
2+
3+
@interface BLPRootListController : PSListController
4+
5+
- (void)clearAuthCache:(id)sender;
6+
- (void)resetSettings:(id)sender;
7+
- (void)openGitHub:(id)sender;
8+
- (void)showCompletionAlert:(NSString *)message;
9+
10+
@end
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#import "BLPRootListController.h"
2+
#import <Preferences/PSSpecifier.h>
3+
4+
@implementation BLPRootListController
5+
6+
- (NSArray *)specifiers {
7+
if (!_specifiers) {
8+
_specifiers = [self loadSpecifiersFromPlistName:@"Root" target:self];
9+
}
10+
return _specifiers;
11+
}
12+
13+
- (void)viewDidLoad {
14+
[super viewDidLoad];
15+
self.title = @"BioLock";
16+
17+
if (@available(iOS 13.0, *)) {
18+
UINavigationBarAppearance *appearance = [[UINavigationBarAppearance alloc] init];
19+
[appearance configureWithOpaqueBackground];
20+
self.navigationController.navigationBar.standardAppearance = appearance;
21+
self.navigationController.navigationBar.scrollEdgeAppearance = appearance;
22+
}
23+
}
24+
25+
- (void)clearAuthCache:(id)sender {
26+
UIAlertController *alert =
27+
[UIAlertController alertControllerWithTitle:@"Clear Cache"
28+
message:@"This will require re-authentication for all protected apps and reset the timer. Continue?"
29+
preferredStyle:UIAlertControllerStyleAlert];
30+
31+
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel"
32+
style:UIAlertActionStyleCancel
33+
handler:nil]];
34+
35+
[alert addAction:[UIAlertAction actionWithTitle:@"Clear"
36+
style:UIAlertActionStyleDestructive
37+
handler:^(UIAlertAction *action) {
38+
39+
CFNotificationCenterPostNotification(
40+
CFNotificationCenterGetDarwinNotifyCenter(),
41+
CFSTR("com.batues.biolock/ClearCache"),
42+
NULL,
43+
NULL,
44+
YES
45+
);
46+
47+
[self showCompletionAlert:@"Authentication cache cleared successfully."];
48+
}]];
49+
50+
[self presentViewController:alert animated:YES completion:nil];
51+
}
52+
53+
- (void)resetSettings:(id)sender {
54+
UIAlertController *alert =
55+
[UIAlertController alertControllerWithTitle:@"Reset Settings"
56+
message:@"This will reset all BioLock settings to defaults. This action cannot be undone."
57+
preferredStyle:UIAlertControllerStyleAlert];
58+
59+
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel"
60+
style:UIAlertActionStyleCancel
61+
handler:nil]];
62+
63+
[alert addAction:[UIAlertAction actionWithTitle:@"Reset"
64+
style:UIAlertActionStyleDestructive
65+
handler:^(UIAlertAction *action) {
66+
67+
NSString *prefsPath = @"/var/mobile/Library/Preferences/com.batues.biolock.plist";
68+
[[NSFileManager defaultManager] removeItemAtPath:prefsPath error:nil];
69+
70+
CFStringRef appID = CFSTR("com.batues.biolock");
71+
CFPreferencesAppSynchronize(appID);
72+
73+
CFNotificationCenterPostNotification(
74+
CFNotificationCenterGetDarwinNotifyCenter(),
75+
CFSTR("com.batues.biolock/ReloadPrefs"),
76+
NULL,
77+
NULL,
78+
YES
79+
);
80+
81+
[self reloadSpecifiers];
82+
[self showCompletionAlert:@"All settings have been reset to defaults."];
83+
}]];
84+
85+
[self presentViewController:alert animated:YES completion:nil];
86+
}
87+
88+
- (void)openGitHub:(id)sender {
89+
NSString *urlString = @"https://github.com/BatuBey5G/BioLock";
90+
NSURL *url = [NSURL URLWithString:urlString];
91+
if (url) {
92+
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
93+
}
94+
}
95+
96+
- (void)showCompletionAlert:(NSString *)message {
97+
UIAlertController *alert =
98+
[UIAlertController alertControllerWithTitle:@"Success"
99+
message:message
100+
preferredStyle:UIAlertControllerStyleAlert];
101+
102+
[alert addAction:[UIAlertAction actionWithTitle:@"OK"
103+
style:UIAlertActionStyleDefault
104+
handler:nil]];
105+
106+
[self presentViewController:alert animated:YES completion:nil];
107+
}
108+
109+
@end

biolockprefs/Makefile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
include $(THEOS)/makefiles/common.mk
2+
3+
BUNDLE_NAME = biolockprefs
4+
5+
biolockprefs_FILES = BLPRootListController.m
6+
biolockprefs_FRAMEWORKS = UIKit
7+
biolockprefs_PRIVATE_FRAMEWORKS = Preferences
8+
biolockprefs_INSTALL_PATH = /Library/PreferenceBundles
9+
biolockprefs_CFLAGS = -fobjc-arc
10+
biolockprefs_EXTRA_FRAMEWORKS = AltList
11+
12+
include $(THEOS_MAKE_PATH)/bundle.mk

biolockprefs/Resources/Info.plist

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>CFBundleDevelopmentRegion</key>
6+
<string>English</string>
7+
<key>CFBundleExecutable</key>
8+
<string>biolockprefs</string>
9+
<key>CFBundleIdentifier</key>
10+
<string>com.batues.biolock.prefs</string>
11+
<key>CFBundleInfoDictionaryVersion</key>
12+
<string>6.0</string>
13+
<key>CFBundleName</key>
14+
<string>biolockprefs</string>
15+
<key>CFBundlePackageType</key>
16+
<string>BNDL</string>
17+
<key>CFBundleShortVersionString</key>
18+
<string>1.1.0</string>
19+
<key>CFBundleVersion</key>
20+
<string>1.1</string>
21+
<key>MinimumOSVersion</key>
22+
<string>15.0</string>
23+
<key>NSPrincipalClass</key>
24+
<string>BLPRootListController</string>
25+
</dict>
26+
</plist>

0 commit comments

Comments
 (0)