簡體   English   中英

程序集 x86:比較字符串不起作用

[英]Assembly x86: comparing strings doesn't work

我正在嘗試在 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 ,它起作用了。 我想知道我在這里做錯了什么。

為了在 x86-assembly 中比較字符串,有一個名為CMPS (Compare Strings)的特殊操作碼。 在您的 BYTE 字符串的情況下,相關的操作碼是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

讓我們從這兩個開始:

str1    db 'mystring'
        mov ecx,str1

用匯編器編譯后,機器碼的原始字節看起來像這樣(這將在加載可執行文件后成為內存內容):

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

最后 4 個零是來自 'mystring' 的 'm' 字節的地址,因為我決定它將在地址 0 處編譯。前 8 個字節是字符串數據(ASCII 編碼), B9mov ecx,imm32指令操作碼。

您不能將 string 放入ecxecx是 32 位寬(4 個字節),而 string 可以有很多字節。 因此,使用ecx您最多可以從字符串中獲取 4 個字節,但這需要mov ecx,DWORD [str1] ,這會將值0x7473796D放入ecx (x86 是小端,因此第一個字節6D在 DWORD 中是最不重要的( 32b) 值)。

但是mov ecx,str1使用str1符號加載ecx ,這是第一個'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) 中取出,或者如何ah : al (high 8b : low 8b) 一起形成ax


我希望您在此之后會明白,您確實比較了兩個指針( str1str2 ),它們總是不相等的,因為它們指向內存中的不同字節。 而不是比較內存中的內容(字符串)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM