• 汇编语言实现一个简单的十六进制转储使用工具


    一个简单的十六进制转储使用工具,演示了汇编语言过程的使用。

    ; 可执行程序名    : hexdump2
    ; 版本    : 1.0
    ; 创建日期    : 7/9/2016
    ; 最后修改    : 7/9/2016
    ; 作者        : Moonlight Poet
    ; 描述        : 一个简单的十六进制转储使用工具,演示了汇编语言过程的使用。
    ; 
    ; 使用以下命令生成该程序 :
    ;     nasm -f elf64 -g -F stabs hexdump2.asm
    ;     ld -o hexdump2 hexdump2.o
    ; 
    
    SECTION .bss            ; 包含未初始化数据的段
        BUFFLEN equ 10
        Buff resb BUFFLEN
    
    SECTION .data            ; 包含已初始化数据的段
    
    ; 这里,我们使用一个由两个部分组成的简单数据结构,
    ; 实现一个十六进制转储使用用具的文本行。
    ; 第一个部分显示了16个字节的、中间用空格隔开的十六进制数。(也就是Dumplin)
    ; 紧跟在后面的是一个由16个字符组成的行,二者之间通过竖线(|)字符分隔。
    ; 因为这两个部分是相邻的,
    ; 所以可以被单独引用或者作为一个连续的单元来引用。
    ; 记住,如果要将DumpLin分开使用,在将其发送到Linux控制台之前,
    ; 必须追加一个EOF。
    
    DumpLin: db " 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
    DUMPLEN equ $-DumpLin
    ASCLin: db "|................|",10
    ASCLINE equ $-ASCLin
    FULLLEN equ $-DumpLin
    
    ; HexDigits表用来把数字值转换为它们的十六进制等值。
    ; 通过一个没有缩放的半字节来进行索引:[HexDigits + eax]
    HexDigits: db "0123456789ABCDEF"
    
    ; 此表用于ASCII字符翻译,实现将其翻译成该十六进制转储行的ASCII部分
    ; 通过使用XLAT或者普通的内存查找。
    ; 所有的可打印的字符被翻译为它们本身。
    ; 高128个字符被转换为ASCII的句号(2Eh)
    ; 低128个字符中的不可打印字符也被转换为ASCII句号,例如:字符127。
    DotXlat:
        db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
        db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
        db 20h,21h,22h,23h,24h,25h,26h,27h,28h,29h,2Ah,2Bh,2Ch,2Dh,2Eh,2Fh
        db 30h,31h,32h,33h,34h,35h,36h,37h,38h,39h,3Ah,3Bh,3Ch,3Dh,3Eh,3Fh
        db 40h,41h,42h,43h,44h,45h,46h,47h,48h,49h,4Ah,4Bh,4Ch,4Dh,4Eh,4Fh
        db 50h,51h,52h,53h,54h,55h,56h,57h,58h,59h,5Ah,5Bh,5Ch,5Dh,5Eh,5Fh
        db 60h,61h,62h,63h,64h,65h,66h,67h,68h,69h,6Ah,6Bh,6Ch,6Dh,6Eh,6Fh
        db 70h,71h,72h,73h,74h,75h,76h,77h,78h,79h,7Ah,7Bh,7Ch,7Dh,7Eh,2Eh
            db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
            db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
            db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
            db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
            db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
            db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
            db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
            db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
        
    SECTION .text        ; 包含代码段
    
    ;----------------------------------------------------------------------------------------------------
    ; ClearLine:    将一个十六进制转储行字符串清零,即将其变为16个0。
    ; 更新日期:    7/9/2016
    ; 输入参数:    没有。
    ; 返回值:    没有。
    ; 修改:        没有。
    ; 调用:        DumpChar
    ; 描述:        通过16次调用DumpChar过程,每次传递给它参数0,
    ;          这个十六进制转储行字符串被清除为二进制0。
    
    ClearLine:
        ;pushad        ; 保存主调程序的所有通用寄存器
        push rax
        push rdx
        mov edx,15    ; 我们将进行16次,从0开始计数
    .poke:     mov eax,0    ; 告诉DumpChar插入一个'0'
        call DumpChar    ; 将'0'插入到十六进制转储字符串中
        sub edx,1    ; DEC并不影响CF!
        jae .poke    ; 如果EDX >= 0 ,回去继续循环。
        ;popad        ; 恢复所有主调程序的通用寄存器。
        pop rdx
        pop rax
        ret        ; 返回
    
    ;----------------------------------------------------------------------------------------------------
    ; DumpChar:    插入一个值到十六进制转储字符串中。
    ; 更新日期: 7/9/2016
    ; 输入参数: 传递一个即将被插入的值到EAX寄存器中。
    ;     传递这个值在改行中的位置(0-15)到EDX寄存器中。
    ; 返回值:     没有。
    ; 修改:     EAX, ASCLin, DumpLin
    ; 调用:     没有。
    ; 描述:     传递到EAX寄存器中的值将其放在十六进制传出部分,
    ;     又放在ASCII部分,
    ;     其位置为传递给EDX的值
    ;     当它不是一个可打印字符时,用一个空格来表示。
    
    DumpChar:
        push rbx    ; 保存主调程序的EBX寄存器
        push rdi    ; 保存主调程序的EDI寄存器
    ; 首先,我们将输入的字符串插入到转储行的ASCII部分
        mov bl,byte [DotXlat+eax]    ; 将不可打印字符翻译成'.'(句号2Eh)(如果可打印就打印了,具体见DotXlat)
        mov byte [ASCLin+edx+1],bl    ; 写入ASCII码部分
    ; 接下来,我们把与插入字符等值的十六进制值
    ; 插入到转储行的十六进制部分:
        mov ebx,eax        ; 保存输入字符的第二个副本
        lea edi,[edx*2+edx]    ; 计算字符串行中的偏移量(EDX*3)
    ; 查找低半字节的字符,并将其插入到字符串:
        and eax,0000000Fh        ; 屏蔽掉除低半字节以外的所有位
        mov al,byte [HexDigits+eax]    ; 查找该半字节的等值字符。
        mov byte [DumpLin+edi+2],al    ; 将这个等值字符写入字符串行。
    ; 查找高半字节的字符,并将其插入到字符串:
        and ebx,000000F0h        ; 屏蔽掉除第二低的半字节之外的所有位
        shr ebx,4            ; 将字节的高四位移到低四位
        mov bl,byte [HexDigits+ebx]    ; 查找与该半字节的等值字符。
        mov byte [DumpLin+edi+1],bl    ; 将这个等值字符写入字符串行。
    ; 任务完成!让我们“回家”:
        pop rdi                ; 恢复主调程序的EDI寄存器的值
        pop rbx                ; 恢复主调程序的EBX寄存器的值
        ret                ; 返回主调程序
    
    ;----------------------------------------------------------------------------------------------------
    ; PrintLine: 将DumpLin现实到标准输出
    ; 更新日期: 7/9/2016
    ; 输入参数: 没有。
    ; 返回值: 没有。
    ; 修改: 没有。
    ; 调用: 内核sys_write
    ; 描述: 将十六进制转储行DumpLin显示到标准输出。
    ; 使用 INT 80h sys_write. 所有寄存器都被保存起来。
    
    PrintLine:
        ;pushad            ; 保存主调程序的所有通用寄存器
        push rax
        push rbx
        push rcx
        push rdx
        mov eax,4        ; 指定sys_write调用
        mov ebx,1        ; 指定文件描述符1;标准输出
        mov ecx,DumpLin        ; 传递字符串行的偏移量
        mov edx,FULLLEN        ; 传递字符串行的大小
        int 80h            ; 进行内核调用显示字符串行
        ;popad            ; 恢复主调程序的所有通用寄存器
        pop rdx
        pop rcx
        pop rbx
        pop rax
        ret            ; 返回主调程序
    
    ;----------------------------------------------------------------------------------------------------
    ; LoadBuff: 通过 INT 80h sys_read 将一个缓冲区从标准输入装满数据。
    ; 更新日期: 7/9/2016
    ; 输入参数: 没有。
    ; 返回值: 从EBP中读入的字节数
    ; 修改: ECX, EBP, Buff
    ; 调用: 内核 sys_write
    ; 描述: 使用 INT 80h sys_read 从标准输入中加载慢慢一缓冲区数据
    ; 并将其放入Buff。
    ; 因为我们开始了一个新的装满数据的缓冲区,所以缓冲区偏移量计数器ECX被设置为零。
    ; 主调程序必须测试EBP中的值:
    ; 如果在返回时EBP中的值为零,表明在标准输入中遇到了EOF(文件结尾)
    ; 如果在返回时EBP中的值比零小,表明发生了某种类型的错误。
    
    LoadBuff:
        push rax        ; 保存主调程序的EAX寄存器
        push rbx        ; 保存主调程序的EBX寄存器
        push rdx        ; 保存主调程序的EDX寄存器
        mov eax,3        ; 指定 sys_read call
        mov ebx,0        ; 指定文件描述符 0:标准输入。
        mov ecx,Buff        ; 指定要被读入数据的缓冲区的偏移地址
        mov edx,BUFFLEN        ; 传递一次要读入的字节数
        int 80h            ; 调用 sys_read 来填满缓冲区
        mov ebp,eax        ; 保存从文件中读入的字节数,以备后用。
        xor ecx,ecx        ; 清除缓冲区指针ECX,将其设为0
        pop rdx            ; 恢复主调用程序的EDX寄存器
        pop rbx            ; 恢复主调用程序的EBX寄存器
        pop rax            ; 恢复主调用程序的EAX寄存器
        ret            ; 返回主调用程序
    
    GLOBAL _start
    
    ;----------------------------------------------------------------------------------------------------
    ; 主程序从这里开始
    ;----------------------------------------------------------------------------------------------------
    _start:
        nop        ; 为GDB输入无操作指令
        nop
    
    ; 在循环开始前需要做的所有初始化工作都从这里开始:
        xor esi,esi    ; 将整个字节计数器清零
        call LoadBuff    ; 从缓冲区读入第一个缓冲区的数据
        cmp ebp,0     ; 如果ebp=0,sys_read到达了标准输入的EOF(文件结尾)
        jbe Exit
    
    ; 审查该缓冲区,并将这些二进制字节转换为十六进制数字值:
    Scan:
        xor eax,eax        ; 将EAX寄存器清零
        mov al,byte [Buff+ecx]    ; 将一个字节从缓冲区读出到AL中
        mov edx,esi        ; 复制整个计数器的值到EDX寄存器中
        and edx,000000Fh    ; 屏蔽掉除字符计数器的最低四位以外的其他位
        call DumpChar        ; 调用字符插入过程
    
    ; 将缓冲区的指针指向下一个字符,并查看以下缓冲区的工作是否已经完成:
        inc esi            ; 递增整个已处理的字符的计数值
        inc ecx            ; 递增缓冲区的指针值
        cmp ecx,ebp        ; 与缓冲区中的字符数相比较
        jb .modTest        ; 如果我们已经处理完了缓冲区中的所有字符……
        call LoadBuff        ; ……再次充满缓冲区
        cmp ebp,0        ; 如果ebp=0,sys_read到达了标准输入的结尾
        jbe Done        ; 如果到达了EOF,我们的任务就完成了
    
    ; 判断一下我们是否到达了一个包含16个值的块的结尾,并且需要显示一行:
    .modTest:
        test esi,0000000Fh    ; 测试计数器中最低的四位是否为零。
        jnz Scan        ; 如果计数器不是16的倍数,继续循环(计数器用来一次扫描16个字节的数,没到的话就继续循环)
        call PrintLine        ; ……否则打印那一行
        call ClearLine        ; 清除十六进制转储行,将其全部设置为0
        jmp Scan        ; 继续扫描缓冲区
    
    ; 全部完成,让我们结束这个“晚会”
    Done:
        call PrintLine        ; 打印“剩余”行
    Exit:                
        mov eax,1        ; 用于退出Syscall的代码
        mov ebx,0        ; 将零作为返回码
        int 80h            ; 进行内核调用

    样例输入:

    hello,world!
    I am moonlightpoet.
    How old are you?
    Hoe are you?
    I'm fine,thank you!And you?

    样例输出:

     68 65 6C 6C 6F 2C 77 6F 72 6C 64 21 0A 49 20 61 |hello,world!.I a|
     6D 20 6D 6F 6F 6E 6C 69 67 68 74 70 6F 65 74 2E |m moonlightpoet.|
     0A 48 6F 77 20 6F 6C 64 20 61 72 65 20 79 6F 75 |.How old are you|
     3F 0A 48 6F 65 20 61 72 65 20 79 6F 75 3F 0A 49 |?.Hoe are you?.I|
     27 6D 20 66 69 6E 65 2C 74 68 61 6E 6B 20 79 6F |'m fine,thank yo|
     75 21 41 6E 64 20 79 6F 75 3F 0A 0A 00 00 00 00 |u!And you?......|
  • 相关阅读:
    getchar()详解
    ACM错误提示
    关于printf()函数和浮点数
    PCB蚀刻,盐酸不好买,三氯化铁不方便,用这个吧【转】
    wps自动半角符转全角符取消笔记
    万恶的oj笔记之【111028】
    hdu1142 深搜+dp+最短路径。
    pl2303电路图。。
    sencha touch 监控 Carousel 旋转事件
    正则表达式限制文本框输入内容
  • 原文地址:https://www.cnblogs.com/moonlightpoet/p/5657784.html
Copyright © 2020-2023  润新知