使用syscall的作用
在安全对抗用syscall直接调用api可以在一定程度上绕过一些EDR或者杀毒软件对一些dll的api hook,可以直接调用设计内核的函数。在此只写一个简单的测试用例。
以NtDelayExecution为例
这个函数就是让程序延迟一段时间执行的一个函数,便于观察syscall成功没有的。
windows syscall的调用号是存在eax这个寄存器里的,要直接设置寄存器的值,所以需要使用简单的汇编代码。x64环境不支持直接内联汇编,所以单独创建一个.asm文件写汇编。
syscall stub结构
一般的syscall stub结构如下:
mov r10, rcx => 4C 8B D1
mov eax, SSN => B8 34 00 00 00 (假设 SSN 是 0x34)
syscall => 0F 05
ret => C3
按照index顺序,第5个字节是调用号ssn,这里假设是0x34,存储大小4个字节,小段写。
实现方法
动态获取ssn
在不同的系统版本了里一个api对应的ssn可能是不一样的,所以动态获取ssn是比较稳妥的。从第5个字节获取到ssn即可:
ULONG GetSSN(HMODULE hNtdll, const char* pName) {
FARPROC FuncAddr = GetProcAddress(hNtdll, pName);
ULONG* ssn = (ULONG*)((ULONGLONG)FuncAddr + 4);
return *ssn;
}
如果要检测当前设备的ntdll有没有被hook,可以检查一下syscall stub的前四个字节是不是常规的值,如果被修改了那可能就是被inline hook过的。
syscall
我们先看NtDelayExecution这个函数:
NTSTATUS NtDelayExecution(
BOOLEAN Alertable,
PLARGE_INTEGER DelayInterval
);
两个参数,第一个填False就行,第二个是延迟的时间,+表示绝对时间,-表示相对时间,这里为了实验结果明显,直接延迟了10s执行。
在传参方面,x64传参是前四个参数用RCX, RDX, R8, R9传参,后面的进栈,但是syscall不一样,syscall的第一个参数进R10,后面的都一样。
还有一点是前面讲过的syscall是call eax,调用号放eax。
综上所述汇编代码可以这样写:
mov eax, ecx;
mov r10, rdx;
mov rdx, r8;
syscall;
ret
然后在cpp里的代码是这样的:
extern "C" NTSTATUS MyNtDelayExecution(ULONG ssn, BOOLEAN Alertable, PLARGE_INTEGER DelayInterval);
NTSTATUS status = MyNtDelayExecution(ssn, FALSE, &interval);
调试
运行程序发现程序确实是延迟了很久。
在vs里导入ntdll的符号表可以看到调用号对应的api:
![]()
调用号0x34,对应的就是NtDelayExecution()。
代码见BlackIce417/windows-syscall: A easy windows syscall test demo,很简单的一个测试demo。
705

被折叠的 条评论
为什么被折叠?



