svc_stalker
svc_stalker copied to clipboard
iOS system call/Mach trap interception for checkra1n'able devices
svc_stalker

Output from intercepting some calls for Call of Duty: Mobile from example/mini_strace.c
This project has been made obsolete by xnuspy
svc_stalker is a pongoOS module which modifies XNU to call exception_triage
during supervisor call exceptions, sending a Mach exception message to userland
exception ports.
svc_stalker supports iOS 13.x and iOS 14.x on checkra1n 0.11.0 and up. Devices with a 4K page size are not supported.
When catch_mach_exception_raise is called, the PID of the process which made
the call is placed in code[0]. code[1] will either be BEFORE_CALL (0)
or CALL_COMPLETED (1). BEFORE_CALL means the call has not happened yet.
CALL_COMPLETED means the call has completed, but control hasn't been returned
back to the process which made it. You can find the call number in the lower
32 bits of x16 inside the saved state of the thread parameter. exception
will hold either EXC_SYSCALL or EXC_MACH_SYSCALL, depending on what was
intercepted.
For both BEFORE_CALL and CALL_COMPLETED, you're free to view/modify
registers in your exception handler before giving control back to the kernel.
svc_stalker adds a new sysctl, kern.svc_stalker_ctl_callnum, so you can
figure out which system call was patched to svc_stalker_ctl:
size_t oldlen = sizeof(long);
long SYS_svc_stalker_ctl = 0;
sysctlbyname("kern.svc_stalker_ctl_callnum", &SYS_svc_stalker_ctl, &oldlen, NULL, 0);
Requires libusb: brew install libusb
Requires perl: brew install perl
Building
Run make inside the top level directory. It'll build the loader and the module.
Usage
After you've built everything, have checkra1n boot your device to a pongo
shell: /Applications/checkra1n.app/Contents/MacOS/checkra1n -p
In the same directory you built the loader and the module, do
loader/loader module/svc_stalker. svc_stalker will patch XNU and in a few
seconds your device will boot. loader will wait a couple more seconds after
issuing stalker-getkernelv in case SEPROM needs to be exploited.
Known Issues
Sometimes a couple of my phones would get stuck at "Booting" after checkra1n's KPF
runs. I have yet to figure out what causes this, but if it happens, try again.
Also, if the device hangs after bootx, try again.
svc_stalker_ctl
svc_stalker will patch the first _enosys system call to instead represent
svc_stalker_ctl. You can find its implementation at module/el1/svc_stalker_ctl.s
and example usage at example/mini_strace.c.
svc_stalker_ctl is your way of managing call interception for different
processes. It takes four arguments, pid, flavor, arg2, and arg3,
respectively. pid is obviously the process you wish to interact with.
flavor is either PID_MANAGE (0) or CALL_LIST_MANAGE (1).
For PID_MANAGE, arg2 controls whether or not calls are intercepted for
pid. arg3 is ignored. If arg2 is non-zero, interception is enabled for
pid. Otherwise, it's disabled. Enabling call interception for pid creates
a table entry for it. When you disable call interception, its table entry and
call list (if present) are freed.
For CALL_LIST_MANAGE, arg2 is a call number, and arg3 is a boolean. If
arg3 is non-zero, interception for arg2 is enabled for pid. Otherwise,
it's disabled. The call list for pid's table entry is created the first time
you add a call number to intercept.
For both flavor arguments, 0 is returned on success.
You can check if svc_stalker_ctl is working right by doing
syscall(<svc_stalker_ctl's syscall number>, -1, PID_MANAGE, 0, 0);.
If it is working correctly, it will return 999. arg2 and arg3 don't matter
in this case.
Errors
Upon error, -1 is returned and errno is set.
General Errors
- Any
flavorbesidesPID_MANAGEandCALL_LIST_MANAGEreturn an error anderrnois set toEINVAL.
Errors Pertaining to PID_MANAGE
errno is set to...
EINVALif:pidis less than-1.
ENOENTif:- You tried to disable call interception for
pidwhen it was never enabled.
- You tried to disable call interception for
ENOSPCif:- There are no more free table entries. This should never happen as long as you are disabling interception for PIDs you no longer wish to intercept calls for.
EEXISTif:- You tried to enable call interception for
pidwhen it was already enabled.
- You tried to enable call interception for
Errors Pertaining to CALL_LIST_MANAGE
errno is set to...
EINVALif:arg2is larger than0x1fffor smaller than-0x1fff. This check does not apply for platform system calls (call number0x80000000)
ENOENTif:- Call interception is not enabled for
pid.
- Call interception is not enabled for
ENOMEMif:kalloc_canblockorkalloc_externalfails while allocatingpid's call list.
Other Notes
You need to register exception ports for your process before you enable call interception for it. Nothing checks if you've done this.
FOR ANY PID YOU REGISTER FOR SYSTEM CALL/MACH TRAP INTERCEPTION, YOU MUST ALSO UN-REGISTER WHEN YOU ARE DONE. Unregistering a previously-registered PID will free its table entry. It's also a good idea to save previous exception ports before registering your own and restoring them when you're done intercepting calls.
A maximum of 1023 processes can have their calls intercepted simultaneously.
I try my best to make sure the patchfinder works on all kernels iOS 13+, so if something isn't working, please file an issue.