
Syscall indirette + DInvoke resi semplici.
Syscall indiretti + DInvoke resi semplici.
Progettato per essere importato direttamente nei tuoi progetti Nim, nimvoke usa macro per astrarre i dettagli delle chiamate di sistema indirette e delle dichiarazioni di delegati in stile DInvoke. Questa libreria è pensata per essere facile da usare e relativamente op-sec friendly fin da subito. I nomi di funzioni e librerie usati nelle macro vengono hashate in fase di compilazione, e gli SSN e gli indirizzi dell'istruzione syscall vengono recuperati indipendentemente da eventuali hook. Tutte le syscall passano attraverso la corretta istruzione syscall in ntdll.dll.
Installa con nimble (nimble install https://github.com/nbaertsch/nimvoke), poi importa semplicemente la libreria pertinente e chiama la macro corrispondente. Vedi examples per maggiori dettagli.
DInvoke:
import winim/lean
import nimvoke/dinvoke
dinvokeDefine(
ZwAllocateVirtualMemory,
"ntdll.dll",
proc (ProcessHandle: Handle, BaseAddress: PVOID, ZeroBits: ULONG_PTR, RegionSize: PSIZE_T, AllocationType: ULONG, Protect: ULONG): NTSTATUS {.stdcall.}
)
var
hProcess: HANDLE = 0xFFFFFFFFFFFFFFFF
shellcodeSize: SIZE_T = 1000
baseAddr: PVOID
status: NTSTATUS
status = ZwAllocateVirtualMemory(
hProcess,
&baseAddr,
0,
&shellcodeSize,
MEM_RESERVE or MEM_COMMIT,
PAGE_READWRITE)
Syscall:
import winim/lean
import nimvoke/syscalls
var
hProcess: HANDLE = 0xFFFFFFFFFFFFFFFF
shellcodeSize: SIZE_T = 1000
baseAddr: PVOID
status: NTSTATUS
status = syscall(NtAllocateVirtualMemory,
hProcess,
&baseAddr,
0,
&shellcodeSize,
MEM_RESERVE or MEM_COMMIT,
PAGE_READWRITE
)
Tutti i binari Nim importano un insieme di funzioni Win32 di base. Tutte le altre funzioni Win32 vengono risolte tramite dynlib, che usa GetProcAddress e LoadLibraryA per risolvere le funzioni a runtime. Questa implementazione DInvoke può impedire l'esposizione di funzioni nuove che non sono usate da altro codice (sia esso il runtime/GC di Nim o il codice della stdlib che importa tramite dynlib), ma non può rimuovere le importazioni principali di Nim né alterare il comportamento della std o delle librerie di terze parti.
All'importazione, nimvoke/syscalls analizza l'EAT di ntdll.dll per trovare tutte le syscall ed estrarre i dati necessari. Le informazioni su tutte le syscall vengono memorizzate in memoria.
type
Syscall* = object
pName*: PCHAR
ord*: WORD
pFunc*: PVOID
pSyscall*: PVOID = NULL
ssn*: WORD
hooked*: bool
...
syscallSeq: seq[Syscall] # stores all syscall data
syscallTable* = initTable[string, ptr Syscall]() # maps syscall `Zw` hashed-name to `Syscall` object's in the `syscallSeq`
Il recupero degli SSN viene effettuato ordinando tutte le syscall per indirizzo e contandole. Questo metodo dovrebbe funzionare indipendentemente da eventuali hook. Nessuna stringa con i nomi delle funzioni viene memorizzata in memoria, ma viene mantenuto un puntatore alle stringhe dei nomi delle funzioni nell'EAT di ntdll.dll per calcolare gli hash.
Le syscall usano un singolo trampoline (da FreshyCalls) per spostare l'SSN in eax, preparare gli argomenti e saltare alla corretta istruzione syscall in ntdll.dll.
Questo trampoline viene allocato in un nuovo heap privato.
Se vuoi eseguire codice prima di questo codice di inizializzazione delle syscall (ad esempio per evasione della sandbox o environmental keying), devi chiamarlo prima dell'istruzione import nimvoke/syscalls nel tuo codice.
Hai suggerimenti? Apri una issue! Anche le PR sono benvenute.