
間接システムコール + DInvoke をシンプルに。
自身の Nim プロジェクトに直接インポートできるように設計された nimvoke は、マクロを使って間接システムコールと DInvoke スタイルのデリゲート宣言の詳細を抽象化します。このライブラリは、すぐに使えて比較的 op-sec フレンドリーになることを意図しています。マクロで使用される関数名とライブラリ名はコンパイル時にハッシュ化され、SSN と syscall 命令のアドレスはフックの有無にかかわらず取得されます。すべてのシステムコールは ntdll.dll 内の正しい syscall 命令を経由します。
nimble でインストールし (nimble install https://github.com/nbaertsch/nimvoke)、関連するライブラリをインポートして対応するマクロを呼び出すだけです。詳細は examples を参照してください。
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)
システムコール:
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
)
すべての Nim バイナリは、一連のコア Win32 関数をインポートします。その他の Win32 関数はすべて dynlib を介して解決されます。dynlib は実行時に関数を解決するために GetProcAddress と LoadLibraryA を使用します。この DInvoke 実装は、他のコード (Nim ランタイム/GC または dynlib を介してインポートする stdlib コード) で使用されていない 新しい 関数が公開されるのを防ぐことができますが、Nim のコアインポートを削除したり、標準またはサードパーティライブラリの動作を変更したりすることはできません。
インポート時に、nimvoke/syscalls は ntdll.dll の EAT を解析してすべてのシステムコールを見つけ、必要なデータを抽出します。すべてのシステムコールに関する情報はメモリに保存されます。
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`
SSN の取得は、すべてのシステムコールをアドレス順にソートして数えることで行われます。この方法は、フックの有無にかかわらず機能するはずです。関数名の文字列はメモリに保存されませんが、ハッシュ計算のために ntdll.dll の EAT 内の関数名文字列へのポインタが保持されます。
システムコールは、単一のトランポリン (FreshyCalls 由来) を使用して、SSN を eax に移動し、引数を準備し、ntdll.dll 内の正しい syscall 命令にジャンプします。このトランポリンは、新しいプライベートヒープ上に割り当てられます。
このシステムコール初期化コードの前にコードを実行したい場合 (サンドボックス回避や環境キーイングなど)、コード内の import nimvoke/syscalls ステートメントより前に呼び出す必要があります。
提案がありますか? イシューを開いてください! PR も歓迎します。