字符串
反转字符串
- 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所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ 汇编记述一08/04
- ♥ Windows 核心编程 _ 用户模式:线程同步三07/23
- ♥ Shell 语法记述 第三篇09/05
- ♥ MASM:概述一08/04
- ♥ Linux 内存映射与普通文件访问的区别03/31
- ♥ Linux 高性能服务器编程:服务器程序规范12/04
热评文章
- 相关指令 0
- X86_64汇编学习记述四 0
- 定位内存与数据处理 0
- MASM:概述一 0
- 关于程序 0
- MASM:OllyDBG二 0