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

汇编语言,字符串,操作,指令 · 浏览次数 : 24

小编点评

**内容简介** 本文包含一系列关于在内存中进行计算的命令,包括使用 `STOSB`、`STOSW` 和 `STOSD` 命令来存储和加载数据。还包含使用 `LODSB`、`LODSW` 和 `LODSD` 命令来计算数组加法和乘法运算的结果。 **关键命令** * `STOSB` * `STOSW` * `STOSD` * `LODSB` * `LODSW` * `LODSD` **代码示例** ```c // STOSB 示例 mov al,0ffh // 初始化填充数据 lea di,byte ptr ds:[String] // 待初始化地址 mov ecx,Count // 初始化字节数 cld // 初始化:方向=前方 rep stosb int 3 main ENDPEND main3.5 LODSB/LODSW/LODSD载入指令 // LODSD 示例 lea esi,dword ptr ds:[ArrayW] mov ecx,lengthof ArrayW // 循环计数器 xor edx,edx // 方向=向前 mov eax,edx // 将输入加载到EAX add edx,eax // 循环计数器 // lodsw 示例 mov esi,offset ArrayDW // 获取基地址 mov edi,esi // 目的指针 cld // 方向=向前 mov ecx,lengthof ArrayDW // 循环计数器 xor eax,eax @@: lodsd // 加载[esi]至EAX mul ArrayMulti ; 将EAX乘以10 stosd ; 将结果从EAX存储至[EDI] // lodsd 示例 mov esi,offset ArrayDW // 获取基地址 mov ecx,lengthof ArrayDW // 获取长度 xor eax,eax @@: lodsd // 加载[esi]至EAX mul ArrayMulti ; 将EAX乘以10 stosd ; 将结果从EAX存储至[EDI] ``` **其他说明** * 代码中包含一些 `异常处理` 的代码,例如 `` ` ` ` ` ` `。 * ` ` ` ` ` ` ` ` 是用来实现数组加法的 `循环`。

正文

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

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

  • 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 许可协议。转载请注明出处!

与5.3 汇编语言:字符串操作指令相似的内容:

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

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

[转帖]linux--Segfault详解

linux--Segfault详解 1 简介1.1 段错误的定义1.2 痛点 2 知识点2.1 报错内容2.2 error number 3 排除步骤(借助汇编)3.1 日志确定错误类型3.2 计算相对地址3.3 反汇编该库文件3.4 查找地址对应的汇编语句3.5 在中间件中查找信息 3 排除步骤(

.NET周刊【5月第3期 2024-05-19】

国内文章 WPF使用Shape实现复杂线条动画 https://www.cnblogs.com/czwy/p/18192720 文章介绍了利用WPF的Shape和动画功能,模仿CSS/SVG实现复杂的线条光效动画效果。首先,通过Polyline和StrokeDashArray实现了虚线动画,再通过S

[转帖]5.3. 调整性能参数

https://help.kingbase.com.cn/v8/perfor/sql-optimization/sql-optimization-13.html SQL性能相关的参数较多,具体见下文。在使用时需注意作用范围,可以考虑通过HINT来指定,尽量缩小影响范围。 成本参数 节点开关参数 多表

[转帖]tidb数据库5.4.3和6.5.3版本性能测试对比

https://tidb.net/blog/5454621f 一、测试需求: 基于历史原因,我们的业务数据库一直使用5.4.3,最近由于研发提出需求:需要升级到6.5.3版本,基于版本不同,需要做个压力测试已验证2个版本之间的性能差异。 二、测试目的: 验证tidb数据库5.4.3和6.5.3版本性

[转帖]etcd的安装教程

Linux 系统中,下载最新版本的ETCD Releases · etcd-io/etcd · GitHub 一.下载方式 ETCD_VER=v3.5.3 # choose either URLGOOGLE_URL=https://storage.googleapis.com/etcdGITHUB_

[转帖]国产服务器CPU架构与行业研究报告(节选五)

https://zhuanlan.zhihu.com/p/548456392 6 人赞同了该文章 ​ 目录 收起 5 服务器CPU市场 5.1 服务器CPU产业链 5.2 服务器CPU市场规模 5.3 竞争格局 5.3.1 多种架构竞争格局 5.3.2 同质化竞争态势凸显,自主可控仍需努力 5.3.

[软件下载] 常用软件安装包链接-阿里云盘

1、Typora导出文件安装包Pandoc 2、JDK1.5 3、JDK17 4、JDK8 5、redis客户端工具RedisDesktopManager 6、数据库链接工具DBeaver 7、Typora带序列号破解 8、ApiPost 接口测试工具 9、IDEA2021+破解工具 10、mysq

[转帖]Skywalking学习及整合springboot

目录 1. Skywalking概述 2. Skywalking主要功能 3. Skywalking主要特性 4. Skywalking架构简介 5. Spring Cloud与Skywalking实战 5.1 Skywalking部署构建 5.3 Spring Cloud整合Skywalking实

C++多态与虚拟:Objects 实例化(Objects Instantiation)探究

一、Objects的创建 依据已有的class CPoint ,我们可以产生一个或多个object(对象),或者说是产生一个instance(实体): CPoint aPoint(7.2); // aPoint._x 初始值为 7.2 aPoint.x(5.3); // aPoint._x 现值为