16位汇编第八讲指令第四讲

2018-01-05 17:48:33 浏览数 (1)

        16位汇编第八讲指令第四讲

一丶串操作类指令

1.什么是串操作?

  1.串操作指令是8086指令系统中比较独特的一类指令,采用比较特殊的数据串寻址方式,在操作主存连续区域

的数据是,特别好用.因而比较常用

简而言之,就是内存中的一段数据,拷贝/读取/修改... 到另一块另内存

重点掌握  MOVS  STOS  LODS CMPS SCAS REP

2.串操作的简介

  1.串操作指令的操作数,是驻村中连续存放的数据串(String 注意string表示串的意思)--也就是一段数据在内存中

是连续的,以字或者字节排列

  2.串操作指令的操作对象是以字(Word)为单位的字串,或者字节(Byte)为单位的字节串--简单理解就是我要操作

1个字节大小,还是两个字节大小

3.串的操作

  串的操作一般使用 SI(源寄存器)和 DI(目的寄存器),可以使用端超越DS:[SI]

  或者目的寄存器 DI  ES:[DI]

每次执行一次串操作,那么SI和DI则会自动修改,地址自增

当然地址自增是看方向标志的  DF  我们要想复制的时候,让其内存自增复制,那么就用CLD让方向初始为自增

否则 反向复制的话就用STD指令让地址自减

4.串操作指令中的串传送指令(MOVS)

作用:

  把字节或者字操作数从内存的源地址,传送到目的地址

分别有两个指令,一个是字节串传送,一个是字传送

MOVSB (后缀b代表byte的意思)  MOVSW(后缀w代表字的意思)

指令:

  MOVSB    ES:[DI] <- DS:[SI]  从si内存中取出一个字节的数据,复制到DI中

    SI<-SI±1,DI<-DI±1  地址随着方向标志自增1或者自减1

  MOVSW    ES:[DI] <- DS:[SI]  从si内存中取出一个字的数据,复制到DI中

    SI<-SI±2,DI<-DI±2  地址随着方向标志自增2或者自减2

 使用例子:

主要是两个花红框的地方,意思是什么,第一个画红框的内容

1.定义一个源地址,里面的内容是HELLO  也就是内存的内容是Hello

2.定义一个目标地址,里面的内容是申请了100个字节,然后初始化为零

第二个画红框的内容

1.首先给si设置偏移这里使用lea设置的, 我们也可以写成   mov si,offset g_szSrc,下面的目的也是一样

2.给目的设置偏移

3.使用movsb,从si内从中取出一个字节,设置到目的内存中

可以看出,方向标志位默认是往下自增的,我并没有使用CLD设置,所以SI 和DI复制完成后,内存就会自增

 会C语言的请看这条注释 : 在C语言中,这个命令就相当于 memcpy(内存拷贝)每次拷贝几个字节,不懂汇编的滤过

当然我们可以使用 MOVSW 只不过一次拷贝两个字节了.具体的自己手工尝试.

 5.串操作指令中的串存储指令STOS(store string)

作用: 把AL或者AX数据传送到目的地址

C语言的请看: 在C语言中,这条命令相当于 memset,清空的作用

这个就简单了,把寄存器的数据,传送到目的寄存器中,也就是 ES:[DI]中

ES:[DI]<-AL/AX

对于这里我说一下ES 和DS 上面我们使用的时候,并没有使用段超越指令,为什么,因为我们的DI 和SI都是数据区了,

也就是说不用使用段超越了,否则你需要自己加,一般来说,ES和DS两个段都是合并到一起的

STOSB,STOSW

分别对应两条指令

    STOSB 字节串存储  ES[DI]<-AL,把AL的数据,放到DI中  

      按照方向标志,DI<-DI±1  自增或者自减1

    STOSW 字串存储: ES:[DI]<-Ax ,把AX中的数据,放到DI中

      按照方向标志,DI<-DI±2  自增或者自减2

使用例子,字节串存储

第一框,代码是给al一个3,然后通过段存储的命令,把3给DI存储

反汇编单步调试,已经完成一个字节保存了,现在看下DI 06的偏移处是不是3

是3,完成了操作

STOSW 则是把AX中的数据,放到DI中,放两个字节,也就是一个字,因为AX是16位寄存器.

具体自己测试即可.

6.串操作指令,串读取LODS(load string)

作用:

   把指定的主存单元的数据传送给AL,或者AX

看到这里已经明白了吧,上面的是吧AL,或者AX中的内容存储到DI中,这个则是读取到DI中,唯一不同的则是从源寄存器中读取(也就是SI)

LODSB  字节串读取  AL<-DS:[SI]  按照方向标志,自增或者自减1  SI<-SI±1

LODSW 字读取         AX<-DS:[SI]  按照方向标志,自增或者自减2  SI<-SI±2

使用例子:

现在可以看到AX中的值是0b46,而SI中的偏移加上段地址所在的物理地址是hello

我们使用了 字读取,也就是读取两个字节,也就是说AX中的值会变为  he 这两个字符的ASCII码

具体的字节读取,自己测试便知道了

 7.串操作命令,串比较CMPS(compare string)

作用:

  讲主存中的源操作数减去目的操作数,然后设置标志位,进位比较两个操作数之间的关系

简而言之:

  意思就是  源内存中的数(可以理解为数,也可能是字符串的ASCII码) 减去 目的内存中的数,根据结果设置标志位

比如  源内存 的数字是  1  目的内存中  也是1  那么 1 -1 就为0,0是结果,根据结果设置一下标志位  则ZF = 1,也就是源和目的相等

C语言的请看:  相当于memcmp(内存比较) 不懂C语言的滤过

也有两个比较,分别是字比较,还有字节比较

字节比较:  CMPSB  DS:[SI]-ES:[DI]     根据方向的标志自增或者自减1  SI<-SI±1,DI<-DI±1

字比较:      CMPSB  DS:[SI]-ES:[DI]     根据方向的标志自增或者自减2 SI<-SI±2,DI<-DI±2

使用例子,字节比较

看下反汇编代码,然后看下标志位

结果相当,则ZF位为1,如果hello 五个字节都要比较,则来五次即可. (下面还有重复前缀指令,暂时是来五次)

具体的字比较,自己测试便知道

8.串操作指令,串扫描SCAS(Scan String)

作用:

  将AL/AX减去目的操作数,以便设置标志,进位比较AL/AX与操作数的关系

简而言之:

  就是你要搜索的字节,放到AL,或者AX中,然后会和DI去比较,然后根据结果设置标志

C语言的请看: 在C语言中,这个相当于 memchr命令

SCASB       AL-ES:[DI]  根据方向标志自增或者自减1  DI<-±1

SCASW      AX-ES:[DI]  根据方向标志自增或者自减2  DI<-±2

使用例子:

现在是68 - 去内存中DI中的值,也是68

设置标志位,AL中寄存器的值减去DI中的值,根据结果设置标志位,因为68-68结果就是0,所以对应的零标志位置位了

有可能68-69,就是负数了,所以CF位会置位

 二丶重复前缀指令(repeat)

1.什么是重复前缀指令?

  简单理解为就是为串操作单独提供的循环指令,比如我们上面要用MOVS串操作指令的时候,把源寄存器中的值,拷贝到目的寄存器中,假设我们有个HELLO 我们要使用五次 MOVSB命令才能拷贝过去

现在提供了一个为串操作的循环

重复前缀分为两类,3条指令

1.配合使用,MOVS,STOS,LODS的时候,使用指令REP前缀,不影响标志

2.配合使用 CMPS,SCAS,指令,使用REPZ和REPNZ前缀

然后重复次数放到cx寄存器中

例如

代码语言:javascript复制
mov cx,3
rep movsb 

代表我要重复三次,movsb的指令,注意,根据方向标志,每次自增或者自减1,因为你使用的movsb

REP指令的作用就是 当CX ≠ 0的时候,继续传送

REPZ,和REPNZ解析

REPZ:

  1.每次执行串指令,则CX-1

  2.判断零标志位(ZF)是否为零 (也就是不想等)

  3.如果CX == 0,或者 ZF == 0 (不想等)则重复执行结束

注意可以简单理解为,    cx !=0 并且  zf == 1 的时候继续循环

REPNZ:

       1.每次执行串指令,则CX-1

  2.判断零标志位(ZF)是否为1 (也就是想等)

  3.如果CX == 0,或者 ZF ==1 (想等)则重复执行结束

 REPNZ/REPNE的前缀,可以简单理解为   cx != 0  && zf ==0 的时候继续

使用就很简单了,上次我们要拷贝的时候必须给五次,这次直接给CX一个值,然后利用前缀指令即可.

 三丶控制转移指令

控制转移指令,用于实现分支,循环,过程等程序的接口,是仅次于传送指令的常用指令

一般来说控制转移指令都是修改IP的偏移或者CS段寄存器的值,让代码跳转(坏处: 断CPU的流水线)实现

程序的执行顺序的改变

①丶无条件转移指令

  无条件转移指令,就是说你给一个偏移,可以无条件的跳转到其地放执行

JMP指令(无条件转移指令)

JMP指令有2个种类,4个类型

1.段内转移

2.段间转移

  段内转移:

    段内转移,就是在一个段中的跳转

又分为短转移,和近转移

短转移(short): 短转移就是一次只能跳一个字节的大小,也就是 ±127

近转移(near): 近转移则是能跳转 -32767 -> 327678大小

例子:

代码语言:javascript复制
JMP SHORT 地址/标号

跳转到地址或者标号的地方

近转移

对于近转移来说,机器码是一个字节,E9是操作码,而0100则是偏移,就是说你跳转的偏移的大小是多少

段间转移

   段间转移,则是去另一个段去跳转

代码语言:javascript复制
JMP FAR PTR  地址,标号

例子:

对于段间转移,则是 段地址:偏移 的形式去转移的,CS的值会修改为段地址,IP会修改为偏移

注意二进制机器码,也是这样的,这个可以自己修改的,因为让代码JMP到自己的代码,然后在JMP回去,就实现了HOOK指令

JMP有两种寻址指令

直接寻址方式

代码语言:javascript复制
JMP AX

地址放在寄存器当中

间接寻址方式

代码语言:javascript复制
JMP  word ptr [BX]  ;需要指明一下

总结

短转移:  使用 short 关键字 例如  jmp short 标号/地址偏移

近转移: 使用  near ptr  例如 jmp near ptr 标号/地址偏移

远转移:  使用 far ptr    例如 jmp  far ptr 标号/地址偏移

然后近转移和短转移,可以直接使用jmp,在编译器中,会自动选择相应的方式去转移

四丶条件转移

 上面说的都是强制转移,现在我们需要使用条件转移指令,比如根据标志位去转移

JCC  (JCC是一个比喻,并没有这条命令,可以吧CC看做任何条件)

JCC  标号

CC条件成立则跳转到标号,否则程序继续顺序执行下一条指令

操作数标号,使用短转移,成为相对寻址方式

其中CC是一张表组成

看到这张表很难记,有个小窍门

1.  z  zero(零的意思)/E  (equal)

  如果为z则是位零的意思,为e则是相等的意思

比如

  jz/je  等于零,或者相等,其中判断的是ZF位

N表示取反的意思

比如

  JS  SF = 1 表示符号为负

  JNS 不看也知道SF = 0 符号位正

其中每个标志位都有一个  判断是否相等或者不想等

比如 CF位

  jC  那么代表CF =1 jNC 那么表示CF = 0

对于上面还不够,因为我们还要判断是否 大于,小于 高于,低于 (高于,低于 是用于无符号的判断,大于小于则是用于符号的判断)

G  有符号的 大于判断

L   有符号的小于判断

A  无符号的高于判断

B 无符号的低于判断

比如,我要判断CF位小于0   JB

如果是CF大于零,则  JNB (不低于) JAE (A是高于 高于等于)

 作业以及资料下载链接: 链接:http://pan.baidu.com/s/1boQijU3 密码:nr3a

0 人点赞