Issue I fixed
Universal Control fails on blacklisted Macs (MacBookPro12,1 and others) even when using FeatureUnlock UC patches, because the outgoing model identifier is not spoofed. Local checks are patched, but remote devices still see the original blacklisted model and reject the connection.
Root cause
Current FeatureUnlock implementation patches UniversalControl.app binaries (userspace) so that local compatibility checks pass, but it does not affect the kernel-level sysctlbyname("hw.model") calls used by UC‑related daemons (UniversalControl, sharingd, rapportd, ControlCenter) when advertising the Mac to other devices. Those calls still return the real model (for example MacBookPro12,1), which is on Apple’s UC blacklist, so iPad/Macs refused the connection.
my solution
Add a small kernel component to FeatureUnlock that:
- Hooks
sysctlbyname("hw.model") via Lilu’s KernelPatcher
- Activates only for UC‑related processes (
UniversalControl, sharingd, rapportd, ControlCenter)
- Checks for a list of known UC‑blacklisted models (MacBookPro12,1, MacBookAir7,1, Macmini7,1, etc.)
- For those models, spoofs the result to a supported model (currently
MacBookPro16,4) and updates the reported length
This leaves all other processes and sysctl calls untouched.
Implementation
I made this as:
kern_uc_model.cpp / kern_uc_model.hpp: UCModelSpoof class with
isBlacklistedModel() helper
hookSysctlByName() using KernelPatcher::routeFunction on _sysctlbyname
hookedSysctlbyname() that filters by process name and model blacklist, then spoofs to MacBookPro16,4
- Integration into
FeatureUnlock.kext init so the hook is set up only when the current machine model is in the UC blacklist.
The code builds successfully with the current FeatureUnlock + Lilu + MacKernelSDK toolchain and loads via kextload on a MacBookPro12,1 running macOS Sequoia 15.7.3 with OCLP.
Testing status
- Kext builds and loads successfully.
- Logging confirms:
- blacklist detection
sysctlbyname hook installation
- spoofing path taken for UC‑related processes.
- I do not currently have an iPad / second Mac to perform a full Universal Control connection test; I’m looking for support and testing help from maintainers.
Implementation is available here:
https://github.com/Cooldode/FeatureUnlock/tree/universal-control-fix
If this approach is acceptable, I can keep the branch updated and open a PR
Issue I fixed
Universal Control fails on blacklisted Macs (MacBookPro12,1 and others) even when using FeatureUnlock UC patches, because the outgoing model identifier is not spoofed. Local checks are patched, but remote devices still see the original blacklisted model and reject the connection.
Root cause
Current FeatureUnlock implementation patches UniversalControl.app binaries (userspace) so that local compatibility checks pass, but it does not affect the kernel-level
sysctlbyname("hw.model")calls used by UC‑related daemons (UniversalControl,sharingd,rapportd,ControlCenter) when advertising the Mac to other devices. Those calls still return the real model (for exampleMacBookPro12,1), which is on Apple’s UC blacklist, so iPad/Macs refused the connection.my solution
Add a small kernel component to FeatureUnlock that:
sysctlbyname("hw.model")via Lilu’sKernelPatcherUniversalControl,sharingd,rapportd,ControlCenter)MacBookPro16,4) and updates the reported lengthThis leaves all other processes and sysctl calls untouched.
Implementation
I made this as:
kern_uc_model.cpp/kern_uc_model.hpp:UCModelSpoofclass withisBlacklistedModel()helperhookSysctlByName()usingKernelPatcher::routeFunctionon_sysctlbynamehookedSysctlbyname()that filters by process name and model blacklist, then spoofs toMacBookPro16,4FeatureUnlock.kextinit so the hook is set up only when the current machine model is in the UC blacklist.The code builds successfully with the current FeatureUnlock + Lilu + MacKernelSDK toolchain and loads via
kextloadon a MacBookPro12,1 running macOS Sequoia 15.7.3 with OCLP.Testing status
sysctlbynamehook installationImplementation is available here:
https://github.com/Cooldode/FeatureUnlock/tree/universal-control-fixIf this approach is acceptable, I can keep the branch updated and open a PR