zoukankan      html  css  js  c++  java
  • 16汇编第十讲完结Call变为函数以及指令的最后讲解

               16汇编完结Call变为函数以及指令的最后讲解

    学了10天的16位汇编,这一讲就结束了,这里总结一下昨天的LOOP指令的缺陷,因为lOOP指令的缺陷,所以我们都改为下面的汇编代码使用了,自己去写,其中条件是你自己写的

    请看汇编代码:

    do while 的汇编代码
     WHILE:
           mov ax,ax
           cmp ax, 10
           jl WHILE
    while 的汇编代码
    
     WHILE:
        cmp ax, 10
         jge WHILE_END
        mov ax,ax
         jmp WHILE
    WHILE_END:

    一丶Call指令(子程序)变为函数调用(重要,这个以后逆向会天天看到所以一定掌握)

    首先我们明白一点,昨天我们写的只是一个单独的子程序(什么保存栈地址,开辟局部变量空间,....都没有写,只是单独的平栈),今天我们就写一个Call变为函数调用的例子,一步一步的看为什么这么做,对以后的逆向很有帮助

    1.首先我们明白Call调用的几种方式

    段内:   一个段中去调用,或者跳转

    段间:   代码不在同一个段中,从一个段跳跃到另一个段

    Call指令分为4中类型 (类似于JMP)

    Call  label(标号) ;                                                       段内调用丶直接寻址

    Call r16/m16 (16位寄存器,或者2字节内存)                段内调用丶间接选址

    Call  far ptr label;                 段间调用丶直接寻址

    Call  far ptr mem;                                                        段间调用丶间接寻址

    昨天我们写的简单的子程序例子:

    mov ax,1
    push ax
    mov bx,2
    push bx
    Call   标号
    add sp,4
    ....
    标号: mov bp,sp
       mov ax,[bp +2]
       mov bx,[bp +4]
       add ax,bx
    ret

    我们压入了两个参数,一个Ax,一个Bx,在Call的时候,会把下一行的地址压入到栈中,也就是 add sp,4的所在的地址

    我们画一下栈

    可以看出,这个就是当前的栈结构了, 我们执行代码的时候,首先 bp 和 sp是平等的了

    现在 bp+2  能寻得bx, bp +4 能寻得 ax

    然后ret  注意,ret只是把sp +2了,也就是弹栈弹出了返回地址,并且给IP了,现在IP就会跳转到下一条指令执行的位置

    也就是(add sp,4)  现在要注意了,bx 和 ax 都还在栈中,我们没办法让栈恢复所以在外面用 add sp,4 让sp的位置(现在的位置+2了,已经在bx的位置了)变为栈底了

    所以这个就是C语言的 C调用约定

    如果我们想StdCall  (std 调用约定,标准的调用约定) 就要用retn 这个指令了,不能是ret了,

    写成 retn  4  代表先把返回地址返回出去,然后再让sp +4 个字节,相当于在函数内部就平栈了,这样外部就不用写

    add sp,4了,不用自己平栈了

     2.由Call 变为函数一步一步来

    现在基于上面的原理我们知道如何平衡一个栈了,但是你有没有发现,为什么我们一开始要把sp 给Bp

    也就是让 BP和SP一样的位置

    这个是有原因的

    首先C语言调用函数的时候会进行几步操作

    1.保存栈底

    2.申请局部变量空间

    3.保存环境

    4.恢复环境

    5.释放局部变量空间

    6.恢复栈底

    7.弹栈返回

    好看上面的我们可能有点不明白,下面我把整体的栈图放出来

    整体的栈是这样的,这里为什么要一开始把bp和sp相等,是有原因的,我们不妨这样想,如果我们申请局部变量空间的

    时候,是不是参数的偏移也要改动,这样每次都要自己计算偏移,相当麻烦,所以只能这样,

    我们以后找参数就  bp + xxx (因为bp一开始就在栈底的值这一栏)  这样就能寻找到参数,你开辟多大的局部变量看空间都和我没关系,所以以后我只需要 bp-xxx 就是找到的局部变量

    现在看下汇编代码的模版吧

    MY_ADD:  ;stdcall    
        push bp
        mov bp, sp    ;1. 保存栈底
        
        sub sp, 10    ;2. 申请局部变量空间这里随你便,申请的时候先抬栈,让sp高一点,但是不会影响bp
        push bx       ;3.保存环境 保存环境的意思就是外面的寄存器的值要保存一下,这样恢复寄存器的值
                     
        mov word ptr [bp-4], 1        ;每次我只需要 bp -xxx 就知道寻找局部变量
        mov word ptr  [bp-2], 2
        mov ax, [bp+6]  ;参数1         ;每次我bp+ xxx 就知道我是找参数,所以不会冲突,打死参数的地址不会变
        add ax, [bp+8]  ;参数2
        
                     ;xxxxxxx
        pop bx          ;4. 恢复环境      弹栈的时候寄存器信息先回复
        mov sp, bp     ;5. 释放局部变量空间  而且恢复变量控件的时候也很容易,直接把bp当前的位置给sp即可,释放空间了
        pop bp        ;恢复以前的栈底的值
        retf         ;6. 返回   ,retf下面详细讲

    在这里主要是掌握bp所在的位置即可,就能明白为什么这样写了,不信的话自己写个程序,看下反汇编,大体的就是这个套路,这里讲解的是为什么这样做,不是和市面的汇编视频一样,你看到 bp -xxx 就知道他在访问局部变量就行

    其实这个是错误的,我们要知其然,并知其所以然

    看下栈图,掌握bp所在的位置

    只要掌握bp所在的位置即可,上面的代码即可明白

     3.Call指令的retf段间转移

    这个我们首先要明白,在Call的时候会把Call下边一条指令的地址保存到栈中,出栈的时候要给IP,让其更改跳转,

    跳转到Call下一条指令执行的位置的地方

    但是现在我们是段间Call,也就是不在一个段中,这个时候栈不光会保存返回地址,还会保存当前CS段寄存器的地址

    这样返回的时候  CS:IP返回,但是现在有一个问题,就是我们自己根本就平不了栈,我们把IP拿出来了,给IP,CS段寄存器根本没办法改,这样我们必须同时修改CS:IP的值才能回到以前的地方,但是现在没办法了,因为你改IP回跳,改CS会跳,必须同时改,弄不了,所以弄一个retf的指令去帮我们去做

    注意retf 你也需要平栈,比如我们压入了两个参数,就要 retf 4, retf会默认把栈顶4个字节的数据取出来分别给 ip和CS段寄存器,但是剩下4个字节都是我们的参数,比如自己去释放,让SP的加4到栈底才可以

    二丶中断指令

    1.什么是中断指令

    中断,是有一种改变程序执行顺序的方法

    中断具有很多的中断类型

    中断的指令有3条

      1.INT i8(i8代表一个八位的立即数)

      2.IRET  IRET 和Call差不对,Call的ret返回的时候会把栈顶的元素弹出两个字节,这两个字节是返回地址,所以可以回到正确的地方执行指令,但是IRET明显比ret保存的东西多,其中ret我们可以手工的pop和jmp去执行,IRET也可以自己去做,但是你要完整的模拟才可以,一般还是调用IRET即可

      3.INTO

    2.中断指令的自我理解

      其实中断指令就是调用硬件提供的API(也称为系统调用)我们前边用过很多次了

    比如显示一个字符串

      

    mov ah,09h
    int 21h

    其中参数是09,int 21h代表执行,还有很多

    介绍下指令

      INT I8:  中断的调用指令: 产生I8号中断,就是调用int代表我要调用了,其中指令是什么使我们给的,是一个八位立即数比如 09

      IRET:  中断的返回指令,理解为返回,可以进行下一条指令的执行

      INTO:  不常用,不讲解.

    3.21h中断,到底是个啥玩意

    我们每次都调用21h什么的,但是不知道他是个啥玩意

    他是DOS提供给用户的,用于调用系统功能的中断(简单理解就是DOS提供的API,让用户调用),他有近百个功能让公户选择使用.包括设备管理,目录管理,和文件管理

    ROM-BIOS(主板的BIOS)也是这种形式,这就是为什么程序一开机显示器就会显示字符,别忘了这个时候系统还没有启动,还没有操作系统一说

    看下图:

      

    现在操作系统也还是这样调用,操作系统的API很多,底层就依赖于这256个中断,只不过操作系统可能处理的方式更多,比如根据AH的值,调用一个函数指针,也就是一个函数的地址,这个函数地址里面又有很多封装,慢慢的操作系统API就越来越多.这些都是我们不关心的

    具体的中断,可以看下第一课所有的课堂资料中的指令字典,寻找一下中断号自己去调用

    比如判断按键的汇编例子

    getkey:    mov ah,01h    ;功能号:ah←01h
        int 21h              ;功能调用
        cmp al,’Y’           ;处理出口参数al
        je yeskey            ;是“Y”
        cmp al,’N’
        je nokey             ;是“N”
        jne getkey
        ...
    yeskey:    ...
    nokey:    ...

    三丶最后的指令详解

    LOCK指令  封锁总线,不让总线接受指令

    这个常用与多线程的时候操作,多线程的操作中的同步对象的 自加锁就是这样实现的

    比如:

      

    lock add word ptr[bp],3

    当我们吧3给内存的时候,其余的程序也可能再往这里写数据,所以同步一下,这样就完成这一条指令,才可以进行下一条指令的操作

    自减锁就是 把add 变为sub,交换锁就是 把指令变为 xchg lock只能同时处理一条指令,这是为防止我们把系统总线都锁死,这样操作系统就会崩溃,信息到不了,不过这个不是我们关心的

    解锁是CPU自己解锁的,没有解锁指令

    HLT 暂停指令

    这个就比较简单的,我们电脑都有休眠功能,就是用的这个指令,让CPU功耗降低,不执行,以前是无限循环NOP指令

    但是NOP也是一个指令也会有功耗,所以现在改为HLT指令了,执行了这个指令HLT不进行任何操作,当我们发送了一条指令过去之后,就会脱离暂停状态

    就好比电脑挂机了,屏幕黑了,就是进入HLT了,我们点击鼠标或者键盘,发送了一条指令,接着就唤醒了

    交权指令

    ESC 6位立即数,reg/mem

    我们都知道,以前算浮点数的时候都是CPU一个去做的,现在有了浮点处理器,也就是协处理器,专门算浮点的一个CPU

    我们的CPU计算浮点数的时候,要把权力交给浮点处理器,这时候就称为交权,在这个时候CPU要等待浮点处理器返回的结果,期间一直等待

    FADD FDIV 是浮点计算指令,就是我们计算指令前面加F就会计算浮点数了

    浮点数有7个寄存器

    ST -> st7 按照标号来的

    浮点处理器的st不能和通用寄存器一样去使用,它是吧ST寄存器压入栈中,让前两个栈中的数据相加返回的

    关于浮点处理,后面再说,这个不是16为汇编中使用的

    WAIT 等待指令

    这个就简单了,CPU交权后,就使用这个指令去等待浮点处理器返回结果

    总结:

      16位汇编在我的博客上都精简了,但是你想搞明白就要多花点时间,细细品味汇编的内容,掌握汇编

    过几天开始32位汇编的讲解,如果觉得好,请评论一下,关注一下,加个粉丝.支持一下,谢谢

    对于Call变为函数的哪里,一定要掌握,不懂的可以去看下C语言的栈内存结构,或者看下它的汇编代码,一定搞明白

    这个以后逆向的时候天天看.

    学习资料: 链接:http://pan.baidu.com/s/1nuPNUFf 密码:x44c

  • 相关阅读:
    2020年. NET Core面试题
    java Context namespace element 'component-scan' and its parser class ComponentScanBeanDefinitionParser are only available on JDK 1.5 and higher 解决方法
    vue 淡入淡出组件
    java http的get、post、post json参数的方法
    vue 父子组件通讯案例
    Vue 生产环境解决跨域问题
    npm run ERR! code ELIFECYCLE
    Android Studio 生成apk 出现 :error_prone_annotations.jar (com.google.errorprone:error) 错误
    记忆解析者芜青【总集】
    LwIP应用开发笔记之十:LwIP带操作系统基本移植
  • 原文地址:https://www.cnblogs.com/iBinary/p/7492517.html
Copyright © 2011-2022 走看看