• [转]汇编语言的准备知识给初次接触汇编者 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 则相反。

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

  • 相关阅读:
    第一章 zookeeper基础概念
    ntp服务
    nfs与dhcp服务
    samba、ftp和ssh服务
    Linux系统的RAID磁盘阵列
    Linux系统中lvm简介
    Linux系统中的计划任务
    Linux权限管理
    Linux用户及组管理
    简谈OSI七层模型(网络层)
  • 原文地址:https://www.cnblogs.com/yahue/p/2575379.html
Copyright © 2020-2023  润新知