
Syscalls indiretos + DInvoke simplificados.
Syscalls indiretos + DInvoke simplificados.
Projetada para ser importada diretamente em seus próprios projetos Nim, a nimvoke usa macros para abstrair os detalhes de fazer chamadas de sistema indiretas e declarações de delegados no estilo DInvoke. Esta biblioteca é feita para ser fácil de usar e relativamente amigável à op-sec por padrão. Os nomes de funções e bibliotecas usados nas macros são transformados em hash em tempo de compilação, e os SSNs e os endereços da instrução syscall são recuperados independentemente de quaisquer hooks. Todas as syscalls passam pela instrução syscall correta em ntdll.dll.
Instale com nimble (nimble install https://github.com/nbaertsch/nimvoke) e então basta importar a biblioteca relevante e chamar a macro correspondente. Consulte examples para mais detalhes.
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 os binários Nim importam um conjunto de funções Win32 principais. Todas as outras funções Win32 são resolvidas via dynlib, que usa GetProcAddress e LoadLibraryA para resolver funções em tempo de execução. Esta implementação de DInvoke pode evitar expor funções novas que não são usadas por outro código (seja o runtime/GC do Nim ou código da stdlib importado via dynlib), mas não pode remover as importações principais do Nim nem alterar o comportamento das bibliotecas padrão ou de terceiros.
Ao importar, nimvoke/syscalls analisa o EAT de ntdll.dll para encontrar todas as syscalls e extrair os dados necessários. As informações sobre todas as syscalls são armazenadas em memória.
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`
A recuperação do SSN é feita ordenando todas as syscalls por endereço e contando-as. Este método deve funcionar independentemente de qualquer hooking. Nenhuma string com nome de função é armazenada em memória, mas um ponteiro para as strings de nomes de funções no EAT de ntdll.dll é mantido para calcular os hashes.
As syscalls usam um único trampolim (de FreshyCalls) para mover o SSN para eax, preparar os argumentos e pular para a instrução syscall correta em ntdll.dll.
Este trampolim é alocado em um novo heap privado.
Se você quiser executar código antes deste código de inicialização de syscalls (como para evasão de sandbox ou chaveamento ambiental), você precisa chamá-lo antes da instrução import nimvoke/syscalls no seu código.
Tem sugestões? Abra uma issue! PRs também são bem-vindos.