zoukankan      html  css  js  c++  java
  • [转]汇编语言的准备知识给初次接触汇编者 3

    “汇编语言”作为一门语言,对应于高级语言的编译器,我们需要一个“汇编器”来把汇编语言
    原文件汇编成机器可执行的代码。高级的汇编器如MASM, TASM 等等为我们写汇编程序
    提供了很多类似于高级语言的特征,比如结构化、抽象等。在这样的环境中编写的汇编程
    序,有很大一部分是面向汇编器的伪指令,已经类同于高级语言。现在的汇编环境已经如
    此高级,即使全部用汇编语言来编写windows 的应用程序也是可行的,但这不是汇编语言
    的长处。汇编语言的长处在于编写高效且需要对机器硬件精确控制的程序。而且我想这里
    的人学习汇编的目的多半是为了在破解时看懂反汇编代码,很少有人真的要拿汇编语言编
    程序吧?(汗......)
    好了,言归正传。大多数汇编语言书都是面向汇编语言编程的,我的帖是面向机器和
    反汇编的,希望能起到相辅相成的作用。有了前面两篇的基础,汇编语言书上对大多数指
    令的介绍应该能够看懂、理解了。这里再讲一讲一些常见而操作比较复杂的指令。我这里
    讲的都是机器的硬指令,不针对任何汇编器。
    无条件转移指令jmp:
    这种跳转指令有三种方式:短(short),近(near)和远(far)。短是指要跳至的目标地址与
    当前地址前后相差不超过128 字节。近是指跳转的目标地址与当前地址在用一个段内,即
    CS 的值不变,只改变EIP 的值。远指跳到另一个代码段去执行,CS/EIP 都要改变。短和
    近在编码上有所不同,在汇编指令中一般很少显式指定,只要写 jmp 目标地址,几乎任何
    汇编器都会根据目标地址的距离采用适当的编码。远转移在32 位系统中很少见到,原因
    前面已经讲过,由于有足够的线性空间,一个程序很少需要两个代码段,就连用到的系统
    模块也被映射到同一个地址空间。
    jmp 的操作数自然是目标地址,这个指令支持直接寻址和间接寻址。间接寻址又可分
    为寄存器间接寻址和内存间接寻址。举例如下(32 位系统):
    jmp 8E347D60 ;直接寻址段内跳转
    jmp EBX ;寄存器间接寻址:只能段内跳转
    jmp dword ptr [EBX] ;内存间接寻址,段内跳转
    jmp dword ptr [00903DEC] ;同上
    jmp fward ptr [00903DF0] ;内存间接寻址,段间跳转
    解释:
    在32 位系统中,完整目标地址由16 位段选择子和32 位偏移量组成。因为寄存器的
    宽度是32 位,因此寄存器间接寻址只能给出32 位偏移量,所以只能是段内近转移。在内
    存间接寻址时,指令后面是方括号内的有效地址,在这个地址上存放跳转的目标地址。比
    如,在[00903DEC]处有如下数据:7C 82 59 00 A7 01 85 65 9F 01
    内存字节是连续存放的,如何确定取多少作为目标地址呢?dword ptr 指明该有效地址
    指明的是双字,所以取
    0059827C 作段内跳转。反之,fward ptr 指明后面的有效地址是指向48 位完全地
    址,所以取19F:658501A7 做远跳转。
    注意:在保护模式下,如果段间转移涉及优先级的变化,则有一系列复杂的保护检
    查,现在可不加理会。将来等各位功力提升以后可以自己去学习。
    条件转移指令jxx:只能作段内转移,且只支持直接寻址。
    =========================================
    调用指令CALL:
    Call 的寻址方式与jmp 基本相同,但为了从子程序返回,该指令在跳转以前会把紧接
    着它的下一条指令的地址压进堆栈。如果是段内调用(目标地址是32 位偏移量),则压
    入的也只是一个偏移量。如果是段间调用(目标地址是48 位全地址),则也压入下一条
    指令的完全地址。同样,如果段间转移涉及优先级的变化,则有一系列复杂的保护检查。
    与之对应retn/retf 指令则从子程序返回。它从堆栈上取得返回地址(是call 指令压进
    去的)并跳到该地址执行。retn 取32 位偏移量作段内返回,retf 取48 位全地址作段间返
    回。retn/f 还可以跟一个立即数作为操作数,该数实际上是从堆栈上传给子程序的参数的
    个数(以字计)返回后自动把堆栈指针esp 加上指定的数*2,从而丢弃堆栈中的参数。这
    里具体的细节留待下一篇讲述。
    虽然call 和ret 设计为一起工作,但它们之间没有必然的联系。就是说,如果你直接
    用push 指令向堆栈中压入一个数,然后执行ret,他同样会把你压入的数作为返回地址,
    而跳到那里去执行。这种非正常的流程转移可以被用作反跟踪手段。
    ==========================================
    中断指令INT n
    在保护模式下,这个指令必定会被操作系统截获。在一般的PE 程序中,这个指令已
    经不太见到了,而在DOS 时代,中断是调用操作系统和BIOS 的重要途径。现在的程序可
    以文质彬彬地用名字来调用windows 功能,如 call user32!getwindowtexta。从程序角度
    看,INT 指令把当前的标志寄存器先压入堆栈,然后把下一条指令的完全地址也压入堆
    栈,最后根据操作数n 来检索“中断描述符表”,试图转移到相应的中断服务程序去执行。
    通常,中断服务程序都是操作系统的核心代码,必然会涉及到优先级转换和保护性检查、
    堆栈切换等等,细节可以看一些高级的教程。
    与之相应的中断返回指令IRET 做相反的操作。它从堆栈上取得返回地址,并用来设
    置CS:EIP,然后从堆栈中弹出标志寄存器。注意,堆栈上的标志寄存器值可能已经被中断
    服务程序所改变,通常是进位标志C, 用来表示功能是否正常完成。同样的,IRET 也不一
    定非要和INT 指令对应,你可以自己在堆栈上压入标志和地址,然后执行IRET 来实现流
    程转移。实际上,多任务操作系统常用此伎俩来实现任务转换。
    广义的中断是一个很大的话题,有兴趣可以去查阅系统设计的书籍。
    ============================================
    装入全指针指令LDS,LES,LFS,LGS,LSS
    这些指令有两个操作数。第一个是一个通用寄存器,第二个操作数是一个有效地址。
    指令从该地址取得48 位全指针,将选择符装入相应的段寄存器,而将32 位偏移量装入指
    定的通用寄存器。注意在内存中,指针的存放形式总是32 位偏移量在前面,16 位选择符
    在后面。装入指针以后,就可以用DS:[ESI]这样的形式来访问指针指向的数据了。
    ============================================
    字符串操作指令
    这里包括CMPS,SCAS,LODS,STOS,MOVS,INS 和OUTS 等。这些指令有一个共同
    的特点,就是没有显式的操作数,而由硬件规定使用DS:[ESI]指向源字符串,用ES:[EDI]
    指向目的字符串,用AL/AX/EAX 做暂存。这是硬件规定的,所以在使用这些指令之前一
    定要设好相应的指针。
    这里每一个指令都有3 种宽度形式,如CMPSB(字节比较)、CMPSW(字比较)、
    CMPSD(双字比较)等。
    CMPSB:比较源字符串和目标字符串的第一个字符。若相等则Z 标志置1。若不等则
    Z 标志置0。指令执行完后,ESI 和EDI 都自动加1,指向源/目标串的下一个字符。如果
    用CMPSW,则比较一个字,ESI/EDI 自动加2 以指向下一个字。
    如果用CMPSD,则比较一个双字,ESI/EDI 自动加4 以指向下一个双字。(在这一点
    上这些指令都一样,不再赘述)
    SCAB/W/D 把AL/AX/EAX 中的数值与目标串中的一个字符/字/双字比较。
    LODSB/W/D 把源字符串中的一个字符/字/双字送入AL/AX/EAX
    STOSB/W/D 把AL/AX/EAX 中的直送入目标字符串中
    MOVSB/W/D 把源字符串中的字符/字/双字复制到目标字符串
    INSB/W/D 从指定的端口读入字符/字/双字到目标字符串中,端口号码由DX 寄存器指
    定。
    OUTSB/W/D 把源字符串中的字符/字/双字送到指定的端口,端口号码由DX 寄存器指
    定。
    串操作指令经常和重复前缀REP 和循环指令LOOP 结合使用以完成对整个字符串的
    操作。而REP 前缀和LOOP 指令都有硬件规定用ECX 做循环计数器。举例:
    LDS ESI,SRC_STR_PTR
    LES EDI,DST_STR_PTR
    MOV ECX,200
    REP MOVSD
    上面的代码从SRC_STR 拷贝200 个双字到DST_STR. 细节是:REP 前缀先检查
    ECX 是否为0,若否则执行一次MOVSD,ECX 自动减1,然后执行第二轮检查、执行......
    直到发现ECX=0 便不再执行MOVSD,结束重复而执行下面的指令。
    LDS ESI,SRC_STR_PTR
    MOV ECX,100
    LOOP1:
    LODSW
    .... (deal with value in AX)
    LOOP LOOP1
    .....
    从SRC_STR 处理100 个字。同样,LOOP 指令先判断ECX 是否为零,来决定是否
    循环。每循环一轮ECX 自动减1。
    REP 和LOOP 都可以加上条件,变成REPZ/REPNZ 和 LOOPZ/LOOPNZ. 这是除了
    ECX 外,还用检查零标志Z. REPZ 和LOOPZ 在Z 为1 时继续循环,否则退出循环,即
    使ECX 不为0。REPNZ/LOOPNZ 则相反。

    备注:忘记是在哪里收集的,没能注明原出处,若读者知道还请指出,谢谢

  • 相关阅读:
    Java 第十一届 蓝桥杯 省模拟赛 梅花桩
    Java 第十一届 蓝桥杯 省模拟赛 梅花桩
    Java 第十一届 蓝桥杯 省模拟赛 梅花桩
    Java 第十一届 蓝桥杯 省模拟赛 元音字母辅音字母的数量
    Java 第十一届 蓝桥杯 省模拟赛 元音字母辅音字母的数量
    Java 第十一届 蓝桥杯 省模拟赛 元音字母辅音字母的数量
    Java 第十一届 蓝桥杯 省模拟赛 最大的元素距离
    Java 第十一届 蓝桥杯 省模拟赛 递增序列
    Java 第十一届 蓝桥杯 省模拟赛 递增序列
    Java 第十一届 蓝桥杯 省模拟赛 最大的元素距离
  • 原文地址:https://www.cnblogs.com/yahue/p/2575379.html
Copyright © 2011-2022 走看看