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

您所在的位置:网站首页 汇编替换字符串中的指定字符是什么 5.3 汇编语言:字符串操作指令

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

2024-07-12 00:19| 来源: 网络整理| 查看: 265

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

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

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这三条指令,该指令的原理为从ESI到EDI中,执行后将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 许可协议。转载请注明出处!



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3