汇编学习——处理字符串

一、传送字符串

   1、MOVS指令

    创建MOVS指令是为了向程序员提供把字符串从一个内存位置传送到另一个内存位置的简单途

径。MOVS指令有3种格式:

  • MOVSB:传送单一字节
  • MOVSW:传送一个字(2字节)
  • MOVSL:传送一个双字(4字节)

      MOVS指令使用隐含的源和目的操作数。隐含的源操作数是ESI寄存器。它指向源字符串的内存

位置。隐含的目标操作数是EDI寄存器。它指向字符串要被复制到的目标内存位置。

       有两种方式加载ESI和EDI值。第一种是使用间接寻址:

movl $output, %edi

/*这条指令把output标签的32位内存位置传送给EDI寄存器*/

        另一种方式是LEA指令,加载一个对象的有效地址:

leal output, %edi

/*把output标签的32位内存位置加载到EDI寄存器中*/

 

.section .data
  value1:
    .ascii "This is a test string\n"

.section .bss
  .lcomm output, 23

.section .text
  .globl _start

_start:
  leal value1, %esi
  leal output, %edi
  movsb
  movsw
  movsl

  movl $1, %eax
  movl $0, %ebx
  int $0x80

      如果EFLAGS寄存器中的DF位被清零,那么每条MOVS指令执行之后ESI和EDI寄存器就会

递增。如果DF位被设置,那么每条MOVS指令执行之后ESI和EDI寄存器就会递减,可以使用下

面的命令:

  • CLD用于将DF标志清零
  • STD用于设置DF标志

       使用STD指令时,ESI和EDI寄存器在每一条MOVS指令执行之后递减,所以它们应该指向字

符串的末尾,而不是开头。

.section .data
  value1:
    .ascii "The is a test string\n"

.section .bss
  .lcomm output, 12

.section .text
  .globl _start

_start:
  leal value1+22, %esi
  leal output+22, %edi

  std
  movsb
  movsw
  movsl

  movl $1, %eax
  movl $0, %ebx
  int $0x80

     如果要复制大型字符串,可以把MOVSL指令放到循环中,通过ECX寄存器设置字符串长度来

进行控制。    

.section .data
  value1:
    .ascii "The is a test string\n"
  
.section .bss
  .lcomm output, 23

.section .text
  .globl _start

_start:
  leal value1, %esi
  leal output, %edi
  movl $23, %ecx
  cld
  
loop1:
  movsb
  loop loop1

  movl $1, %eax
  movl $0, %ebx
  int $0x80

     2、REP前缀

     REP指令的特殊之处在于它不执行什么操作。这条指令用于按照特定次数重复执行字符串指

令,由ECX寄存器中的值进行控制。这和使用循环类似,但是不需要额外的LOOP指令,REP指

令重复地执行紧跟在它后面的字符串指令,直到ECX寄存器中的值为零。这就是为什么称它为前

缀的原因。

     1)逐字节地传送字符串

.section .data
  value1:
    .ascii "The is a test string.\n"

.section .bss
  .lcomm output, 23

.section .text
  .globl _start

_start:
  leal value1, %esi
  leal output, %edi
  movl $23, %ecx
  cld
  rep movsb

  movl $1, %eax
  movl $0, %ebx
  int $0x80 

     2)逐块地传送字符串

     也可以使用MOVSW和MOVSL指令在每次迭代中传送1字节以上的数据。

.section .data
  value1:
    .ascii "The is a test string.\n"
  value2:
    .ascii "Oops"
  
.section .bss
  .lcomm output, 23

.section .text 
  .globl _start

_start:
  leal value1, %esi
  leal output, %edi
  movl $6, %ecx
  cld 
  rep movsl 

  movl $1, %eax
  movl $0, %ebx
  int $0x80

     3)传送大型字符串

.section .data
  string1:
    .asciz "This is a test of the conversion program!\n"
  length:
    .int 43
  divisor:
    .int 4
  
.section .bss
  .lcomm buffer, 43

.section .text
  .globl _start

_start:
  leal string1, %esi
  leal buffer, %edi
  movl length, %ecx
  shrl $2, %ecx
  
  cld 
  rep movsl
  movl length, %ecx
  andl $3, %ecx
  rep movsb

  movl $1, %eax
  movl $0, %ebx
  int $0x80

     4)按照相反的顺序传送字符串

     可以把DF标志设置为对字符串进行向后处理,按相反的方向在内存位置之间传送。

.section .data
  value1:
    .asciz "This is a test string\n"
  
.section .bss
  .lcomm output, 24

.section .text
  .globl _start

_start:
  leal value1+22, %esi
  leal output+22, %edi
  movl $23, %ecx
  std
  rep movsb

  movl $1, %eax
  movl $0, %ebx
  int $0x80

     3、其他REP指令

     除了监视ECX寄存器值之外,还有监视零标志(ZF)的状态的REP指令。

         

     REPE和REPZ指令是相同指令的同义词,REPNE和REPNZ指令是同义词。

二、存储和加载字符串

      1、LODS指令

       LODS指令用于把内存中的字符串传送到EAX寄存器中。也有三种不同的格式:

  • LODSB:把一个字节加载到AL寄存器中
  • LODSW:把一个字(2字节)加载到AX寄存器中
  • LODSL:把一个双字(4字节)加载到EAX寄存器中

        LODS指令使用ESI寄存器作为隐含的源操作数。ESI寄存器必须包含要加载的字符串所在的

内存位置。数据传送完成之后,LODS指令按照加载的数据的数量递增或递减(取决于DF标志状

态)ESI寄存器。

      2、STOS指令

        使用LODS指令把字符串值存到EAX寄存器之后,可以使用STOS指令把它存放

在另一个内存位置中。也有三种格式:

  • STOSB:存储AL寄存器中一个字节的数据
  • STOSW:存储AX寄存器中一个字(2字节)的数据
  • STOSL:存储EAX寄存器中一个双字(4个字节)的数据

       STOS指令使用EDI寄存器作为隐含的目标操作数。执行STOS指令时,它按照使

用的数据长度递增或者递减EDI寄存器的值

       STOS指令真正能够提供的方便是和REP指令一起使用,多次把一个字符串复制到大型字符

串值中的时候。

.section .data
  space:
    .ascii " "
  
.section .bss
  .lcomm buffer, 256

.section .text
  .globl _start

_start:
  leal space, %esi
  leal buffer, %edi
  movl $256, %ecx
  cld
  lodsb
  rep stosb
  
  movl $1, %eax
  movl $0, %ebx
  int $0x80

      3、构建自己的字符串函数

      STOS和LODS指令可以用于各种字符串操作。通过使ESI和EDI寄存器指向相同的字符串,

可以对字符串执行简单的操作。可以使用LODS指令遍历字符串,一次把一个字符加载到AL寄存

器中,对这个字符执行某些操作,然后使用STOS指令把新的字符加载回字符串中。

.section .data
  string1:
    .asciz "This is a TEST, of the conversion program!\n"
  length:
    .int 43

.section .text
  .globl _start

_start:
  leal string1, %esi #加载string1内存位置到ESI寄存器中
  movl %esi, %edi
  movl length, %ecx #加载字符串长度到ECX寄存器
  cld  #设置增址模式

loop1:
  lodsb #加载一个字符到AL寄存器
  cmpb $'a', %al #小于a的ASCII值(0x61)
  jl skip
  cmpb $'z', %al #大于z的ASCII值(0x7a)
  jg skip
  subb $0x20, %al #如果是小写字母,则减去0x20把它转换为大写字母

skip:
  stosb #加载EAX寄存器
  loop loop1
  
end:
  pushl $string1
  call printf
  addl $4, %esp
  pushl $0
  call exit

三、比较字符串

    1、CMPS指令

     CMPS指令系列用于比较字符串值,也有3种格式:

  • CMPSB:比较字节值
  • CMPSW:比较字(2字节)值
  • CMPSL:比较双字(4字节)值

      CMPS指令隐含的源和目标操作数的位置同样存储在ESI和EDI寄存器中,每次执

行CMPS指令时,根据DF标志的设置,ESI和EDI寄存器按照被比较的数据的长度递

增或者递减。

      CMPS指令从源字符串中减去目标字符串,并且适当地设置EFLAGS寄存器的进

位、符号、溢出、零、奇偶校验和辅助进位标志。CMPS指令执行之后,可以根据字

符串的值,使用一般的条件跳转到分支。

.section .data
  value1:
    .ascii "Test"
  value2:
    .ascii "Test"
  
.section .text
  .globl _start

_start:
  movl $1, %eax
  leal value1, %esi
  leal value2, %edi
  cld 
  cmpsl 
  je equal
  movl $1, %ebx
  int $0x80

equal:
  movl $0, %ebx
  int $0x80

    2、CMPS和REP一起使用

    REP指令不再两个重复过程之间检查标志的状态,它只关心ECX寄存器中的计数

器。解决方案是使用REP指令系列中的其他指令:REPE、REPNE、REPZ和

REPNZ。

.section .data
  value1:
    .ascii "This is a test of the CMPS instructions"
  value2:
    .ascii "This is a test of the CMPS Instructions"

.section .text
  .globl _start

_start:
  movl $1, %eax
  leal value1, %esi
  leal value2, %edi
  movl $39, %ecx
  cld
  repe cmpsb
  je equal
  movl %ecx, %ebx
  int $0x80

equal:
  movl $0, %ebx
  int $0x80

    3、字符串不等

     最常用于比较字符串的方法称为词典式顺序(lexicographical order)。这通常称

为字典顺序(dictionary order),因为这是字典对词进行排序的标准。基本规则如

下:

  • 按字母表顺序、较低的字母小于较高的字母
  • 大写字母小于小写字母

      比较不同长度的字符串时,按照长度短一些的字符串中的字符数量进行比较。如

果短字符串大于长字符串中相同数量的字符,那么短字符串就大于长字符串。如果短

字符串小于长字符串中相同数量的字符,那么短字符串就小于长字符串。如果短字符

串等于长字符串中相同数量的字符,那么长字符串就大于短字符串。

       使用这一规则,下面的例子就为真:

  • “test”大于“boomerang”
  • “test”小于“velocity”
  • “test”小于“test1”

      例子如下:

.section .data
  string1:
    .ascii "test"
  length1:
    .int 4
  string2:
    .ascii "test1"
  length2:
    .int 5

.section .text
  .globl _start

_start:
  leal string1, %esi
  leal string2, %edi
  movl length1, %eax
  movl length2, %ecx
  ja longer
  xchg %ecx, %eax
  
longer:
  cld
  repe cmpsb
  je equal
  jg greater
  
less:
  movl $1, %eax
  movl $255, %ebx
  int $0x80

greater:
  movl $1, %eax
  movl $1, %ebx
  int $0x80

equal:
  movl length1, %ecx
  movl length2, %eax
  cmpl %ecx, %eax
  jg greater
  jl less
  movl $1, %eax
  movl $0, %ebx
  int $0x80

四、扫描字符串

   1、SCAS指令

    SCAS指令系列用于扫描字符串搜索一个或者多个字符。也有三个版本:

  •  SCASB:比较内存中的一个字节和AL寄存器的值
  •  SCASW:比较内存中的一个字和AX寄存器的值
  •  SCASL:比较内存中的一个双字和EAX寄存器的值

     SCAS指令使用EDI寄存器作为隐含的目标操作数。EDI寄存器必须包含要扫描的

字符串的内存地址。

  • REPE:扫描字符串的字符,查找不匹配搜索字符的字符
  • REPNE:扫描字符串的字符,查找匹配搜索匹配搜索字符的字符
.section .data
  string1:
    .ascii "This is a test - a long text string to scan."
  length:
    .int 44
  string2:
    .ascii "-"

.section .text
  .globl _start

_start:
  leal string1, %edi
  leal string2, %esi
  movl length, %ecx
  lodsb
  cld
  repne scasb
  jne notfound
  subw length, %cx
  neg %cx
  movl $1, %eax
  movl %ecx, %ebx
  int $0x80
  
notfound:
  movl $1, %eax
  movl $0, %ebx
  int $0x80

   2、搜索多个字符

    SCASW和SCASL指令扫描字符串,查找AX或者EAX寄存器中的字符序列,但是

它们并不进行逐字符的比较。而是每次比较之后,EDI寄存器要么递增2(对于

SCASW),要么递增4(对SCASL),而不是递增1。如下所示:

.section .data
  string1:
    .ascii "This is a test - a long text string to scan."
  length:
    .int 11
  string2:
    .ascii "test"

.section .text
  .globl _start

_start:
  leal string1, %edi
  leal string2, %esi
  movl length, %ecx
  lodsl
  cld
  repne scasl
  jne notfound
  subw length, %cx
  neg %cx
  movl $1, %eax
  movl %ecx, %ebx
  int $0x80
  
notfound:
  movl $1, %eax
  movl $0, %ebx
  int $0x80
  

  问题如下:

   这说明字符序列也必须按照适当的顺序出现在字符串中。

   3、计算字符串长度

    SCAS指令的一个非常有用的功能是确定零结尾(也称为空结尾)的字符串的长

度。这些字符串经常在C程序中使用,但是也通过.asciz声明在汇编语言程序中使

用。例子如下:

.section .data
  string1:
    .asciz "Testing, one, two, three, testing.\n"

.section .text
  .globl _start

_start:
  leal string1, %edi
  movl $0xffff, %ecx
  movb $0, %al
  cld
  repne scasb
  jne notfound
  subw $0xffff, %cx
  neg %cx
  dec %cx
  movl $1, %eax
  movl %ecx, %ebx
  int $0x80

notfound:
  movl $1, %eax
  movl $0, %ebx
  int $0x80

转载于:https://my.oschina.net/u/2537915/blog/694987

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值