diff --git a/FeatureUnlock/kern_start.cpp b/FeatureUnlock/kern_start.cpp index 32d0008..47cfc94 100644 --- a/FeatureUnlock/kern_start.cpp +++ b/FeatureUnlock/kern_start.cpp @@ -8,6 +8,7 @@ #include #include #include +#include "kern_uc_model.hpp" #include #include #include "kern_dyld_patch.hpp" @@ -18,6 +19,8 @@ // Original function pointers static mach_vm_address_t orig_cs_validate {}; +static UCModelSpoof ucModelSpoof; + // Boot-arg configurations bool allow_sidecar_ipad; @@ -724,6 +727,13 @@ static void pluginStart() { detectMachineProperties(); detectSupportedPatchSets(); detectNumberOfPatches(); + + // Initialize Universal Control model spoofing for blacklisted models + if (UCModelSpoof::isBlacklistedModel()) { + DBGLOG(MODULE_SHORT, "Initializing UC model spoofing for blacklisted Mac"); + ucModelSpoof.init(); + } + lilu.onPatcherLoadForce([](void *user, KernelPatcher &patcher) { KernelPatcher::RouteRequest csRoute = getKernelVersion() >= KernelVersion::BigSur ? @@ -731,6 +741,9 @@ static void pluginStart() { KernelPatcher::RouteRequest("_cs_validate_range", patched_cs_validate_range, orig_cs_validate); if (!patcher.routeMultipleLong(KernelPatcher::KernelID, &csRoute, 1)) SYSLOG(MODULE_SHORT, "failed to route cs validation pages"); + if (UCModelSpoof::isBlacklistedModel()) { + ucModelSpoof.processKernelPatches(patcher); + } }); } diff --git a/FeatureUnlock/kern_uc_model.cpp b/FeatureUnlock/kern_uc_model.cpp new file mode 100644 index 0000000..ca68562 --- /dev/null +++ b/FeatureUnlock/kern_uc_model.cpp @@ -0,0 +1,166 @@ +// +// kern_uc_model.cpp +// FeatureUnlock.kext +// +// Universal Control Model Spoofing +// Intercepts sysctlbyname for UC processes to spoof blacklisted models +// + +#include "kern_uc_model.hpp" +#include +#include + +// Static member initialization +int (*UCModelSpoof::orgSysctlbyname)(const char *, void *, size_t *, void *, size_t) = nullptr; + +// Shared blacklist of models that don't support Universal Control +static const char *UC_BLACKLISTED_MODELS[] = { + "MacBookPro12,1", + "MacBookPro11,4", + "MacBookPro11,5", + "MacBookAir7,1", + "MacBookAir7,2", + "iMac16,1", + "iMac16,2", + "Macmini7,1", + "MacPro6,1" +}; + +void UCModelSpoof::init() { + DBGLOG("featureunlock", "UC: Initializing Universal Control model spoofing"); + // TODO: Add UCModelSpoof-specific initialization here if needed in future. +} + +bool UCModelSpoof::isBlacklistedModel() { + char model[64] = {}; + size_t len = sizeof(model); + + if (sysctlbyname("hw.model", model, &len, nullptr, 0) != 0) { + return false; + } + + for (size_t i = 0; i < arrsize(UC_BLACKLISTED_MODELS); i++) { + if (strcmp(model, UC_BLACKLISTED_MODELS[i]) == 0) { + DBGLOG("featureunlock", "UC: Detected blacklisted model: %s", model); + return true; + } + } + + return false; +} + +void UCModelSpoof::processKernelPatches(KernelPatcher &patcher) { + if (!isBlacklistedModel()) { + DBGLOG("featureunlock", "UC: Model not blacklisted, skipping patch"); + return; + } + + if (!hookSysctlByName(patcher)) { + SYSLOG("featureunlock", "UC: Failed to hook sysctlbyname"); + } +} + +bool UCModelSpoof::hookSysctlByName(KernelPatcher &patcher) { + DBGLOG("featureunlock", "UC: Attempting to hook sysctlbyname"); + + // Find sysctlbyname in the kernel + mach_vm_address_t kern_sysctlbyname = patcher.solveSymbol( + KernelPatcher::KernelID, + "_sysctlbyname" + ); + + if (!kern_sysctlbyname) { + SYSLOG("featureunlock", "UC: Failed to find sysctlbyname symbol"); + return false; + } + + // Route the function to our hook + if (patcher.routeFunction( + kern_sysctlbyname, + reinterpret_cast(hookedSysctlbyname), + reinterpret_cast(&orgSysctlbyname) + )) { + DBGLOG("featureunlock", "UC: Successfully hooked sysctlbyname"); + return true; + } + + SYSLOG("featureunlock", "UC: Failed to route sysctlbyname"); + return false; +} + +int UCModelSpoof::hookedSysctlbyname(const char *name, void *oldp, size_t *oldlenp, + void *newp, size_t newlen) { + // Call original function first + int result = orgSysctlbyname(name, oldp, oldlenp, newp, newlen); + + // Only process successful hw.model queries + if (result != 0 || name == nullptr || oldp == nullptr || oldlenp == nullptr || + strcmp(name, "hw.model") != 0) { + return result; + } + + // Get calling process name + proc_t proc = current_proc(); + if (proc == nullptr) { + return result; + } + + char procname[MAXCOMLEN + 1] = {}; + proc_name(proc_pid(proc), procname, sizeof(procname)); + + // Check if caller is a Universal Control-related process + const char *uc_processes[] = { + "UniversalControl", + "sharingd", + "rapportd", + "ControlCenter" + }; + + bool isUCProcess = false; + for (size_t i = 0; i < arrsize(uc_processes); i++) { + if (strcmp(procname, uc_processes[i]) == 0) { + isUCProcess = true; + break; + } + } + + if (!isUCProcess) { + return result; + } + + // Check if the returned model is blacklisted + char *model = static_cast(oldp); + + // Ensure null termination + if (*oldlenp == 0 || model[*oldlenp - 1] != '\0') { + DBGLOG("featureunlock", "UC: hw.model buffer not null-terminated, skipping spoof"); + return result; + } + + bool isBlacklisted = false; + for (size_t i = 0; i < arrsize(UC_BLACKLISTED_MODELS); i++) { + if (strcmp(model, UC_BLACKLISTED_MODELS[i]) == 0) { + isBlacklisted = true; + break; + } + } + + if (!isBlacklisted) { + return result; + } + + // Spoof to MacBookPro16,4 (2019 16" MBP - more future-proof) + const char *spoofed_model = "MacBookPro16,4"; + size_t spoofed_len = strlen(spoofed_model) + 1; + if (*oldlenp < spoofed_len) { + DBGLOG("featureunlock", "UC: Buffer too small (%zu < %zu)", *oldlenp, spoofed_len); + return result; + } + strlcpy(model, spoofed_model, *oldlenp); + *oldlenp = spoofed_len; + + DBGLOG("featureunlock", "UC: Spoofed model to %s for process %s", + spoofed_model, procname); + + return result; +} diff --git a/FeatureUnlock/kern_uc_model.hpp b/FeatureUnlock/kern_uc_model.hpp new file mode 100644 index 0000000..848b83f --- /dev/null +++ b/FeatureUnlock/kern_uc_model.hpp @@ -0,0 +1,50 @@ +// +// kern_uc_model.hpp +// FeatureUnlock.kext +// +// Universal Control Model Spoofing +// Fixes Universal Control on blacklisted Macs (MacBookPro12,1, etc.) +// + +#ifndef kern_uc_model_hpp +#define kern_uc_model_hpp + +#include + +class UCModelSpoof { +public: + /** + * Initialize Universal Control model spoofing + */ + void init(); + + /** + * Check if current Mac model is blacklisted for UC + */ + static bool isBlacklistedModel(); + + /** + * Process kernel patches for Universal Control + */ + void processKernelPatches(KernelPatcher &patcher); + +private: + /** + * Hook sysctlbyname to spoof model for UC processes + */ + bool hookSysctlByName(KernelPatcher &patcher); + + /** + * Original sysctlbyname function pointer + */ + static int (*orgSysctlbyname)(const char *name, void *oldp, size_t *oldlenp, + void *newp, size_t newlen); + + /** + * Hooked sysctlbyname implementation + */ + static int hookedSysctlbyname(const char *name, void *oldp, size_t *oldlenp, + void *newp, size_t newlen); +}; + +#endif /* kern_uc_model_hpp */