ReadConsoleInputA 引发访问冲突

Posted

技术标签:

【中文标题】ReadConsoleInputA 引发访问冲突【英文标题】:ReadConsoleInputA throws an Access Violation 【发布时间】:2019-08-02 09:48:35 【问题描述】:

我正在尝试学习如何使用 windows api(而不仅仅是使用 C 调用、irvine32 或 masm32)并且遇到了 ReadConsoleInputA 的问题(WriteConsoleA 工作正常)。

另外,我不明白为什么在函数的 PROC 原型中,大多数示例在 ReadConsoleInput/WriteConsole 的末尾附加了 A 或 W,你能解释一下原因吗?

.data
consoleOutHandle dd ?
consoleInHandle dd ?
bufferlen dd ?
buffer db ?
bufferSize DWORD ?
message db "Enter a number:", 0
lmessage equ $-message


.code
main PROC

    invoke GetStdHandle, STD_OUTPUT_HANDLE
    mov consoleOutHandle, eax

    invoke ReadConsoleInputA, consoleOutHandle, offset buffer, 128, bufferSize
main endp
end main

它抛出:访问冲突写入位置0x00000004。

按照 Michael Petch 的建议,我现在有了以下代码:

.data
consoleOutHandle dd ?
consoleInHandle dd ?
byteswritten dd ?
bufferlen dd ?
buffer db 128 DUP(?)
bufferSize dd ?
message db "Enter a number:", 0
lmessage equ $-message


.code
main PROC
    invoke GetStdHandle, STD_INPUT_HANDLE
    mov consoleInHandle, eax
    invoke GetStdHandle, STD_OUTPUT_HANDLE
    mov consoleOutHandle, eax
    mov eax, lmessage
    invoke WriteConsoleA, consoleOutHandle, offset message, eax, bytesWritten, 0

    invoke ReadConsoleInputA, consoleInHandle, offset buffer, 128, offset bufferSize
main endp
end main

现在它抛出“触发断点”。

反汇编:

invoke ReadConsoleInputA, consoleInHandle, offset buffer, 128, offset bufferSize
00E71066  push        offset bufferSize (0E74090h)  
00E7106B  push        80h  
00E71070  push        offset buffer (0E74010h)  
00E71075  push        dword ptr [consoleInHandle (0E74004h)]  
00E7107B  call        _ReadConsoleInputA@16 (0E7100Ah)  
--- No source file -------------------------------------------------------------
00E71080  int         3    **---> Breakpoint here**
00E71081  int         3  

【问题讨论】:

做到了,但现在它“触发了断点”。我过去曾尝试过您的建议,但我不断收到“触发断点”或“访问冲突”。 是的,我已根据您的建议更新了原始帖子。感谢您的链接(A&W) @MichaelPetch:这有点太强了。修复一个明显且无趣的错误(例如未能保留足够空间)似乎是一个好主意,但它并不是问题的全部。之前出现过很多这样的问题。我建议 OP 至少应该添加一个带有更改版本的第二个代码块(以及一个带有反汇编的代码块,因为 cmets 没有格式)。 但是无论如何,现在这个错误只是缺少ret,导致函数的末尾掉入int3填充,MASM和/或链接器正是出于这个原因在函数之间放置的.至少有几个关于函数结束的问题,但可能没有 MASM 遇到int3 谢谢,我已遵循您的建议并将该信息附加到问题中。 【参考方案1】:

您询问了 WinAPI 函数末尾的 AW 后缀的用途。以A 结尾的函数表示Ansi,以W 结尾的函数是Wide。微软以这种方式记录它们:

Unicode 和 ANSI 函数 当 Microsoft 将 Unicode 支持引入 Windows 时,它通过提供两组并行的 API 来简化转换,一组用于 ANSI 字符串,另一组用于 Unicode 字符串。例如,有两个函数可以设置窗口标题栏的文本:

SetWindowTextA 采用 ANSI 字符串。 SetWindowTextW 采用 Unicode 字符串。

在第一版代码中

您没有为buffer 分配必要的空间。你有:

buffer db ?

这为缓冲区分配了一个字节。应该是:

buffer db 128 DUP(?)

您使用了STD_OUTPUT_HANDLE 而不是STD_INPUT_HANDLE

ReadConsoleInputA 的最后一个参数是一个指向 DWORD 的指针,它将返回读取的事件数。更改变量名称bufferSize 可能会使代码更具可读性。来自ReadConsoleInputA 文档:

BOOL WINAPI ReadConsoleInput(
 _In_  HANDLE        hConsoleInput,
 _Out_ PINPUT_RECORD lpBuffer,
 _In_  DWORD         nLength,
 _Out_ LPDWORD       lpNumberOfEventsRead
);

如果您只读取键盘,您应该使用ReadConsoleA,因为ReadConsoleInputA 将处理键盘和鼠标事件,并且可能在读取您的字符串之前过早返回。 ReadConsoleA 多了一个参数,你可以设置为NULL:

BOOL WINAPI ReadConsole(
 _In_     HANDLE  hConsoleInput,
 _Out_    LPVOID  lpBuffer,
 _In_     DWORD   nNumberOfCharsToRead,
 _Out_    LPDWORD lpNumberOfCharsRead,
 _In_opt_ LPVOID  pInputControl
);

要退出程序,您需要调用ExitProcess


在第二版代码中

您的代码可以:

invoke WriteConsoleA, consoleOutHandle, offset message, eax, bytesWritten, 0

bytesWritten 需要是一个指针,因为那是一个输出参数。来自WriteConsoleA 文档:

BOOL WINAPI WriteConsole(
 _In_             HANDLE  hConsoleOutput,
 _In_       const VOID    *lpBuffer,
 _In_             DWORD   nNumberOfCharsToWrite,
 _Out_            LPDWORD lpNumberOfCharsWritten,
 _Reserved_       LPVOID  lpReserved
 );

根据您的第二个代码示例,使用ReadConsoleA 而不是ReadConsoleInputA 的代码版本可能如下所示:

.data
consoleOutHandle dd ?
consoleInHandle dd ?
bytesWritten dd ?
bufferlen dd ?
buffer db 128 DUP(?)
numEvents dd ?
message db "Enter a number:", 0
lmessage equ $-message

.code
main PROC
    invoke GetStdHandle, STD_INPUT_HANDLE
    mov consoleInHandle, eax
    invoke GetStdHandle, STD_OUTPUT_HANDLE
    mov consoleOutHandle, eax
    mov eax, lmessage
    invoke WriteConsoleA, consoleOutHandle, offset message, eax, offset bytesWritten, 0

    invoke ReadConsoleA, consoleInHandle, offset buffer, 128, offset numEvents, 0
    invoke ExitProcess, 0
main endp
end main

使用 MASM 的 sizeof 运算符可以稍微清理一下这段代码。代码可以写成:

.data
consoleOutHandle dd ?
consoleInHandle dd ?
buffer db 128 DUP(?)
bytesWritten dd ?
numEvents dd ?
message db "Enter a number:", 0

.code
main PROC
    invoke GetStdHandle, STD_INPUT_HANDLE
    mov consoleInHandle, eax
    invoke GetStdHandle, STD_OUTPUT_HANDLE
    mov consoleOutHandle, eax
    invoke WriteConsoleA, consoleOutHandle, offset message, sizeof message, offset bytesWritten, 0
    invoke ReadConsoleA, consoleInHandle, offset buffer, sizeof buffer, offset numEvents, 0
    invoke ExitProcess, 0
main endp
end main

【讨论】:

天哪,谢谢!实际上,ReadConsoleInput 在第一次击键后返回。这非常有效。

以上是关于ReadConsoleInputA 引发访问冲突的主要内容,如果未能解决你的问题,请参考以下文章

访问动态 2D 字符数组时引发访问冲突异常

Qt 5.5 和 OpenGL:尝试调用 paintGL() 时引发读取访问冲突异常

C++ - 使用 std::sort 对结构向量进行排序导致读取访问冲突

WSDL可调用接口方法调用时出现访问冲突错误...?

在 filePath.exe 中的 0x793F3729 (vcruntime140d.dll) 处引发异常:0xC0000005:访问冲突写入位置 0xCDCDCDCD

在项目 2017.1.exe 中的 0x0FDD053F (ucrtbased.dll) 处引发 bmp 文件异常错误:0xC0000005:访问冲突写入位置 0xCDCDCDCD