x86 Assembler using MASM32 Tutorial 11 - A String Compare Function to Compare Two Strings, One Hard Coded and One Loaded from a File



Watch the video or follow the tutorial.



This, the 11th tutorial we will create a string comparison function that will take two separate string variables, one in memory and one loaded from a file and compare them to see if they are the same string. If they are then one output is displayed and if they are not then another one is. So, first of all open Visual MASM, if you haven't installed Visual MASM yet you can learn how to do it here. Once installed open it and learn create the default code template needed for all my tutorials here. This default code will be the starting point for most of our tutorials including this one. So, once all this is done you should be looking at the code window as seen below:



Scroll down to the .data section and add the following eight variables:

openerr DB "Error Creating File", 0
file DB "D:\Temp\File.txt", 0
err_cap DB "Error", 0
readerr DB "Unable to Read from File", 0
strString DB "1111-2222-3333-4444-5555-6666", 0
len1 Equ $ - strString - 1

strCap1 DB "Comparison Successful", 0
strMsg1 DB "Congratulations the strings match", 0
strCap2 DB "Comparison Unsuccessful", 0
strMsg2 DB "Unlucky the strings don't match", 0

Then scroll down to the uninitialised data section which starts with .data? and add the following code:

fhandle DD ?
bytesread DD ?
bytesBuf BYTE 50 DUP(?)

In the .code section after start: the following code should be added

; CreateFile Function, Fails if no FIle Exists
Push 0
Push FILE_ATTRIBUTE_NORMAL
Push OPEN_EXISTING
Push 0
Push 0
Push GENERIC_READ
Push Offset file
Call CreateFile

Mov fhandle, Eax
Cmp Eax, 0FFFFFFFFh

Jnz file_opened

; If Error Previous Jump Ignored and Now We Declare Error With a MessageBox
Push MB_OK
Push Offset err_cap
Push Offset openerr
Push 0
Call MessageBox

Jmp end_

file_opened:

; Read from file
Push 0
Lea Eax, bytesread
Push Eax
Push SIZEOF bytesBuf
Lea Eax, bytesBuf
Push Eax
Push fhandle
Call ReadFile

Cmp bytesread, 0
Jnz bytes_have_been_read

; Error Reading from File
Push MB_OK
Push Offset err_cap
Push Offset readerr
Push 0
Call MessageBox

Jmp end_

bytes_have_been_read:

; Compare len1 and len2 if the are different the strings don't match
Mov Eax, len1
Mov Ebx, bytesread
Cmp Eax, Ebx
Jnz strings_dont_match

; set Esi to memory address of strString1
; set Ecx (for Repz) to strString1 length including 0 string terminator
; set Edi to address of strString2
Mov Esi, Offset strString
Mov Ecx, len1 + 1
Mov Edi, Offset bytesBuf

; Repeat until the values in the memory of [EDI] and [ESI] are different, or Ecx = 0
Repz Cmpsb

Cmp Ecx, 0

; If Ecx = 0 then cmpsb command got to end of string
; If Ecx != 0 then cmpsb found bytes that didnt match
Jz strings_match
Jmp strings_dont_match

strings_match:

; Strings match MessageBox Congratulations
Push MB_OK
Push Offset strCap1
Push Offset strMsg1
Push 0
Call MessageBox
Jmp end_close_file

strings_dont_match:

; Strings don't match MessagBox Error
Push MB_OK
Push Offset strCap2
Push Offset strMsg2
Push 0
Call MessageBox

end_close_file:

Push fhandle
Call CloseHandle

end_:


The first function call is to CreateFile using the OPEN_EXISTING parameter which opens an existing file at the location named in the fileName variable. On my computer the location is D:\Temp so you should change this line to whatever location you have access to on your computer. The file name is not so important just make sure that the file exists at that location or an error will occur. After the CreatfIle function is complete the contents of the Eax register is loaded into the fhandle variable. If the CreateFile function was successful then the Eax register will hold the memory address of the handle to the created file, if not it will hold an error code. The compare command tests to see the Eax register holds the error code and if it doesn't the jump if not zero command takes us the part of the code that should run if the file is ready to be read. If the Eax register does hold an error code then a message box is displayed reporting the error and the program ends.

If the file is ready to be read then we read the contents of the file into the bytesBuffer variable which needs to be long enough to hold the entire contents of the file. A compare between the variable bytesRead and zero is conducted and if there was an error reading from the file then bytesRead will be zero, if not then it will be non-zero. Either way the appropriate message box is displayed showing the contents of the file if no error occurred and reporting an error message if it did.

The next section compares the values of len1 and len2 which are the lengths of the two strings, after a compare command between the two registers that contain len1 and len2 if they are different then the strings are not equal and there is no need to conduct the string comparison, the code jumps to the strings_dont_match label and an error message is displayed and the program ends. Next we load the memory address of the two strings into the Esi and Edi registers which are generally used during a Cmpsb command. Ecx is loaded with the value of len1 which tells us how many times we should conduct the comparison. Ecx will be reduced by one after each iteration, when it reaches zero the comparison function ends.

Repz Cmpsb means repeat until the zero flag indicates that the values in Esi and Edi are not equal and Cmpsb means compare strings byte by byte whereas for example Cmpsw means compare strings word by word which is compare every two byte values. After the Repz Cmpsb command a compare command is conducted between the value in Ecx and zero. If Ecx contains zero then the comparison functions compared all the values in the two strings and they were all equal. If it is not zero then the function found bytes that were not equal. A different Jump command is given for each outcome.

If the value in Ecx got to zero then a MessageBox reporting a successful comparison is shown otherwise an error is reported. If the file was successfully opened it has to be closed with the CloseHandle cAPI

The entire code looks like this:

; *************************************************************************
; 32-bit Windows Program
; *************************************************************************

.686                                      ; Enable 80686+ instruction set
.model flat, stdcall                ; Flat, 32-bit memory model (not used in 64-bit)
option casemap: none         ; Case sensitive syntax

; *************************************************************************
; MASM32 proto types for Win32 functions and structures
; *************************************************************************
include c:\masm32\include\windows.inc
include c:\masm32\include\user32.inc
include c:\masm32\include\kernel32.inc
include c:\masm32\include\masm32rt.inc     ; for using ustr$() and such like

; *************************************************************************
; MASM32 object libraries
; *************************************************************************
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib

; *************************************************************************
; Our data section.
; *************************************************************************
.data

openerr DB "Error Creating File", 0
file DB "D:\Temp\File.txt", 0
err_cap DB "Error", 0
readerr DB "Unable to Read from File", 0
strString DB "1111-2222-3333-4444-5555-6666", 0
len1 Equ $ - strString - 1

strCap1 DB "Comparison Successful", 0
strMsg1 DB "Congratulations the strings match", 0
strCap2 DB "Comparison Unsuccessful", 0
strMsg2 DB "Unlucky the strings don't match", 0

; *************************************************************************
; Our unintialised data section.
; *************************************************************************
.data?

fhandle DD ?
bytesread DD ?
bytesBuf BYTE 50 DUP(?)

; *************************************************************************
; Our constant section.
; *************************************************************************
.const



; *************************************************************************
; Macros
; *************************************************************************



; *************************************************************************
; Our executable assembly code starts here in the .code section
; *************************************************************************
.code

start:

    ; CreateFile Function, Fails if no FIle Exists
    Push 0
    Push FILE_ATTRIBUTE_NORMAL
    Push OPEN_EXISTING
    Push 0
    Push 0
    Push GENERIC_READ
    Push Offset file
    Call CreateFile

    Mov fhandle, Eax
    Cmp Eax, 0FFFFFFFFh

    Jnz file_opened

    ; If Error Previous Jump Ignored and Now We Declare Error With a MessageBox
    Push MB_OK
    Push Offset err_cap
    Push Offset openerr
    Push 0
    Call MessageBox

    Jmp end_

    file_opened:

    ; Read from file
    Push 0
    Lea Eax, bytesread
    Push Eax
    Push SIZEOF bytesBuf
    Lea Eax, bytesBuf
    Push Eax
    Push fhandle
    Call ReadFile

    Cmp bytesread, 0
    Jnz bytes_have_been_read

    ; Error Reading from File
    Push MB_OK
    Push Offset err_cap
    Push Offset readerr
    Push 0
    Call MessageBox

    Jmp end_

    bytes_have_been_read:

    ; Compare len1 and len2 if the are different the strings don't match
    Mov Eax, len1
    Mov Ebx, bytesread
    Cmp Eax, Ebx
    Jnz strings_dont_match

    ; set Esi to memory address of strString1
    ; set Ecx (for Repz) to strString1 length including 0 string terminator
    ; set Edi to address of strString2
    Mov Esi, Offset strString
    Mov Ecx, len1 + 1
    Mov Edi, Offset bytesBuf

    ; Repeat until the values in the memory of [EDI] and [ESI] are different, or Ecx = 0
    Repz Cmpsb

    Cmp Ecx, 0

    ; If Ecx = 0 then cmpsb command got to end of string
    ; If Ecx != 0 then cmpsb found bytes that didnt match
    Jz strings_match
    Jmp strings_dont_match

strings_match:

    ; Strings match MessageBox Congratulations
    Push MB_OK
    Push Offset strCap1
    Push Offset strMsg1
    Push 0
    Call MessageBox
    Jmp end_close_file

strings_dont_match:

    ; Strings don't match MessagBox Error
    Push MB_OK
    Push Offset strCap2
    Push Offset strMsg2
    Push 0
    Call MessageBox

end_close_file:

    Push fhandle
    Call CloseHandle

end_:

    Push 0
    Call ExitProcess

end start


Run the program as many times as you like changing the string value in teh file to see what happens.

Thank you for following this tutorial, I hope you found it useful. In the next tutorial we will have a look at comparing two strings and determining whether they match or not, so until then, enjoy.

Link to a text file with complete source code and more comments - here.