zoukankan      html  css  js  c++  java
  • 汇编3栈帧,参数传递,串操作,混合汇编,x64,asm文件

    基础知识2

    1. 选择结构

      1. 通过判断 + 条件跳转指令来实现

    2. 循环结构

      1. 通过判断 + 条件跳转指令来实现(会有一个向上跳转的语句)

    1. 函数调用约定

      1. C调用约定: 由外部平衡栈

      2. 标准调用约定 : 由函数内部平衡栈

      3. 对象调用约定 : 由函数内部平衡栈, 寄存器用于保存对象的首地址(就是this指针)

      4. 快速调用约定 : 由函数内部平衡栈(传参都是从右往左传递.), 用到 ecx , edx 来依次传递前两个参数.

    1. 通过 call 指令, call指令的原理是: 将call指令的下一条指令的地址压入栈中, 然后再进行跳转.

    2. 通过 ret 指令, 来结束函数的调用,回到调用点, 原理: 将保存在栈中的返回地址 pop 到 eip .

    3. 需要在函数内部访问形参的方式:

      1. 通过 esp 来访问, 缺点: esp寄存器是受到一些栈操作指令被改变的(例如:push,call,ret) , 当它被改变之后, 就不能通过一个固定的偏移来定位栈里面的参数了.

      2. 通过 ebp 来访问的 , 原理: 进入到函数内部时, 将esp的值保存到ebp, 然后ebp是不会受到栈操作指令的映 像, 可以使用一个固定的偏移来定位栈里面的参数.

      3. 函数编写的要求:

        1. 保证在函数内部不会修改寄存器的值, 如果要修改,需要保证离开函数之后, 将修改的寄存器的值恢复 回去.

        2. 保证离开函数之后, 栈是平衡的(进入到函数时,栈顶esp指向了哪里, 那么在离开函数之后,esp也必须还 指向了那里),ebp也一样.

      4. 在函数内部如果要使用局部变量.

        1. 打开函数栈帧通过 push ebp; mov ebp,esp;

        2. 开辟局部变量占用的栈空间:

        3. 通过 ebp-4 来定位在栈中的局部变量: ebp-4 是局部变量1, ebp-8 是局部变量2

    int fun( )
    {  
       int nArr[]={1,2,3,4,5};  
       printf("%d",nArr[0]);
    }

     

    fun proc
    push ebp     ;            
    ; 打开栈帧
    mov ebp , esp;/
    sub esp , 20 ; 开辟局部变量的栈空间
    mov [ebp-4] , 1; 初始化局部变量
    mov [ebp-8] , 2; 初始化局部变量
    mov [ebp-0ch] , 3; 初始化局部变量
    mov [ebp-010h] , 4; 初始化局部变量
    mov [ebp-014h] , 5; 初始化局部变量
    mov esp , ebp; 恢复局部变量占用的栈空间
    pop ebp      ; 恢复栈帧 ret fun endp

     

    5.字符串的汇编

    char* pStr = "abcdefg";
    dword ptr [ebp-18h],0BA7B40h
    //dword ptr [pStr],offset string "abcdefg" (0BA7B40h)  

    在内存输入00BA7B40

     

    6.指针

    //函数
    void fun2(char* p) {
     p = "123456";
    }
    //调用时
    fun2(pStr);
    00BA19EC 8B 45 E8             mov         eax,dword ptr [pStr]  
    00BA19EF 50                   push        eax  
    00BA19F0 E8 4F F8 FF FF       call        fun2 (0BA1244h)  
    00BA19F5 83 C4 04             add         esp,4  

    //进函数 p = "123456";
    00BA17D8 mov dword ptr [ebp+8],0BA7B30h      
    //00BA17D8 mov dword ptr [p],offset string "123456" (0BA7B30h)  

    7.二级指针

    //函数
    void fun3(char** pp, char*& pr) {
     *pp = "123456";
     pr = "456789";
    }
    //调用时
    fun3(&pStr, pStr);
    00BA19F8 8D 45 E8             lea         eax,[pStr]  
    00BA19FB 50                   push        eax  
    00BA19FC 8D 4D E8             lea         ecx,[pStr]  
    00BA19FF 51                   push        ecx  
    00BA1A00 E8 02 F9 FF FF       call        fun3 (0BA1307h)  
    00BA1A05 83 C4 08             add         esp,8  
    //进函数
     *pp = "123456";
    00BA1838 8B mov eax,dword ptr [ebp+8]  
    //00BA1838 8B mov eax,dword ptr [pp]
    00BA183B C7    mov         dword ptr [eax],0BA7B30h  
    //00BA183B mov dword ptr [eax],offset string "123456" (0BA7B30h)
     pr = "456789";
    00BA1841 mov eax,dword ptr [ebp+0Ch]  
    //00BA1841 8B mov eax,dword ptr [pr]  
    00BA1844 C7    mov         dword ptr [eax],0BA7B38h  
    00BA1844 C7 mov dword ptr [eax],offset string "456789" (0BA7B38h)

    8.指针 引用 值传递

    //函数
    void fun2(int n, int* p, int& r) {
     n = 10;
     *p = 10;
     r = 10;
    }
    //调用时,先传最右边的
    fun2(n, &n, n);
    //int&
    00BA1A0F 8D 45 DC             lea         eax,[ebp-24h]  
    00BA1A12 50                   push        eax  
    //int*
    00BA1A13 8D 4D DC             lea         ecx,[ebp-24h]  
    00BA1A16 51                   push        ecx  
    //int
    00BA1A17 8B 55 DC             mov         edx,dword ptr [ebp-24h]  
    00BA1A1A 52                   push        edx  
    //进入函数
    00BA1A1B E8 29 F8 FF FF       call        00BA1249  
    //C调用
    00BA1A20 83 C4 0C             add         esp,0Ch  

    //进函数
    n = 10;
    00BA1768 mov dword ptr [ebp+8],0Ah
    //00BA1768 mov dword ptr [n],0Ah
     *p = 10;
    00BA176F mov eax,dword ptr [ebp+0Ch]  
    00BA1772 C7 mov dword ptr [eax],0Ah  
    //00BA176F mov eax,dword ptr [p]  
    //00BA1772 mov dword ptr [eax],0Ah
     r = 10;
    00BA1778 mov eax,dword ptr [ebp+10h]  
    00BA177B mov dword ptr [eax],0Ah  
    //00BA1778 mov eax,dword ptr [r]  
    //00BA1778 mov dword ptr [eax],0Ah

    9.强转

    char* p = (char*)&n;
    *p = 0;
    *(int*)p = 0;
    p = (char*)0;


    char* p = (char*)&n;
    lea         eax,[n]  
    mov         dword ptr [p],eax  

     *p = 0;
    mov         eax,dword ptr [p]  
    mov         byte ptr [eax],0  
     *(int*)p = 0;
    mov         eax,dword ptr [p]  
    mov         dword ptr [eax],0  
     p = (char*)0;
    mov         dword ptr [p],0  

     

     

    串操作指令

     

    • rep 重复操作前缀

      • 默认将重复的次数保存在ecx中. 每重复一次,就递减ecx的值. 当ecx的值等于0时,就不再重复.

      • 不能单独使用 , 也不能和任意的指令搭配使用,只能和串操作指令结合使用,例如:

        mov ecx,10 
        rep inc eax; // 错误
    • stos

      • 默认操作数是 edi 和 al/ax/eax ,

      • Q功能 : 将 al/ax/eax 的值填充到edi指向的内存中. 填充完之后, 会自增/自减(取决于 DF 方向标志) edi 的值 , edi的值在自增的时候有(1/2/4)的可能,实际增加多少,取决于指令的后缀, 它的后缀 stosb , stosw,

      • 和 rep 指令结合使用之后,功能类似于 memset

     

     

    • lods

      • 默认操作数: esi 和 eax

      • 功能: 将esi指向的内存的数据保存到eax中.

    • movs

      • 默认操作数 edi ,

      • 功能 : 将esi指向的内存的数据保存到 edi 中 和 rep 结合之后, 功能类似 memcpy

         

    • cmps

      • 默认操作数是edi,esi

      • 功能: 将esi和edi指向的内存进行比较, 将比较的结果设置到eflags寄存器中. 和 repe 结合,功能类似 memcmp

         

         

    • scas

      • 默认操作数:

      • 功能 : 使用edi指向的字节和eax进行比较, 将比较的结果设置到标志寄存器中 和 repne 结合,功能类似 strlen

    int main(){
    char buff[100];
    char buff2[] = "hello world";
    // 1. 将buff全部填充为0(memset(buff,0,100))
    _asm
    {
    lea edi,[buff]; mov ecx,100; mov al ,0;
    rep stosb;
    }
    // 2. 将buff2的内容拷贝到buff中(memcpy(buff,buu2,11)
    _asm {
    lea esi, [buff2]; lea edi, [buff]; mov ecx, 11;
    rep movsb;
    }

    // 3. 比较buff2和buff的内存是否一样(memcmp(buff2,buff)) int nCmpFlag = 0;
    _asm
    {
    lea esi, [buff2]; lea edi, [buff]; mov ecx, 11;
    repe cmpsb;
    mov [nCmpFlag ],ecx;
    }
    if (nCmpFlag == 0) { printf("内存相等 ");
    }
    else {
    printf("内存不相等 ");
    }

    // 4. 计算buff2字符串长度(strlen(buff2))
     _asm
    {
     lea edi,[buff2];
     mov al,0;
     mov ecx,0xFFFFFFFF;
     repne scasb;
     not ecx;  //按位取反得到原码
     dec ecx;  // ecx存的是补码,-1获取反码
    }

     

     

     

     

    混合编程

    c和汇编一起出现在同一个源文件中

    内嵌汇编

    1. 通过 _asm 关键字来实现

    2. 单行内联汇编

      int main() 
      {  
         int n;
         _asm mov [n] , 10
      }

       

     

    1. 块状内联汇编

    int main() 
    {  
       _asm   {
           mov eax,100;
           add eax, 20;
      }
    }

     

    1. 在内联汇编中定义一个字节的机器码

      int main()
      {
         _asm    
        {  
             jmp _FLAG  
                 _emit 0xe9; 直接在此处定义1个字节的机器码,
            ;初始值是0xe9         _FLAG:  
        }
      }

       

     

    使用asm文件

    在64位程序中,不能使用 _asm 关键字来使用内联汇编了.

    1. 给项目添加一个宏汇编编译器.

    1. 添加一个.asm的文件

    2. 指定这个asm的文件使用汇编编译器来编译(否则默认是不编译的)

       

    1. 在asm中定义汇编代码及结构体

      .model flat,stdcall

      .data

      ; 声明结构体的原型
      MyStruct struct
      cChar db ?
      nNum dd ?
      MyStruct ends


      .code

      ;
      ; 如果在汇编的函数声明中加上了
      ; 参数的声明,汇编编译器会自动
      ; 加上打开栈帧的代码.
      fun proc obj:DWORD; fun(MyStruct* obj)
      ;push ebp
      ;mov ebp ,esp
      ;sub esp , 0
      local obj2:MyStruct; 在函数内部定义局部变量
      ; 访问局部结构体变量的字段
      lea eax,[obj2];
      mov [eax+MyStruct.nNum],0ah
      mov byte ptr [eax+MyStruct.cChar],041h

      ; [ebp+8] => MyStruct* obj
      mov eax,[ebp+8];
      mov [eax+MyStruct.nNum],0ah
      mov byte ptr [eax+MyStruct.cChar],041h

      ;mov esp , ebp
      ;pop ebp
      ret 4
      fun endp

      end

       

    1. 在源文件中调用汇编代码.

    2. 声明函数原型(注意要使用 extern"C"

      extern "C" void __stdcall fun(MyStruct* obj);

       

    3. 直接调用函数即可.

    裸函数

    在函数名中加上关键字 _declspec(naked)

     void _declspec(naked) fun()
    {
        _asm ret;
    }

     

    1. 编译器不会在裸函数的内部生成额外的代码. 写了多少就有多少.

    2. 如果裸函数有形参, 那么需要在函数内部自己编写汇编来打开栈帧.

    3. 如果裸函数有局部变量

    4. 局部变量不能赋初始值

    5. 还需自己编写汇编代码开辟局部变量所占的栈空间

    // 裸函数 
    int _declspec(naked) fun(int n)
    {
       _asm
      {  
           push ebp;  
           mov ebp,esp;  
           sub esp,400  
      }
     int m;
       m = 200; //对应汇编 mov dword ptr [ebp-8],0C8h
       n = 100; //对应汇编 mov dword ptr [ebp+8],64h  
     _asm {
         mov eax, n;
         mov esp,ebp;
         pop ebp;  ret
    }
    }

     

    x64汇编

    函数传参方式: 头4个参数依次使用 rcx,rdx,r8,r9 来传递, 第4个之后的参数使用栈来传递(从右往左) , 栈平衡着是函 数内部.

    反汇编引擎和汇编引擎

     

    反汇编引擎 - 能够将一些机器码翻译成汇编代码. 汇编引擎 - 能够将汇编代码翻译器成机器码.

    关于 RadAsm 的坑

    • RadAsm 中十六进制不能以 0x 开头,需要在末尾添加 h

      正确示范:mov eax, [100h]

      但是执行完在OD里面是 mov eax,100h

      想要实现下面的语义,

      mov eax, dword ptr [0x100]

      需要

      mov eax, DS:[100h]

    • lea eax, [100h] 想实现上面的功能需要写这个代码 mov eax, 100h lea eax, [eax]

    • 栈是什么?栈帧是什么?

      • 线程最少由一个线程内核对象和一个栈组成?

      • 栈: 是 ss 起始的一块特殊的内存空间

      • (栈)帧: 栈帧是栈中的一块区域,栈帧 以函数为单位

    • 栈的操作

      • 通常栈在调试器的增长方向是自上而下

      • 调试器中,栈视图的最上方指向的是 esp

    • 对栈的操作

      • 入栈 push(af) esp-n

      • 出栈 pop(af) esp+4

    • call 和 ret

      • call = push 下一条指令 + jmp 目标地址

      • ret = mov eip, [esp], add esp, 4 + n*(4)

  • 相关阅读:
    Serialization and deserialization are bottlenecks in parallel and distributed computing, especially in machine learning applications with large objects and large quantities of data.
    Introduction to the Standard Directory Layout
    import 原理 及 导入 自定义、第三方 包
    403 'Forbidden'
    https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2
    These interactions can be expressed as complicated, large scale graphs. Mining data requires a distributed data processing engine
    mysqldump --flush-logs
    mysql dump 参数
    mysql dump 参数
    如果是在有master上开启了该参数,记得在slave端也要开启这个参数(salve需要stop后再重新start),否则在master上创建函数会导致replaction中断。
  • 原文地址:https://www.cnblogs.com/ltyandy/p/11054431.html
Copyright © 2011-2022 走看看