
Syscalls indirectas + DInvoke simplificado.
Syscalls indirectas + DInvoke simplificado.
Diseñado para importarse directamente en tus propios proyectos de Nim, nimvoke usa macros para abstraer los detalles de hacer llamadas al sistema indirectas y declaraciones de delegados estilo DInvoke. Esta librería pretende ser fácil de usar y relativamente amigable con la op-sec de serie. Los nombres de funciones y librerías usados en las macros se hashean en tiempo de compilación, y los SSN y las direcciones de las instrucciones syscall se obtienen independientemente de cualquier hook. Todas las syscalls pasan por la instrucción syscall correcta en ntdll.dll.
Instala con nimble (nimble install https://github.com/nbaertsch/nimvoke), luego simplemente importa la librería relevante y llama a la macro correspondiente. Consulta examples para más detalles.
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
)
Todos los binarios de Nim importan un conjunto de funciones Win32 principales. Todas las demás funciones Win32 se resuelven mediante dynlib, que usa GetProcAddress y LoadLibraryA para resolver funciones en tiempo de ejecución. Esta implementación de DInvoke puede evitar exponer funciones nuevas que no sean usadas por otro código (ya sea el runtime/GC de Nim o el código de la stdlib que importa vía dynlib), pero no puede eliminar las importaciones principales de Nim ni alterar el comportamiento de la stdlib o de librerías de terceros.
Al importar, nimvoke/syscalls analiza el EAT de ntdll.dll para encontrar todas las syscalls y extraer los datos necesarios. La información sobre todas las syscalls se almacena en 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`
La obtención del SSN se realiza ordenando todas las syscalls por su dirección y contándolas. Este método debería funcionar independientemente de cualquier hooking. No se almacenan cadenas con nombres de funciones en memoria, pero se mantiene un puntero a las cadenas de nombres de funciones en el EAT de ntdll.dll para calcular los hashes.
Las syscalls usan un único trampolín (de FreshyCalls) para mover el SSN a eax, preparar los argumentos y saltar a la instrucción syscall correcta en ntdll.dll.
Este trampolín se asigna en un nuevo heap privado.
Si quieres ejecutar código antes de este código de inicialización de syscalls (como para evasión de sandbox o keying ambiental), necesitas llamarlo antes de la sentencia import nimvoke/syscalls en tu código.
¿Tienes sugerencias? ¡Abre un issue! Los PR también son bienvenidos.