我试图在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
时,它就有效。
我想知道我在这里做错了什么。
提前致谢, 大安
答案 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编码),B9
是mov ecx,imm32
指令操作码。
你不能将字符串放入ecx
,ecx
是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)出来,或ah
:al
(高8b:低8b)如何形成ax
我希望你在此之后理解,你确实比较了两个指针(str1
vs str2
),它们总是不相等,因为它们指向内存中的不同字节。而不是比较内存中的内容(字符串)。