我的程序在循环中使用 NtWaitForSingleObject 和 NtDelayExecution 时遇到了一个奇怪的问题。问题是函数 NtWaitForSingleObject 偶尔会返回错误 0xC0000008 (STATUS_INVALID_HANDLE),但仅当我从代码中删除 std::cout 语句时才返回。这种行为让我发疯,我不知道出了什么问题。
事情是这样的:
如果我在系统调用之后有两个 std::cout 语句(NtDelayExecution_Syscall 和 NtWaitForSingleObject_Syscall),则一切都会按预期工作。 如果我删除 std::cout 语句(或仅保留其中之一),NtWaitForSingleObject_Syscall 将返回 0xC0000008(无效句柄)。 我已经测试了寄存器和变量中的值,在调用 NtWaitForSingleObject 之前它们看起来是正确的。传递给函数的句柄是 GetCurrentProcess() 的结果,它应该是有效的。
这是我的代码:
汇编代码(.asm):
.code
; func NtDelayExecution
NtDelayExecution_Syscall proc
mov rax, 34h
syscall
ret
NtDelayExecution_Syscall endp
; func NtWaitForSingleObject
NtWaitForSingleObject_Syscall proc
mov rax, 04h
syscall
ret
NtWaitForSingleObject_Syscall endp
end
C++ 代码(.cpp):
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <iostream>
extern "C" LONG NtDelayExecution_Syscall(
BOOLEAN Alertable,
PLARGE_INTEGER DelayInterval
);
extern "C" LONG NtWaitForSingleObject_Syscall(
HANDLE hProcess,
BOOLEAN Alertable,
PLARGE_INTEGER DelayInterval
);
void StartMonitor(HANDLE hProcessToMonitor) {
// Установка нулевого таймаута
LARGE_INTEGER integer;
integer.QuadPart = -10000 * 1000;
LARGE_INTEGER timeout;
timeout.QuadPart = 0;
LONG result;
while (true) {
result = NtDelayExecution_Syscall(FALSE, &integer);
std::cout << result << std::endl; // PROBLEM: Without this line, the error appears
result = NtWaitForSingleObject_Syscall(hProcessToMonitor, FALSE, &timeout);
std::cout << result << std::endl; // PROBLEM: Without this line, the error appears
}
return;
}
int main() {
HANDLE hProcess = GetCurrentProcess(); // Using GetCurrentProcess() handle
StartMonitor(hProcess);
return 0;
}
我尝试过的:
禁用优化:我尝试禁用编译器优化(MSVC 中的 /Od),但行为没有改变。
缓冲 std::cout:尝试使用 std::ios_base::sync_with_stdio(false) 禁用与 C stdio 的同步,没有变化。
添加了人工变量和堆栈填充:我添加了像 volatile int padding[10] 这样的变量;看看是否是堆栈问题 - 没有效果。
尝试在变量上使用alignas(16):没有帮助。
检查调试器中的寄存器:当错误发生时,RCX(保存句柄)为FFFFFFFFFFFFFFFF。
我的问题:
为什么 std::cout 的存在会影响 NtWaitForSingleObject_Syscall 的结果?
输出流如何影响这些系统调用的行为?
我还可以采取哪些其他调试步骤来隔离根本原因?
这可能与内存对齐、堆栈管理或 MSVC 特有的内容有关吗?
在低级系统调用中,NtWaitForSingleObject 处理来自 GetCurrentProcess() 的进程句柄是否存在已知问题?
我花了几个小时试图诊断这个问题,任何见解将不胜感激!
如果在 ntdll.dll 中查找实际的 Zw/Nt api 实现,可以看到它都是从
mov r10,rcx
指令开始的。所以第一个参数移至 r10
寄存器。如果在调试器下,将 r10
中的 NtWaitForSingleObject
(在 mov r10,rcx
之后)特殊更改为 0,我们就得到了 0xC0000008
(无效句柄)。
所以错误在于
NtWaitForSingleObject
(和NtDelayExecution
)的错误实现。 r10
中为随机值,受std::cout << result << std::endl;
影响
不清楚什么需要自己实现它
Zw/Nt
,而是从ntdll.dll
导入它,即使这样做,也不需要硬编码SSN
数字(在不同的Windows版本中可能不同)但得到它在运行时(如果在ntdll.dll中构建所有Zw
导出的表并按函数地址对其进行排序,则按地址排序的表中Zw
条目的索引将恰好是SSN
)