代码如下:

SECTION .bss
    BUFFLEN equ 16    ; 我们一次从这个文本读取16个字节
    Buff resb BUFFLEN ; 文本缓冲区本身

SECTION .data
    HexStr: db " 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", 10
    HEXLEN equ $-HexStr
    Digits: db "0123456789ABCDEF"

SECTION .text

global _start

_start:
    nop ; 这个无操作指令让gdb非常高兴

; 从标准输入中读取满满一缓冲区的文本
Read: mov eax, 3       ; 指定sys_read系统调用
      mov ebx, 0       ; 指定文件描述符0:标准输入
      mov ecx, Buff    ; 传递即将从中读取数据的缓冲区的地址
      mov edx, BUFFLEN ; 传递一次循环中要读入的字节数
      int 80h          ; 调用sys_read来填充缓冲区
      mov ebp, eax     ; 复制sys_read的返回值并妥善保管
      cmp eax, 0       ; 如果“eax = 0”,则sys_read到了标准输入的结尾
      je  Done         ; 如果(与0相比较)相等,则跳转

    ; 设置寄存器用于处理缓冲区步骤:
    mov esi, Buff   ; 将文本缓冲区的地址放入esi寄存器中
    mov edi, HexStr ; 将线性字符串的地址放入edi寄存器中
    xor ecx, ecx    ; 将线性字符串指针清0

; 检查缓冲区,将二进制数值转换为16进制位元
Scan:
    xor eax, eax  ; 将寄存器eax清零
    ; 这里我们可以计算相对于HexStr的偏移地址,它等于ecx中的值乘以3
    mov edx, ecx  ; 将字符串计数器的值复制到edx中
    shl edx, 1    ; 通过左移位操作将指针的值乘以2
    add edx, ecx  ; 完成乘以3操作

    ; 从缓冲区中获取一个字符,并将其同时存入eax和ebx中:
    mov al, byte [esi+ecx] ; 从输入缓冲中取出一个字节,送入al
    mov ebx, eax           ; 为了求得第二个“半字节”,将该字节复制到bl中

    ; 查找低半字节符并将其插入字符串
    and al, 0Fh                 ; 屏蔽除了低半字节之外的所有内容
    mov al, byte [Digits+eax]   ; 查找与该半字节相等的字符
    mov byte [HexStr+edx+2], al ; 将最低有效位(LSB)字符位元(digit)写入线性字符串

    ; 查找高半字节符并将其插入字符串
    shr bl, 4                   ; 将字符的高四位移入低四位
    mov bl, byte [Digits+ebx]   ; 查找与该半字节相等的字符
    mov byte [HexStr+edx+1], bl ; 将最高有效位(MSB)字符位元写入线性字符串

    ; 将缓冲区指针指向下一个字符,看一下任务是否完成
    inc ecx      ; 增加线性字符串指针的值
    cmp ecx, ebp ; 与缓冲区中的字符数进行比较
    jna Scan     ; 如果ecx小于等于缓冲区中的字符数,转回去继续循环

; 将这行十六进制写到标准输出:
Write: mov eax, 4      ; 指定sys_write系统调用
       mov ebx, 1      ; 指定文件描述符,1是标准输出
       mov ecx, HexStr ; 传递线性字符串的偏移地址
       mov edx, HEXLEN ; 传递线性字符串的大小
       int 80h         ; 调用sys_write
       jmp Read        ; 回去继续循环,加载另一满满的缓冲区数据

Done: mov eax, 1 ; 指定Exit系统调用
      mov ebx, 0 ; 返回一个零代码
      int 80h    ; 进行系统调用来终止程序

使用方法:

./hexdump < inputfile > outputfile