首页 > 解决方案 > ReadConsoleInputA 引发访问冲突

问题描述

我正在尝试学习如何使用 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  

标签: windowswinapiassemblyx86masm

解决方案


您问WinAPI 函数末尾的A和后缀是干什么用的。W以 nsi 结尾的函数A表示Ansi,以 ide 结尾的W函数W。微软以这种方式记录它们:

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

  • to 的最后一个参数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

推荐阅读