堆栈的作用

汇编语言中的堆栈就是高级语言中的栈。

堆栈主在汇编程序设计中主要有三个作用:

  • 过程调用&返回指令
  • 参数传递
  • 局部变量

过程调用&返回指令

过程调用中的过程指什么?

汇编语言中的过程就是高级语言里面说的子程序,调用子程序(过程、函数)的本质就是控制转移,它与无条件转移的区别是调用子程序需要考虑返回

过程调用指令用于由主程序转移到子程序;

过程返回指令用于由子程序返回到主程序。

指令 中文名 格式 解释 备注
CALL 过程调用指令 CALL LABEL 段内直接调用LABEL 与jmp的区别在于call指令会在调用label之前保存返回地址(call 中return之后主程序还可以继续执行,jmp 当label执行完毕后不能返回主程序继续执行)
RET 段内过程返回指令 RET 使子程序结束,继续执行主程序

call指令的背后

段内直接调用的背后操作其实是两步:

(1)把返回地址(EIP内容)压入堆栈

(2)使得EIP内容为目标地址偏移,从而实现转移

返回地址:紧随过程调用指令的下一条指令的地址(有效地址)

目标地址:子程序开始处的地址(有效地址)

与无条件转移相比,过程调用指令call只是多了第一步(保护现场)。

ret指令的背后

过程返回指令的执行其实进行的是如下操作:

从堆栈中弹出地址偏移,送到指令指针寄存器EIP中,这个返回地址通常就是在执行相应的调用指令时所压入堆栈的返回地址。

参数传递

入口参数:主程序传给子程序的参数

出口参数:子程序传给主程序的参数

参数传递的方法主要有:寄存器传递法堆栈传递法、约定内存单元传递法、call后续区传递法等。具体情况需要事先约定好。

一般c语言的习惯是使用堆栈传递入口参数,使用寄存器传递出口参数,因为一般入口参数比较多,出口参数比较少。

局部变量

这里就一个结论,堆栈可以用于安排动态局部变量。

算术逻辑运算指令

乘除指令

无符号数乘法指令(MUL)

指令格式:

MUL OPRD

该指令实现两个无符号数的乘法运算,乘数是OPRD,被乘数位于AL、AX或EAX中(由OPRD的尺寸决定)。

需要注意的是乘积之后尺寸翻倍,两个8位的数乘积为16位,结果存放在AX中,类似的,两个16位数的乘积结果为32位,放在DX:AX中,最后,64位的乘积放在EDX:EAX中。

操作数OPRD可以是通用寄存器、存储单元,但是不能是立即数

有符号数乘法指令(IMUL)

有符号乘法指令有三种使用形式:

IMUL OPRD;
IMUL DEST,SRC;
IMUL DEST,SRC1,SEC2;

具体解释如下:

  • IMUL OPRD

    单操作数乘法指令和无符号数乘法的规则差不多,只是在乘的时候把乘数和被乘数都当成有符号数。

  • IMUL DEST,SRC

    数据流方向是DEST<=DEST*SRC

    要求目的操作数DEST只能是16位或32位通用寄存器,源操作数SRC可以是通用寄存器或存储单元,需与目的操作数尺寸一致,可以是一个立即数(尺寸不能超过目的操作数)。

    乘数和被乘数均作为有符号数。

  • IMUL DEST,SRC1,SEC2

    数据流方向为DEST<=SRC1*SRC2

    目的操作数DEST只能是16位或32位通用寄存器。

    SRC1可以是通用寄存器或者存储单元,须与目的操作数尺寸一致,但不能是立即数

    SRC2只能是一个立即数,尺寸不能超过目的操作数

    被乘数和乘数均为有符号数。

无符号数除法指令(DIV)

一般格式:

DIV OPRD

OPRD是除数,被除数位于AX、DX:AX或EDX:EAX中,由OPRD的尺寸决定,被除数的尺寸翻倍,商在AL、AX或者EAX中,余数在AH、DX或者EDX中,商和余数的尺寸和OPRD相同。

操作数OPRD可以是通用寄存器,可以是存储单元,但不能是立即数

注意使用DIV指令时要防止除溢出,比如:

比如上图,除完之后应该商300余0,可是300超出了AL的表示范围,这时候就产生了溢出情况,在实际使用中要注意防范类似情况。

有符号数除法指令(IDIV)

指令格式:

IDIV OPRD

基本原则和DIV一样,不同之处在于:

  • 除法时有符号的
  • 如果不能整除,余数的符号与被除数符号一致,而且余数的绝对值小于除数的绝对值。

总结

指令 中文名
MUL 无符号数乘法指令
IMUL 有符号数乘法指令
IMUL DEST,SRC 有符号数乘法指令
IMUL DEST,SRC1,SRC2 有符号数乘法指令
DIV 无符号数除法指令
IDIV OPRD 有符号数除法指令

符号拓展指令

符号拓展指令的实质是用被拓展寄存器的符号位占据目标拓展寄存器。

指令 中文名 格式 解释
CBW 字节转化为字指令 CBW 把寄存器AL中的符号拓展到寄存器AH;
如果AL最高有效位、为0,则AH=0,如果AL最高位为1,则AH=FFH
CWD 字转化为双字指令 CWD 把寄存器AX中的符号拓展到寄存器DX;
AX最高位0和1不同情况的拓展策略同CBW
CDQ 双字转化为四字指令 CDQ 把寄存器EAX中的符号拓展到EDX;
AX最高位0和1不同情况的拓展策略同CBW
CWDE 字转化为双字指令 CWDE 把AX中的符号拓展到EAX的高16位;
AX最高位0和1不同情况的拓展策略同CBW

使用举例:

符号拓展传送指令(MOVSX)

一般格式:

MOVSX DEST,SRC

把SRC符号拓展后送到DEST。

目的操作数的尺寸必须大于源操作数的尺寸。源操作数的尺寸可以是8位或16位,目的操作数的尺寸可以是8位或16位。

使用举例:

零拓展传送指令(MOVZX)

一般格式:

MOVZX DEST,SRC

把SRC零拓展后送到DEST。

源操作数可以是8位或16位,目的操作数可以是16位或32位。

使用举例:

逻辑运算指令

需要注意:

只有通用寄存器或者存储单元可作为目的操作数,用于存放运算结果。

指令 中文名 格式 解释 备注
NOT 否运算指令 NOT OPRD 把操作数OPRD按位取反,然后送回OPRD
AND 与运算指令 AND DEST,SRC 把两个操作数进行与运算之后结果送回DEST 同1得1,否则得0
OR 或运算指令 OR DEST,SRC 把两个操作数进行或运算之后结果送回DEST 同0得0,否则得1
XOR 异或运算 XOR DEST,SRC 把两个操作数进行异或运算之后结果送回DEST 相同得0不同得1
TEST 测试指令 TEST DEST,SRC 与AND指令类似,将各位相与,但是结果不送回DEST,仅影响状态位标志,指令执行后,ZF、PF、SF反映运算结果,CF和OF被清零 通常用于检测某些位是否为1,但又不希望改变操作数的值

test使用举例:

判断AL中的位6和位2是否有一位为1:

test al,01000100B;

随后,判断标志位ZF,如果ZF为0,说明al第6位和第2位都为0,否则说明二者有一个为1.

移位指令

一般移位指令

指令 中文名 格式 解释 备注
SAL 算术左移 SAL OPRD,count 把操作数oprd左移count位,右边补0 与shl指令一样 通过截取count的低5位,实际的移位数被限于0到31之间。
SHL 逻辑左移 SHL OPRD,count 把操作数oprd左移count位,右边补0 与sal指令一样 通过截取count的低5位,实际的移位数被限于0到31之间。
SAR 算术右移 SAR OPRD,count 把操作数oprd右移count位,同时每右移一位,左边补符号位,移出的最低位进入标志位CF 通过截取count的低5位,实际的移位数被限于0到31之间。
SHR 逻辑右移 SHR OPRD,count 把操作数oprd右移count位,左边补0,移出的最低位进入标志位CF 通过截取count的低5位,实际的移位数被限于0到31之间。

循环移位指令

指令 中文名 格式 解释 备注
ROL 左循环移位指令 ROL OPRD,count 左循环移一位之后最高位移到最低位的同时也进入CF 通过截取count的低5位,实际的移位数被限于0到31之间。
ROR 右循环移位指令 ROR OPRD,count 右循环移一位之后最低位移到最高位的同时也进入CF 通过截取count的低5位,实际的移位数被限于0到31之间。
RCL 带进位左循环移位 RCL OPRD,count 相当于CF在最高位直接参与循环移位 大循环左移 通过截取count的低5位,实际的移位数被限于0到31之间。
RCR 带进位右循环移位 RCR OPRD,count 相当于CF在最高位直接参与循环移位 大循环右移 通过截取count的低5位,实际的移位数被限于0到31之间。

使用实例:

实现把al的最低位送到bl的最低位,仍保持al不变。

ror bl,1;//bl循环右移一位
ror al,1;//al循环右移一位,最低位进入cf
rcl bl,1;//bl带进位左移,带进了来自al的最低位(cf)
rol al,1;//恢复al

双精度移位指令

双精度移位指令是为了方便地把一个操作数的部分内容通过移位复制到另一个操作数。

格式:

  • 双精度左移:SHLD OPRD1,OPRD2,count
  • 双精度右移:SHRD OPRD1,OPRD2,count

解释:

  • SHLD OPRD1,OPRD2,count

    将OPRD1左移指定的count位,在低端空出的位用操作数OPRD2高端的count位填补,但是OPRD2内容保持不变,操作数OPRD1中最后移出的位保留在进位标志CF中。

  • SHRD OPRD1,OPRD2,count

    将OPRD1右移指定的count位,空出的位用OPRD2低端的count位填补,但是OPRD2内容保持不变,操作数OPRD1中最后移出的位保留在进位标志CF中。

分支程序设计

无条件和条件转移指令

段内转移和段间转移

  • 段内转移(近转移):仅仅重新设置指令指针寄存器EIP的转移,由于没有调整CS,所以转移后继续执行的指令仍在同一代码段中。
  • 段间转移(远转移):不仅重新设置EIP,而且重新设置代码段寄存器CS的转移,由于重置了CS,转移后继续执行的指令在另一代码段中。

对于段内转移和段间转移需要注意:

  • 条件转移指令循环指令只能实现段内转移
  • 无条件转移指令过程调用指令以及返回指令,既可以是段内转移,也可以是段间转移
  • 软中断指令中断返回指令一定是段间转移

直接转移和间接转移

  • 直接转移:转移指令中直接给出转移目标地址的转移;
  • 间接转移:转移指令中给出包含转移目标地址的寄存器或者存储单元的转移;

需要注意无条件转移指令和过程调用指令集可以是直接转移也可以是间接转移。

无条件转移指令

无条件转移指令分为4种:

  • 段内直接转移
  • 段内间接转移
  • 段间直接转移
  • 段间间接转移

需要说明的是无条件转移指令均不影响标志寄存器的状态标志。

无条件段内直接转移
JMP LABEL;

标号LABEL表示要转移的目标位置(转移目的地)。

无条件段内直接转移的机器码构成如下:

操作码OP  地址差rel

地址差rel实际上是LABEL所指定的指令的地址偏移与紧跟JMP指令的下一条指令的地址偏移之间的差值,rel可正可负,这样才可以实现前后的跳转。地址差rel可以用一个字节表示,也可用4个字节或2字节表示,如果只用一个字节表示,就称之为短(short)转移,否则称为近(near)转移。一般如果当汇编器汇编到某条转移指令时可以计算出地址差rel,汇编器会自动判断出应该用1字节表示rel还是4字节或2字节,否则汇编器会使用较多的位数来表示地址差。所以,当程序员在写程序时能顾及出用8位就可以表示出地址差,那么可以在标号前加一个汇编器操作符**“SHORT”**来指定用一个字节表示地址差,表示转移的目的地就在附近。

无条件段内间接转移
JMP OPRD

OPRD是32位通用寄存器或者双字存储单元,比如:

JMP ECX;
JMP DWORD PTR [EBX];
无条件段间转移指令JMP

段间转移指令和段内转移指令差不多,只是涉及到改变代码段寄存器CS的内容,情况较为复杂,在之后的文章中介绍。

条件转移指令

条件转移指令在前一篇文章已经介绍,这里不再赘述,只是需要特别明确一下rel偏移量的概念。

多路分支的实现

多路分支实际上指的就是switch-case的汇编实现,这里的实现原理主要是通过无条件间接转移指令和目标地址表来实现多路分支,举个例子:

考虑一下多路分支程序:

int  cf319(int x,  int operation)
{int  y;//多路分支switch ( operation ) {case 1:y = 3*x;break;case 2:y = 5*x 6;break;case 4:case 5:y = x*x ;break;case 8:y = x*x 4*x;break;default:y = x ;}if ( y > 1000 )y = 1000;return  y;
}

上面的程序为了更加具有一般性,刻意没有安排连续的case值,其汇编的实现如下:

 push  ebpmov   ebp, esp; switch ( operation ) {mov   eax, DWORD PTR [ebp 12]  ;取得参数operation(case值)dec   eax                      ;从0开始计算,所以先减去1cmp   eax, 7                   ;从0开始计算,最多就是7ja    SHORT LN2cf319           ;超过,则转default(LN2cf319对应defalut处理语句);jmp   DWORD  PTR  LN12cf319[ eax*4 ]     ;这句是关键,这里实现了多路分支;LN12cf319:                           ;多向分支目标地址表DD    LN6cf319                   ; case 1DD    LN5cf319                   ; case 2DD    LN2cf319                   ; defaultDD    LN4cf319                   ; case 4DD    LN4cf319                   ; case 5DD    LN2cf319                   ; defaultDD    LN2cf319                   ; defaultDD    LN3cf319                   ; case 8

看上面的汇编实现,其核心思想是巧妙地运用了无条件段内间接转移和目标地址表,因为目标地址表每项占4个字节,所以跳转的地址是目标地址表的起始地址加case对象(这里是operation)乘以4,如果operation为0,跳到目标地址表的首地址,即就是LN6c,如果operation为1,跳到LN5c,以此类推就实现了多路分支的巧妙跳转。

使用该方法的一般建议是:当多路分支数超过5时,考虑无条件间接转移方式和目标地址表结合实现多路分支会更高效。

循环程序设计

循环指令

循环指令类似于条件转移指令,其采用的是段内相对转移的方式,是通过在指令指针寄存器EIP上加一个地址差的方式实现的转移,需要注意的是循环指令中的这个地址差只用了一个字节(8位)来表示,所以转移范围仅在-128-127之间。在保护方式(32位代码段)下,ECX作为循环计数器,实方式下,以CX为循环计数器,循环指令不影响各标志位。

指令 中文名 格式 解释 备注
LOOP 计数循环指令 LOOP LABEL 使ECX的值减1,当ECX的值不为0的时候跳转至LABEL,否则执行LOOP之后的语句
LOOPE 等于循环指令 LOOPE LABEL 使ECX的值减1,如果结果不等于0并且零标志ZF等于1(表示相等),那么就转移到LABEL,否则执行LOOPE之后的语句 ECX的减1并不影响标志位,ZF是否为1取决于循环指令之前指令对其的影响。
LOOPZ 零循环指令 LOOPZ LABEL 使ECX的值减1,如果结果不等于0并且零标志ZF等于1(表示相等),那么就转移到LABEL,否则执行LOOPZ之后的语句 ECX的减1并不影响标志位,ZF是否为1取决于循环指令之前指令对其的影响。
LOOPNE 不等于循环指令 LOOPE LABEL 使ECX的值减1,如果结果不等于0并且零标志ZF等于0(表示不相等),那么就转移到LABEL,否则执行LOOPNE之后的语句 ECX的减1并不影响标志位,ZF是否为1取决于循环指令之前指令对其的影响。
LOOPNZ 非零循环指令 LOOPNZ LABEL 使ECX的值减1,如果结果不等于0并且零标志ZF等于0(表示不相等),那么9就转移到LABEL,否则执行LOOPNZ之后的语句 ECX的减1并不影响标志位,ZF是否为1取决于循环指令之前指令对其的影响。
JECXZ 计数转移指令 JECXZ LABEL 当寄存器ECX的值为0时转移到LABEL,否则顺序执行 注意与LOOP的关系是JECXZ是直接判断ECX,没有先减ECX
通常在循环开始之前使用该指令,所以循环次数为0时,就可以跳过循环体

子程序设计

调用约定

  • -_cdecl被称为 C 调用约定。缺省调用约定。参数按照从右至左的顺序入堆栈,函数本身不清理堆栈。

  • _stdcall被称为 pascal 调用约定。参数按照从右至左的顺序入堆栈,函数自身清理堆栈。

  • _fastcall 是快速调用约定。通过 寄存器传递参数。前两个参数由 ECXEDX 传送,其他参数按照从右至左的顺序入堆栈,函数自身清理堆栈。

汇编总结(3)——程序设计初步相关推荐

  1. 【微机原理与接口技术学习实践】汇编语言程序设计初步——debug编写调试指令序列

    halo~我是bay_Tong桐小白 本文内容是桐小白个人对所学知识进行的总结和分享,知识点会不定期进行编辑更新和完善,了解最近更新内容可参看更新日志,欢迎各位大神留言.指点 [微机原理与接口技术学习 ...

  2. C语言及程序设计初步例程-33 三种循环语句

    贺老师教学链接  C语言及程序设计初步 本课讲解 问题:求1+2+3+-+100 用while语句实现的程序 #include <stdio.h> int main() {int i=1, ...

  3. C语言及程序设计初步例程-37 循环嵌套

    贺老师教学链接  C语言及程序设计初步 本课讲解 求  #include <stdio.h> int main() {int sum=0, i=1, t;while (i<=6){t ...

  4. C语言及程序设计初步例程-42 将数据输出到文本文件

    贺老师教学链接  C语言及程序设计初步 本课讲解 文件的操作步骤 #include <stdio.h> #include <stdlib.h> int main() {FILE ...

  5. 龚本灿c语言程序设计,c语言程序设计初步-求索学堂.ppt

    c语言程序设计初步-求索学堂 C语言程序设计 主讲人 龚本灿 个人联系方式 QQ: 190026892 网上答疑.教学资料及作业地址: 三峡大学网站->求索学堂->统一身份认证后-> ...

  6. C语言将0xea转换为字符ea,eA第10章 c语言程序设计初步.ppt

    eA第10章 c语言程序设计初步 第10章 C语言程序设计初步 1. C语言的字符集 2. 标识符: 3. 关键字 4. 运算符 5. 分隔符 6. 注释符 标识符练习 1.下列选项中,可以作为C语言 ...

  7. C语言及程序设计初步例程-4 C语言程序初体验

    贺老师教学链接   C语言及程序设计初步 本课讲解 让程序会计算:求a和b两个数之和 #include <stdio.h> int main( ) {int a,b,sum;scanf(& ...

  8. c++实训案列教程单元一程序设计初步-知识点提问

    <c++实训案列教程>单元一程序设计初步 知识点提问: 什么是计算机的主机和外设?常见的外设有那些?答:系统外部的设备叫外设,主机是指计算机除去输入输出设备以外的主要机体部分--通常包括C ...

  9. 视频教程-C语言及程序设计初步-C/C++

    C语言及程序设计初步 烟台大学计算机学院教师,二十年余教师生涯,看出了在错综复杂的教育环境中,坚持教育教学的价值与前景.和学生并肩,与不良学风作斗争,为IT菜鸟建跑道,让大一的孩子会编程,为迷茫的大学 ...

最新文章

  1. php简单的log文件
  2. linux修改windows注册表,妙招:让修改的注册表立即生效的几种方法
  3. [置顶] Oracle数据操作和控制语言详解
  4. ECommon.Dapper 轻量级的dapper扩展
  5. 解决smtp出现(500, b'Error: bad syntax')
  6. java uml下划线_Java UML类图
  7. java foxpro,在Visual FoxPro中更新顶部
  8. 手机怎么提高图片分辨率?手机怎么改照片分辨率dpi?
  9. 外贸找客户软件:Yellow Page Spider 8.7.1 Crack
  10. 任务16:16_Flume案例_断点续传文件(案例测试)
  11. 货币政策市场化协同与大数据机制研究
  12. 投资 - 出口 - 消费
  13. 赛门铁克SSL证书chrome不支持解决方法
  14. offsetParent、offsetTop、offsetLeft、offsetHeight、offsetWidth
  15. 关于STM32与OpenMv通讯踩过的那些坑(1)
  16. MySQL--函数--使用/示例
  17. R语言统计入门第六章——回归与相关性
  18. Google人脸识别系统Facenet paper解析
  19. php5.5 反序列化利用工具_利用Python反序列化运行加载器实现免杀
  20. Photoshop2020 安装失败FATAL: Error (Code = 146)

热门文章

  1. Android-ubuntu配置
  2. echarts 动态数据动画效果
  3. 腾讯AI Lab发布智能创作助手「文涌 (Effidit)」,用技术助力「文思泉涌」
  4. html 微信登陆,登录包含微信登录.html
  5. linux有没有处理器漏洞,【图片】为什么linux mint上cpu漏洞直到现在也没完全修复?_linux吧_百度贴吧...
  6. 自律的程序员生活是什么样的?
  7. 熊分享-负重前行有我陪伴!
  8. IDE(ATA)硬盘,SATA硬盘,SCSI硬盘和SAS硬盘的比较
  9. mac运行zookeeper的zkServer.sh,显示FAILED TO WRITE PID
  10. java 定时器 倒计时_定时器倒计时demo