
Syscalls indirects + DInvoke simplifiés.
Appels système indirects + DInvoke rendus simples.
Conçue pour être importée directement dans vos propres projets Nim, nimvoke utilise des macros pour abstraire les détails de la réalisation d'appels système indirects et des déclarations de délégués de style DInvoke. Cette bibliothèque se veut facile à utiliser et relativement op-sec friendly par défaut. Les noms de fonctions et de bibliothèques utilisés dans les macros sont hachés au moment de la compilation, et les SSN ainsi que les adresses des instructions syscall sont récupérés indépendamment de tout hook. Tous les syscalls passent par l'instruction syscall appropriée dans ntdll.dll.
Installez avec nimble (nimble install https://github.com/nbaertsch/nimvoke), puis importez simplement la bibliothèque concernée et appelez la macro correspondante. Voir examples pour plus de détails.
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)
Syscalls :
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
)
Tous les binaires Nim importent un ensemble de fonctions Win32 de base. Toutes les autres fonctions Win32 sont résolues via dynlib, qui utilise GetProcAddress et LoadLibraryA pour résoudre les fonctions à l'exécution. Cette implémentation de DInvoke peut empêcher l'exposition de nouvelles fonctions qui ne sont pas utilisées par d'autres codes (que ce soit le runtime/GC de Nim ou le code de la bibliothèque standard important via dynlib), mais ne peut pas supprimer les importations de base de Nim ni modifier le comportement des bibliothèques standard ou tierces.
À l'importation, nimvoke/syscalls analyse l'EAT de ntdll.dll pour trouver tous les syscalls et extraire les données nécessaires. Les informations sur tous les syscalls sont stockées en mémoire.
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`
La récupération des SSN s'effectue en triant tous les syscalls par adresse puis en les comptant. Cette méthode devrait fonctionner indépendamment de tout hooking. Aucune chaîne de nom de fonction n'est stockée en mémoire, mais un pointeur vers les chaînes de noms de fonctions dans l'EAT de ntdll.dll est conservé pour calculer les hachages.
Les syscalls utilisent un trampoline unique (de FreshyCalls) pour déplacer le SSN dans eax, préparer les arguments et sauter vers l'instruction syscall appropriée dans ntdll.dll.
Ce trampoline est alloué sur un nouveau tas privé.
Si vous souhaitez exécuter du code avant ce code d'initialisation des syscalls (par exemple pour l'évasion sandbox ou l'environmental keying), vous devez l'appeler avant l'instruction import nimvoke/syscalls dans votre code.
Des suggestions ? Ouvrez une issue ! Les PR sont également les bienvenues.