C++ 中 NtWaitForSingleObject 的神秘行为 - 在没有 std::cout 的情况下返回无效句柄错误

问题描述 投票:0回答:1

我的程序在循环中使用 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() 的进程句柄是否存在已知问题?

我花了几个小时试图诊断这个问题,任何见解将不胜感激!

c++ debugging assembly winapi visual-c++
1个回答
0
投票

如果在 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

© www.soinside.com 2019 - 2024. All rights reserved.