注:以下内容为学习笔记,多数是从书本、资料中得来,只为加深印象,及日后参考。然而本人表达能力较差,写的不好。因非翻译、非转载,只好选原创,但多数乃摘抄,实为惭愧。但若能帮助一二访客,幸甚!
一.传送字符串
把字符串从一个内存位置复制到另一个内存位置。
1.MOVS
3种格式:
MOVSB:传送单一字节
MOVSW:传送一个字(2字节)
MOVSL:传送一个双字(4字节)
MOVS使用隐含的源(ESI)、目的(EDI)操作数。
两种加载ESI、EDI值的方式:
1)间接寻址:
movl
$output,
%edi
2)lea指令加载一个对象的有效地址
leal
output,
%esi
每次执行MOVS指令时,数据传送后,ESI和EDI寄存器会自动改变,为另一次传送做准备。
ESI、EDI可能递增也可能递减,这取决于EFLAGS中的DF标志。如果DF被清零则递增,DF被设置,则递减。
CLD将DF清零
STD设置DF标志
示例:
# An example of the MOVS instructions
.section .data
str:
.ascii "Hi AT&T.\n"
.section .bss
.lcomm output, 9
.section .text
.global _start
_start:
nop
leal str, %esi
leal output, %edi
movsb
movsw
movsl
movl $1, %eax
movl $0, %ebx
int $0x80
调试:
liury@liury-laptop:~/program/asm/working_with_string/movs_example$ make
as -gstabs -o movs_example.o movs_example.s
ld -o movs_example movs_example.o
liury@liury-laptop:~/program/asm/working_with_string/movs_example$ ls
makefile movs_example movs_example.o movs_example.s
liury@liury-laptop:~/program/asm/working_with_string/movs_example$ gdb ./movs_example
GNU gdb (GDB) 7.1-ubuntu ( www.39magic.com)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/liury/program/asm/working_with_string/movs_example/movs_example...done.
(gdb) l
1
# An example of the MOVS instructions
2
.section .data
3
str:
4
.ascii
"Hi AT&T.\n"
5
6
.section .bss
7
.lcomm
output, 9
8
9
.section .text
10
.global _start
(gdb) b *_start+1
Breakpoint 1 at 0x8048075: file movs_example.s, line 15.
(gdb) r
Starting program: /home/liury/program/asm/working_with_string/movs_example/movs_example
Breakpoint 1, _start () at movs_example.s:15
15
leal
str,
%esi
(gdb) n
16
leal
output,
%edi
(gdb) print /x $esi
$1 = 0x8049094
(gdb) print /x str
$2 = 0x41206948
(gdb) print /x &str
$3 = 0x8049094
(gdb) n
18
movsb
(gdb) x /9cb &output
0x80490a0 <output>:
0 '\000'
0 '\000'
0 '\000'
0 '\000'
0 '\000'
0 '\000'
0 '\000'
0 '\000'
0x80490a8 <output+8>:
0 '\000'
(gdb) x /s &output
0x80490a0 <output>:
""
(gdb) n
19
movsw
(gdb) x /s &output
0x80490a0 <output>:
"H"
(gdb) n
20
movsl
(gdb) x /s &output
0x80490a0 <output>:
"Hi "
(gdb) n
22
movl
$1,
%eax
(gdb) x /s &output
0x80490a0 <output>:
"Hi AT&T"
(gdb) c
Continuing.
Program exited normally.
(gdb) q
对于要传送大型字符串,可用循环:
leal datas, %esi
leal output, %edi
mvol $100, %ecx
cld
loop_cp:
movsb
loop loop_cp
2.REP
可更简单地传送大型字符串。它自己并不执行什么操作,这条指令用于按照特定次数重复执行字符串指令,由ECX寄存器中的值进行控制。例如
leal datas, %esi
leal output, %edi
mvol $100, %ecx
cld
rep movsb
MOVSW,MOVSL传送大型字符串效率更高,但小心不能除尽的情况。
有些REP指令除监视ECX外还监视ZF(零标志)的状态。
魔术--------------------------------------------------
指令
描述
-------------------------------------------
REPE
等于时重复
REPNE
不等于时重复
REPNZ
不为0时重复
REPZ
为0时重复
---------------------------------------------------
二.存储和加载字符串
1.LODS
用于把内存中的字符串传送到EAX。三种形式:
LODSB:把一个字节加载到AL
LODSW:把一个字加载到AX
LODSL:把一个双字加载到EAX
LODS指令使用ESI寄存器作为隐含的源操作数。ESI必须包含要加载的字符串所在的内存地址。
同样加载后ESI递增或递减取决于DF标志。
2.STOS
把字符串从EAX放到一个内存地址。
STOSB:存储AL中一个字节数据
STOSW:存储AX中一个字数据
STOSL:存储EAX中一个双字数据
STOS指令使用EDI作为隐含的目标操作数。可方便地与REP配合:
leal space, %esi
leal buffer, %edi
movl $256, %ecx
cld
lodsb
rep stosb
字符串处理示例,小写变大写:
# Converting lower to upper case
.section .data
str1:
.asciz "This is a TEST, of the program, hello AT&R!\n"
len:
.int . - str1
.section .text
.global _start
_start:
nop
leal str1, %esi
movl %esi, %edi
movl len, %ecx
cld
1:
lodsb
cmpb $'a', %al
jl skipb
cmpb $'z', %al
jg skipb
subb $0x20, %al
skipb:
stosb
loop 1b
end:
pushl $str1
call printf
addl $4, %esp
pushl $0
call exit
结果:
liury@liury-laptop:~/program/asm/working_with_string/str_process$ ./convert
THIS IS A TEST, OF THE PROGRAM, HELLO AT&R!
三.比较字符串
1.CMPS
三种格式:CMPSB,CMPSW,CMPSL
隐含的源和目的操作数同样存储在ESI和EDI寄存器中。每次执行CMPS时,根据DF的设置,ESI和EDI递增或递减
CMPS指令从源字符串减去目标字符串,并且适当地设置EFLAGS的寄存器的进位、符号、溢出、零、奇偶校验和辅助进位标志。后面可跟跳转指令。 魔术教学
2.CMPS和REP一起使用
示例:
# An example of using the REPE CMPS instruction
.section .data
output:
.asciz "The len is %d.\n"
str1:
.ascii "This is a test of the cmps instructions."
str2:
.ascii "This is a test of the CMPS instructions."
len:
.int . - str2
.section .text
.global _start
_start:
nop
movl len, %ecx
pushl %ecx
pushl $output
call printf
addl $8, %esp
leal str1, %esi
leal str2, %edi
movl len, %ecx
movl $1, %eax
cld
repe cmpsb
je equal
movl %ecx, %ebx
int $0x80
equal:
movl $0, %ebx
int $0x80
运行结果:
liury@liury-laptop:~/program/asm/working_with_string/cmps_rep$ make
as -gstabs -o cmps_rep.o cmps_rep.s
ld -dynamic-link /lib/ld-linux.so.2 -lc -o cmps_rep cmps_rep.o
liury@liury-laptop:~/program/asm/working_with_string/cmps_rep$ ./cmps_rep
The len is 40.
liury@liury-laptop:~/program/asm/working_with_string/cmps_rep$ echo $?
17
解析:
上面的代码首先尝试来'.'运算符作为当前地址,求字符串长度的方法,为验证其正确性,程序开始处打印来一下即40。
然后比较两个字符串,直到第一个不相同的字符结束,即‘cmps’与‘CMPS’中的c处。
系统调用结束,调用号保存在EAX中,返回值保存在EBX中。则该程序的返回值若比较结束,即所有字符都相同则返回0,若不同则返回ECX(传到EBX返回)的值,本例为17(通过echo $?打印),17为从后往前数,第一个不同的字符的位置,即c的位置。
四.扫描字符串
1.SCAS
用于扫描字符串搜索一个或多个字符。三种形式:
SCASB,SCASW,SCASL,分别比较内存中的一个自己、字、双字和AL、AX、EAX的值。
SCAS使用EDI作为隐含的目标操作数。EDI必须包含要扫描的字符串的内存地址。指令执行时EDI按DF值递增或递减。
进行比较时会相应地设置EFLAGS标志。
可与REPE,REPNE一起使用:
REPE:扫描字符串,查找不匹配搜索字符的位置
REPNE:扫描字符串,查找匹配搜索字符的位置
示例:
# An example of the SCAS instruction
.section .data
str:
.ascii "Hello AT&T!"
len:
.int . - str
char:
.ascii "&"
.section .text
.global _start
_start:
nop
leal str, %edi
leal char, %esi
movl len, %ecx
lodsb
cld
repne scasb
jne notfound
subw len, %cx # len是长度,cx值为找到的位置距离末尾的位置,
# 则%cx - len为正着数位置的负数
neg %cx # neg 为求补指令,即负数变正数
movl %ecx, %ebx # 作为中断的返回值,可在程序退出后echo $?查看
movl $1, %eax
int $0x80
notfound:
movl $1, %eax
movl $0, %ebx
int $0x80
结果:
liury@liury-laptop:~/program/asm/working_with_string/find_char$ make
as -gstabs -o find_char.o find_char.s
ld -o find_char find_char.o
liury@liury-laptop:~/program/asm/working_with_string/find_char$ ./find_char
liury@liury-laptop:~/program/asm/working_with_string/find_char$ echo $?
9
2.搜索多个字符
SCASW,SCASL可以搜索2或4个字符的序列,但他们不会逐字符比较,而是每次递增2或4.
如在“abctestaaabb”中搜索用SCASL搜索“test”会用“test”与第一个串中的"abct", "esta", "aabb"依次比较,而不会逐字符增减寻找"test" 所以结果是找不到。
3.计算字符串的长度
SCAS指令的一个非常有用的功能是确定0结尾的字符串的长度。
示例:
# Finding the len of a string useing the SCAS instruction
.section .data
output:
.asciz "The len of the str is : %d.\n"
str:
.asciz "I am learning AT&T assembly language."
len:
.int . - str - 1 # 减一表示去掉最后的'0'
.section .text
.global _start
_start:
nop
pushl len
pushl $output
call printf
addl $8, %esp
leal str, %edi
movl $0xffff,%ecx # 支持的最大长度0xfffff = 65535
movb $0, %al
cld
repne scasb # 每次迭代ECX递减
jne notfound
subw $0xffff,%cx # %cx-0xffff为进行了多少次迭代的负数
neg %cx # 求补,即负数变整数
dec %cx # 减去‘0’,即字符串长度不包含最后的‘0’
movl $1, %eax
movl %ecx, %ebx
int $0x80
notfound:
movl $1, %eax
movl $0, %ebx
int $0x80
运行:
liury@liury-laptop:~/program/asm/working_with_string/str_len$ make
as -o str_len.o str_len.s
ld -dynamic-linker /lib/ld-linux.so.2 -lc -o str_len str_len.o
liury@liury-laptop:~/program/asm/working_with_string/str_len$ ./str_len
The len of the str is : 37.
liury@liury-laptop:~/program/asm/working_with_string/str_len$ echo $?
37
用两种方法求得字符串的长度,两者相等。