Is it possible to declare a string with an uninitialized size in assembly? - string

I am currently declaring my string variable like this:
source BYTE 20 DUP (0)
Then, later I have this code:
mov edx, OFFSET source
mov ecx, SIZEOF source
call ReadString
However, when I try to get the length of the string it always returns 20. Is there a way to get the length of what the user types? Or is it possible to declare an uninitialized size for the string? I have searched my textbook and found nothing so far.

No, it is not implemented in the way you're thinking of. If you want to have a "dynamic" string you have to program it by yourself. The "string variable" you declared is actually a buffer that gets the bytes from ReadString. Imagine it as a glass of water. The size of the glass doesn't change, i.e. you can't get the volume of the water by asking for the size of the glass.
However, Irvine's ReadString returns in EAX the amount of characters inputted by the user. Don't forget to add the terminating null when you work with that amount!
INCLUDE Irvine32.inc
.DATA
buffer BYTE 20 DUP (?)
msg1 BYTE "SIZEOF = ",0
msg2 BYTE "Result of ReadString = ",0
.CODE
main PROC
lea edx, msg1
call WriteString
mov eax, SIZEOF buffer
call WriteInt
call CrLf
mov edx, OFFSET buffer
mov ecx, SIZEOF buffer - 1 ; "-1": save space for the terminating null
call ReadString
lea edx, msg2
call WriteString
call WriteInt
call CrLf
exit
main ENDP
END main

Related

when I remove this "WORD 13" it is working fine, but I want to move the whole 'str1

include irvine32.inc
.data
str1 WORD 13
BYTE 'Source String',0
desti BYTE 80 DUP(?)
.code
main PROC
mov esi,offset str1
inc esi
mov edi,offset desti
call dumpregs
mov ecx, lengthof str1
rep movsb
mov edx,offset desti
call WriteString
call crlf
exit
main ENDP
END main
What does MASM's lengthof calculate? Just the 2-byte word and ignore the byte array on a separate line?
You could do byte 13, 0, 'Source String',0 to get the same data bytes in memory but have MASM treat the whole thing as one "variable".
Or you could put a label at the end and calculate the size yourself with str1_len equ end-start instead of relying on MASM's magic that treats labelled data specially as a "variable".
Any time you want to do anything that isn't exactly what MASM expects, you typically have to work around it. e.g. dword ptr to load 4 bytes from a "variable" that was declared with a different size.

How do i reverse a string on emu8086 assembly language [duplicate]

I have to do a simple calculator in assembly using EMU8086, but every time I try to launch it EMU8086 gives this error:
INT 21h, AH=09h -
address: 170B5
byte 24h not found after 2000 bytes.
; correct example of INT 21h/9h:
mov dx, offset msg
mov ah, 9
int 21h
ret
msg db "Hello$"
I checked the other stuff, but there were no mistakes:
data segment
choice db ?
snum1 db 4 dup(?)
snum2 db 4 dup(?)
sres db 4 dup(?)
num1 db ?
num2 db ?
res db ?
;;menu1 db "Chose a function to procced", 10, 13, "Add [+]", 10, 13, "Sub [-]", 10, 13
;;menu2 db "Mul [*]", 10, 13, "Div [/]", 10, 13, "Mod [%]", 10, 13, "Pow [^]", 10, 13, "Exit [x]$"
messStr db "Enter Your Choice:",10,13,"",10,13,"Add --> +",10,13,"Sub --> -",10,13,"Mul --> *",10,13,"Div --> /",10,13,"Mod --> %",10,13,"Pow --> ^",10,13,"Exit --> X",10,13,"$"
msg1 db "Enter first number$"
msg2 db "Enter second number$"
msg3 db "Press any key to procced$"
msg4 db "The result is $"
ends
stack segment
dw 128 dup(0)
ends
code segment
assume cs:code, ds:data, ss:stack
newline proc ;; new line
push ax
push dx
mov ah, 2
mov DL, 10
int 21h
mov ah, 2
mov DL, 13
int 21h
pop dx
pop ax
ret
endp
printstr proc ;; print string
push BP
mov BP, SP
push dx
push ax
mov dx, [BP+4]
mov ah, 9
int 21h
pop ax
pop dx
pop BP
ret 2
endp
inputstr proc ;; collect input
push BP
mov BP, SP
push bx
push ax
mov bx, [BP+4]
k1:
mov ah, 1
int 21h
cmp al, 13
je sofk
mov [bx], al
inc bx
jmp k1
sofk:
mov byte ptr [bx], '$'
pop ax
pop bx
pop BP
ret 2
endp
getNums proc ;; get the numbers
call newline
push offset msg1
call printstr
call newline
push offset snum1
call inputstr
call newline
push offset msg2
call printstr
call newline
push offset snum2
call inputstr
ret
endp
start:
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
;; print the main menu
call newline
push offset msg4
call printstr
;; collect the input
call newline
mov bx, offset choice
mov ah, 1
int 21h
mov [bx], al
;; check it
mov al, choice
cmp al, '+'
jne cexit
call getNums
jmp cont
cexit:
cmp al, 'x'
je cend
cont:
;; pause before going to the main menu
call newline
push offset msg3
call printstr
mov bx, offset choice
mov ah, 1
int 21h
call newline
call newline
call newline
jmp start
cend:
mov ax, 4c00h
int 21h
ends
end start
I cut most of the code segment because it wasn't important here.
After experimenting with the code I found that the problem was related to the lengths of the messages in the data segment. menu1 & menu2 were too long and any message after them can't be printed (msg1 & msg2 are printed, but nothing after them). I checked if I should merge menu1 & menu2, but it didn't help out. Please help me find out what is wrong with it.
The error message means you use int 21h / AH=09h on a string that didn't end with a $ (ASCII 24h). The system-call handler checked 2000 bytes without finding one.
Often, that means your code or data is buggy, e.g. in a fixed string you forgot a $ at the end, or if copying bytes into a buffer then you maybe overwrote or never stored a '$' in the first place.
But in this case, it appears that EMU8086 has a bug assembling push offset msg4. (In a way that truncates the 00B5h 16-bit address to 8-bit, and sign-extends back to 16, creating a wrong pointer that points past where any $ characters are in your data.)
Based on the error message below I know you are using EMU8086 as your development environment.
INT 21h, AH=09h -
address: 170B5
byte 24h not found after 2000 bytes.
; correct example of INT 21h/9h:
mov dx, offset msg
mov ah, 9
int 21h
ret
msg db "Hello$"
I'm no expert on EMU8086 by any stretch of the imagination. I do know why your offsets don't work. I can't tell you if there is a proper way to resolve this, or if it's an EMU8086 bug. Someone with a better background on this emulator would know.
You have created a data segment with some variables. It seems okay to me (but I may be missing something). I decided to load up EMU8086 to actually try this code. It assembled without error. Using the debugger I single stepped to the push offset msg1 line near the beginning of the program. I knew right away from the instruction encoding what was going on. This is the decoded instruction I saw:
It shows the instruction was encoded as push 0b5h where 0b5h is the offset. The trouble is that it is encoded as a push imm8 . The two highlighted bytes on the left hand pane show it was encoded with these bytes:
6A B5
If you review an instruction set reference you'll find the encodings for PUSH instruction encoded with 6A is listed as:
Opcode* Instruction Op/En 64-Bit Mode Compat/Leg Mode Description
6A ib PUSH imm8 I Valid Valid Push imm8.
You may say that B5 fits within a byte (imm8) so what is the problem? The smallest value that can be pushed onto the stack with push in 16-bit mode is a 16-bit word. Since a byte is smaller than a word, the processor takes the byte and sign extends it to make a 16-bit value. The instruction set reference actually says this:
If the source operand is an immediate of size less than the operand size, a sign-extended value is pushed on the stack
B5 is binary 10110101 . The sign bit is the left most bit. Since it is 1 the upper 8 bits placed onto the stack will be 11111111b (FF). If the sign bit is 0 then then 00000000b is placed in the upper 8 bits. The emulator didn't place 00B5 onto the stack, it placed FFB5. That is incorrect! This can be confirmed if I step through the push 0b5h instruction and review the stack. This is what I saw:
Observe that the value placed on the stack is FFB5. I could not find an appropriate syntax (even using the word modifier) to force EMU8086 to encode this as push imm16. A push imm16 would be able to encode the entire word as push 00b5 which would work.
Two things you can do. You can place 256 bytes of dummy data in your data segment like this:
data segment
db 256 dup(?)
choice db ?
... rest of data
Why does this work? Every variable defined after the dummy data will be an offset that can't be represented in a single byte. Because of this EMU8086 is forced to encode push offset msg1 as a word push.
The cleaner solution is to use the LEA instruction. This is the load effective address instruction. It takes a memory operand and computes the address (in this case the offset relative to the data segment). You can replace all your code that uses offset with something like:
lea ax, [msg1]
push ax
AX can be any of the general purpose 16-bit registers. Once in a register, push the 16-bit register onto the stack.
Someone may have a better solution for this, or know a way to resolve this. If so please feel free to comment.
Given the information above, you may ask why did it seem to work when you moved the data around? The reason is that the way you reorganized all the strings (placing the long one last) caused all the variables to start with offsets that were less than < 128. Because of this the PUSH of an 8-bit immediate offset sign extended a 0 in the top bits when placed on the stack. The offsets would be correct. Once the offsets are >= 128 (and < 256) the sign bit is 1 and the value placed on the stack sign will have an upper 8 bits of 1 rather than 0.
There are other bugs in your program, I'm concentrating on the issue directly related to the error you are receiving.
I reviewed your code and concentrated on the following sequence of instructions:
mov bx, offset choice ; here you set BX to the address of 'choice'
mov ah, 1
int 21h ; here you 'READ CHARACTER FROM STANDARD INPUT, WITH ECHO'
mov [bx], al ; because INT 21h does preserve BX, you are writing back the result of the interrupt call (AL) back to the memory location at BX, which is named 'choice'
;; check it
mov al, choice ; HERE you are moving a BYTE variable named 'choice' to AL, overwriting the result of the last INT 21h call
cmp al, '+' ; ... and compare this variable to the ASCII value of '+'
jne cexit ; if this variable is unequal to '+' you jump to 'cexit'
call getNums ; otherwise you try to get another number from the input/STANDARD CONSOLE
So your sequence
mov bx, offset choice ; here you set BX to the address of 'choice'
...
mov [bx], al ; because INT 21h does preserve BX, you ...
...
mov al, choice
essentially means, that you are setting BX to the address of 'choice', then setting 'choice'([BX]) to AL and copying it back to AL.
This is redundant.
After that, you compare that char to '+' and...
if that char equals to '+', you get the next char with call getNums and then continue with cont:.
if that char does not equal to '+', you compare it to 'x', the exit-char. If it's not 'x', you fall through to cont:
No error here.
So your problem with menu1 and menu2 may stem from some escape characters included in your strings like %,/,\. For example, % is a MACRO character in some assemblers which may create problems.
simple solution is that your strings should always end in '$'
change DUP(?) to DUP('$') and all other strings end with ,'$'

How to compare two strings in assembly?

I'm new in assembly. I want to compare two string using "cmps". I read some examples and I write this :
GETSTR MACRO STR
MOV AH,0AH
LEA DX,STR
INT 21H
ENDM
PRINTSTR MACRO STR
MOV AH,09H
LEA DX,STR
INT 21H
ENDM
EXTRA SEGMENT
DEST DB ?
EXTRA ENDS
DATA SEGMENT
SOURCE DB ?
STR1 DB 0AH,0DH,'ENTER STR : ' ,'$'
ENTER DB 10,13,'$'
SAME DB 0AH,0DH,'TWO STR ARE THE SAME ' ,'$'
NSAME DB 0AH,0DH,'TWO STR ARE NOT THE SAME ' ,'$'
USER DB 6,10 DUP('$')
USER1 DB 6,10 DUP('$')
DATA ENDS
CODE SEGMENT
ASSUME DS:DATA,CS:CODE,ES:EXTRA
START:
MOV AX,DATA
MOV DS,AX
MOV AX,EXTRA
MOV ES,AX
PRINTSTR STR1
GETSTR USER1
PRINTSTR STR1
GETSTR USER
LEA BX,USER
MOV SI,BX
LEA BX,USER1
MOV DI,BX
CLD
MOV CX,5
REPE CMPSB
JCXZ MTCH
PRINTSTR NSAME
JMP ENDPR
MTCH:
PRINTSTR SAME
ENDPR:
MOV AH,4CH
INT 21H
CODE ENDS
END START
I have some question:
what is exactly the numbers 6,10 in the code below :
USER DB 6,10 DUP('$')
Is there any mistake with the Macros?
Is it necessary to declare EXTRA SEGMENT ?
For any similar strings input the output is : "they are not the same?" what is the reason?
The number 6 defines the number of characters plus 1 that you want DOS to input. The number 10 defines the length of the buffer that follows. Actually the number 7 would have been enough!
The macros seem fine.
You don't need the EXTRA segment. Moreover putting it into ES is wrong because both strings that you will be comparing are in the DATA segment.
Also both LEA instructions must fetch an address that is 2 higher. The first byte will still be the maximum number of bytes to read (6) and the second byte will be the number of bytes actually read [0,5]
The comparison you're making indifferably uses 5 characters. If you don't take into account the real number of characters as reported by DOS in the second byte it's no wonder results might not be satisfying.

Reading to and from arrays in Assembly?

I'm having a bit of trouble reading to and from arrays in assembly.
It's a fairly simple program (albeit at this point, far from finished). All I'm trying to do at this point is read a string of (what we're assuming is numbers), converting it to a decimal number, and printing it. Here's what I've got so far. As of now, it prints str1. After you enter a number and hit enter, it prints str1 again and freezes. Can anyone offer some insight as to what all I'm doing wrong?
INCLUDE Irvine32.inc
.data
buffersize equ 80
buffer DWORD buffersize DUP (0)
str1 BYTE "Enter numbers to be added together. Press (Q) to Quit.", 0dh, 0ah,0;
str2 BYTE "The numbers entered were: ", 0dh, 0ah, 0
str3 BYTE "The total of numbers entered is: ", 0dh, 0ah, 0
error BYTE "Invalid Entry. Please try again.", 0dh, 0ah,0
value DWORD 0
.code
main PROC
mov edx, OFFSET str1
call Writestring
Input:
call readstring
mov buffer[edi], eax
cmp buffer[edi], 0
JL NOTDIGIT
cmp buffer[edi], 9
JG NOTDIGIT
call cvtDec
mov edx, buffer[edi]
call WriteString
jmp endloop
Notdigit:
mov edx, OFFSET error
call writestring
exit
cvtDec:
mov eax, buffer[edi]
AND eax,0Fh
mov buffer[edi],edx
ret
endloop:
main ENDP
END MAIN
First off, Mr. Irvine created the function called WriteString, but you use 2 variations - writestring and Writestring; you do use the correct case of the function in one place. Get into the habit of using the correct names of functions now, and it will cut down on bugs later.
Second, you created a label called Notdigit but yet you use JL NOTDIGIT and JG NOTDIGIT in your code. Again, use the correct spelling. MASM should of given you an A2006 error "undefined symbol"
You also declared your entry point as main, but you close your code section with END MAIN instead of END main.
If you have MASM set up properly (by adding option casemap:none at the top of your source. Or just open irvine32.inc and uncomment the line that says OPTION CASEMAP:NONE)
Let's look at the ReadString procedure comment in irvine32.asm:
; Reads a string from the keyboard and places the characters
; in a buffer.
; Receives: EDX offset of the input buffer
; ECX = maximum characters to input (including terminal null)
; Returns: EAX = size of the input string.
; Comments: Stops when Enter key (0Dh,0Ah) is pressed. If the user
; types more characters than (ECX-1), the excess characters
; are ignored.
ReadString takes an address of the buffer to hold the inputed string in edx, you are using the address of your prompt str1, maybe you meant to use buffer? You also did not put the size of the buffer into ecx
Your using edi as an index into your buffer, what value does edi contain? Your trying to put the value of eax into it, what does eax contain??? Both edi and eax probably contain garbage; not what you want.
Look at this carefully:
cvtDec:
mov eax, buffer[edi]
AND eax,0Fh
mov buffer[edi],edx
Your putting a value (That you think is an ASCII value of a number) into eax then converting to a decimal value... ok... Next, you are putting whatever is in edx back into your buffer. Is that what you want?

Stop BufferOverflow - NASM Input

I am trying to write some basic input/output code to the terminal in Linux with NASM. I want to allow the user to input data but my problem is that I get a buffer overflow if the user enters more data than the buffer length. I am attempting to check if the inputted data is greater than the bufferlength and if so then ask the user to "Enter Data:" again.
Here is my current code:
SECTION .bss
BUFFLENGTH equ 8 ;The max length of our Buffer
Buff: resb BUFFLENGTH ;The buffer itself
SECTION .data
Prompt: db "Enter Data: ",10
PromptLen: equ $-Prompt
SECTION .text
global _start
_start:
DisplayPrompt:
mov eax, 4
mov ebx, 1
mov ecx, Prompt
mov edx, PromptLen
int 80h
Read:
mov eax, 3 ;Specify sys_read call
mov ebx, 0; Specify File Descriptor 0 : STDIN (Default to keyboard input)
mov ecx, Buff; pass offset of the buffer to read to
mov edx, BUFFLENGTH ; Tell sys_read to read BUFFLEN
int 80h ;make kernel call
mov esi, eax
cmp byte[ecx+esi], BUFFLENGTH ;compare the returned bufferSize to BUFFLENGTH
jnbe DisplayPrompt ;Jump If Not Below or Equal To BUFFLENGTH
Write:
mov edx, eax ;grab the size of the buffer that was used (charachter length)
mov eax, 4 ;specify sys_write
mov ebx, 1 ; specify File Descriptor 1: STDOUT
mov ecx, Buff ;pass the offset of the Buffer
int 80h ;make kernel call
Exit:
mov eax, 1 ; Code for Exit syscall
mov ebx, 0 ; Exit code { = 0; Program ran OK }
int 80h ; make kernel call
I believe my error is in how I am comparing the data, here:
mov esi, eax
cmp byte[ecx+esi], BUFFLENGTH ;compare the returned bufferSize to BUFFLENGTH
jnbe DisplayPrompt ;Jump If Not Below or Equal To BUFFLENGTH
Any help would be appreciated. Thanks.
What you are calling "buffer overflow" here isn't the common definition of buffer overflow. If I understand correctly, what you are considering "buffer overflow" in this scenario is "The data spills over into terminal instead of limiting the user to not enter more data than the bufferlength". But in fact, the user can't enter more data than the buffer length. What is happening is that your read() reads 8 bytes from stdin and the remaining bytes "are still" in stdin where bash reads from when your program exits and the "\n" at the makes it try to execute the "spilling bytes" like you call them. There is no reason to change this since it's not a security issue at all. The user can't execute commands as the owner of the program that way.
If you really wanted to get rid of this, you could use malloc() to allocate a 'big enough' buffer. That way no matter how much the user inputs, the buffer will be big enough (depending on how much RAM you have, etc.) and you won't see those "spilling bytes" anymore.

Resources