• 汇编2.汇编版本的helloworld


    寻址方式

    • 立即数寻址

    • 寄存器寻址

    • 存储器寻址

      • 直接寻址 : mov ax, [ 01000h ]; 直接在[]内给出一个内存地址

      • 寄存器间接寻址: mov ax ,[si]; 在[]以寄存器的值给出内存地址.

      • 寄存器相对寻址: mov ax,[si+0ch][]以寄存器的值和一个数相加之后作为内存地址.

        struct MyStruct{
           int n1;
           char ch;
           int n2;
        };
        MyStruct stc;
        stc.n1 = 0;
        stc.n2 = 10;
        //假设stc的内存首地址是0x1000
        // 以汇编形式访问结构体字段:
        mov bx , 0x1000; // bx保存了结构体首地址
        mov [bx+0] , 0; //stc.n1
        mov [bx+8] , 10;//stc.n2
      • 基址变址寻址: mov ax,[si+bx] 使用两个寄存器相加之和作为内存地址

        char szBuff[10];
        for(int i = 0; i<10;++i){
           szBuff[i] = 0;
        }
        // 假设szBuff首地址是0x1000
        mov si , 0x1000;
        xor bx,bx;

        for(int i =0;i<10;++i){
           // szBuff[i] = 0;
           mov [si+bx] , 0;
        inc bx;
        }
      • 相对基址变址寻址: mov ax,[si+bx+0ch] 使用[]内的表达式的相加之和作为内存地址.

      • 在16位汇编中, 要使用存储器寻址的时候, 如果希望用寄存器寻址, 那么只能使用si,di,bx,bp寄存器, 然后这些寄存器不能任意组合.

     

    条件跳转指令

    有有符号跳转和无符号跳转之分.

    常见条件跳转指令:

    • 有符号跳转:

      • jg 大于

      • jge 大于等于

      • jl 小于

      • jle 小于等于

    • 无符号跳转

      • ja 大于, cf0 且 zf0则跳转

      • jae 大于等于 , cf ==0 则跳转

      • jb 小于, cf==1则跳转

      • jbe 小于等于, cf1 或 zf1则跳转

    • 不区分符号跳转

      • je zf==1 则跳转

      • jne zf==0 则跳转

    32位汇编

    第一个汇编项目


    .386 ;告诉汇编器, 使用32位汇编的语法来编译
    .model flat , stdcall ;默认使用平坦模式,默认使用stdcall的调用约定
    option casemap:none


    .code ; 定义一个代码段

    sldkfjlaskdjfmain:
    ret
     
    end sldkfjlaskdjfmain; 指定程序入口点
    end ; 结束代码段

    masm汇编器的语法

    1. 每个asm源码文件中都以下面的指令打头:

      .386 ;告诉汇编器, 使用32位汇编的语法来编译
      .model flat , stdcall ;默认使用平坦模式,默认使用stdcall的调用约定
      option casemap:none
    2. 必须自己定义一个代码段, 定义代码段使用.code伪指令 , 汇编指令就写在.codeend之间

    3. 程序必须要有一个入口点.在C语言中, 就固定了是main函数. 在masm里面, 可以在代码段中使用end 标签的方式来指定入口点.

    4. 数据的定义必须放在数据段, 数据段使用.data指令来定义.

      .data ; 定义数据段
      .const ; 定义常量数据(不可修改的数据)
      .code ; 定义代码段
      1. 在数据段中定义数据, 可以使用d系列指令

        .data 
        ch db 'a'         ; 相当于 char ch='a';

        buff db 0,0,0,0 ; 相当于 char buff[]={0,0,0,0};

        str db "hello" , 0; 相当于 char str[]={'h','e','l','l','o' , 0 }; 也就是说,这里不会自动加上字符串结束符''

        str2 db "hello " , 0; 在masm中没有转义字符.
        str2 db "hello", 0ah , 0 ; 0ah是' '

        ; 其它类型
        var1 dw 100 ; word类型
        var2 dd 100 ; dword类型

        ; dup用于重复定义数据 , dup前是一个重复的次数, dup圆括号内是需要重复的初始化值.
        arr dd 100 dup(0) ; int arr[100]={0};

    汇编版本的helloworld


    .386 ;告诉汇编器, 使用32位汇编的语法来编译
    .model flat , stdcall ;默认使用平坦模式,默认使用stdcall的调用约定
    option casemap:none ; 不区分大小写

    ; 包含名为`msvcrt.inc`头文件(c语言的所有库函数)
    include msvcrt.inc
    ; 包含库文件
    includelib msvcrt.lib

    ;include windows.inc
    ;include user32.inc
    ;includelib user32.lib

    .data
    g_str db "hello world",0dh,0ah, 0 ; ==0d0a

    .code ; 定义一个代码段

    main:
    push offset g_str;
    call crt_printf
    add esp , 4
      ret
     
    end main; 说明程序入口点
    end ; 结束代码段

     

    汇编程序基础

    三大程序结构

    • 顺序结构

    • 选择结构

      • 在c中, if else , switch

    • 循环结构

    模拟选择结构

    模拟if-else

    int n =0;
    scanf("%d",&n);

    if( n == 1){
       printf("星期一 ");
    }
    else if(n==2){
       printf("星期二 ");
    }
    else if(n==3){
       printf("星期三 ");
    }

    汇编版本

    .data 
    n dd 0 ; 定义一个全局变量, 名字为n

    .code
    _main:
    cmp n , 1
    je _FLAG1
    cmp n , 2
    je _FLAG2
    cmp n , 3
    je _FLAG3
    _FLAG1:
    printf("星期一 ");  
      jmp _ENDIF
    _FLAG2:
    printf("星期二 ");
    jmp _ENDIF
    _FLAG3:
      printf("星期三 ");
    _ENDIF:
    end _main
    end

    模拟switch-case

    switch( n )
    {
       case 1: printf("星期1 ");break;
       case 2: printf("星期2 ");break;
       case 3: printf("星期3 ");break;
    }

    汇编版本1(和if-else的一样)

    汇编版本1 : 使用跳转表

    .386 ;告诉汇编器, 使用32位汇编的语法来编译
    .model flat , stdcall ;默认使用平坦模式,默认使用stdcall的调用约定
    option casemap:none ; 不区分大小写
    
    ; 包含名为`msvcrt.inc`头文件(c语言的所有库函数)
    include msvcrt.inc
    ; 包含库文件
    includelib msvcrt.lib
    
    ;include windows.inc
    ;include user32.inc
    ;includelib user32.lib
    
    .data
     g_str db "hello world",0dh,0ah, 0 ; 
    ==0d0a
     str1 db  "星期一",0dh,0ah,0
      str2 db  "星期2",0dh,0ah,0
      str3 db  "星期3",0dh,0ah,0
     
    .code ; 定义一个代码段
    
    main: 
    .code
    	jmp being
    	jmptable dd _FLAG1,_FLAG2,_FLAG3 ; 在code段定义数据
    being:
    	mov eax , 1 ; ; 
    	dec eax;
    	jmp [jmptable+eax*4]; 根据eax的值,来跳转不同的位置.
    
    _FLAG1:
    	invoke crt_printf, offset str1;  
        jmp _ENDIF
    _FLAG2:	
    	invoke crt_printf , offset str2;
    	jmp _ENDIF
    _FLAG3:
        invoke crt_printf ,offset str1;
    _ENDIF:
    
    end main; 说明程序入口点
    end ; 结束代码段
    

    模拟循环结构

    使用条件跳转模拟循环

    int i =0;
    while (i < 10)
    {
       ++i;
    }

    汇编版本1:

        xor eax,eax ; 使用eax寄存器作为i
       
    _WHILE:
       cmp eax , 10 ;
       jge _ENDWHILE
    inc eax
    jmp _WHILE
    _ENDWHILE:

    汇编版本2 : loop 循环, 该指令使用ecx作为默认寄存器, 保存着循环次数, loop指令执行之后, 会判断ecx的值是否等于0 , 如果等于了,就不会跳转, 如果没有等于, 就先将ecx递减1, 然后跳转

        mov ecx , 10; 
    _WHILE:

    loop _WHILE ;

     

    函数结构

    • 函数调用和函数返回语句call,ret

      • call 目标地址 - 调用函数的指令

        • 会将call指令的下一条指令的地址push到栈中.

        • 跳转到目标地址.

      • ret 返回指令

        • 其实就是pop eip ,call指令将一个返回地址保存到栈中, ret就默认把栈中的地址取出设置到eip这样就能回调函数的调用点了.

        • ret 字节数 - 返回时,顺便平衡指定字节栈空间.

    • 定义函数

      • 通过ret指令来回到函数的调用点.

    • 调用函数

      • 函数的传参是通过栈来完成的.

        • 调用函数之前, 先将实参压入栈中.

        • 进入函数之后, 就可以从栈中取出参数了.

        • 在传参的时候, 是从右往左依次将参数入栈,还是从左往右,需要有一个函数调用约定

          调用约定名传参顺序栈平衡者
          _cdecl - C调用约定 从右往左 函数外部
          _stdcall - 标准调用约定 从右往左 函数内部
          _thiscall - 对象调用 从右往左,this指针保存到ecx寄存器 函数内部
          fastcall - 快速调用约定 前两个参数通过ecx,edx来传递, 后面的参数从右往左依次入栈传递 函数内部
      • 通过call + 函数地址完成调用

    • 在函数内部定位栈中的参数

  • 相关阅读:
    基础薄弱的反思
    最短路SPFA
    乌龟棋
    石子归并
    Linux学习2
    java 基础 数组
    java 基础 异常
    java 基础 接口
    java 基础 instance of
    solidity“abi.encode/abi.encodePacked”使用golang编码
  • 原文地址:https://www.cnblogs.com/ltyandy/p/11045289.html
Copyright © 2020-2023  润新知