简体   繁体   中英

Why doesn't this simple assembly program return a number?

Why doesn't this simple assembly program return a number?

I'm trying to get it to print 12 hex, but its printing gibberish.

global _start

section .text

_start:

    mov eax, 0x4
    mov ebx, 0x1
    mov ecx, var1
    mov edx, 2
    int 0x80

    ;exit the program gracefully
    mov eax, 0x1
    mov ebx, 0x5
    int 0x80

section .data

    var1:   db 0x12

The primary stumbling block you have run into is attempting to write the value of 0x12 (ASCII 18) to stdout . A quick check of the ASCII chart will reveal that this value is non-printable. In assembly, you can only write characters to stdout . What that means is when faced with writing a numeric value , you have to separate the value into its digits, convert the digits to their ASCII representation (by adding '0' or 0x30 or 48 decimal) to the value.

It's no different than manually converting a number to a string in C or any other language. You basically divide the original number by 10 repeatedly, saving the remainder each time as the digit to a buffer, then writing the buffer out in reverse order to write the ASCII representation of the number to stdout (there are many example on SO).

Before looking at an example that will separate all digits into a buffer and write them to stdout , let's just cover some basics in your example and get it to print something (anything). The first concept in assembly you need to cement in your brain is that all labels (variables) in assembly point to a memory address not a numeric value. When you assign a label in assembly, eg:

var1  db 0x12

you are storing a single byte of data whose value is 12-hex at the memory location pointed to by var1 . var1 is a pointer to that memory location. In nasm, if you want to operate on/or reference the value at that location, you must dereference the pointer to the address by enclosing the pointer in [ ] (just like dereferencing a pointer in C with *var1 ).

The next concept to cement, is that sys_write (syscall 4) expects a beginning memory address, not a value, stored in ecx . It will then write edx bytes of data to the file descriptor stored in ebx (1 - stdout ). mov ing the address of var1 into edx will allow us to dereference the value at that address by operating on [edx] . To convert individual digits to their ASCII value you add '0' (or 0x30) to them, but what will the result be if we just add '0' to [edx] ? (present value 0x12 (18) + 0x30 (48) = 0x42 (66) -- which just happens to be ASCII 'B' )

(we will aslo cheat and append a newline ( 0xa ) to the end of var1 and make it 2-bytes so we don't have to mess with a separate call)

Putting this together with your example would result in:

section .text

    global _start

_start:

        mov     eax, 4              ; linux sys_write
        mov     ebx, 1              ; stdout
        mov     ecx, var1           ; mov address of var1 to ecx
        add     byte [ecx], 0x30    ; add '0' to first byte of var1
        mov     edx, 2              ; number of chars to print
        int     0x80                ; syscall

        mov     eax, 0x1            ; __NR_exit 1
        xor     ebx, ebx            ; exit code of 0
        int     0x80

section .data

    var1  db 0x12, 0xa  ; ASCII 18 (non-printable) with newline

Compiling and running the program will result in:

$ ./convert
B

Now we can move on to a full example that will separate each digit for the value stored at var1 and then print each digit of 0x12 (decimal 18) to stdout followed by a newline . (outputting a conversion to the hex representation is left to you -- there are several examples you can search the web and find, int 80h.org comes to mind.)

A brief example in nasm would be:

section .text

    global _start

_start:

        mov     edi, result     ; address of buffer in destination index
        xor     eax, eax        ; zero out eax

        mov     al, [var1]      ; put the value of var1 in al to divide
        mov     bl, 10          ; base 10 divisor to find remainder

        ; separate remainder digits into result buffer
 remloop:
        div     bl              ; divide current value by 10
        mov     [edi], ah       ; move the remainder to result
        cmp     al, 0           ; is the quotient zero?
        je      printchar       ; if it is we are done
        xor     ah, ah
        inc     edi             ; move offset in result string (note digits
        jmp     remloop         ; of answer are stored in reverse order)

    printchar:
        add    byte [edi], 0x30 ; add ascii '0' to digit to get printable char
        mov     eax, 4          ; linux sys_write
        mov     ebx, 1          ; stdout
        mov     ecx, edi        ; point to current digit
        mov     edx, 1          ; number of chars to print
        int     0x80            ; syscall
        dec     edi             ; point to next digit
        cmp     edi, result     ; are we past the final digit?
        jge     printchar       ; if not, keep printing to stdout

        ; print newline
        mov     eax, 4          ; linux sys_write
        mov     ebx, 1          ; stdout
        mov     ecx, newline    ; address of newline
        mov     edx, 1          ; number of chars to print
        int     0x80            ; syscall

        ; exit the program gracefully
        mov eax, 0x1            ; __NR_exit 1
        mov ebx, 0x5            ; exit code of 5
        int 0x80

section .data

    var1  db 0x12       ; ASCII 18 (non-printable)
    result times 8 db 0 ; 8 byte buffer for result
    newline db 0xa      ; newline character

If you build the code:

nasm -f elf -o convert.o convert.asm
ld -m elf_i386 -o convert convert.o

You can then display the ASCII output of 0x12 or 18 ASCII:

$ ./convert
18

For an excellent web reference for Assembly see The Art of Assembly Language Programming . Read it. All of it. It's that good. While it is primarily written for 8086, all principles are 100% applicable to current assembly programming. The only differences are register sizes, calling conventions and syscall numbers for x86_64.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM