汇编x86:比较字符串不起作用

时间:2016-08-08 12:51:27

标签: assembly x86 nasm

我试图在Assembly中创建一个检查两个字符串的程序。

section .data
str1 db 'mystring'
str2 db 'mystring'

output db 'cmp went fine'
len equ $-output

section .text
global main

main:
    mov ecx, str1
    cmp ecx, str2
    je ifBody0
    int 80h

    mov eax, 1
    mov ebx, 0
    int 80h

ifBody0:
    mov eax, 4
    mov ebx, 1
    mov ecx, output
    mov edx, outputlen
    int 80h

奇怪的是,当我调用条件跳转:je [label]时,它不起作用。但是,当我将je更改为jne时,它就有效。 我想知道我在这里做错了什么。

提前致谢, 大安

2 个答案:

答案 0 :(得分:4)

为了比较x86-assembly中的字符串,有一个名为CMPS(Compare Strings)的特殊OpCode。对于BYTE字符串,相关的OpCode为CMPSB。您可以通过将ESI设置为源字符串并将EDI设置为目标字符串来使用它。等式检查的长度(最好是最长的字符串)在ECX中设置。 小心溢出!

所以你的代码看起来像这样:

section .data
str1 db 'mystring',0
str1len equ $-str1
str2 db 'mystring',0

output db 'cmp went fine',0x0a,0
outputlen equ $-output
output2 db 'cmp went wrong',0x0a,0
output2len equ $-output2

section .text
global main

main:
    lea esi, [str1]
    lea edi, [str2]
    mov ecx, str1len  ; selects the length of the first string as maximum for comparison
    rep cmpsb         ; comparison of ECX number of bytes
    mov eax, 4        ; does not modify flags 
    mov ebx, 1        ; does not modify flags 
    jne ifWrong       ; checks ZERO flag

ifRight:              ; the two strings do match
    mov ecx, output
    mov edx, outputlen
    int 80h
    jmp exit
ifWrong:              ; the two strings don't match
    mov ecx, output2
    mov edx, output2len
    int 80h
exit:                 ; sane shutdown
    mov eax, 1
    mov ebx, 0
    int 80h

答案 1 :(得分:2)

让我们从这两个开始:

str1    db 'mystring'
        mov ecx,str1

用汇编程序编译之后,机器代码的原始字节看起来像这样(在将可执行文件加载到内存后,这将成为内存的内容):

6D 79 73 74 72 69 6E 67  mystring
B9 00 00 00 00           ¹....

最后4个零是' m'来自' mystring'的字节,因为我决定它将在地址0处编译。前8个字节是字符串数据(ASCII编码),B9mov ecx,imm32指令操作码。

你不能将字符串放入ecxecx是32位宽(4字节),而字符串可以有很多字节。因此,使用ecx,您可以从字符串中获取最多4个字节,但这需要mov ecx,DWORD [str1],这会将值0x7473796D放入ecx(x86是小端,所以第一个字节6D在DWORD(32b)值中最不重要。)

mov ecx,str1使用ecx符号加载str1,该符号是第一个'm'字节(0x00000000)的地址。

要比较两个字符串,你将两个地址加载到某些寄存器中,然后从这些地址加载字节,逐个比较它们,直到找到一些差异(或字符串结束)(有更快的算法,但它们更复杂,需要你知道前面的字符串的长度,而逐字节比较可以很容易地使用类似C的零终止字符串。

谈论字符串的长度,你应该以某种方式定义一个。在C中,在字符串的最后一个字符(在该示例中将位于B9之前)之后放置零是常见的,在C ++中std::string是将长度作为直接提取的值的结构/相比。或者您可以在源代码中对其进行硬编码,例如outputlen

在汇编程序中编程时,应始终注意要处理的位数,并选择正确的寄存器大小(或扩展值),并更正内存缓冲区大小,以处理所需的值。

使用字符串意味着您必须决定字符串的编码。 ASCII为每个字符8位(1字节),UTF-8每个字形具有可变字节数,早期版本的UTF-16(UCS-2)每个字形有2个字节(作为Java,但当前Utf-16是可变长度),Utf-32固定为每个字形4个字节。因此,使用ASCII编码的字符串来获取它的第一个字符表示执行mov al,BYTE [str1](或mov ecx,str1 mov al,[ecx] - > al = 6Dh = 'm')使用Utf-32获取第二个字符你需要做的角色mov eax,DWORD [utf32str + 4]。使用Utf-8,单个字符最多可以包含1到6个字节的IIRC,因此您必须以相当复杂的方式处理它,以识别有效的utf-8代码并读取正确的字节数。但是如果你只是想知道两个utf-8字符串是否相等,你可以逐字节地比较它们,而不需要自己处理字形。

当然你应该知道寄存器的大小,在x86上你应该知道如何处理寄存器的子部分,即。例如ax部分(低16b)从整个eax(32b)出来,或ahal(高8b:低8b)如何形成ax

我希望你在此之后理解,你确实比较了两个指针(str1 vs str2),它们总是不相等,因为它们指向内存中的不同字节。而不是比较内存中的内容(字符串)。