简体   繁体   English

为什么这个简单的汇编程序不返回数字?

[英]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. 我正在尝试使它打印12进制,但其打印乱码。

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 . 您遇到的主要绊脚石试图将0x12 (ASCII 18)的写入stdout A quick check of the ASCII chart will reveal that this value is non-printable. 快速检查ASCII图表将发现此值不可打印。 In assembly, you can only write characters to stdout . 在汇编中,您只能将字符写入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. 这就是说,当面对一个数字值时 ,您必须将该值分成数字,然后将数字转换为ASCII表示形式(通过将'0'0x3048位十进制加)。

It's no different than manually converting a number to a string in C or any other language. 这与以C或任何其他语言将数字手动转换为字符串没有什么不同。 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). 您基本上可以将原始数字重复除以10 ,每次将剩余的数字作为数字保存到缓冲区中,然后以相反的顺序写出缓冲区,以将数字的ASCII表示形式写到stdout (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). 在看一个将所有数字分隔到缓冲区并将它们写入stdout的示例之前,让我们仅介绍示例中的一些基础知识并使其打印任何内容。 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指向的存储位置存储一个单字节数据,其值为12-hex var1 is a pointer to that memory location. var1是指向该内存位置的指针。 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 ). 在nasm中,如果要在该位置上操作/或引用该 ,则必须通过将指针括在[ ]来将指针解除引用到地址(就像在C中使用*var1解除引用指针一样)。

The next concept to cement, is that sys_write (syscall 4) expects a beginning memory address, not a value, stored in ecx . 巩固的下一个概念是sys_write (syscall 4)期望在ecx存储的起始内存地址而不是值。 It will then write edx bytes of data to the file descriptor stored in ebx (1 - stdout ). 然后,它将edx数据字节写入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] . mov荷兰国际集团的地址var1edx将使我们能够在解引用该地址的值通过对操作[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] ? 要将单个数字转换为ASCII值,请在其上添加'0' (或0x30),但是如果仅在[edx]添加'0' ,结果将是什么? (present value 0x12 (18) + 0x30 (48) = 0x42 (66) -- which just happens to be ASCII 'B' ) (当前值0x12 (18)+ 0x30 (48)= 0x42 (66)-恰好是ASCII'B '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) (我们还将作弊,并在var1的末尾添加一个newline0xa ),并将其0xa 2个字节,这样我们就不必再为单独的调用弄乱了。)

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 . 现在我们来看一个完整的示例,它将存储在var1处的值的每个数字分开,然后将0x12 (十进制18)的每个数字打印到stdout然后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.) (输出转换为十六进制表示形式的工作就交给您了-您可以在网上搜索几个示例, 然后就会想到int 80h.org 。)

A brief example in nasm would be: nasm中的一个简单示例是:

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: 然后,您可以显示0x1218 ASCII的ASCII输出:

$ ./convert
18

For an excellent web reference for Assembly see The Art of Assembly Language Programming . 有关汇编的出色Web参考,请参见汇编语言编程的艺术 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. 尽管它主要是为8086编写的,但所有原理都可100%适用于当前的汇编编程。 The only differences are register sizes, calling conventions and syscall numbers for x86_64. 唯一的区别是x86_64的寄存器大小,调用约定和系统调用号。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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