Created
May 12, 2025 20:07
-
-
Save gremlinbeet/4960774f8a08bff6b1667eb69da24f7b to your computer and use it in GitHub Desktop.
nt!PsSyscallProviderDispatch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Pseudocode and structs for nt!PsSyscallProviderDispatch. | |
| // For ntosknrl win11 24H2 10.0.26100.1742. | |
| // Restored by Cyra, adjusted by @sixtyvividtails. | |
| // | |
| // See actual research: | |
| // by @gal_kristal: https://gist.github.com/Kristal-g/eec050b3fcea2a77715ef0cff4acf841 | |
| // by @0xfluxsec: https://fluxsec.red/alt-syscalls-for-windows-11 | |
| // name's mine // @gal_kristal: _PS_SYSCALL_PROVIDER_SERVICE_DESCRIPTOR_GROUP | |
| struct $SVC_DESCRIPTOR | |
| { | |
| /*00*/ void* HandlersBase; // all syscall handlers are at HandlersBase + offset | |
| /*08*/ $SVC_TABLE* LowTable; // for syscall numbers < 0x1000 | |
| /*10*/ $SVC_TABLE* HighTable; // for syscall numbers >= 0x1000 | |
| /*18*/ // size | |
| }; | |
| // name's mine // @gal_kristal: _PS_SYSCALL_PROVIDER_SERVICE_DESCRIPTOR | |
| struct $SVC_TABLE | |
| { | |
| /*00*/ uint SyscallEntriesCount; // number of entries in table, up to 0x1000 | |
| /*04*/ $SYSCALL_ENTRY SyscallEntry[]; // indexed from 0 to SyscallEntriesCount-1 | |
| /*4004*/ // maximum size | |
| }; | |
| // name's mine // @gal_kristal: _PS_SYSCALL_PROVIDER_SERVICE_ENTRY | |
| struct $SYSCALL_ENTRY | |
| { | |
| uint StackArgsCount: 4; // number of non-register arguments | |
| uint NeedsGenericDispatch: 1; // PspSyscallProviderServiceDispatch[Generic] | |
| uint Reserved: 3; | |
| uint PackedOffset: 24; // real offset = PackedOffset << 4 | |
| operator uint&() { return *(uint*)this; } | |
| }; | |
| // Up to 0x20 distinct descriptors allowed system-wide. | |
| // Each process bound to one via EPROCESS.SyscallProviderDispatchContext.Slot. | |
| $SVC_DESCRIPTOR PspServiceDescriptorGroupTable[0x20]; | |
| // Invoked from nt!KiSystemCall64 when | |
| // (ETHREAD.Tcb.Header.Minimal or ETHREAD.Tcb.Header.AltSyscall) | |
| // | |
| // Return values: | |
| // <1: handled, trapFrame->Rax contains return status | |
| // =1: needs regular syscall dispatch in KiSystemCall64 | |
| // >1: raise STATUS_INVALID_SYSTEM_SERVICE | |
| schar PsSyscallProviderDispatch(_Inout_ _KTRAP_FRAME *trapFrame): | |
| ETHREAD* currentThread = PsGetCurrentThread() | |
| if currentThread->Tcb.Header.Minimal: | |
| PsPicoSystemCallDispatch(trapFrame) // invokes PspPicoProviderRoutines[1] | |
| return 0 | |
| // notice potential TOCTOU? Hint: NtSetContextThread (most likely unusable) | |
| uint syscallNumber = trapFrame->Rax & ~0x6000 | |
| uint syscallIndex = syscallNumber & 0xFFF | |
| bool isHighSyscall = (syscallNumber & 0x1000) != 0 | |
| EPROCESS* currentProcess = IoThreadToProcess(currentThread) | |
| uint slot = currentProcess->SyscallProviderDispatchContext.Slot | |
| if slot >= 0x20: | |
| KeBugCheckEx(0x1E0, 5, slot, currentProcess->SyscallProvider, 0) | |
| $SVC_DESCRIPTOR& svcDescriptor = PspServiceDescriptorGroupTable[slot] | |
| $SVC_TABLE* svcTable = isHighSyscall? svcDescriptor.HighTable: svcDescriptor.LowTable | |
| if not svcTable: | |
| return 1 // needs regular dispatch | |
| if syscallIndex >= svcTable->SyscallEntriesCount: | |
| trapFrame->Rax = STATUS_INVALID_SYSTEM_SERVICE // 0xC000001C | |
| return 0 | |
| $SYSCALL_ENTRY syscallEntry = svcTable->SyscallEntry[syscallIndex] | |
| if syscallEntry == 0: | |
| return 1 // needs regular dispatch | |
| if syscallEntry == 1: | |
| return 2 // raise STATUS_INVALID_SYSTEM_SERVICE | |
| if isHighSyscall: | |
| NTSTATUS st = PspEnsureGuiThreadAndBatchFlush(currentThread) | |
| if st == STATUS_UNSUCCESSFUL: | |
| // transform status if needed using extra table of bytes beyond packed shadow table | |
| schar* shadowSyscallInfo = (schar*)KeServiceDescriptorTableShadow[1].Base | |
| + sizeof(uint) * KeServiceDescriptorTableShadow[1].Limit | |
| if shadowSyscallInfo[syscallIndex] == 1: | |
| st = STATUS_INVALID_SYSTEM_SERVICE // was not intended to be called | |
| if FAILED(st): | |
| trapFrame->Rax = st | |
| return 0 | |
| // unlike regular syscall packed tables, offset here is unsigned; so one can specify | |
| // driver image base as svcDescriptor.HandlersBase, then use RVAs for handlers | |
| uint offset = syscallEntry.PackedOffset << 4 | |
| void* syscallHandler = (char*)svcDescriptor.HandlersBase + offset // function to invoke | |
| NTSTATUS st = STATUS_SUCCESS | |
| if syscallEntry.NeedsGenericDispatch: | |
| int rez = PspSyscallProviderServiceDispatchGeneric(trapFrame, syscallHandler, | |
| syscallEntry.StackArgsCount, syscallNumber, &st) | |
| if rez: | |
| return (schar)rez | |
| else: | |
| st = PspSyscallProviderServiceDispatch(trapFrame, syscallHandler, | |
| syscallEntry.StackArgsCount) | |
| trapFrame->Rax = st | |
| return 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment