美文网首页汇编(MOV,SUB,PUSH,POP,...)
汇编开发(二):数据传输,地址和算法

汇编开发(二):数据传输,地址和算法

作者: _凌浩雨 | 来源:发表于2019-02-14 12:12 被阅读45次

    1. 数据传输指令

    1). 操作数类型
    [label:] mnemonic [operands][; comment]
    

    指令可以有零个,一个,两个或者三个操作数。此处忽略label和comment, 如下:

    mnemonic
    mnemonic [destination]
    mnemonic [destination],[source]
    mnemonic [destination],[source-1],[source-2]
    

    操作输的基本类型:

    • 立即数: 使用数字文本表达式
    • 寄存器: CPU中的寄存器
    • 内存: 直接引用内存地址
    2). 直接内存操作数
    操作数 描述
    reg8 8位普通用途的寄存器:AH, AL, BH, BL, CH, CL, DH, DL
    reg16 16位普通用途的寄存器:AX, BX, CX, DX, SI, DI, SP, BP
    reg32 32位普通用途的寄存器:EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP
    reg 任何普通用途的寄存器
    sreg 16位段寄存器:CS, DS, SS, ES, FS, GS
    imm 8,16,32位立即数
    imm8 8位字节立即数
    imm16 16位字立即数
    imm32 32位双字立即数
    reg/mem8 8位操作数,可以是8位普通寄存器或内存字节
    reg/mem16 16位操作数,可以是16位普通寄存器或内存字节
    reg/mem32 32位操作数,可以是32位普通寄存器或内存字节
    mem 8,16,32内存操作数
    3). MOV 指令
    • 功能:MOV指令从源操作数赋值数据到目标操作数中。
    • 格式:
    MOV destination,source
    
    • 操作数规则

      • 两个操作数必须是相同的大小
      • 两个操作数不能是内存操作数
      • 指令指针寄存器(IP, EIP, RIP)不能作为目标操作数
    MOV reg,reg
    MOV mem,reg
    MOV reg,mem
    MOV mem,imm
    MOV reg,imm
    
    • 覆盖值
    .data
        oneByte BYTE 78h            ; 定义一个8bit变量
        oneWord WORD 1234h          ; 定义一个16bit变量
        oneDword DWORD 12345678h    ; 定义一个32bit变量
    
    .code
    main PROC                   ; 定义主函数开始位置
        mov eax, 0              ; 为eax寄存器赋值 -- EAX = 0000 0000h
        mov al, oneByte         ; 为al赋值         -- EAX = 0000 0078h
        mov ax, oneWord         ; 为ax赋值         -- EAX = 0000 1234h
        mov eax, oneDword       ; 为eax赋值            -- EAX = 1234 5678h
        mov ax, 0               ; 为ax赋值         -- EAX = 1234 0000h
    
    4). 整数0和符号的扩展
    • 问题
      主要原因:占用字节空间大的寄存器低字节赋值时,高字节为做更改
      解决方法:第一种用MOVZX指令代替,第二种用MOVSX代替
    COMMENT &
        第一种情况是将小位数的数赋值给大位数的寄存器时,未赋值的高字节未置空
        第二种情况是将小位数的负数赋值给大位数的寄存器时,为赋值的高字节未置F
    &
    .data
        count WORD 10           ; 定义变量
        signedVar SWORD -16     ; 定义负数
    
    .code
    main PROC                   ; 定义主函数开始位置
        mov ecx, 100000         ; 为ecx寄存器赋值,此时ECX = 000186A0
        mov cx, count           ; 为ecx寄存器赋值,此时ECX = 0001000A
        
        mov cx, signedVar       ; 为ecx寄存器赋值,此时ECX = 0001FFF0,解决方法之一:可在此之前将ecx设置为FFFF FFFFh
    
    • MOVZX 指令

    功能:赋值源操作数的内容到目标操作数,为16位或32位的值进行0的扩展。这个指令仅被使用在无符号整型中, 作用域:

    MOVZX reg32,reg/mem8
    MOVZX reg32,reg/mem16
    MOVZX reg16,reg/mem8
    

    示例:

    COMMENT &
        MOVZX 解决0扩展问题
    &
    .data
        byte1 BYTE 9Bh          ; 定义字节变量
        word1 WORD 0A69Bh       ; 定义字变量
    
    .code
    main PROC                   ; 定义主函数开始位置
        ; 寄存器->寄存器
        mov bx, 0A69Bh          ; 为bx赋值         ebx = 7EFD A69Bh 
        movzx eax, bx           ; 将bx的值赋值给eax, eax = 76AB 342Bh -> eax = 0000 A69Bh
        movzx edx, bl           ; 将bl的值赋值给edx, edx = 00F9 1005h -> edx = 0000 009Bh
        movzx cx, bl            ; 将bl的值赋值给cx, ecx = 0000 0000h -> ecx = 0000 009Bh
    
    
    图1.png
    • MOVSX 指令

    功能:赋值源操作数的内容到目标操作数,为16位或32位的值进行符号的扩展。这个指令仅被使用在有符号整型中, 作用域:

    MOVSX reg32,reg/mem8
    MOVSX reg32,reg/mem16
    MOVSX reg16,reg/mem8
    

    示例:

    COMMENT &
        MOVZX 解决符号扩展问题
    &
    .data
        byteVal BYTE 10001111b          ; 定义字节变量
    
    .code
    main PROC                   ; 定义主函数开始位置
        ; 内存 -> 寄存器
        movsx ax, byteVal       ; 将byteVal的值赋值给ax寄存器,ax = 342Bh -> ax = FF8Fh
        ; 寄存器->寄存器
        mov bx, 0A69Bh          ; 为bx赋值,bx = E000h -> bx = A69Bh
        movsx eax, bx           ; 将bx的值赋值给eax, eax = 76AB FF8Fh -> eax = FFFF A69Bh
        movsx edx, bl           ; 将bl的值赋值给edx, edx = 0035 1005h -> edx = FFFF FF9Bh
        movsx cx, bl            ; 将bl的值赋值给cx, cx = 0000h -> cx = FF9Bh
    
    图2.png
    5). LAHF和SAHF--EFlags状态
    COMMENT &
        LANF加载EFLAGS值到AH,SANF保存AH值
    &
    .data
        saveflags BYTE ?        ; 定义保存flag的变量
        newflags BYTE ?         ; 定义新的flag的值
    
    .code
    main PROC                   ; 定义主函数开始位置
        lahf                    ; 加载flag到ah     eax = 76AB462Bh
        mov saveflags, ah       ; 将ah的数值保存到flag中 saveflags = 70
    
        mov saveflags, 100      ; 赋值 saveflags = 100
        mov ah, saveflags       ; 赋值 eax = 76AB642Bh
        sahf                    ; 保存 
    
    6). XCHG指令

    功能:交换两个操作数的内容。形式:

    XCHG reg,reg
    XCHG reg,mem
    XCHG mem,reg
    

    注:XCHG不接立即数操作数。

    COMMENT &
        XCHG,交换数值
    &
    .data
        val1 WORD 10        ; 定义变量1
        val2 WORD 20        ; 定义变量2
    
    .code
    main PROC                   ; 定义主函数开始位置
        ; 交换val1 和 val2的数值
        mov ax, val1            ; 将val1的值存入ax中
        xchg ax, val2           ; 交换ax与val2的值
        mov val1, ax            ; 将ax的值放入val1中
    
    7). 直接偏移操作数

    功能: 为数组变量名添加一个移位。

    COMMENT &
        直接偏移操作数,访问数组元素
    &
    .data
        arrayB BYTE 10h, 20h, 30h, 40h, 50h ; 定义字节数组元素
        arrayW WORD 100h, 200h, 300h        ; 定义字数组元素
        arrayD DWORD 10000h, 20000h         ; 定义双字数组元素
    
    .code
    main PROC                   ; 定义主函数开始位置, eax = 76AB342Bh
        mov al, [arrayB + 3]        ; 访问BYTE数组第4个元素, eax = 76AB3440h
        mov ax, [arrayW + 2 * 2]    ; 访问WORD数组第3个元素, eax = 76AB0300h
        mov eax, [arrayD + 4 * 1]   ; 访问DWORD数组第2个元素, eax = 00020000h
    
    8). 综合示例
    .486        ; 定义32位程序可以接受32位的寄存器和地址
    .model flat, stdcall    ; 选择程序的内存模式为平坦模式,stdcall调用习惯
    .stack 4096             ; 设置运行的堆栈大小为4096字节
    ExitProcess PROTO, dwExitCode: DWORD    
    
    COMMENT &
        直接偏移操作数,访问数组元素
    &
    .data
        val1 WORD 1000h
        val2 WORD 2000h
        arrayB BYTE 10h, 20h, 30h, 40h, 50h ; 定义字节数组元素
        arrayW WORD 100h, 200h, 300h        ; 定义字数组元素
        arrayD DWORD 10000h, 20000h         ; 定义双字数组元素
    
    
    .code
    main PROC                   ; 定义主函数开始位置
        ; MOVZX 指令示范            
        mov bx, 0A69Bh              
        movzx eax, bx               ; eax = 0000 A69Bh
        movzx edx, bl               ; edx = 0000 009Bh
        movzx cx, bl                ; cx = 009Bh
    
        ; MOVSZ 指令示范
        mov bx, 0A69Bh
        movsx eax, bx               ; eax = FFFF A69Bh
        movsx edx, bl               ; edx = FFFF FF9Bh
        mov bl, 7Bh
        movsx cx, bl                ; cx = 007Bh
    
        ; 内存 -> 内存 数值交换
        mov ax, val1                ; ax = 1000h
        xchg ax, val2               ; ax = 2000h, val2 = 1000h
        mov val1, ax                ; val1 = 2000h
    
        ; 直接偏移地址(BYTE数组)
        mov al, arrayB              ; al = 10h
        mov al, [arrayB + 1]        ; al = 20h
        mov al, [arrayB + 2]        ; al = 30h
    
        ; 直接偏移地址(WORD数组)
        mov ax, arrayW              ; ax = 100h
        mov ax, [arrayW + 2]        ; ax = 200h
    
        ; 直接偏移地址(DWORD数组)
        mov eax, arrayD             ; eax = 0001 0000h
        mov eax, [arrayD + 4]       ; eax = 0002 0000h
    
    
        INVOKE ExitProcess, 0   ; 退出程序
    main ENDP           ; 函数结束位置, ENDP 之前的内容,要与PROC 
    END main            ; 设置了函数的入口与出口
    

    2. 加与减

    1). INC与DEC指令
    • 作用域:
    INC reg/mem
    DEC reg/mem
    
    • 示例代码:
    COMMENT &
        加减运算
    &
    .data
        myWord WORD 1000h
        
    .code
    main PROC                   ; 定义主函数开始位置
        inc myWord              ; myWord变量自加        myWord = 1001h
        mov bx, myWord          ; 将bx值设置为myWord  bx = 1001h
        dec bx                  ; bx值进行自减   bx = 1000h
    
    2). ADD 指令
    • 使用方法: ADD dest,source
    COMMENT &
        加减运算
    &
    .data
        var1 DWORD 10000h
        var2 DWORD 20000h
        
    .code
    main PROC                   ; 定义主函数开始位置
        mov eax, var1           ; 将var1的值赋值给eax, eax = 10000h
        add eax, var2           ; 将var2的值与eax的值相加, eax = 30000h
    
    3). SUB 指令
    • 使用方法:SUB dest,source
    COMMENT &
        加减运算
    &
    .data
        var1 DWORD 10000h
        var3 DWORD 30000h
        
    .code
    main PROC                   ; 定义主函数开始位置
        mov eax, var3           ; 将var3的值赋值给eax, eax = 30000h
        sub eax, var1           ; 将eax的值减去1000h, eax = 20000h
    
    4). NEG 指令
    • 使用方法:NEG reg/mem
    COMMENT &
        加减运算
    &
    .data
        var1 DWORD 10000h
        var3 DWORD 30000h
        
    .code
    main PROC                   ; 定义主函数开始位置
        mov eax, var3           ; 将var3的值赋值给eax, eax = 30000h
        sub eax, var1           ; 将eax的值减去1000h, eax = 20000h
        neg eax                 ; eax由20000h取反+1为FFFE0000
    
    5). 实现算术表达式
    • 示例代码:
    COMMENT &
        加减运算
    &
    .data
        Rval DWORD ?
        Xval DWORD 26
        Yval DWORD 30
        Zval DWORD 40
        
    .code
    main PROC                   ; 定义主函数开始位置
        ; Rval = -Xval + (Yval - Zval)
        ; 第一步: -Xval
        mov eax, Xval
        neg eax                     ; eax = -26
        ; 第二步:(Yval - Zval)
        mov ebx, Yval       
        sub ebx, Zval               ; ebx = -10
        ; 第三步:计算Rval
        add eax, ebx
        mov Rval, eax               ; Rval = -36
    
    6). 标志位测试
    .code
    main PROC                   ; 定义主函数开始位置
        ; ZF 零标志位示例
        mov cx, 1
        sub cx, 1                   ; ZF = 1
        mov ax, 0FFFFh
        inc ax                      ; ZF = 1
    
        ; SF 符号标志位示例
        mov cx, 0
        sub cx, 1                   ; SF = 1
        mov ax, 7FFFh
        add ax, 2                   ; SF = 1
    
        ; CF 进位标志位示例
        mov al, 0FFh
        add al, 1                   ; CF = 1, al = 00h
    
        ; OF 溢出标志位示例
        mov al, +127
        add al, 1                   ; OF = 1
        mov al, -128
        sub al, 1                   ; OF = 1
    

    3. 数据相关的运算符和指令

    1). OFFSET 操作数

    功能:获取偏移地址

    COMMENT &
        数据相关的运算符和指令运算
    &
    .data
        bVal BYTE ?
        wVal WORD ?
        dVal DWORD ?
        dVal2 DWORD ?
        myArray WORD 1, 2, 3, 4, 5
        
    .code
    main PROC                   ; 定义主函数开始位置
        ; OFFSET 偏移
        mov esi, OFFSET bVal    ; ESI = 00904000h
        mov esi, OFFSET wVal    ; ESI = 00904001h
        mov esi, OFFSET dVal    ; ESI = 00904003h
        mov esi, OFFSET dVal2   ; ESI = 00904007h
        mov esi, OFFSET myArray + 4 ; ESI = 0090400Fh
    
    2). ALIGN 指令

    功能:对齐字节大小,将小字节内存向大字节内存靠拢

    .data
        ; ALIGN 对齐
        bVal1 BYTE ?            ; 00404000h
        ALIGN 2
        wVal1 WORD ?            ; 00404002h
        bVal2 BYTE ?            ; 00404004h
    
    3). PTR 操作数

    功能:将大字节的变量转换为小字节的变量,将小字节的数组拼接为打字节的变量

    .data
        myDouble DWORD 12345678h
        wordList WORD 5678h, 1234h
    
    .code
    main PROC                   ; 定义主函数开始位置
        ; PTR 指针
        mov ax, WORD PTR myDouble   ; ax = 5678h
        mov ax, WORD PTR [myDouble + 2] ; ax = 1234h
        mov eax, DWORD PTR wordList ; eax = 12345678h
    
    4). TYPE 操作数

    功能:返回变量的所占用的字节大小

    .data
        bVal BYTE ?
        wVal WORD ?
        dVal DWORD ?
    
    .code
    main PROC                   ; 定义主函数开始位置
        ; TYPE 获取变量所占内存大小
        mov eax, TYPE bVal      ; eax = 0000 0001h
        mov eax, TYPE wVal      ; eax = 0000 0002h
        mov eax, TYPE dVal      ; eax = 0000 0004h
    
    5). LENGTHOF 操作数

    功能:获取数组长度

    .data
        byte1 BYTE 10, 20, 30
        array1 WORD 30 DUP(?), 0, 0
        array2 WORD 5 DUP(3 DUP(?))
        array3 DWORD 1, 2, 3, 4
        digitStr BYTE "12345678", 0
    
    .code
    main PROC                   ; 定义主函数开始位置
        ; LENGTHOF 计算数组长度
        mov eax, LENGTHOF byte1 ; eax = 0000 0003h
        mov eax, LENGTHOF array1 ; eax = 0000 0020h
        mov eax, LENGTHOF array2 ; eax = 0000 000Fh
        mov eax, LENGTHOF array3 ; eax = 0000 0004h
        mov eax, LENGTHOF digitStr ; eax = 0000 0009h
    
    6). SIZEOF 操作数

    功能:计算变量所占用的位数

    .data
        intArray WORD 32 DUP(0)
    
    .code
    main PROC                   ; 定义主函数开始位置
        ; SIZEOF 计算所占用的字节位数
        mov eax, SIZEOF intArray    ; eax = 0000 0040h
    
    7). LABEL 指令

    **功能:设置别名,使用内存地址访问之后定义的变量 **

    .data
        val16 LABEL WORD
        val32 DWORD 12345678h
        LongValue LABEL DWORD
        val1 WORD 5678h
        val2 WORD 1234h
    .code
    main PROC                   ; 定义主函数开始位置
        ; LABEL 地址访问
        mov ax, val16           ; ax = 1234h
        mov dx, [val16 + 2]     ; dx = 5678h
        mov eax, LongValue      ; eax = 1234 5678h
    

    4. 间接寻址

    1). 间接操作数

    功能:使用地址访问对应的地址的数值

    • 保护模式
    .data
        byteVal BYTE 10h
    
    .code
    main PROC                   ; 定义主函数开始位置
        ; 间接操作
        mov esi, OFFSET byteVal
        mov al, [esi]
    
    • 使用PTR间接操作
    inc BYTE PTR [esi]
    
    2). 数组
    .data
        arrayW WORD 1000h, 2000h, 3000h
    
    .code
    main PROC                   ; 定义主函数开始位置
        ; 数组
        mov esi, OFFSET arrayW
        mov ax, [esi]           ; ax = 1000h
        add esi, 2
        mov ax, [esi]           ; ax = 2000h
        add esi, 2
        mov ax, [esi]           ; ax = 3000h
    
    3). 索引操作数
    • 表现形式
    constant[reg]
    [constant + reg]
    

    示例:

    .data
        arrayB BYTE 10h, 20h, 30h
    
    .code
    main PROC                   ; 定义主函数开始位置
        ; 索引操作数
        mov esi, 0
        mov al, arrayB[esi]     ; al = 10h
    
    
    • 添加位移
    .data
        arrayW WORD 1000h, 2000h, 3000h
    
    .code
    main PROC                   ; 定义主函数开始位置
        ; 添加位移
        mov esi, OFFSET arrayW
        mov ax, [esi]           ; ax = 1000h
        mov ax, [esi + 2]       ; ax = 2000h
        mov ax, [esi + 4]       ; ax = 3000h
    
    • 使用16位寄存器
    mov al,arrayB[si]
    mov ax,arrayW[di]
    mov eax,arrayD[bx]
    
    • 索引操作数中的比例因子
    .data
        arrayD DWORD 100h, 200h, 300h, 400h
    
    .code
    main PROC                   ; 定义主函数开始位置
        ; 索引操作数中的比例因子
        mov esi, 3 * TYPE arrayD
        mov eax, arrayD[esi]        ; eax = 0000 0400h
    
    4). 指针

    概念:一个变量存储另一个变量的地址

    .data
    arrayB BYTE 10h,20h,30h,40h
    ptrB DWORD arrayB
    
    • TYPEDEF 操作数

    功能:用户可自定义类型名

    PBYTE TYPEDEF PTR BYTE
    

    5. JMP 和 LOOP 指令

    1). JMP 指令
    • 功能

      一种非传统的传递,类似于C语言中的goto语句

    • 语法

    JMP destination
    
    • 创建循环
    top:
    .
    .
    jmp top
    
    2). LOOP 指令
    • 功能

    一种非传统的传递,类似于C语言中的循环语句

    • 语法
    LOOP destination
    
    • 循环
     mov ax,0
     mov ecx,5
    L1:
     inc ax
     loop L1
    

    每次循环为ax自加1,ecx进行自减,循环结束时,ax=5,ecx=0

    • 循环内部修改ECX的值
    .data
    count DWORD ?
    .code
        mov ecx,100     ; 设置循环计数
    top:
        mov count,ecx   ; 保存循环计数
        .
        mov ecx,20      ; 修改ECX
        .
        mov ecx,count   ; 恢复循环计数
        loop top
    
    • 嵌套循环
    .data
        count DWORD ?
    .code
        mov ecx,100     ; 设置外层循环计数
    L1:
        mov count,ecx   ; 保存外层循环计数
        mov ecx,20      ; 设置内层循环计数
    L2:
        .   
        .
        loop L2         ; 重复内层循环
        mov ecx,count   ; 恢复外层循环计数
        loop L1         ; 重复外层循环
    
    3). 数组和
    .486        ; 定义32位程序可以接受32位的寄存器和地址
    .model flat, stdcall    ; 选择程序的内存模式为平坦模式,stdcall调用习惯
    .stack 4096             ; 设置运行的堆栈大小为4096字节
    ExitProcess PROTO, dwExitCode: DWORD    
    
    
    COMMENT &
        数组求和
    &
    .data
        intArray DWORD 10000h, 20000h, 30000h, 40000h
    
    .code
    main PROC                   ; 定义主函数开始位置
        mov edi, OFFSET intArray    ; 获取数组偏移地址
        mov ecx, LENGTHOF intArray  ; 设置循环计数
        mov eax, 0                  ; 存储数组元素之和
    L1:
        add eax, [edi]              ; 将数组元素值加入eax
        add edi, TYPE intArray      ; 获取下一个数组元素地址
        LOOP L1                     ; 循环
    
        INVOKE ExitProcess, 0   ; 退出程序2
    main ENDP           ; 函数结束位置, ENDP 之前的内容,要与PROC 
    END main            ; 设置了函数的入口与出口
    
    4). 字符串复制
     .486       ; 定义32位程序可以接受32位的寄存器和地址
    .model flat, stdcall    ; 选择程序的内存模式为平坦模式,stdcall调用习惯
    .stack 4096             ; 设置运行的堆栈大小为4096字节
    ExitProcess PROTO, dwExitCode: DWORD    
    
    
    COMMENT &
        数组求和
    &
    .data
        source BYTE "This is the source string", 0
        target BYTE SIZEOF source DUP(0)
    
    .code
    main PROC                   ; 定义主函数开始位置
        mov esi, 0              ; 索引寄存器
        mov ecx, SIZEOF source  ; 循环计数
    L1:
        mov al, source[esi]     ; 从源字符串获取一个字符, mov指令不能够有两个内存操作数
        mov target[esi], al     ; 存储到目标字符串对应位置
        inc esi                 ; 移动到下一个字符串
        LOOP L1                 ; 循环
    
        INVOKE ExitProcess, 0   ; 退出程序
    main ENDP           ; 函数结束位置, ENDP 之前的内容,要与PROC 
    END main            ; 设置了函数的入口与出口
    
    5). 数组求和——64位
    ; 数组求和
    
    ExitProcess PROTO
    
    .data
    intArray QWORD 1000000000000h, 2000000000000h, 3000000000000h, 4000000000000h
    
    .code
    main PROC
        mov rdi, OFFSET intArray        ; 设置偏移地址
        mov rcx, LENGTHOF intArray      ; 初始化循环计数
        mov rax, 0                      ; 数组之和
    L1:
        add rax, [rdi]                  ; 加入当前位置的数组值
        add rdi, TYPE intArray          ; 移动得到下一个数组元素
        LOOP L1                         ; 重复执行,直到rcx=0
        mov ecx, 0                      ; 退出程序并返回
    
        call ExitProcess                ; 退出程序
    main ENDP
    END
    

    相关文章

      网友评论

        本文标题:汇编开发(二):数据传输,地址和算法

        本文链接:https://www.haomeiwen.com/subject/yemujqtx.html