首页 > 编程语言 >5.3 汇编语言:字符串操作指令

5.3 汇编语言:字符串操作指令

时间:2023-08-22 16:25:05浏览次数:50  
标签:main 5.3 字节 汇编语言 EDI eax 指令 字符串 include

本章将深入研究字符串操作指令,这些指令在汇编语言中具有重要作用,用于处理字符串数据。我们将重点介绍几个关键的字符串操作指令,并详细解释它们的功能和用法。通过清晰的操作示例和代码解析,读者将了解如何使用这些指令进行字符串比较、复制、填充等常见操作。我们还将探讨不同指令之间的区别,并提供实际的示例程序,展示字符串操作指令在实际场景中的应用。通过学习本章,读者将能够拓展汇编技能,为处理字符串数据提供高效而精确的解决方案。

常见的字符串操作指令包括:

  • MOVSB / MOVSW / MOVSX:在两个存储器地址之间复制一个字节、一个字或一个双字。其中 MOVSB 复制一个字节,MOVSW 复制一个字,MOVSX 复制一个双字。
  • CMPSB / CMPSW / CMPSD:比较两个存储器地址中的一个字节、一个字或一个双字,并将比较结果存储在条件码寄存器中。其中 CMPSB 比较一个字节,CMPSW 比较一个字,CMPSD 比较一个双字。
  • LODSB / LODSW / LODSD:从存储器中读取一个字节、一个字或一个双字,并将其存储在累加器中。其中 LODSB 读取一个字节,LODSW 读取一个字,LODSD 读取一个双字。
  • STOSB / STOSW / STOSD:将一个字节、一个字或一个双字写入存储器,并将累加器的值相应地更新。其中 STOSB 写入一个字节,STOSW 写入一个字,STOSD 写入一个双字。
  • SCASB / SCASW / SCASD:在存储器地址中扫描一个字节、一个字或一个双字,并将扫描结果存储在条件码寄存器中。其中 SCASB 扫描一个字节,SCASW 扫描一个字,SCASD 扫描一个双字。

这些字符串操作指令通常是通过累加器(即 AH、AL、AX 或 EAX 等寄存器)来控制读取或写入的数据大小,同时还需要通过 DF 标志位来控制是向存储地址增加还是减小。在使用字符串操作指令时,需要仔细理解这些指令的语法和操作方式,以便正确地处理字符串数据。

3.1 MOVSB/MOVSW/MOVSD

移动串指令包括了MOVSB、MOVSW、MOVSD这三条指令,该指令的原理为从ESIEDI中,执行后将ESI地址里面的内容移动到EDI指向的内存空间中,该指令常用于对特定字符串的复制操作。

  • MOVSB指令:将一个字节从ESI地址指向的内存单元复制到EDI地址指向的内存单元,同时增加或减少ESI和EDI(取决于方向标志位的状态)。
  • MOVSW指令:将两个字节从ESI地址指向的内存单元复制到EDI地址指向的内存单元,
  • MOVSD指令:将四个字节从ESI地址指向的内存单元复制到EDI地址指向的内存单元。这些指令都可用于复制字符串或移动缓冲区。
  .386p
  .model flat,stdcall
  option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
  ; 逐字节拷贝
  SrcString    BYTE "hello lyshark",0h      ; 源字符串
  SrcStringLen EQU $ - SrcString - 1        ; 计算出原始字符串长度
  DstString    BYTE SrcStringLen dup(?),0h  ; 目标内存地址
  szFmt BYTE '字符串: %s 长度: %d ',0dh,0ah,0
  
  ; 四字节拷贝
  ddSource DWORD 10h,20h,30h               ; 定义三个四字节数据
  ddDest   DWORD lengthof ddSource dup(?)  ; 得到目标地址

.code
  main PROC
    ; 第一种情况: 实现逐字节拷贝
    cld                         ; 清除方向标志
    mov esi,offset SrcString    ; 取源字符串内存地址
    mov edi,offset DstString    ; 取目标字符串内存地址
    mov ecx,SrcStringLen        ; 指定循环次数,为原字符串长度
    rep movsb                   ; 逐字节复制,直到ecx=0为止
    
    lea eax,dword ptr ds:[DstString]
    mov ebx,sizeof DstString
    invoke crt_printf,addr szFmt,eax,ebx
    
    ; 第二种情况: 实现4字节拷贝
    lea esi,dword ptr ds:[ddSource]
    lea edi,dword ptr ds:[ddDest]
    cld
    rep movsd
    
    ; 使用loop循环逐字节复制
    lea esi,dword ptr ds:[SrcString]
    lea edi,dword ptr ds:[DstString]
    mov ecx,SrcStringLen
    cld                               ; 设置方向为正向复制
  @@: movsb                             ; 每次复制一个字节
    dec ecx                           ; 循环递减
    jnz @B                            ; 如果ecx不为0则循环
    
    lea eax,dword ptr ds:[DstString]
    mov ebx,sizeof DstString
    invoke crt_printf,addr szFmt,eax,ebx
    
    invoke ExitProcess,0
  main ENDP
END main

3.2 CMPSB/CMPSW/CMPSD

比较串指令包括CMPSB、CMPSW、CMPSD比较ESI、EDI执行后将ESI指向的内存操作数同EDI指向的内存操作数相比较,其主要从ESI指向内容减去EDI的内容来影响标志位。这些指令通常用于比较字符串中的字符,可影响方向标志、零标志和符号标志位的状态。

CMPSB指令是将ESI和EDI地址指向的内存单元中的一个字节进行比较,同时增加或减少ESI和EDI(取决于方向标志位的状态)。

  .386p
  .model flat,stdcall
  option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
  ; 逐字节比较
  SrcString    BYTE "hello lyshark",0h
  DstStringA   BYTE "hello world",0h
.const
  szFmt BYTE '字符串: %s',0dh,0ah,0
  YES BYTE "相等",0
  NO  BYTE "不相等",0
  
.code
  main PROC
    ; 实现字符串对比,相等/不相等输出
    lea esi,dword ptr ds:[SrcString]
    lea edi,dword ptr ds:[DstStringA]
    mov ecx,lengthof SrcString
    cld
    repe cmpsb
    je L1
    jmp L2

  L1: lea eax,YES
    invoke crt_printf,addr szFmt,eax
    jmp lop_end

  L2: lea eax,NO
    invoke crt_printf,addr szFmt,eax
    jmp lop_end
  lop_end:
    int 3

    invoke ExitProcess,0
  main ENDP
END main

CMPSW 是对比一个字类型的数组,指令是将ESI和EDI地址指向的内存单元中的两个字节进行比较,只有当数组中的数据完全一致的情况下才会返回真,否则为假。

  .386p
  .model flat,stdcall
  option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
  Array1 WORD 1,2,3,4,5      ; 必须全部相等才会清空ebx
  Array2 WORD 1,3,5,7,9
.const
  szFmt BYTE '数组: %s',0dh,0ah,0
  YES BYTE "相等",0
  NO  BYTE "不相等",0
  
.code
  main PROC
    lea esi,Array1
    lea edi,Array2
    mov ecx,lengthof Array1
    
    cld
    repe cmpsw
    je L1
    lea eax,NO
    invoke crt_printf,addr szFmt,eax
    jmp lop_end

  L1: lea eax,YES
    invoke crt_printf,addr szFmt,eax
    jmp lop_end
  
  lop_end:
    int 3

    invoke ExitProcess,0
  main ENDP
END main

CMPSD则是比较双字数据,指令将ESI和EDI地址指向的内存单元中的四个字节进行比较,同样可用于比较数组,这里就演示一下比较单数的情况。

  .386p
  .model flat,stdcall
  option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
  var1 DWORD 1234h
  var2 DWORD 5678h
.const
  szFmt BYTE '两者: %s',0dh,0ah,0
  YES BYTE "相等",0
  NO  BYTE "不相等",0
  
.code
  main PROC
    lea esi,dword ptr ds:[var1]
    lea edi,dword ptr ds:[var2]
    
    cmpsd
    je L1
    lea eax,dword ptr ds:[YES]
    invoke crt_printf,addr szFmt,eax
    jmp lop_end
    
  L1: lea eax,dword ptr ds:[NO]
    invoke crt_printf,addr szFmt,eax
    jmp lop_end

  lop_end:
    int 3

    invoke ExitProcess,0
  main ENDP
END main

3.3 SCASB/SCASW/SCASD

扫描串指令包括SCASB、SCASW、SCASD其作用是把AL/AX/EAX中的值同EDI寻址的目标内存中的数据相比较,这些指令在一个长字符串或者数组中查找一个值的时候特别有用。

  • SCASB指令:将AL寄存器中的值与EDI地址指向的内存单元中的一个字节进行比较,同时增加或减少EDI(取决于方向标志位的状态)。
  • SCASW指令:将AX寄存器中的值与EDI地址指向的内存单元中的两个字节进行比较。
  • SCASD指令:将EAX寄存器中的值与EDI地址指向的内存单元中的四个字节进行比较。这些指令通常用于在一个长字符串或数组中查找一个特定值的位置,可影响方向标志、零标志和符号标志位的状态。
  .386p
  .model flat,stdcall
  option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
  szText BYTE "ABCDEFGHIJK",0
.const
  szFmt BYTE '字符F所在位置: %d',0dh,0ah,0

.code
  main PROC
    ; 寻找单一字符找到会返回第几个字符
    lea edi,dword ptr ds:[szText]
    mov al,"F"
    mov ecx,lengthof szText -1
    cld
    repne scasb                 ; 如果不相等则重复扫描
    je L1
    xor eax,eax                 ; 如果没找到F则清空eax
    jmp lop_end
    
  L1: sub ecx,lengthof szText -1
    neg ecx                     ; 如果找到输出第几个字符
    invoke crt_printf,addr szFmt,ecx
  
  lop_end:
    int 3

  main ENDP
END main

如果我们想要对数组中某个值是否存在做判断,则可以使用SCASD指令扫描一个数组中是否存在一个特定的值,通过循环指令(如LOOP或JECXZ)逐个4字节扫描,来检查EAX寄存器中的值是否与目标数组中的值匹配。如果匹配成功,则方向标志位将被设置为与扫描方向相反的方向,如果没有找到匹配项,方向标志位将保持不变。

在使用循环指令时,需要在每次循环中比较数组当前位置的值是否与目标值相等,如果相等就跳出循环,如果没有找到匹配项,就继续循环指令知道数组的最后元素。

  .386p
  .model flat,stdcall
  option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
  MyArray DWORD 65,88,93,45,67,89,34,67,89,22
.const
  szFmt BYTE '数值: %d 存在',0dh,0ah,0
.code
  main PROC
    lea edi,dword ptr ds:[MyArray]
    mov eax,34
    mov ecx,lengthof MyArray - 1
    cld
    repne scasd
    je L1
    xor eax,eax
    jmp lop_end

  L1: sub ecx,lengthof MyArray - 1
    neg ecx
    invoke crt_printf,addr szFmt,ecx,eax
  lop_end:
    int 3

  main ENDP
END main

3.4 STOSB/STOSW/STOSD

存储指令主要包括STOSB、STOSW、STOSD其作用是把AL/AX/EAX中的数据储存到EDI给出的地址中,执行后EDI的值根据方向标志的增加或减少,该指令常用于初始化内存或堆栈。

  • STOSB指令:将AL寄存器中的值存储到EDI地址指向的内存单元中,同时增加或减少EDI(取决于方向标志位的状态)。
  • STOSW指令:将AX寄存器中的值存储到EDI地址指向的两个字节内存单元中。
  • STOSD指令:将EAX寄存器中的值存储到EDI地址指向的四个字节内存单元中。这些指令常用于初始化内存、堆栈和缓冲区。
  .386p
  .model flat,stdcall
  option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
  Count  DWORD 100
  String BYTE 100 DUP(?),0

.code
  main PROC
  
    ; 利用该指令初始化字符串
    mov al,0ffh                   ; 初始化填充数据
    lea di,byte ptr ds:[String]   ; 待初始化地址
    mov ecx,Count                 ; 初始化字节数
    cld                           ; 初始化:方向=前方
    rep stosb                     ; 循环填充
    
    ; 存储字符串: 使用A填充内存
    lea edi,dword ptr ds:[String]
    mov al,"A"
    mov ecx,Count
    cld
    rep stosb

    int 3

  main ENDP
END main

3.5 LODSB/LODSW/LODSD

载入指令主要包括LODSB、LODSW、LODSD起作用是将ESI指向的内存位置向AL/AX/EAX中装载一个值,同时ESI的值根据方向标志值增加或减少,如下分别完成加法与乘法计算,并回写到内存中。

  • LODSB指令:将ESI地址指向的一个字节复制到AL寄存器中,同时增加或减少ESI(取决于方向标志位的状态)。
  • LODSW指令:将ESI地址指向的两个字节复制到AX寄存器中
  • LODSD指令:将ESI地址指向的四个字节复制到EAX寄存器中。
  .386p
  .model flat,stdcall
  option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
  ArrayW      WORD 1,2,3,4,5,6,7,8,9,10
  ArrayDW     DWORD 1,2,3,4,5
  ArrayMulti  DWORD 10
  
  szFmt BYTE '计算结果: %d ',0dh,0ah,0

.code
  main PROC
    ; 利用载入命令计算数组加法
    lea esi,dword ptr ds:[ArrayW]
    mov ecx,lengthof ArrayW
    xor edx,edx
    xor eax,eax
  @@: lodsw          ; 将输入加载到EAX
    add edx,eax
    loop @B
    
    mov eax,edx    ; 最后将相加结果放入eax
    invoke crt_printf,addr szFmt,eax
    
    ; 利用载入命令(LODSD)与存储命令(STOSD)完成乘法运算
    mov esi,offset ArrayDW   ; 源指针
    mov edi,esi              ; 目的指针
    cld                      ; 方向=向前
    
    mov ecx,lengthof ArrayDW ; 循环计数器
  L1: lodsd                    ; 加载[esi]至EAX
    mul ArrayMulti           ; 将EAX乘以10
    stosd                    ; 将结果从EAX存储至[EDI]
    loop L1
    
    ; 循环读取数据(存在问题)
    mov esi,offset ArrayDW     ; 获取基地址
    mov ecx,lengthof ArrayDW   ; 获取长度
    xor eax,eax
  @@: lodsd
    invoke crt_printf,addr szFmt,eax
    dec ecx
    loop @B 

    int 3

  main ENDP
END main

本文作者: 王瑞
本文链接: https://www.lyshark.com/post/f6603608.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

标签:main,5.3,字节,汇编语言,EDI,eax,指令,字符串,include
From: https://www.cnblogs.com/LyShark/p/17648812.html

相关文章

  • 5.4 汇编语言:算数运算指令集
    算术运算指令集是计算机中的一组基本操作,用于对数字执行常见的算术运算操作。这些指令都是计算机中非常基础的运算指令,可以用于实现所有常见的算术运算操作,并可以通过组合使用实现更加复杂的数学运算。在实际编程中,程序员可以根据具体需求选择合适的运算指令,实现程序中的算术运算......
  • 5.5 汇编语言:函数调用约定
    函数是任何一门高级语言中必须要存在的,使用函数式编程可以让程序可读性更高,充分发挥了模块化设计思想的精髓,今天我将带大家一起来探索函数的实现机理,探索编译器到底是如何对函数这个关键字进行实现的,并使用汇编语言模拟实现函数编程中的参数传递调用规范等。说到函数我们必须要提......
  • python 使用re模块,配合正则表达式来查找字符串的想要的字符串
    一,首先:我们现了解一下python中正则表达式的基本规则有那些?1,字符串"\d"匹配0~9之间的一个数值eg:'dsas212b321321'使用r'\d'结果:(它会一次匹配一个数字依次查找)212321321  2,字符"+"重复前面一个匹配字符一次或者多次eg:'dsas212b321321'方式1:使用r'\d+'结果:(......
  • C#,连接数据库字符串汇总
     //-------------------SQLServer连接数据库字符串-------------20161216-----------------------------DataSource=服务器地址;InitialCatalog=数据库名;UserID=sa;Password=sa //-------------------ACCESS连接数据库字符串----------20161216--------------------......
  • 5.1 汇编语言:汇编语言概述
    汇编语言是一种面向机器的低级语言,用于编写计算机程序。汇编语言与计算机机器语言非常接近,汇编语言程序可以使用符号、助记符等来代替机器语言的二进制码,但最终会被汇编器编译成计算机可执行的机器码。相较于高级语言(如C、Python等),汇编语言学习和使用难度相对较大,需要对计算机内......
  • c#json处理字符串
    usingNewtonsoft.Json;//定义一个JSON字符串stringjsonStr="{\"username\":\"test\",\"age\":18}";//将JSON字符串解析成对象dynamicobj=JsonConvert.DeserializeObject(jsonStr);//访问对象属性stringname=obj.username......
  • iOS开发之-- 字符串的操作,去掉某一个字符或者替换成其他字符
    一个简单的操作,记录下:NSString*strUrl=[urlStringstringByReplacingOccurrencesOfString:@""withString:@""];去掉空格NSString*strUrl=[urlStringstringByReplacingOccurrencesOfString:@""withString:@"/"];替换字符 仅做记录!作者:稻草人11......
  • ios 开发之--逆序输出字符串
    //字符串反转NSString*str=@"abcedfghijklmnopqrstuvwxyz";NSMutableString*string=[NSMutableStringstringWithCapacity:str.length];intj=(int)str.length;for(inti=j-1;i>=0;i--){[stringappendFormat:@"%c......
  • 常见结构体 日期 字符串的操作 很实用
    取余和取整:常用结构体的操作:日期的操作: 字符串的操作: 作者:稻草人11223......
  • iOS学习之--字符串的删除替换(字符串的常用处理,删除,替换)
    字符串操作,比较简单,仅做记录!1、删除NSString*str1=@"<hello,word!>";//删除字符串两端的尖括号NSMutableString*mString=[NSMutableStringstringWithString:str1];//第一个参数是要删除的字符的索引,第二个是从此位开始要删除的位数[mStringdeleteCh......