Skip to content

Instantly share code, notes, and snippets.

@theevilbit
Last active June 17, 2024 21:56
Show Gist options
  • Save theevilbit/cea32b23195f740e0a270d205ed50613 to your computer and use it in GitHub Desktop.
Save theevilbit/cea32b23195f740e0a270d205ed50613 to your computer and use it in GitHub Desktop.
Tools for working with widgets
#!/bin/bash
# Check for required command line arguments
if [ $# -ne 2 ]; then
echo "Usage: $0 <input_file> <output_dir>"
exit 1
fi
input_file=$1
output_dir=$2
# Check if the input file exists
if [ ! -f "$input_file" ]; then
echo "Error: Input file does not exist."
exit 1
fi
# Check if the output directory exists, if not create it
if [ ! -d "$output_dir" ]; then
mkdir -p "$output_dir"
fi
# Initialize variables
inside_data=0
base64_string=""
file_counter=1
# Process each line from the input file
while IFS= read -r line
do
# Check if the line contains the opening <data> tag
if [[ $line =~ \<data\> ]]; then
inside_data=1
continue
fi
# Check if the line contains the closing </data> tag
if [[ $line =~ \</data\> ]]; then
inside_data=0
# Decode and write to a new file
output_file="$output_dir/decoded$file_counter.txt"
echo "$base64_string" | base64 --decode 2>/dev/null | plutil -convert xml1 -o - - > "$output_file"
base64_string=""
((file_counter++))
continue
fi
# If we are inside a <data> tag, remove all spaces and append to base64_string
if [ $inside_data -eq 1 ]; then
base64_string+=$(echo "$line" | tr -d '[:space:]')
fi
done < "$input_file"
# Check for any remaining base64 data after the last line (just in case)
if [ -n "$base64_string" ]; then
output_file="$output_dir/decoded$file_counter.txt"
echo "$base64_string" | base64 --decode > "$output_file"
fi
#import <Foundation/Foundation.h>
@interface WidgetHandler : NSObject
+ (void)processWidgetsFromPlist:(NSString *)plistPath;
@end
@implementation WidgetHandler
+ (void)processWidgetsFromPlist:(NSString *)plistPath {
// Load required frameworks
if (![self loadFrameworkWithPath:@"/System/Library/PrivateFrameworks/ChronoServices.framework" className:@"CHSWidget"]) {
return;
}
if (![self loadFrameworkWithPath:@"/System/Library/Frameworks/Intents.framework" className:@"INIntent"]) {
return;
}
if (![self loadFrameworkWithPath:@"/System/Library/PrivateFrameworks/ChronoServices.framework" className:@"CHSWidgetDescriptor"]) {
return;
}
NSLog(@"Successfully loaded required frameworks.");
// Load the plist file
NSDictionary *plistDict = [NSDictionary dictionaryWithContentsOfFile:plistPath];
if (!plistDict) {
NSLog(@"Error: Unable to load plist from path: %@", plistPath);
return;
}
NSLog(@"Successfully loaded plist file.");
// Retrieve the array of widget instances under "widgets.instances"
NSArray *instances = [plistDict valueForKeyPath:@"widgets.instances"];
if (!instances) {
NSLog(@"Error: 'instances' key not found in the 'widgets' dictionary.");
return;
}
NSLog(@"Successfully retrieved widget instances.");
// Process each instance
for (NSData *instanceData in instances) {
NSError *error = nil;
NSDictionary *instanceDict = [NSPropertyListSerialization propertyListWithData:instanceData options:NSPropertyListImmutable format:nil error:&error];
if (error) {
NSLog(@"Error decoding instance data to NSDictionary: %@", error.localizedDescription);
continue;
}
NSString *uuid = [instanceDict objectForKey:@"uuid"];
NSData *widgetData = [instanceDict objectForKey:@"widget"];
if (uuid && widgetData) {
NSLog(@"Processing UUID: %@", uuid);
[self decodeAndLogWidgetData:widgetData];
} else {
NSLog(@"Error: Missing 'uuid' or 'widget' data in instance.");
}
}
// Retrieve and decode the array of widget descriptors
NSArray *descriptorPlists = [plistDict valueForKeyPath:@"widgets.widgets"];
if (!descriptorPlists) {
NSLog(@"Error: 'widgets' key not found in the 'widgets' dictionary.");
return;
}
NSLog(@"Successfully retrieved array of widget descriptor plists.");
for (NSData *descriptorPlist in descriptorPlists) {
NSError *error = nil;
NSDictionary *descriptorDict = [NSPropertyListSerialization propertyListWithData:descriptorPlist options:NSPropertyListImmutable format:nil error:&error];
if (error) {
NSLog(@"Error decoding descriptor plist to NSDictionary: %@", error.localizedDescription);
continue;
}
NSData *descriptorData = [descriptorDict objectForKey:@"encodedDescriptor"];
if (descriptorData) {
[self decodeAndLogWidgetDescriptor:descriptorData];
} else {
NSLog(@"Error: 'encodedDescriptor' key not found in descriptor plist.");
}
}
}
+ (BOOL)loadFrameworkWithPath:(NSString *)frameworkPath className:(NSString *)className {
NSBundle *frameworkBundle = [NSBundle bundleWithPath:frameworkPath];
if (![frameworkBundle load]) {
NSLog(@"Error: Could not load framework at path: %@", frameworkPath);
return NO;
}
if (!NSClassFromString(className)) {
NSLog(@"Error: %@ class not found in framework at path: %@", className, frameworkPath);
return NO;
}
return YES;
}
+ (void)decodeAndLogWidgetData:(NSData *)widgetData {
NSError *error = nil;
id widgetObject = [NSKeyedUnarchiver unarchivedObjectOfClass:NSClassFromString(@"CHSWidget") fromData:widgetData error:&error];
if (error) {
NSLog(@"Error decoding widget: %@", error.localizedDescription);
} else {
NSLog(@"Decoded widget: %@", widgetObject);
// Retrieve and log the intent property
id intent = [widgetObject valueForKey:@"intent"];
if (intent) {
NSLog(@"Intent: %@", intent);
} else {
NSLog(@"No intent found in widget object.");
}
}
}
+ (void)decodeAndLogWidgetDescriptor:(NSData *)descriptorData {
NSError *error = nil;
Class descriptorClass = NSClassFromString(@"CHSWidgetDescriptor");
id descriptorObject = [NSKeyedUnarchiver unarchivedObjectOfClass:descriptorClass fromData:descriptorData error:&error];
if (error) {
NSLog(@"Error decoding widget descriptor: %@", error.localizedDescription);
} else {
NSLog(@"Decoded widget descriptor: %@", descriptorObject);
}
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
if (argc < 2) {
NSLog(@"Error: No plist file path provided.");
return 1;
}
NSString *plistPath = [NSString stringWithUTF8String:argv[1]];
[WidgetHandler processWidgetsFromPlist:plistPath];
}
return 0;
}
import Foundation
class WidgetHandler {
static func processWidgets(fromPlist plistPath: String) {
// Load required frameworks
guard loadFramework(atPath: "/System/Library/PrivateFrameworks/ChronoServices.framework", className: "CHSWidget"),
loadFramework(atPath: "/System/Library/Frameworks/Intents.framework", className: "INIntent"),
loadFramework(atPath: "/System/Library/PrivateFrameworks/ChronoServices.framework", className: "CHSWidgetDescriptor") else {
return
}
print("Successfully loaded required frameworks.")
// Load the plist file
guard let plistDict = NSDictionary(contentsOfFile: plistPath) else {
print("Error: Unable to load plist from path: \(plistPath)")
return
}
print("Successfully loaded plist file.")
// Retrieve the array of widget instances under "widgets.instances"
guard let instances = plistDict.value(forKeyPath: "widgets.instances") as? [Data] else {
print("Error: 'instances' key not found in the 'widgets' dictionary.")
return
}
print("Successfully retrieved widget instances.")
// Process each instance
for instanceData in instances {
do {
guard let instanceDict = try PropertyListSerialization.propertyList(from: instanceData, options: [], format: nil) as? [String: Any],
let uuid = instanceDict["uuid"] as? String,
let widgetData = instanceDict["widget"] as? Data else {
print("Error: Missing 'uuid' or 'widget' data in instance.")
continue
}
print("Processing UUID: \(uuid)")
decodeAndLogWidgetData(widgetData)
} catch {
print("Error decoding instance data to NSDictionary: (error.localizedDescription)")
}
}
// Retrieve and decode the array of widget descriptors
guard let descriptorPlists = plistDict.value(forKeyPath: "widgets.widgets") as? [Data] else {
print("Error: 'widgets' key not found in the 'widgets' dictionary.")
return
}
print("Successfully retrieved array of widget descriptor plists.")
for descriptorPlist in descriptorPlists {
do {
guard let descriptorDict = try PropertyListSerialization.propertyList(from: descriptorPlist, options: [], format: nil) as? [String: Any],
let descriptorData = descriptorDict["encodedDescriptor"] as? Data else {
print("Error: 'encodedDescriptor' key not found in descriptor plist.")
continue
}
decodeAndLogWidgetDescriptor(descriptorData)
} catch {
print("Error decoding descriptor plist to NSDictionary: \(error.localizedDescription)")
}
}
}
static func loadFramework(atPath frameworkPath: String, className: String) -> Bool {
guard let frameworkBundle = Bundle(path: frameworkPath), frameworkBundle.load() else {
print("Error: Could not load framework at path: \(frameworkPath)")
return false
}
guard NSClassFromString(className) != nil else {
print("Error: \(className) class not found in framework at path: \(frameworkPath)")
return false
}
return true
}
static func decodeAndLogWidgetData(_ widgetData: Data) {
do {
let widgetClass = NSClassFromString("CHSWidget") as? NSObject.Type
guard let widgetObject = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [widgetClass!], from: widgetData) as? NSObject,
let intent = widgetObject.value(forKey: "intent") else {
print("No intent found in widget object.")
return
}
print("Decoded widget: \(widgetObject)")
print("Intent: \(intent)")
} catch {
print("Error decoding widget: \(error.localizedDescription)")
}
}
static func decodeAndLogWidgetDescriptor(_ descriptorData: Data) {
do {
let descriptorClass = NSClassFromString("CHSWidgetDescriptor") as? NSObject.Type
guard let descriptorObject = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [descriptorClass!], from: descriptorData) as? NSObject else {
print("Error decoding widget descriptor: (error.localizedDescription)")
return
}
print("Decoded widget descriptor: \(descriptorObject)")
} catch {
print("Error decoding widget descriptor: (error.localizedDescription)")
}
}
}
// Main execution
if CommandLine.argc < 2 {
print("Error: No plist file path provided.")
exit(1)
}
let plistPath = String(cString: CommandLine.arguments[1])
WidgetHandler.processWidgets(fromPlist: plistPath)
#!/bin/zsh
STORE="/tmp/store"
WIDGETPLIST="/Users/$USER/Library/Containers/com.apple.notificationcenterui/Data/Library/Preferences/com.apple.notificationcenterui.plist"
BEFORE="com.apple.notificationcenterui-before.plist"
AFTER="com.apple.notificationcenterui-after.plist"
INSERTER="/tmp/inserter.sh"
echo "[i] This tool will create a script to nest a new widget in NotificationCenter settings"
echo "[i] Taking old config"
mkdir $STORE
cp $WIDGETPLIST $STORE/$BEFORE
if [ ! -f $STORE/$BEFORE ]; then
echo "[-] Failed to copy plist, ensure Terminal has access to the directory (TCC!!)..."
echo "[i] Exiting..."
exit 1
fi
read -s -k '?[!] Now add your widget to NotificationCenter via the GUI and then press any key'
echo ""
cp $WIDGETPLIST $STORE/$AFTER
if [ ! -f $STORE/$AFTER ]; then
echo "[-] Failed to copy plist, ensure Terminal has access to the directory (TCC!!)..."
echo "[i] Exiting..."
exit 1
fi
plutil -convert xml1 $STORE/$BEFORE
plutil -convert xml1 $STORE/$AFTER
ENTRY=$(diff $STORE/$BEFORE $STORE/$AFTER | grep \> | grep -v data\> | tr -d '\>' | tr -d ' ' | tr -d '\t' | tr -d '\n')
echo "[i] Creating inserter script at $INSERTER"
cat << EOF > $INSERTER
#!/bin/zsh
plutil -insert widgets.instances -data $ENTRY -append ~/Library/Containers/com.apple.notificationcenterui/Data/Library/Preferences/com.apple.notificationcenterui.plist
killall NotificationCenter
EOF
chmod +x $INSERTER
echo "[i] Cleaning up"
rm -rf $STORE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment