ReadConsoleInputA引发访问冲突

时间:2019-08-02 09:48:35

标签: windows winapi assembly x86 masm

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

1 个答案:

答案 0 :(得分:2)

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

  

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

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

第一版代码

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

    buffer db ?
    

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

    buffer db 128 DUP(?)
    
  • 您使用的是STD_OUTPUT_HANDLE而不是STD_INPUT_HANDLE

  • ReadConsoleInputA的最后一个参数是指向DWORD的指针,该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