字符串
反转字符串
- 4个常量
1 2 3 4 5 6 7 8 |
section .data SYS_WRITE equ 1 STD_OUT equ 1 SYS_EXIT equ 60 EXIT_CODE equ 0 NEW_LINE db 0xa INPUT db "hello world." |
1 2 |
section .bss OUTPUT resb 12 |
1 2 3 4 5 6 7 8 9 |
_start: mov rsi, INPUT xor rcx,rcx cld mov rdi, $+15 call calculateStrLen xor rax, rax xor rdi, rdi jmp reverseStr |
1 2 3 4 5 6 7 |
calculateStrLen: cmp byte [rsi], 0 je exitFromRoutine lodsb push rax inc rcx jmp calculateStrLen |
1 2 3 |
exitFromRoutine: push rdi ret |
1 2 3 4 5 6 7 8 |
reverseStr: cmp rcx, 0 je printRes pop rax mov [OUTPUT + rdi], rax dec rcx inc rdi jmp reverseStr |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
printRes: mov rdx, rdi mov rax, 1 mov rdi, 1 mov rsi, OUTPUT syscall jmp printNewLine printNewLine: mov rax, SYS_WRITE mov rdi, STD_OUT mov rsi, NEW_LINE mov rdx, 1 syscall jmp exit exit: mov rax, SYS_EXIT mov rdi, EXIT_CODE syscall |
解析
- 从
_start
开始- 把
INPUT
地址放入rsi
寄存器 - 通过异或将
rcx
清零 - 用
cld
清除方向标志位,让后续字符串操作从低地址向高地址进行。
- 把
1 2 3 |
mov rsi, INPUT xor rcx,rcx cld |
- 保存返回地址,当
exitFromRoutine
结束后,正确返回
1 |
mov rdi, $+15 |
calculateStrLen
- 先判断
rsi
寄存器是不是指向0,如果是,意味着到了字符串的尾部了,退出函数
- 先判断
- (设置cld后),lodsb每次都会将
rsi
从左到右移动一个字节 - 然后把
rax
值压入栈 - 同样的道理,把字符串的其他符号都压入栈
1 2 3 4 |
lodsb push rax inc rcx jmp calculateStrLen |
- 循环节后,字符串所有字符都被压入栈中
- 开始执行
exitFromRoutine
,返回到calculateStrLen
下一句 - 然后把
rax
和rdi
清零,开始条用reverseStr
1 2 3 |
xor rax, rax xor rdi, rdi jmp reverseStr |
- 循环,检查计数器,即字符串的长度,如果它为零,我们将所有符号写入缓冲区并可以打印它
- 检查计数器后,将第一个符号从堆栈弹出到
rax
寄存器,并将其写入OUTPUT
缓冲区 - 输出,换行,退出
1 2 3 4 5 6 7 |
printResult: mov rdx, rdi mov rax, 1 mov rdi, 1 mov rsi, OUTPUT syscall jmp printNewLine |
字符串操作
rep
:当rcx
不为零时重复movsb
:复制字节字符串cmpsb
:字节串比较scasb
:字节串扫描stosb
:将字节写入字符串
问题
- 这个程序跑起来似乎有问题,后续观察下:todo
cld
- 在x86架构的汇编语言中,
CLD
指令用于清除方向标志(Direction Flag, DF
) - 方向标志是在标志寄存器(
Flags Register
)中的一个位,它影响某些字符串操作的处理方向- 当方向标志被设置时(通过使用
STD
指令),一些字符串操作,如MOVS
(移动字符串)、LODS
(加载字符串)、STOS
(存储字符串)、SCAS
(扫描字符串)等,将从高地址向低地址操作。这意味着它们将从内存的末尾开始,并向内存的开始方向移动。 - 当方向标志被清除时(通过使用
CLD
指令),这些操作将从低地址向高地址进行,从内存的开始位置并向内存的末尾方向移动
- 当方向标志被设置时(通过使用
1 2 |
exitFromRoutine: ret |
$
- 返回
$
定义的字符串在内存中的位置
$$
- 返回当前
section
在内存中开始的位置
宏
- 宏中定义的所有标签必须以
%%
开头
单行
1 2 |
%define macro_name(parameter) value |
1 2 |
%define argc rsp + 8 %define cliArg1 rsp + 24 |
1 2 3 4 5 6 |
;; ;; argc will be expanded to rsp + 8 ;; mov rax, [argc] cmp rax, 3 jne .mustBe3args |
多行
1 2 3 4 5 |
%macro number_of_parameters instruction instruction instruction %endmacro |
1 2 3 4 |
%macro bootstrap 1 push ebp mov ebp,esp %endmacro |
1 2 |
_start: bootstrap |
示例
print
__syscall_write
带有2个参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
%macro PRINT 1 pusha pushf jmp %%astr %%str db %1, 0 %%strln equ $-%%str %%astr: _syscall_write %%str, %%strln popf popa %endmacro %macro _syscall_write 2 mov rax, 1 mov rdi, 1 mov rsi, %%str mov rdx, %%strln syscall %endmacro |
- 使用这个宏
1 |
label: PRINT "Hello World!" |
标准宏
- 定义数据结构
1 2 3 4 |
struc person name: resb 10 age: resb 1 endstruc |
- 示例
1 2 3 4 5 6 7 8 9 |
section .data p: istruc person at name db "name" at age db 25 iend section .text _start: mov rax, [p + person.name] |
%include
- 可以包含其他汇编文件并跳转到那里的标签或使用
%include
指令调用函数
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ Linux 高性能服务器编程:高性能服务器架构二12/05
- ♥ Make&&Makefile03/23
- ♥ 打包_7z生成自解压打包exe07/11
- ♥ Linux_ 命令大全 文档编辑03/16
- ♥ Linux 内存映射与普通文件访问的区别03/31
- ♥ Linux 高性能服务器编程:I/O复用一12/11