Last active
December 9, 2021 10:03
-
-
Save chvancooten/083dbdfd4a10261ee8dfecb4caf07e6c to your computer and use it in GitHub Desktop.
Failed attempt to get Syswhispers2 to work in Nim
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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> | |
#include <winternl.h> | |
#define SW2_SEED 0x42A16EF3 | |
#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); | |
// typedef struct _UNICODE_STRING | |
// { | |
// USHORT Length; | |
// USHORT MaximumLength; | |
// PWSTR Buffer; | |
// } UNICODE_STRING, *PUNICODE_STRING; | |
#ifndef InitializeObjectAttributes | |
#define InitializeObjectAttributes( p, n, a, r, s ) { \ | |
(p)->Length = sizeof( OBJECT_ATTRIBUTES ); \ | |
(p)->RootDirectory = r; \ | |
(p)->Attributes = a; \ | |
(p)->ObjectName = n; \ | |
(p)->SecurityDescriptor = s; \ | |
(p)->SecurityQualityOfService = NULL; \ | |
} | |
#endif | |
typedef struct _PS_ATTRIBUTE | |
{ | |
ULONG Attribute; | |
SIZE_T Size; | |
union | |
{ | |
ULONG Value; | |
PVOID ValuePtr; | |
} u1; | |
PSIZE_T ReturnLength; | |
} PS_ATTRIBUTE, *PPS_ATTRIBUTE; | |
// typedef struct _OBJECT_ATTRIBUTES | |
// { | |
// ULONG Length; | |
// HANDLE RootDirectory; | |
// PUNICODE_STRING ObjectName; | |
// ULONG Attributes; | |
// PVOID SecurityDescriptor; | |
// PVOID SecurityQualityOfService; | |
// } OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; | |
// typedef struct _CLIENT_ID | |
// { | |
// HANDLE UniqueProcess; | |
// HANDLE UniqueThread; | |
// } CLIENT_ID, *PCLIENT_ID; | |
typedef struct _PS_ATTRIBUTE_LIST | |
{ | |
SIZE_T TotalLength; | |
PS_ATTRIBUTE Attributes[1]; | |
} PS_ATTRIBUTE_LIST, *PPS_ATTRIBUTE_LIST; | |
EXTERN_C NTSTATUS NtAllocateVirtualMemory( | |
IN HANDLE ProcessHandle, | |
IN OUT PVOID * BaseAddress, | |
IN ULONG ZeroBits, | |
IN OUT PSIZE_T RegionSize, | |
IN ULONG AllocationType, | |
IN ULONG Protect); | |
EXTERN_C NTSTATUS NtWriteVirtualMemory( | |
IN HANDLE ProcessHandle, | |
IN PVOID BaseAddress, | |
IN PVOID Buffer, | |
IN SIZE_T NumberOfBytesToWrite, | |
OUT PSIZE_T NumberOfBytesWritten OPTIONAL); | |
EXTERN_C NTSTATUS NtOpenProcess( | |
OUT PHANDLE ProcessHandle, | |
IN ACCESS_MASK DesiredAccess, | |
IN POBJECT_ATTRIBUTES ObjectAttributes, | |
IN PCLIENT_ID ClientId OPTIONAL); | |
EXTERN_C NTSTATUS NtCreateThreadEx( | |
OUT PHANDLE ThreadHandle, | |
IN ACCESS_MASK DesiredAccess, | |
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, | |
IN HANDLE ProcessHandle, | |
IN PVOID StartRoutine, | |
IN PVOID Argument OPTIONAL, | |
IN ULONG CreateFlags, | |
IN SIZE_T ZeroBits, | |
IN SIZE_T StackSize, | |
IN SIZE_T MaximumStackSize, | |
IN PPS_ATTRIBUTE_LIST AttributeList OPTIONAL); | |
EXTERN_C NTSTATUS NtClose( | |
IN HANDLE Handle); | |
EXTERN_C NTSTATUS NtProtectVirtualMemory( | |
IN HANDLE ProcessHandle, | |
IN OUT PVOID * BaseAddress, | |
IN OUT PSIZE_T RegionSize, | |
IN ULONG NewProtect, | |
OUT PULONG OldProtect); | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{.passC:"-masm=intel".} | |
# INCLUDE SYSCALLS.C FROM SYSWHISPERS2 TO DYNAMICALLY RESOLVE SYSCALL IDS | |
{.emit: """ | |
#include "../templates/syscalls.h" | |
SW2_SYSCALL_LIST SW2_SyscallList; | |
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 its 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; | |
} | |
""".} | |
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 | |
proc SW2_GetSyscallNumber(FunctionHash: DWORD): DWORD {.importc: "SW2_GetSyscallNumber".} | |
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, 40 | |
mov ecx, 1688680562 | |
call SW2_GetSyscallNumber | |
add rsp, 40 | |
mov rcx, [rsp +8] | |
mov rdx, [rsp+16] | |
mov r8, [rsp+24] | |
mov r9, [rsp+32] | |
mov r10, rcx | |
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, 40 | |
mov ecx, 2637503528 | |
call SW2_GetSyscallNumber | |
add rsp, 40 | |
mov rcx, [rsp +8] | |
mov rdx, [rsp+16] | |
mov r8, [rsp+24] | |
mov r9, [rsp+32] | |
mov r10, rcx | |
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, 40 | |
mov ecx, 3595428325 | |
call SW2_GetSyscallNumber | |
add rsp, 40 | |
mov rcx, [rsp +8] | |
mov rdx, [rsp+16] | |
mov r8, [rsp+24] | |
mov r9, [rsp+32] | |
mov r10, rcx | |
syscall | |
ret | |
""" | |
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, 40 | |
mov ecx, 589551407 | |
call SW2_GetSyscallNumber | |
add rsp, 40 | |
mov rcx, [rsp +8] | |
mov rdx, [rsp+16] | |
mov r8, [rsp+24] | |
mov r9, [rsp+32] | |
mov r10, rcx | |
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, 40 | |
mov ecx, 2199165319 | |
call SW2_GetSyscallNumber | |
add rsp, 40 | |
mov rcx, [rsp +8] | |
mov rdx, [rsp+16] | |
mov r8, [rsp+24] | |
mov r9, [rsp+32] | |
mov r10, rcx | |
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, 40 | |
mov ecx, 598550847 | |
call SW2_GetSyscallNumber | |
add rsp, 40 | |
mov rcx, [rsp +8] | |
mov rdx, [rsp+16] | |
mov r8, [rsp+24] | |
mov r9, [rsp+32] | |
mov r10, rcx | |
syscall | |
ret | |
""" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Adding this and rewriting the asm definitions NimlineWhispers creates worked for me 😃
Since nim compiles down to C, you can pull in most header only libraries like this.
PS: I found this by chance while stumbling my way through SysWhispers2 and nim 🤣