汇编学习(8) 宏,IO

2022-12-13 17:31:05 浏览数 (2)

本篇介绍

本篇介绍下汇编中的宏和IO操作,其中IO操作包括控制台IO和文件IO

首先宏并不是汇编支持的,而是nasm 汇编器支持的,这个也容易想到,汇编本身是一套指令,而宏就是将若干指令替换成一个符号,在编译的时候再展开到代码中,这完全是编译层面的能力。 接下来看一个代码例子,nasm汇编的使用;

代码语言:javascript复制
; macro.asm
extern printf

�fine double_it(r)    sal r, 1    ; single line macro

%macro  prntf   2           ; multiline macro with 2 arguments
    section .data
            %%arg1  db  %1,0            ; first argument
            %%fmtint    db  "%s %ld",10,0   ; formatstring
    section .text               ; the printf arguments
        mov rdi, %%fmtint
        mov rsi, %%arg1
        mov rdx,[%2]        ; second argument
        mov rax,0           ; no floating point  
        call printf
%endmacro

section .data                           
    number  dq  15                  
section .bss
section .text                           
    global main                     
main:
push rbp
mov rbp,rsp
    prntf   "The number is", number
    mov     rax, [number]
    double_it(rax)
    mov     [number],rax
    prntf   "The number times 2 is", number
leave
ret 
结果如下:
The number is 15
The number times 2 is 30

可以看到有2种宏,一种是单行的,这时候用�fine 就可以了。另外一种是多行的,这时候需要用%macro 和 %endmacro。而且对于多行的宏,可以指定参数的个数,%n表示第n个入参地址,如果宏内部需要用变量,变量名字前就需要用%% 做前缀,比如%%arg1,%%fmtint。%% 就是告诉nasm每次调用宏就需要创建新的变量实例,这样就不会遇到变量重复定义的问题了。 接下来也可以反编译看下结果:

代码语言:javascript复制
0000000000401130 <main>:
  401130:   55                      push   rbp
  401131:   48 89 e5                mov    rbp,rsp
  401134:   48 bf 46 40 40 00 00    movabs rdi,0x404046
  40113b:   00 00 00 
  40113e:   48 be 38 40 40 00 00    movabs rsi,0x404038
  401145:   00 00 00 
  401148:   48 8b 14 25 30 40 40    mov    rdx,QWORD PTR ds:0x404030
  40114f:   00 
  401150:   b8 00 00 00 00          mov    eax,0x0
  401155:   e8 d6 fe ff ff          call   401030 <printf@plt>
  40115a:   48 8b 04 25 30 40 40    mov    rax,QWORD PTR ds:0x404030
  401161:   00 
  401162:   48 d1 e0                shl    rax,1
  401165:   48 89 04 25 30 40 40    mov    QWORD PTR ds:0x404030,rax
  40116c:   00 
  40116d:   48 bf 64 40 40 00 00    movabs rdi,0x404064
  401174:   00 00 00 
  401177:   48 be 4e 40 40 00 00    movabs rsi,0x40404e
  40117e:   00 00 00 
  401181:   48 8b 14 25 30 40 40    mov    rdx,QWORD PTR ds:0x404030
  401188:   00 
  401189:   b8 00 00 00 00          mov    eax,0x0
  40118e:   e8 9d fe ff ff          call   401030 <printf@plt>
  401193:   c9                      leave  
  401194:   c3                      ret 

这样就可以看到宏被展开后的结果了,基本可以和把宏用指令替换对上。

控制台 IO

有时候我们需要直接从控制台读写,这时候除了用c的io函数外,也可以直接用read,write的系统调用,下面是一个例子,用来从控制台读取内容,然后显示到控制台上:

代码语言:javascript复制
; console2.asm
section .data                           
    msg1    db      "Hello, World!",10,0            
    msg2    db      "Your turn (only a-z): ",0
    msg3    db      "You answered: ",0
    inputlen equ    10          ;inputbuffer
    NL      db      0xa                 
section .bss                            
    input resb inputlen 1       ;provide space for ending 0
section .text                           
    global main                     
main:
push    rbp 
mov     rbp,rsp
    mov     rdi, msg1       ; print first string 
    call    prints    
    mov     rdi, msg2   ; print second string, no NL 
    call    prints
    mov     rdi, input      ; address of inputbuffer
    mov     rsi, inputlen   ; length of inputbuffer 
    call    reads           ; wait for input 
    mov     rdi, msg3       ; print third string and add the input string
    call    prints
    mov     rdi, input      ; print the inputbuffer
    call    prints
    mov     rdi,NL          ; print NL
    call    prints     
leave
ret
;----------------------------------------------------------
prints:
push    rbp
mov     rbp, rsp
push    r12                 ; callee saved

; Count characters 
        xor     rdx, rdx    ; length in rdx
        mov     r12, rdi   
.lengthloop:
        cmp     byte [r12], 0
        je      .lengthfound
        inc     rdx
        inc     r12
        jmp     .lengthloop
.lengthfound:       ; print the string, length in rdx
        cmp     rdx, 0      ; no string (0 length)
        je      .done
        mov     rsi,rdi     ; rdi contains address of string
        mov     rax, 1      ; 1 = write
        mov     rdi, 1      ; 1 = stdout
        syscall
.done:
pop r12
leave 
ret
;----------------------------------------------------------
reads:
section .data
section .bss
        .inputchar  resb    1  
section .text
push    rbp
mov rbp, rsp
    push    r12         ; callee saved
    push    r13         ; callee saved
    push    r14         ; callee saved
    mov     r12, rdi    ; address of stringbuffer
    mov     r13, rsi    ; max length in r13
    xor     r14, r14    ; character counter
.readc: 
        mov     rax, 0          ; read
        mov     rdi, 1          ; stdin
        lea     rsi, [.inputchar]   ; address of input
        mov     rdx, 1          ; # of characters to read
        syscall
        mov     al, [.inputchar]    ; char is NL?
        cmp     al, byte[NL]
        je      .done           ; NL end
        cmp     al, 97          ; lower than a?
        jl      .readc          ; ignore it
        cmp     al, 122         ; higher than z?
        jg      .readc          ; ignore it
        inc     r14             ; inc counter
        cmp     r14, r13
        ja      .readc          ; buffer max reached, ignore
        mov     byte [r12], al  ; safe the char in the buffer
        inc     r12             ; point to next char in buffer
        jmp     .readc 
.done:
        inc     r12
        mov     byte [r12],0    ; add end 0 to stringbuffer
    pop     r14         ; callee saved
    pop     r13         ; callee saved
    pop     r12         ; callee saved
leave
ret

输出:
./console2 
Hello, World!
Your turn (only a-z): hello
You answered: hello

这儿的关键点在于读写时候的系统调用,比如写的时候也是用rdi,rsi,rdx来传参的,用rax指定系统调用号,用代码中可以看到,读的系统调用号是0,写是1。

文件IO

接下来是一段文件IO 的例子,涉及到创建文件,读写文件,关闭删除文件,代码比较多,不过逻辑比较简单:

代码语言:javascript复制
; file.asm
section .data
; expressions used for conditional assembly
    CREATE      equ     1
    OVERWRITE   equ     1
    APPEND      equ     1
    O_WRITE     equ     1
    READ        equ     1
    O_READ      equ     1
    DELETE      equ     1
            
; syscall symbols
    NR_read     equ     0 
    NR_write    equ     1 
    NR_open     equ     2 
    NR_close    equ     3
    NR_lseek    equ     8 
    NR_create   equ     85
    NR_unlink   equ     87

; creation and status flags
    O_CREAT     equ     00000100q  
    O_APPEND    equ     00002000q

; access mode    
    O_RDONLY    equ     000000q 
    O_WRONLY    equ     000001q 
    O_RDWR      equ     000002q

; create mode (permissions)
    S_IRUSR     equ     00400q  ;user read permission
    S_IWUSR     equ     00200q  ;user write permission
 
    NL          equ     0xa                 
    bufferlen   equ     64
   
    fileName db     "testfile.txt",0
    FD       dq     0   ; file descriptor

    text1    db     "1. Hello...to everyone!",NL,0
    len1     dq     $-text1-1                  ;remove 0
    text2    db     "2. Here I am!",NL,0
    len2     dq     $-text2-1                   ;remove 0
    text3    db     "3. Alife and kicking!",NL,0
    len3     dq     $-text3-1                   ;remove 0
    text4    db     "Adios !!!",NL,0
    len4     dq     $-text4-1

    error_Create    db "error creating file",NL,0
    error_Close     db "error closing file",NL,0
    error_Write     db "error writing to file",NL,0
    error_Open      db "error opening file",NL,0
    error_Append    db "error appending to file",NL,0
    error_Delete    db "error deleting file",NL,0
    error_Read      db "error reading file",NL,0
    error_Print     db "error printing string",NL,0
    error_Position  db "error positioning in file",NL,0
        
    success_Create  db "File created and opened",NL,0
    success_Close   db "File closed",NL,NL,0
    success_Write   db "Written to file",NL,0
    success_Open    db "File opened for reading/(over)writing/updating",NL,0
    success_Append  db "File opened for appending",NL,0
    success_Delete  db "File deleted",NL,0
    success_Read    db "Reading file",NL,0
    success_Position db "Positioned in file",NL,0
    
section .bss                            
    buffer resb bufferlen
section .text                           
    global main                     
main:
    push rbp
    mov  rbp,rsp
%IF CREATE
;CREATE AND OPEN A FILE, THEN CLOSE -----------------------------------------
; create and open file
    mov     rdi, fileName
    call    createFile
    mov     qword [FD], rax ; save descriptor

; write to file #1
    mov     rdi, qword [FD]
    mov     rsi, text1
    mov     rdx, qword [len1]
    call    writeFile
    
; close file
    mov     rdi, qword [FD]
    call    closeFile
%ENDIF
%IF OVERWRITE
;OPEN AND OVERWRITE A FILE, THEN CLOSE ---------------------------------------
; open file 
    mov     rdi, fileName 
    call    openFile  
    mov     qword [FD], rax ; save file descriptor
    
; write to file #2 OVERWRITE!
    mov     rdi, qword [FD]
    mov     rsi, text2
    mov     rdx, qword [len2]
    call    writeFile    

; close file
    mov     rdi, qword [FD]
    call    closeFile    
%ENDIF
%IF APPEND
;OPEN AND APPEND TO A FILE, THEN CLOSE ---------------------------------------
; open file to append
    mov     rdi, fileName 
    call    appendFile  
    mov     qword [FD], rax ; save file descriptor
    
; write to file #3 APPEND!
    mov     rdi, qword [FD]
    mov     rsi, text3
    mov     rdx, qword [len3]
    call    writeFile       

; close file
    mov     rdi, qword [FD]
    call    closeFile 
%ENDIF
%IF O_WRITE
;OPEN AND OVERWRITE AT AN OFFSET IN A FILE, THEN CLOSE -----------------------
; open file to write
    mov     rdi, fileName 
    call    openFile  
    mov     qword [FD], rax ; save file descriptor

; position file at offset
    mov     rdi, qword[FD]
    mov     rsi, qword[len2] ;offset at this location
    mov     rdx, 0
    call    positionFile    

; write to file at offset
    mov     rdi, qword[FD]
    mov     rsi, text4
    mov     rdx, qword [len4]
    call    writeFile 

; close file
    mov     rdi, qword [FD]
    call    closeFile 
%ENDIF
%IF READ
;OPEN AND READ FROM A FILE, THEN CLOSE ---------------------------------------
; open file to read
    mov     rdi, fileName 
    call    openFile  
    mov     qword [FD], rax ; save file descriptor  
      
; read from file
    mov     rdi, qword [FD]
    mov     rsi, buffer
    mov     rdx, bufferlen    
    call    readFile
    mov     rdi,rax
    call    printString

; close file
    mov     rdi, qword [FD]
    call    closeFile 
%ENDIF
%IF O_READ
;OPEN AND READ AT AN OFFSET FROM A FILE, THEN CLOSE ---------------------------------------
; open file to read
    mov     rdi, fileName 
    call    openFile  
    mov     qword [FD], rax ; save file descriptor  

; position file at offset
    mov     rdi, qword[FD]
    mov     rsi, qword[len2]        ;skip the first line
    mov     rdx, 0
    call    positionFile          

; read from file
    mov     rdi, qword [FD]
    mov     rsi, buffer
    mov     rdx, 10    ;number of characters to read
    call    readFile
    mov     rdi,rax
    call    printString

; close file
    mov     rdi, qword [FD]
    call    closeFile 
%ENDIF
%IF DELETE
;DELETE A FILE --------------------------------------------------   
; delete file   UNCOMMENT NEXT LINES TO USE
    mov     rdi, fileName
    call    deleteFile                                      
%ENDIF


leave
ret

; FILE MANIPULATION FUNCTIONS-------------------------------------
;-----------------------------------------------------------------
global readFile
readFile:
    mov     rax, NR_read
    syscall                         ; rax contains # of characters read
    cmp     rax, 0 
    jl      readerror
    mov     byte [rsi rax],0    ; add a terminating zero to the string
    mov     rax, rsi
    
    mov     rdi, success_Read
    push    rax     ; caller saved
    call    printString
    pop     rax     ; caller saved
    ret
readerror:
        mov     rdi, error_Read 
        call    printString
        ret  
;-----------------------------------------------------------------
global deleteFile
deleteFile:
    mov     rax, NR_unlink
    syscall
    cmp     rax, 0 
    jl      deleteerror
    mov     rdi, success_Delete
    call    printString
    ret
deleteerror:
        mov     rdi, error_Delete 
        call    printString
        ret  
;-----------------------------------------------------------------
global appendFile
appendFile:
    mov     rax, NR_open 
    mov     rsi,  O_RDWR|O_APPEND
    syscall
    cmp     rax, 0 
    jl      appenderror
    mov     rdi, success_Append
    push    rax     ; caller saved
    call    printString
    pop     rax     ; caller saved
    ret
appenderror:
        mov     rdi, error_Append 
        call    printString
        ret  
;-----------------------------------------------------------------
global openFile
openFile:
    mov     rax, NR_open 
    mov     rsi, O_RDWR
    syscall
    cmp     rax, 0 
    jl      openerror
    mov     rdi, success_Open
    push    rax     ; caller saved
    call    printString
    pop     rax     ; caller saved
    ret
openerror:
        mov     rdi, error_Open 
        call    printString
        ret  
;-----------------------------------------------------------------
global writeFile
writeFile:
    mov     rax, NR_write
    syscall
    cmp     rax, 0 
    jl      writeerror
    mov     rdi, success_Write
    call    printString
    ret
writeerror:
        mov     rdi, error_Write 
        call    printString
        ret

;-----------------------------------------------------------------
global positionFile
positionFile:
    mov     rax, NR_lseek
    syscall
    cmp     rax, 0 
    jl      positionerror
    mov     rdi, success_Position
    call    printString
    ret
positionerror:
        mov     rdi, error_Position 
        call    printString
        ret    
;-----------------------------------------------------------------
global closeFile
closeFile:
    mov     rax, NR_close
    syscall
    cmp     rax, 0 
    jl      closeerror
    mov     rdi, success_Close
    call    printString
    ret
closeerror:
        mov     rdi, error_Close 
        call    printString
        ret    
;-----------------------------------------------------------------
global createFile
createFile:
    mov     rax, NR_create
    mov     rsi, S_IRUSR |S_IWUSR 
    syscall
    cmp     rax, 0          ; file descriptor in rax
    jl      createerror
    mov     rdi, success_Create
    push    rax     ; caller saved
    call    printString
    pop     rax     ; caller saved
    ret
createerror:
        mov     rdi, error_Create 
        call    printString
        ret    

; PRINT FEEDBACK
;-----------------------------------------------------------------
global printString
printString:

; Count characters 
    mov     r12, rdi
    mov     rdx, 0 
strLoop:
    cmp     byte [r12], 0
    je      strDone
    inc     rdx                 ;length in rdx
    inc     r12
    jmp     strLoop
strDone:
    cmp     rdx, 0              ; no string (0 length)
    je      prtDone
    mov     rsi,rdi
    mov     rax, 1 
    mov     rdi, 1
    syscall
prtDone:

    ret

运行结果:
File created and opened
Written to file
File closed

File opened for reading/(over)writing/updating
Written to file
File closed

File opened for appending
Written to file
File closed

File opened for reading/(over)writing/updating
Positioned in file
Written to file
File closed

File opened for reading/(over)writing/updating
Reading file
2. Here I am!
Adios !!!
3. Alife and kicking!
File closed

File opened for reading/(over)writing/updating
Positioned in file
Reading file
Adios !!!
File closed

File deleted

这儿都是通过系统调用操作的IO,libc的文件IO本质上也是这样,当然libc还会有一层cache。从这段代码可以看出系统调用的返回值也是通过rax传递的。

0 人点赞