Error comparing strings in assembly language: 0xC0000005: Access violation reading location 0x00000043

0

The code is supposed to compare an inputted string with various hardcoded options to provide a menu.

This is the code I'm using

.stack 4096
 ...
    GetStdHandle proto :dword
    ReadConsoleA  proto :dword, :dword, :dword, :dword, :dword
    WriteConsoleA proto :dword, :dword, :dword, :dword, :dword
    STD_INPUT_HANDLE equ -10
 ...
.data
 ...
    bufSize = 80
    inputHandle DWORD ?
    buffer db bufSize dup(?)    
    choice DWORD ?
    letterC DWORD "C"
 ...
.code
 ...
    invoke GetStdHandle, STD_INPUT_HANDLE
    mov inputHandle, eax
    invoke ReadConsoleA, inputHandle, addr buffer, bufSize, addr choice,0
    mov ESI, choice
    mov EDI, letterC
    CLD            
    MOV ECX,10        
    REPE CMPSB        
 ...

It always gives me an error in the last line REPE CMPSB:

Exception thrown at 0x00F6195F in Application.exe: 0xC0000005: Access violation reading location 0x00000043.

x86
inline-assembly
asked on Stack Overflow Apr 13, 2018 by Joe • edited Apr 13, 2018 by Joe

1 Answer

3

The problem is that you set both the source and the destination register to wrong values.
I'm gonna explain that:

The function ReadConsole has the following signature:

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

With

mov ESI, choice

you are copying your fourth parameter of ReadConsole, choice, to ESI.
choice contains the NumberOfCharsRead, so it is no memory pointer, but rather a length value. This doesn't work.

With

mov EDI, letterC

you move the value of letterC to EDI rather than its address. To get the address to EDI, you'd have to use

mov EDI, OFFSET letterC

This error becomes obvious in your error message which reads

Access violation reading location 0x00000043.

The value of 0x00000043 equals to the ASCII char value of the character C, 0x43. To get its address - which would be correct - you can use the LEA instruction like lea EDI, letterC or the OFFSET directive which is more efficient. Reading or writing to a value as address will almost always result in an error.


To make it right, take into consideration the following approach:

invoke ReadConsoleA, inputHandle, addr buffer, bufSize, addr choice,0
CLD            
MOV  ESI, OFFSET buffer   ; address of buffer is source
MOV  EDI, OFFSET letterC  ; address of letterC is destination
MOV  ECX, choice          ; length of string is choice (bytes read)
REPE CMPSB                ; execute

This solution is better, but it contains a major error: REPE CMPSB compares characters and continues comparing them as long as they're equal. But your letterC is only one character long, so you will get an overflow after passing the C character - the second iteration.


Your question is unclear on what you really expect, so the following is a mere speculation:
A possible alternative would be using REPNE SCASB with the character letterC in AL (MOV AL, letterC) which would search for the first occurrence of C:

invoke ReadConsoleA, inputHandle, addr buffer, bufSize, addr choice,0
CLD            
MOV   EDI, OFFSET buffer  ; address of buffer is search source
MOV   EAX, letterC        ; value of letterC
MOV   ECX, choice         ; length of string is choice (bytes read)
REPNE SCASB               ; execute
answered on Stack Overflow Apr 13, 2018 by zx485 • edited Apr 15, 2018 by zx485

User contributions licensed under CC BY-SA 3.0