HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Hexdump utility in x86 NASM assembly

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
hexdumpx86utilityassemblynasm

Problem

It's a simple hexdump that prints to stdout. I wanted to handle correctly input coming from the user typing at the terminal, this made the logic a little more complicated than just exiting after reading less than requested. It doesn't use the stack or memory variables, except for the tables and buffers, so I employed every register freely.

I'm learning, so I would like for every possible improvement to be pointed out.

``
; Description : Prints stdin to stdout as hex and displays ASCII next to it.
; It handles correctly the cases in which a sys_read returns
; less than was requested but an EOF was not received, this
; allows the user to dump arbitrary text from stdin to a file
; in the following way: ./hexdump >> dump.txt
;
; Build using these commands:
; nasm -f elf hexdump.asm
; ld -o hexdump hexdump.o -m elf_i386 -s
;
; Usage:
; ./hexdump ?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
abcdefghijklmnopqrstuvwxyz'
db '{|}~...........................................................'
db '...............................................................'
db '.......'
align 4
Template: db '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 '
DivPos: db '| '
AsciiPart: db '................',NEW_LINE
TEMPLATELENGTH: equ $-Template
align 2
HexTable: db '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2'
db '02122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40'
db '4142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606'
db '162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f8081'
db '82838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a'
db '2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2'
db 'c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e'
db '3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'

Solution

Some random thoughts:

  • Unused defines: STDIN, EXIT_SUCCESS



  • I'd restructure the code above Ok: to do jz Last; jl Err and just let jg fall thru (unless Last is further away than it looks).



  • I don't believe it's safe to use esp as a general purpose register like this.



  • Why do you xor edx,edx above .l0?



  • I find your ebp/esp/edi logic confusing. I've got an alternative, but I'm not on linux, so I can't actually run this (and I HATE posting code I haven't run), but how about something like:



.

; Description     : Prints stdin to stdout as hex and displays ASCII next to it.
;                   It handles correctly the cases in which a sys_read returns
;                   less than was requested but an EOF was not received, this
;                   allows the user to dump arbitrary text from stdin to a file 
;                   in the following way: ./hexdump >> dump.txt
;
; Build using these commands:
;   nasm -f elf hexdump.asm
;   ld -o hexdump hexdump.o -m elf_i386 -s
;
; Usage:
;   ./hexdump ?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz'
            db '{|}~...........................................................'
            db '...............................................................'
            db '.......'
align 4
Template: db '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 '
DivPos:   db '| '
AsciiPart: db '................',NEW_LINE
TEMPLATELENGTH: equ $-Template
align 2
HexTable: db '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2'
          db '02122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40'
          db '4142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606'
          db '162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f8081'
          db '82838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a'
          db '2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2'
          db 'c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e'
          db '3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'

section .bss
BUFFERSIZE: equ 16
Buffer: resb BUFFERSIZE

section .text

global _start ; make it visible

_start:
        mov ebx,STDIN
        lea ecx,[Buffer]
        mov edx,BUFFERSIZE
        mov esi,BUFFERSIZE

Fill:
        mov eax,SYS_READ
        int 80h                           ; call
        cmp eax,0                         ; check return value
        jl Err                            ;  error
        jz Last                           ; 0 = EOF

        add ecx, eax                      ; Update where to load data
        sub edx, eax                      ; How many more bytes needed?
        jnz Fill                          ; Keep trying until we get 16 bytes

        ; At this point, we have read exactly 16 bytes.

        call OutputLine                   ; Turn esi bytes in Buffer into
                                          ; Template and print it

        jmp _start                        ; Get next line

; At this point, we have hit EOF.  Output any leftovers
; in the buffer.
Last:
        sub esi, edx                      ; How many did we really read?
        jz End                            ; Did we end on exactly 16 bytes?

        ; Blank out the Template and AsciiPart
        mov bx, 0x2020                    ; '  '
        mov edx,BUFFERSIZE

WriteBlanks:
        dec edx
        mov [Template+edx*2+edx],bx       ; write blank over Template
        mov [AsciiPart+edx],bl            ; write blank over AsciiPart
        jnz WriteBlanks

        call OutputLine                   ; Turn esi bytes in Buffer into
                                          ; Template and print it

; Exit the program
End:    mov ebx,EXIT_SUCCESS              ; EXIT_SUCCESS
Exit:   mov eax,SYS_EXIT
        int 80h

Err:    mov ebx,-1                        ; EXIT_FAILURE
        jmp Exit

; Input: esi: Number of chars to process from Buffer
; Clobbers: eax, ebx, edx, edx, esi
OutputLine:
        xor eax, eax
NextChar:                                 ; Process next char
        dec esi
        mov al,[Buffer+esi]               ; read byte from buffer
        mov bx,[HexTable+eax*2]           ; get corresponding symbols
        mov [Template+esi*2+esi],bx       ; write to template
        mov bl,[AsciiTable+eax]           ; get ASCII symbol
        mov [AsciiPart+esi],bl            ; write to template
        jnz NextChar                      ; ecx is an offset not a counter

        mov eax,SYS_WRITE
        mov ebx,STDOUT
        mov ecx,Template
        mov edx,TEMPLATELENGTH
        int 80h
        cmp eax,TEMPLATELENGTH
        jne Err

        ret


It looks right, but even if it is slightly flawed it should give you some ideas.

Code Snippets

; Description     : Prints stdin to stdout as hex and displays ASCII next to it.
;                   It handles correctly the cases in which a sys_read returns
;                   less than was requested but an EOF was not received, this
;                   allows the user to dump arbitrary text from stdin to a file 
;                   in the following way: ./hexdump >> dump.txt
;
; Build using these commands:
;   nasm -f elf hexdump.asm
;   ld -o hexdump hexdump.o -m elf_i386 -s
;
; Usage:
;   ./hexdump << input_file

section .data
STDIN: equ 0
STDOUT: equ 1
STDERR: equ 2
EXIT_SUCCESS: equ 0
SYS_READ: equ 3
SYS_WRITE: equ 4
SYS_EXIT: equ 1
NEW_LINE: equ 0ah
AsciiTable: db '................................ !"',"#$%&'()*+,-./0123456789:;"
            db '<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz'
            db '{|}~...........................................................'
            db '...............................................................'
            db '.......'
align 4
Template: db '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 '
DivPos:   db '| '
AsciiPart: db '................',NEW_LINE
TEMPLATELENGTH: equ $-Template
align 2
HexTable: db '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2'
          db '02122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40'
          db '4142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606'
          db '162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f8081'
          db '82838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a'
          db '2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2'
          db 'c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e'
          db '3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'

section .bss
BUFFERSIZE: equ 16
Buffer: resb BUFFERSIZE

section .text

global _start ; make it visible

_start:
        mov ebx,STDIN
        lea ecx,[Buffer]
        mov edx,BUFFERSIZE
        mov esi,BUFFERSIZE

Fill:
        mov eax,SYS_READ
        int 80h                           ; call
        cmp eax,0                         ; check return value
        jl Err                            ; < 0 -> error
        jz Last                           ; 0 = EOF

        add ecx, eax                      ; Update where to load data
        sub edx, eax                      ; How many more bytes needed?
        jnz Fill                          ; Keep trying until we get 16 bytes

        ; At this point, we have read exactly 16 bytes.

        call OutputLine                   ; Turn esi bytes in Buffer into
                                          ; Template and print it

        jmp _start                        ; Get next line

; At this point, we have hit EOF.  Output any leftovers
; in the buffer.
Last:
        sub esi, edx                      ; How many did we really read?
        jz End                            ; Did we end on exactly 16 byte

Context

StackExchange Code Review Q#138537, answer score: 4

Revisions (0)

No revisions yet.