Search code examples
assemblyx86-64nasm

How to change variable value in x86_64 assembly (nasm)


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


Solution

  • 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:
        xor ecx, ecx ; initialize some variable to keep count
    
    .loop:
        mov al, byte [other + rcx] ; get the nth character of other.
        cmp al, 0x00 ; if we reached the end of the string
        je endLoop ; end the function
        mov [text + rcx], 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