目录

049-字串相关指令SCASB与REPNE,REPNZ

一、SCASB   指令

二、REPNE   指令

三、实例运用

050-字串相关指令SCASW,SCASD与REPNE,REPNZ

一、SCASW   指令

二、SCASD   指令

三、实例运用

四、作业

051-字串相关指令CMPSB与REPE

一、CMPSB

二、REPE/REPZ 指令

三、代码测试


        在需要高性能处理内存块的场景下,x86提供的带重复前缀的字符串指令是无可替代的利器。 本文深入解析其中用于扫描(Scan)的SCAS指令家族及其控制逻辑。

049-字串相关指令SCASBREPNE,REPNZ

知识点:

REPNE/REPNZ   指令      //REP:重复,N:notZzero

SCASB   指令

 

一、SCASB   指令

   scas cmp byte ptr [edi],al  //对标志位的影响相当于sub指令

//同时还会修改寄存器EDI的值:如果标志DF0,则 inc EDI;如果DF1,则 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,每执行一次,ecx1,如果ecx!=0ZF==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,SCASDREPNE,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的值:如果标志DF0,则 inc EDI;如果DF1,则 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-字串相关指令CMPSBREPE

知识点:

REPE/REPZ   指令

CMPSB       指令,有三种形式:CMPSBCMPSWCMPSD

 

一、CMPSB

//cmp sub类似,只是不把计算存放在操作数中,而只影响标志位

//SCASB//scasw//scasd执行的是 cmp byte/word/dword ptr [edi],al/ax/eax;

   //对标志位的影响相当于sub指令,同时还会修改寄存器EDI的值:如果标志DF0,则 inc EDI;如果DF1,则 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的值:如果标志DF0,则 inc EDI;如果DF1,则 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串操作详解

13、x86条件置位指令(SETxx)完全指南:从标志位解读到条件判断实战

14、游戏逆向工程实战:从CALL分析、基址定位到冷却破解与内存修改

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐