这只是一个学习笔记,难免会有些杂乱,记录的都是一些汇编中有关String的要点。
1、在汇编语言看来,只要是在内存中连续的存储单元(byte)中存储的东西都叫做“String”,而不管这些东西是不是人可以识别的字符。
2、汇编语言的String是你通过在R额Register中设定相应的值来指定的。比如在EAX中指定String的首地址,在ECX中指定String的长度。
3、String分为Source String和Destination String。Source String就是你读入的String,Destination String就是你要写入的String。它们的区别仅是寄存器的不同
· Source String使用ESI(Extend Source Index)作为地址寄存器,使用ECX表示它的长度。
· Destination String使用EDI(Extend Destination Index)指向它的地址,同样使用ECX表示它的长度。
· Data coming from a source string or going to a destination string must begin the trip from, end the trip at, or pass through register EAX.
4、STOSB
这个指令是STOre String by Byte的助记法。它的功能是把某个byte书写到Destination String中多少次。
它使用以下几个寄存器:
· EDI 必须指向Destination String。
· ECX 中存放需要写入的次数。需要写入的东西在AL中。
· AL 中存放想要在String中存放的数值。
这时候,如果执行STOSB指令,将会执行下面的两个操作:
· AL 中存放的一个byte的值被放到EDI所指向的内存中;
· EDI 加1,这样它就指向了下一个可以写入的单元(byte)。
这时的ECX(其中存放着“次数”)不会自动减1,需要你自己来设定(使用DEC和LOOP),或者使用下面的命令:
rep stosb
REP是一个指令前缀,表明CPU应该以什么样的态度对待它之后那个指令(这里是STOSB)
在这个指令中,还有一个非常重要的地方,就是DF标记位(Direction Flag)。当DF为1时,对于内存的访问是从大到小的(地址),如果DF为0,那么就从小到大。一般都是从小到大。
有两个指令可以设定DF的值,一个是CLD,一个是STD。前者把DF设为0,后者把DF设为1。即前者为从小到大查找(STOSB中的EDI每次增加1),后者从大到下查找(STOSB中的EDI每次减1)。POPF也会影响DF的值。
还有几个与STOS相关的指令:
· STOSB:STOre String by Byte,存储8-bits的数据(AL),EDI每次移动1个byte(根据DF确定方向)
· STOSW:STOre String by Word,存储16-bits的数据(AX),EDI每次移动2个byte(根据DF确定方向)
· STOSD:STOre String by Double,存储32-bits的数据(EAX),EDI每次移动4个byte(根据DF确定方向)
在它们之前使用 REP,全都是对ECX减1。
5、LOOP
如果仅仅使用STOSB指令,那么仅能进行一次写入操作。需要一个循环,同时减少ECX的值,才能不断写入。这可以用两个指令实现:
DoChar: stosb ; Note that there’s no REP prefix!
add al,'1' ; Bump the character value in AL up by 1
aaa ; Adjust AX to make this a BCD addition
add al,'0' ; Basically, put binary 3 in AL’s high nybble
dec ecx ; Decrement the count by 1..
jnz DoChar ; ..and loop again if ECX > 0
最后的这两个指令也可以用一个指令代替,即LOOP:它首先把ECX减1,然后检查ZF标记位,看是否跳转。当ECX不为0时,跳转,否则就不跳转:
DoChar: stosb ; Note that there’s no REP prefix! add al,'1' ; Bump the character value in AL up by 1 aaa ; Adjust AX to make this a BCD addition add al,'0' ; Make sure we have binary 3 in AL’s high nybble loop DoChar ; Go back & do another char until ECX goes to 0
6、MOVSB
MOVS也有三种形式,都是把数据从 ESI 所指向的内存拷贝到 EDI 所指向的内存,拷贝的次数为 ECX ,根据 DF(Direction Flas) 来确定方向:
· MOVSB:MOVe String by Byte,每次操作8-bits(1个byte)的数据,每拷贝1个byte的数据ECX减一
· MOVSW:MOVe String by Word,每次操作16-bits(2个byte)的数据,每拷贝2个byte的数据ECX减一
· MOVSD:MOVe String by Double,每次操作32-bits(4个byte)的数据,每拷贝4个byte的数据ECX减一
在这些指令之前使用 REP ,可以把它们变成全自动机关枪,一直执行到 ECX 变成0。
7、SCASB
这个指令和上面两个指令的用法基本相似。它表示SCAn String by Byte,用来搜索字符串。
它设定以下几个寄存器:
· DF 设定为0或1,表明搜索的方向是uphill还是downhill。
· EDI 中存放需要搜索的字符串的地址。我感觉这里有些诡异,为什么不放在ESI中呢?
· AL 需要查找的东西放在AL中。
· ECX 最大查找的次数放在ECX中。
这里使用 REPNE SCASB 进行自动搜索。在搜索过程中,[EDI] 所指向的byte每次都和 AL 中的值进行比较,如果相等,则停止查找。如果不相等,那么 EDI 加1, ECX 减1,继续尝试下一个[EDI]。当搜索找到所要的值的时候,EDI 指向所要查找的 byte 的下一个 byte。它要停下来有两种情况,一是找到AL中的值,一是ECX为0。
这里的 REPNE 的意思是:Repeat SCASB as long as [EDI] does not equal AL.
如果使用 REPE 作为前缀:Repeat SCASB as long as [EDI] equals AL.
SCASB 会设定ZF的值,如果[EDI] 和 AL中的值相同(为0),那么ZF为1,表明找到AL中的值;如果不同(不为0),那么ZF为0,表明没有找到AL中的值。
· 这样,当使用REPNE时,ZF为1时停下。当使用REPE时,ZF为0时停下。
· 判断是否是ECX用完时(即找不到和AL相同的值,或全是和AL相同的值),REPNE 用 JNZ,REPE 用 JZ。