feat: Unlock password-locked reels (auto unlock and view passowrd)

This commit is contained in:
faroukbmiled
2026-04-05 03:51:09 +01:00
parent b99c20a254
commit 5282d67103
5 changed files with 199 additions and 1 deletions
+1
View File
@@ -51,6 +51,7 @@ A feature-rich iOS tweak for Instagram, forked from [SCInsta](https://github.com
- Always show progress scrubber
- Disable auto-unmuting reels (properly blocks mute switch, volume buttons, and announcer broadcasts) **\***
- Confirm reel refresh
- Unlock password-locked reels **\***
- Hide reels header
- Hide reels blend button
- Disable scrolling reels
+189
View File
@@ -0,0 +1,189 @@
#import "../../InstagramHeaders.h"
#import "../../Utils.h"
#import <objc/runtime.h>
#import <objc/message.h>
// Password-locked reels use IGMediaOverlayProfileWithPasswordView as a blur overlay.
// The password is stored in the _asnwer ivar (IG typo). We read it at runtime,
// then provide buttons to auto-fill + submit or reveal + copy the password.
#define SCI_PW_BTN_TAG 1342
static NSString * _Nullable sciGetPassword(id overlayView) {
Class cls = [overlayView class];
while (cls && cls != [UIView class]) {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(cls, &count);
for (unsigned int i = 0; i < count; i++) {
const char *name = ivar_getName(ivars[i]);
if (name && strcmp(name, "_asnwer") == 0) {
id value = object_getIvar(overlayView, ivars[i]);
free(ivars);
if ([value isKindOfClass:[NSString class]] && [(NSString *)value length] > 0)
return (NSString *)value;
return nil;
}
}
if (ivars) free(ivars);
cls = class_getSuperclass(cls);
}
// Fallback: scan for any password-related string ivar
cls = [overlayView class];
while (cls && cls != [UIView class]) {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(cls, &count);
for (unsigned int i = 0; i < count; i++) {
const char *type = ivar_getTypeEncoding(ivars[i]);
if (!type || type[0] != '@') continue;
@try {
id value = object_getIvar(overlayView, ivars[i]);
if (![value isKindOfClass:[NSString class]] || [(NSString *)value length] == 0) continue;
const char *name = ivar_getName(ivars[i]);
if (!name) continue;
NSString *lower = [[NSString stringWithUTF8String:name] lowercaseString];
if ([lower containsString:@"answer"] || [lower containsString:@"asnwer"] ||
[lower containsString:@"password"] || [lower containsString:@"secret"]) {
free(ivars);
return (NSString *)value;
}
} @catch(id e) {}
}
if (ivars) free(ivars);
cls = class_getSuperclass(cls);
}
return nil;
}
static UITextField * _Nullable sciFindTextField(UIView *root) {
NSMutableArray *stack = [NSMutableArray arrayWithObject:root];
while (stack.count > 0) {
UIView *v = stack.lastObject;
[stack removeLastObject];
if ([v isKindOfClass:[UITextField class]]) return (UITextField *)v;
for (UIView *sub in v.subviews) [stack addObject:sub];
}
return nil;
}
static UIView * _Nullable sciFindSubmitButton(UIView *root) {
NSMutableArray *stack = [NSMutableArray arrayWithObject:root];
while (stack.count > 0) {
UIView *v = stack.lastObject;
[stack removeLastObject];
if ([NSStringFromClass([v class]) containsString:@"IGDSMediaTextButton"]) return v;
for (UIView *sub in v.subviews) [stack addObject:sub];
}
return nil;
}
%hook IGMediaOverlayProfileWithPasswordView
- (void)didMoveToSuperview {
%orig;
if (!self.superview) return;
if (![SCIUtils getBoolPref:@"unlock_password_reels"]) return;
[self sciAddButtons];
}
- (void)layoutSubviews {
%orig;
if (![SCIUtils getBoolPref:@"unlock_password_reels"]) return;
[self sciAddButtons];
}
%new - (void)sciAddButtons {
if ([self viewWithTag:SCI_PW_BTN_TAG]) return;
UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:18 weight:UIImageSymbolWeightBold];
UIButton *unlockBtn = [UIButton buttonWithType:UIButtonTypeCustom];
unlockBtn.tag = SCI_PW_BTN_TAG;
[unlockBtn setImage:[UIImage systemImageNamed:@"lock.open.fill" withConfiguration:config] forState:UIControlStateNormal];
unlockBtn.tintColor = [UIColor colorWithRed:1.0 green:0.85 blue:0.0 alpha:1.0];
unlockBtn.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.5];
unlockBtn.layer.cornerRadius = 20;
unlockBtn.translatesAutoresizingMaskIntoConstraints = NO;
[unlockBtn addTarget:self action:@selector(sciUnlockTapped) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:unlockBtn];
UIButton *eyeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
eyeBtn.tag = SCI_PW_BTN_TAG + 1;
[eyeBtn setImage:[UIImage systemImageNamed:@"eye.fill" withConfiguration:config] forState:UIControlStateNormal];
eyeBtn.tintColor = [UIColor whiteColor];
eyeBtn.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.5];
eyeBtn.layer.cornerRadius = 20;
eyeBtn.translatesAutoresizingMaskIntoConstraints = NO;
[eyeBtn addTarget:self action:@selector(sciShowPasswordTapped) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:eyeBtn];
[NSLayoutConstraint activateConstraints:@[
[unlockBtn.topAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.topAnchor constant:200],
[unlockBtn.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-16],
[unlockBtn.widthAnchor constraintEqualToConstant:40],
[unlockBtn.heightAnchor constraintEqualToConstant:40],
[eyeBtn.topAnchor constraintEqualToAnchor:unlockBtn.bottomAnchor constant:12],
[eyeBtn.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-16],
[eyeBtn.widthAnchor constraintEqualToConstant:40],
[eyeBtn.heightAnchor constraintEqualToConstant:40],
]];
}
%new - (void)sciUnlockTapped {
UIImpactFeedbackGenerator *haptic = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleMedium];
[haptic impactOccurred];
NSString *password = sciGetPassword(self);
if (!password) {
[SCIUtils showErrorHUDWithDescription:@"No password found"];
return;
}
UITextField *textField = sciFindTextField(self);
if (!textField) {
[SCIUtils showErrorHUDWithDescription:@"No text field found"];
return;
}
textField.text = password;
[textField sendActionsForControlEvents:UIControlEventEditingChanged];
[[NSNotificationCenter defaultCenter] postNotificationName:UITextFieldTextDidChangeNotification object:textField];
if (textField.delegate) {
if ([textField.delegate respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)])
[textField.delegate textField:textField shouldChangeCharactersInRange:NSMakeRange(0, 0) replacementString:password];
if ([textField.delegate respondsToSelector:@selector(textFieldDidChangeSelection:)])
[textField.delegate textFieldDidChangeSelection:textField];
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
UIView *submitBtn = sciFindSubmitButton(self);
if (submitBtn && [submitBtn isKindOfClass:[UIControl class]]) {
[(UIControl *)submitBtn setHidden:NO];
[(UIControl *)submitBtn sendActionsForControlEvents:UIControlEventTouchUpInside];
}
});
}
%new - (void)sciShowPasswordTapped {
UIImpactFeedbackGenerator *haptic = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight];
[haptic impactOccurred];
NSString *password = sciGetPassword(self);
if (!password) {
[SCIUtils showErrorHUDWithDescription:@"No password found"];
return;
}
[[UIPasteboard generalPasteboard] setString:password];
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Password"
message:password
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Copied!" style:UIAlertActionStyleCancel handler:nil]];
UIViewController *topVC = topMostController();
if (topVC) [topVC presentViewController:alert animated:YES completion:nil];
}
%end
+6
View File
@@ -425,6 +425,12 @@
@interface IGSundialViewerNavigationBarOld : UIView
@end
@interface IGMediaOverlayProfileWithPasswordView : UIView
- (void)sciAddButtons;
- (void)sciUnlockTapped;
- (void)sciShowPasswordTapped;
@end
@interface IGUFIInteractionCountsView : UIView
@end
+1
View File
@@ -85,6 +85,7 @@
[SCISetting switchCellWithTitle:@"Always show progress scrubber" subtitle:@"Forces the progress bar to appear on every reel" defaultsKey:@"reels_show_scrubber"],
[SCISetting switchCellWithTitle:@"Disable auto-unmuting reels" subtitle:@"Prevents reels from unmuting when the volume/silent button is pressed" defaultsKey:@"disable_auto_unmuting_reels" requiresRestart:YES],
[SCISetting switchCellWithTitle:@"Confirm reel refresh" subtitle:@"Shows an alert when you trigger a reels refresh" defaultsKey:@"refresh_reel_confirm"],
[SCISetting switchCellWithTitle:@"Unlock password-locked reels" subtitle:@"Shows buttons to reveal and auto-fill the password on locked reels" defaultsKey:@"unlock_password_reels"],
]
},
@{
+2 -1
View File
@@ -46,7 +46,8 @@ BOOL dmVisualMsgsViewedButtonEnabled = false;
@"disable_auto_unmuting_reels": @(YES),
@"doom_scrolling_reel_count": @(1),
@"no_seen_visual": @(YES),
@"send_audio_as_file": @(YES)
@"send_audio_as_file": @(YES),
@"unlock_password_reels": @(YES)
};
[[NSUserDefaults standardUserDefaults] registerDefaults:sciDefaults];