Skip to content

Instantly share code, notes, and snippets.

@dnicolson
Created July 1, 2026 17:39
Show Gist options
  • Select an option

  • Save dnicolson/a11918eaa5d793b050bb0e9912a387d0 to your computer and use it in GitHub Desktop.

Select an option

Save dnicolson/a11918eaa5d793b050bb0e9912a387d0 to your computer and use it in GitHub Desktop.
SpringBoardServices bug: Calling setIconState with the state returned by getIconState can reorder apps above a 2×2 large widget
#!/bin/sh
set -eu
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname "$0")" && pwd)"
BUILD_DIR="$SCRIPT_DIR/build"
mkdir -p "$BUILD_DIR"
clang \
-fobjc-arc \
-framework Foundation \
-o "$BUILD_DIR/sbservices-bug" \
"$SCRIPT_DIR/main.m"
echo "Built $BUILD_DIR/sbservices-bug"
#import <Foundation/Foundation.h>
#import <dlfcn.h>
#import <objc/runtime.h>
@interface MDKMobileDevice : NSObject
+ (NSArray *)connectedDevices;
- (NSString *)UDID;
- (BOOL)connectAndReturnError:(NSError **)error;
- (BOOL)startSessionAndReturnError:(NSError **)error;
- (BOOL)stopSessionAndReturnError:(NSError **)error;
- (BOOL)disconnectAndReturnError:(NSError **)error;
- (id)connectToSpringBoardServiceWithOptions:(NSDictionary *)options error:(NSError **)error;
@end
@interface MDKSpringBoardService : NSObject
- (id)iconStateWithError:(NSError **)error;
- (BOOL)setIconState:(id)state error:(NSError **)error;
- (void)invalidate;
@end
@interface MDKServiceConnection : NSObject
- (BOOL)sendMessage:(id)message error:(NSError **)error;
- (NSData *)receiveDataOfLength:(NSUInteger)length error:(NSError **)error;
@end
static BOOL DlopenBinary(NSString *path, NSError **error) {
void *handle = dlopen(path.fileSystemRepresentation, RTLD_NOW | RTLD_GLOBAL);
if (handle != NULL) {
return YES;
}
if (error != NULL) {
NSString *message = [NSString stringWithUTF8String:dlerror() ?: "unknown dlopen error"];
*error = [NSError errorWithDomain:@"sbservices-bug"
code:1
userInfo:@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Failed to load %@: %@", path, message]
}];
}
return NO;
}
static BOOL LoadMobileDeviceKit(NSError **error) {
NSString *binaryPath = @"/Applications/Apple Configurator.app/Contents/Frameworks/ConfigurationUtilityKit.framework/Versions/A/Frameworks/MobileDeviceKit.framework/Versions/A/MobileDeviceKit";
if (![[NSFileManager defaultManager] fileExistsAtPath:binaryPath]) {
if (error != NULL) {
*error = [NSError errorWithDomain:@"sbservices-bug"
code:1
userInfo:@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Missing framework binary at %@", binaryPath]
}];
}
return NO;
}
if (!DlopenBinary(binaryPath, error)) {
return NO;
}
return NSClassFromString(@"MDKMobileDevice") != nil && NSClassFromString(@"MDKSpringBoardService") != nil;
}
static id ResolveDevice(NSString *requestedUDID) {
Class deviceClass = NSClassFromString(@"MDKMobileDevice");
NSArray *devices = [deviceClass connectedDevices];
if (requestedUDID.length == 0) {
return devices.firstObject;
}
for (id device in devices) {
if ([[device UDID] isEqualToString:requestedUDID]) {
return device;
}
}
return nil;
}
static NSString *ReadbackDumpPath(void) {
NSString *currentDirectory = [[NSFileManager defaultManager] currentDirectoryPath];
return [currentDirectory stringByAppendingPathComponent:@"sbservices-bug-probe-before.plist"];
}
static NSString *AfterDumpPath(void) {
NSString *currentDirectory = [[NSFileManager defaultManager] currentDirectoryPath];
return [currentDirectory stringByAppendingPathComponent:@"sbservices-bug-probe-after.plist"];
}
static BOOL WritePlistDump(id object, NSString *path, NSError **error) {
if (object == nil) {
if (error != NULL) {
*error = [NSError errorWithDomain:@"sbservices-bug"
code:10
userInfo:@{
NSLocalizedDescriptionKey: @"Refusing to write a nil icon state payload."
}];
}
return NO;
}
NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:object
format:NSPropertyListXMLFormat_v1_0
options:0
error:error];
if (plistData == nil) {
return NO;
}
return [plistData writeToFile:path options:NSDataWritingAtomic error:error];
}
static MDKServiceConnection *ServiceConnectionForSpringBoardService(MDKSpringBoardService *springBoardService) {
Ivar ivar = class_getInstanceVariable([springBoardService class], "mServiceConnection");
if (ivar == NULL) {
return nil;
}
id value = object_getIvar(springBoardService, ivar);
if (![value isKindOfClass:NSClassFromString(@"MDKServiceConnection")]) {
return nil;
}
return (MDKServiceConnection *)value;
}
static BOOL RawSetIconState(MDKSpringBoardService *springBoardService, id state, NSError **error) {
MDKServiceConnection *serviceConnection = ServiceConnectionForSpringBoardService(springBoardService);
if (serviceConnection == nil) {
if (error != NULL) {
*error = [NSError errorWithDomain:@"sbservices-bug"
code:11
userInfo:@{
NSLocalizedDescriptionKey: @"Unable to access the underlying MDKServiceConnection for SpringBoard."
}];
}
return NO;
}
NSDictionary *message = @{
@"command": @"setIconState",
@"iconState": state,
};
if (![serviceConnection sendMessage:message error:error]) {
return NO;
}
NSData *response = [serviceConnection receiveDataOfLength:4 error:error];
if (response == nil) {
return NO;
}
NSLog(@"read raw setIconState response bytes=%lu", (unsigned long)response.length);
return YES;
}
int main(int argc, const char *argv[]) {
@autoreleasepool {
NSError *error = nil;
if (!LoadMobileDeviceKit(&error)) {
NSLog(@"framework load failed: %@", error.localizedDescription);
return 1;
}
NSString *requestedUDID = argc > 1 ? [NSString stringWithUTF8String:argv[1]] : nil;
id device = ResolveDevice(requestedUDID);
if (device == nil) {
NSLog(@"device not found%@", requestedUDID.length ? [NSString stringWithFormat:@": %@", requestedUDID] : @"");
return 1;
}
if (![device connectAndReturnError:&error]) {
NSLog(@"connect failed: %@", error.localizedDescription);
return 1;
}
if (![device startSessionAndReturnError:&error]) {
NSLog(@"startSession failed: %@", error.localizedDescription);
[device disconnectAndReturnError:nil];
return 1;
}
MDKSpringBoardService *springBoardService = [device connectToSpringBoardServiceWithOptions:nil error:&error];
if (springBoardService == nil) {
NSLog(@"connectToSpringBoardService failed: %@", error.localizedDescription);
[device stopSessionAndReturnError:nil];
[device disconnectAndReturnError:nil];
return 1;
}
id rawState = [springBoardService iconStateWithError:&error];
if (![rawState isKindOfClass:[NSArray class]]) {
NSLog(@"iconState failed: %@", error.localizedDescription ?: @"unexpected response");
[springBoardService invalidate];
[device stopSessionAndReturnError:nil];
[device disconnectAndReturnError:nil];
return 1;
}
if (!WritePlistDump(rawState, ReadbackDumpPath(), &error)) {
NSLog(@"readback dump failed: %@", error.localizedDescription);
[springBoardService invalidate];
[device stopSessionAndReturnError:nil];
[device disconnectAndReturnError:nil];
return 1;
}
NSLog(@"wrote raw readback payload to %@", ReadbackDumpPath());
if (!RawSetIconState(springBoardService, rawState, &error)) {
NSLog(@"raw setIconState failed: %@", error.localizedDescription);
[springBoardService invalidate];
[device stopSessionAndReturnError:nil];
[device disconnectAndReturnError:nil];
return 1;
}
NSLog(@"wrote icon state back unchanged via raw service connection");
id afterState = [springBoardService iconStateWithError:&error];
if (![afterState isKindOfClass:[NSArray class]]) {
NSLog(@"post-write iconState failed: %@", error.localizedDescription ?: @"unexpected response");
[springBoardService invalidate];
[device stopSessionAndReturnError:nil];
[device disconnectAndReturnError:nil];
return 1;
}
if (!WritePlistDump(afterState, AfterDumpPath(), &error)) {
NSLog(@"after dump failed: %@", error.localizedDescription);
[springBoardService invalidate];
[device stopSessionAndReturnError:nil];
[device disconnectAndReturnError:nil];
return 1;
}
NSLog(@"wrote post-write payload to %@", AfterDumpPath());
[springBoardService invalidate];
[device stopSessionAndReturnError:nil];
[device disconnectAndReturnError:nil];
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment