x86汇编字符串处理:SCASB/SCASW指令与REPNE/REPE重复前缀详解
摘要:本文介绍了x86汇编中的字符串操作指令SCASB、SCASW、SCASD及其与重复前缀REPNE/REPNZ、REPE/REPZ的配合使用。SCAS指令用于比较内存内容与寄存器值,同时根据DF标志调整EDI;REPNE/REPNZ在ECX≠0且ZF=0时重复执行,适用于查找操作;REPE/REPZ则在ECX≠0且ZF=1时重复,适用于比较字符串。文章通过实例演示了计算字符串长度、定位特定字符
目录
050-字串相关指令SCASW,SCASD与REPNE,REPNZ
在需要高性能处理内存块的场景下,x86提供的带重复前缀的字符串指令是无可替代的利器。 本文深入解析其中用于扫描(Scan)的SCAS指令家族及其控制逻辑。
049-字串相关指令SCASB与REPNE,REPNZ
知识点:
REPNE/REPNZ 指令 //REP:重复,N:not,Z:zero
SCASB 指令
一、SCASB 指令
scas cmp byte ptr [edi],al //对标志位的影响相当于sub指令
//同时还会修改寄存器EDI的值:如果标志DF为0,则 inc EDI;如果DF为1,则 dec EDI。
DF方向标志:该标志位用来控制串处理指令的处理方向。若DF=1则串处理过程中地址自动递减,否则自动递增。(direct)
二、REPNE 指令
REPNE SCAS BYTE PTR ES:[EDI] // 当ECX!=0并且ZF==0时 重复执行后边的指令 每执行一次ECX的值减1
REPNE 和REPNZ 是同一条指令的不同助记符
三、实例运用
1、计算字符串长度
2、定位特定字符
小结:REPNZ/REPNE与CMPS指令结合使用,表示当串未结束(CX=1)且当对应串元素不相同(ZF=0)时,继续重复执行串比较指令.
案例1:用REPNZ 和SCASB计算字符串的长度:
#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
int i=0;
char *s="s12345678";
__asm{
mov al,0; //把空字符/字符串结束符给al
mov edi,s; //把字符串地址给edi
mov ecx,0xFF;
repnz scasb;
mov eax,0xFF;
sub eax,ecx; //eax就是字符串的长度(包括最后一个字符)
mov i,eax;
}
printf("%d",i);
return 0;
}
![]()
注:
scasb会执行scas cmp byte ptr [edi],al指令,执行后,edi+1,指向字符串的下一字符,同时,如果[edi]不等于al,则ZF=0
repnz 重复执行scasb,每执行一次,ecx减1,如果ecx!=0且ZF==0,则继续执行scasb,直到ZF==0
PUSH EBP
MOV EBP,ESP
SUB ESP,8
PUSH EDI
MOV DWORD PTR SS:[EBP-4],0 //int i=0
MOV DWORD PTR SS:[EBP-8],49.004020F4 ; ASCII "s12345678"
MOV AL,0
MOV EDI,DWORD PTR SS:[EBP-8] //mov edi,s
MOV ECX,0FF
REPNE SCAS BYTE PTR ES:[EDI]
MOV EAX,0FF
SUB EAX,ECX //计算字符长度(其实直接将ECX取反就能得到字符长度(NOT ECX)
MOV DWORD PTR SS:[EBP-4],EAX
MOV EAX,DWORD PTR SS:[EBP-4]
PUSH EAX ; /<%d>
PUSH 49.00402100 ; |format = "%d"
CALL DWORD PTR DS:[<&MSVCR100.printf>] ; \printf
ADD ESP,8
XOR EAX,EAX
POP EDI
MOV ESP,EBP
POP EBP
RETN
案例2:用REPNZ 和SCASB查找某字符在字符串中的位置:
#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
int i=0;
char *s="s12345678";
__asm{
mov al,'3';
//查找字符‘3’
mov edi,s; //把字符串地址给edi
mov ecx,-1; //把ECX赋值为0xFFFFFFFF
repnz scasb;
NOT ECX; //取反后,就能得到字符’3’的位置
mov i,ECX;
}
printf("%d",i);
return 0;
}

050-字串相关指令SCASW,SCASD与REPNE,REPNZ
知识点:
REPNE/REPNZ 指令
SCASB 指令 //scas byte ptr es:[edi]
SCASW 指令 //scas word ptr es:[edi]
SCASD 指令 //scas dword ptr es:[edi]
SCAS 指令
__asm{
scasb;
scasw;
scasd;
}
上面代码生成的汇编指令如下:
00401004 |. AE SCAS BYTE PTR ES:[EDI]
00401005 |. 66:AF SCAS WORD PTR ES:[EDI]
00401007 |. AF SCAS DWORD PTR ES:[EDI]
一、SCASW 指令
cmp word ptr [edi],ax //对标志位的影响相当于sub指令
//同时还会修改寄存器EDI的值:如果标志DF为0,则 inc EDI;如果DF为1,则 dec EDI。
二、SCASD 指令
cmp dword ptr [edi],eax
SCAS BYTE PTR ES:[EDI] // 当ECX!=0并且ZF==0时 重复执行后边的指令 每执行一次EDI的值加 1
SCAS WORD PTR ES:[EDI] // 当ECX!=0并且ZF==0时 重复执行后边的指令 每执行一次EDI的值加 2
SCAS DWORD PTR ES:[EDI] // 当ECX!=0并且ZF==0时 重复执行后边的指令 每执行一次EDI的值加 4
REPNE SCAS BYTE PTR ES:[EDI] // 当ECX!=0并且ZF==0时 重复执行后边的指令 每执行一次ECX的值减1
REPNE SCAS WORD PTR ES:[EDI] // 当ECX!=0并且ZF==0时 重复执行后边的指令 每执行一次ECX的值减1
REPNE SCAS DWORD PTR ES:[EDI] // 当ECX!=0并且ZF==0时 重复执行后边的指令 每执行一次ECX的值减1
REPNE 和REPNZ 是同一条指令的不同助记符
三、实例运用
1、查找DWORD数据
在"sabcdef12345678" 定位"f123"
小结:REPNZ/REPNE与CMPS指令结合使用,表示当串未结束(CX=1)且当对应串元素不相同(ZF=0)时,继续重复执行串比较指令.
四、作业
1、用最近几课的知识编写代码 计算下列字串长度!
WCHAR *ws1=L"s1234567"; //提示 WCAHR是以为字为单位(占2个字节) 用SCASW
案例:查找宽字符的位置(汉字/中文/宽字符集)
#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
int i=0;
wchar_t *s1=L"12345678";
__asm{
mov ax,L'7';
mov edi,s1;
mov ecx,-1;
repnz scasw; // while (ecx!=0 且 ZF==0) cmp word ptr [edi],ax
not ecx; //取反,得到字符所在位置
mov i,ecx;
}
printf("%d",i);
return 0;
}
![]()
PUSH EBP
MOV EBP,ESP
SUB ESP,8
PUSH EDI
MOV DWORD PTR SS:[EBP-4],0
MOV DWORD PTR SS:[EBP-8],50.004020F4 ; UNICODE "12345678"
MOV AX,37
MOV EDI,DWORD PTR SS:[EBP-8]
MOV ECX,-1
REPNE SCAS WORD PTR ES:[EDI]
NOT ECX
MOV DWORD PTR SS:[EBP-4],ECX
MOV EAX,DWORD PTR SS:[EBP-4]
PUSH EAX ; /<%d>
PUSH 50.00402108 ; |format = "%d"
CALL DWORD PTR DS:[<&MSVCR100.printf>] ; \printf
ADD ESP,8
XOR EAX,EAX
POP EDI
MOV ESP,EBP
POP EBP
RETN
宽字符“12345678”内存图:

用命令栏查看内存时,注意:
EBP-8得到的是栈区中保存字符串地址的指针地址(0012FF74):

[EBP-8]得到的才是字符串的首地址004020F4
因此,查看字符的命令是: db [EBP-8]
051-字串相关指令CMPSB与REPE
知识点:
REPE/REPZ 指令
CMPSB 指令,有三种形式:CMPSB、CMPSW、CMPSD
一、CMPSB
//cmp 与sub类似,只是不把计算存放在操作数中,而只影响标志位
//SCASB//scasw//scasd,执行的是 cmp byte/word/dword ptr [edi],al/ax/eax;
//对标志位的影响相当于sub指令,同时还会修改寄存器EDI的值:如果标志DF为0,则 inc EDI;如果DF为1,则 dec EDI。
CMPSB: cmps byte ptr [edi],byte ptr [esi]//对标志位的影响相当于sub指令
CMPSW: cmps word ptr [edi],word ptr [esi]
CMPSD: cmps dword ptr [edi],dword ptr [esi]
//同时还会修改寄存器EDI的值:如果标志DF为0,则 inc EDI;如果DF为1,则 dec EDI。
二、REPE/REPZ 指令
//上一节课我们了解了REPNE/REPNZ
REPNE SCAS BYTE PTR ES:[EDI] // 当ECX!=0并且ZF==0时 重复执行后边的指令 每执行一次ECX的值减1
REPE/REPZ cmpsb //// 当ECX!=0并且ZF==1时 重复执行后边的指令 每执行一次ECX的值减1 (可用于比较字符串是否相同)
三、代码测试
1、比较字符串是否相等
MOV EDI,s1
MOV ESI,s2
MOV ECX,0xFFFFFFFF
XOR EAX,EAX //查找字串结束标志 '\0' eax=0
REPNE SCASB // edi,al
NOT ECX //计算字串长度存ECX
MOV EDI,s1
mov ESI,s2
XOR EDX,EDX
REPE CMPSB //不相等则退出
int _tmain(int argc, _TCHAR* argv[])
{
char *s1="abcde21";
char *s2="abcde1";
__asm
{
//mov al,0
xor al,al//eax=0
mov edi,s1//
mov ecx,-1 //0xFFFF FFFF
repnz scasb;
not ecx // '\0' 计算s1字串长度,指定repz循环次数
mov edi,s1
mov esi,s2
repz cmpsb // edi,esi ,当ECX==0时,停止执行cmpsb,如果这时ZF还是等于1,则说明字符串相等(其实这不严谨,如果s2长度大于s1呢?)
}
return 0;
}
PUSH EBP
MOV EBP,ESP
SUB ESP,8
PUSH ESI
PUSH EDI
MOV DWORD PTR SS:[EBP-8],51.004020E4 ; ASCII "abcde21"
MOV DWORD PTR SS:[EBP-4],51.004020EC ; ASCII "abcde1"
XOR AL,AL
MOV EDI,DWORD PTR SS:[EBP-8]
MOV ECX,-1
REPNE SCAS BYTE PTR ES:[EDI]
NOT ECX
MOV EDI,DWORD PTR SS:[EBP-8]
MOV ESI,DWORD PTR SS:[EBP-4]
REPE CMPS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
XOR EAX,EAX
POP EDI
POP ESI
MOV ESP,EBP
POP EBP
RETN
计算机科学与技术 & 计算机网络技术:双专业课程体系完全导航指南
本系列目录
1、逆向工程基础:Ollydbg(OD)调试器实战与MOV指令寻址方式精讲
2、汇编语言核心概念精讲:从ADD、SUB、MOVSX/MOVZX到LEA与寄存器详解
3、x86汇编条件跳转指令完全解析:从CMP到有/无符号跳转与If实现
4、x86汇编函数调用完全解析:栈帧(EBP/ESP)构建与三种调用约定(cdecl/stdcall/fastcall)对比
5、汇编逆向还原核心:if-else与switch-case结构的识别与C代码重构
6、编译器优化揭秘:对比for循环的汇编实现与INC vs ADD指令的性能抉择
7、x86浮点运算基础:FPU寄存器、FLD/FSTP与FADD/FSUB等指令精讲
8、汇编位移指令全解与逆向实战:从SHR/SHL到ROL/ROR,逆向分析strcmp
9、汇编位运算指令精讲:掌握AND/OR/XOR/NOT四大核心操作
10、x86汇编字符串处理:SCASB/SCASW指令与REPNE/REPE重复前缀详解
11、汇编实战:用REPNZ SCASB与REPZ CMPSB从零实现strcmpA/W
12、x86汇编批量操作指令:LOOP循环控制与STOS/LODS串操作详解
更多推荐



所有评论(0)