Doing syscalls directly is not a good idea because this is not a stable ABI. The numbers can in theory change between service packs and even a plain update.
The instruction used on 32-bit Windows is not the same on all systems either!
Windows NT and 2000 always uses int 2e
. Windows XP started using SysEnter/SysCall
when running on "newer" Intel/AMD CPUs (Pentium II, AMD K7, and later). Because Windows XP also supported older CPUs, it used a little helper function (SystemCallStub
) to enter kernel mode. This function (and later, the address of this function) is stored in a memory page accessible by all processes called _KUSER_SHARED_DATA
located at 0x7ffe0000.
The original int 2e
method is still supported, but I'm not sure why 64-bit Windows bothers checking which method to use, since every CPU it runs on supports SysCall
. My Windows 8 machine does not check:
0:000> uf ntdll!NtTerminateProcess
ntdll!ZwTerminateProcess:
000007ff`1ad52ea0 4c8bd1 mov r10,rcx
000007ff`1ad52ea3 b82a000000 mov eax,2Ah
000007ff`1ad52ea8 0f05 syscall
000007ff`1ad52eaa c3 ret
These are just implementation details anyway, and they can change at any time. See https://j00ru.vexillium.org/syscalls/nt/64/ for a reverse-engineered table of x64 NT system-call numbers broken down by Windows kernel version. (Do not use in portable code, only for experiments to satisfy your curiosity about how Windows and/or asm works.)
int 2e
is probably a little bit slower, so just use SysCall
in 64-bit code and int 2e
in 32-bit code if you want to stay "portable".