I am trying to change the variable value in x86_64 asm
Here is my approach
section .data
text db "Hello, World!",10
section .text
global _start
_start:
mov rax, 1
mov rdi, 1
mov rsi, text
mov rdx, 14
syscall
mov rax , "He"
mov [text], rax
syscall
mov rax, 1
mov rdi, 1
mov rsi, text
mov rdx, 14
syscall
mov rax, 60
mov rdi, 0
syscall
But that outputs
Hello, World!
Heorld!
I have tried to use : mov word [text], "He"
but that doesnt work neither
CodePudding user response:
The thing you call a variable is a label that basically holds the address of the value in memory. When you want to change the value you need to use brackets []
and dereference the address that points to that location. Then you can change the values one by one. For example, lets define a one-byte variable:
v: db 0x00
To change the value you can do
mov byte[v], 0x02
As you can see we specified the size with byte
If we had the following variable:
abc: dw 0x0000
the variable abc
would only hold the address of the first byte of the data but the data itself is a word (2 bytes). That is why to change the variable's value we need to do:
mov word[abc], 0xDEAD
which would be equivalent to
mov byte[abc], 0xAD
mov byte[abc 1], 0xDE
Note that the least first byte of the 2-byte value is in the earlier memory address, this is called little-endian order.
A string is essentially a bunch of "bytes" next to each other (it doesn't use little endian). To change a string value one by one you can do:
text: db "Hello World", 0
mov byte [text], 'A' ; Aello World
mov byte [text 1], 'B' ; ABllo World
mov byte [text 2], 'C' ; ABClo World
; and etc
Also finally we can take a look at your code:
text db "Hello, World!",10
mov rax , "He"
mov [text], rax
syscall
This is not valid (as pointed out by @vitsoft) because you are putting "He"
inside of rax
before calling syscall
which uses rax
to determine what it's gonna do.
As a matter of fact this line of code
mov word [text], "He"
is perfectly valid. I don't know why you couldn't get that to work. "He"
is essentially resolved to 0x6548
and you do a normal mov
as a word. As I mentioned before because of the little-endian order for words, 0x48 ('H')
will be placed in the first byte of text
which is already "H"
and similarly 0x65 ('e')
will be placed in the second byte of text
which is already "e"
.
Edit:
Lets say you don't know the length of a string which you want to copy to another string/location. In that case you should loop over that string and do the changes one by one. I will leave a sample code here which you would need to fix and adapt:
start:
mov ecx, 0 ; initialize some variable to keep count
.loop:
mov edx, other ; and address of other to edx
add edx, ecx ; get the nth character's address
mov al, byte [edx] ; get the nth character of other.
cmp al, 0x00 ; if we reached the end of the string
je endLoop ; end the function
mov ebx, text ; move the address of text to ebx
add ebx, ecx ; get the address of the nth character of text
mov [ebx], al ; write the nth character of other to nth position of text
inc ecx ; increase counter
jmp .loop ; loop
endLoop:
ret
text: db "Hello World", 0
other: db "ABC", 0
CodePudding user response:
Regardless of odd syscall
after memory modification, reason of output is the following.
Initially bytes at address text
are:
0 1 2 3 4 5 6 7 8 9 a b c d
48 65 6c 6c 6f 2c 20 57 6f 72 6c 64 21 0a
H e l l o , ' ' W o r l d ! \n
After
mov rax , "He"
rax
contains 0x6548 in two lower bytes, six other bytes are zeroed. As x86_64 is little endian, after
mov [text], rax
Memory is:
0 1 2 3 4 5 6 7 8 9 a b c d
48 65 00 00 00 00 00 00 6f 72 6c 64 21 0a
H e \0 \0 \0 \0 \0 \0 o r l d ! \n
Zero bytes are just not printed on terminal.