这篇文章没有参考任何官方文档
是作者反汇编整理出来的
编译方法:
nasm -f win64 -o 1.obj 1.asm
gcc 1.obj -o 1.exe
.\1
一、打印
global main
extern puts
SECTION .text
main:
sub rsp, 20h
mov rcx, message
call puts
add rsp, 20h
ret
message:
db "ASM!",0
等同于
global main
extern printf
section .text
main:
sub rsp, 20h
lea rcx, [message]
call printf
add rsp, 20h
ret
message:
db "ASM!",0
第一行:全局声明main主函数
第二行:声明外部函数,链接时用的
第三行:代码段开始,nasm特有
第四行:main函数
接下来:
为栈分配 32 字节空间(20h 十六进制表示 32)。这通常是为局部变量或函数调用准备栈空间。
将存储字符串 "ASM!" 的地址(即标签 `message` 的位置)放入寄存器 `rcx`,作为参数传递给函数。
调用外部函数 `puts`,该函数用于输出字符串到标准输出。
恢复栈指针,释放之前分配的 32 字节空间。
从函数返回。
message部分
db "ASM!",0 ; 定义一个字节序列,包含字符串 "ASM!" 和字符串结束标志 0。
注意,printf是lea,puts是mov,功能差不多,但不能混用
二、简单的程序
global main
extern printf
extern scanf
extern system
SECTION .data
Format db "请输入用户名:",0
aS db "%s",0
byte_404012 db "请输入密码:",0
Buffer db "用户名和密码:",0
Command db "pause",0
string1 db "错误",0
string2 db "正确",0
Admin db "admin",0
Password db "123456",0
SECTION .bss
username resb 50
password resb 50
SECTION .text
main:
push rbp
mov rbp, rsp
sub rsp, 190
; 输出提示scanf用户名
lea rcx, [Format]
call printf
; 读取用户名
lea rdx, [username]
lea rcx, [aS]
call scanf
; 输出提示scanf密码
lea rcx, [byte_404012]
call printf
; 读取密码
lea rdx, [password]
lea rcx, [aS]
call scanf
; cmp用户名
lea rsi, [Admin]
lea rdi, [username]
call compare_strings
cmp rax, 1
jne error
; cmp密码
lea rsi, [Password]
lea rdi, [password]
call compare_strings
cmp rax, 1
jne error
lea rcx, [string2]
call printf
jmp end
compare_strings:
push rbp
mov rbp, rsp
sub rsp, 16
xor rax, rax
xor rcx, rcx
loop_compare:
mov dl, [rsi + rcx]
mov al, [rdi + rcx]
cmp dl, al
jne not_l
cmp dl, 0
je l
inc rcx
jmp loop_compare
not_l:
mov rax, 0
leave
ret
l:
mov rax, 1
leave
ret
end:
; 暂停system
lea rcx, [Command]
call system
mov eax, 0
add rsp, 190
pop rbp
ret
error:
lea rcx, [string1]
call printf
jmp end
以下是对这段 NASM 汇编代码的逐行解释:
global main
extern printf
extern scanf
extern system
- 声明全局符号
main,表示这是程序的入口点。同时声明外部函数printf、scanf和system,表示程序将调用这些外部库函数。
SECTION.data
Format db "请输入用户名:",0
aS db "%s",0
byte_404012 db "请输入密码:",0
Buffer db "用户名和密码:",0
Command db "pause",0
string1 db "错误",0
string2 db "正确",0
Admin db "admin",0
Password db "123456",0
.data段用于存储初始化的数据。Format存储字符串 "请输入用户名:" 和字符串结束符0。aS存储格式化字符串"%s",用于读取字符串输入。byte_404012存储字符串 "请输入密码:" 和字符串结束符0。Buffer存储字符串 "用户名和密码:" 和字符串结束符0。Command存储字符串 "pause",用于暂停程序执行。string1存储字符串 "错误" 和字符串结束符0。string2存储字符串 "正确" 和字符串结束符0。Admin存储字符串 "admin" 和字符串结束符0,作为管理员用户名。Password存储字符串 "123456" 和字符串结束符0,作为管理员密码。
SECTION.bss
username resb 50
password resb 50
.bss段用于存储未初始化的数据。username预留 50 个字节的空间,用于存储用户输入的用户名。password预留 50 个字节的空间,用于存储用户输入的密码。
SECTION.text
main:
push rbp
mov rbp, rsp
sub rsp, 190
main函数开始。push rbp:将基址指针rbp压入栈,用于保存当前栈帧的基地址。mov rbp, rsp:将栈指针rsp的值赋给rbp,建立新的栈帧。sub rsp, 190:为局部变量和临时数据在栈上分配 190 个字节的空间。
; 输出提示 scanf 用户名
lea rcx, [Format]
call printf
- 输出提示用户输入用户名的字符串。
lea rcx, [Format]:将Format字符串的地址加载到寄存器rcx中,作为printf的参数。call printf:调用printf函数输出字符串
; 读取用户名
lea rdx, [username]
lea rcx, [aS]
call scanf
- 读取用户输入的用户名。
lea rdx, [username]:将username的地址加载到寄存器rdx中,作为scanf的存储地址。lea rcx, [aS]:将格式化字符串"%s"的地址加载到寄存器rcx中,作为scanf的格式参数。call scanf:调用scanf函数读取用户输入的字符串,并存储到username中。
; 输出提示 scanf 密码
lea rcx, [byte_404012]
call printf
- 输出提示用户输入密码的字符串。
lea rcx, [byte_404012]:将byte_404012字符串的地址加载到寄存器rcx中,作为printf的参数。call printf:调用printf函数输出字符串。
; 读取密码
lea rdx, [password]
lea rcx, [aS]
call scanf
- 读取用户输入的密码。
lea rdx, [password]:将password的地址加载到寄存器rdx中,作为scanf的存储地址。lea rcx, [aS]:将格式化字符串"%s"的地址加载到寄存器rcx中,作为scanf的格式参数。call scanf:调用scanf函数读取用户输入的字符串,并存储到password中。
; cmp 用户名
lea rsi, [Admin]
lea rdi, [username]
call compare_strings
cmp rax, 1
jne error
- 比较用户输入的用户名和管理员用户名。
lea rsi, [Admin]:将管理员用户名Admin的地址加载到寄存器rsi中。lea rdi, [username]:将用户输入的用户名username的地址加载到寄存器rdi中。call compare_strings:调用compare_strings函数比较两个字符串。cmp rax, 1:比较返回值rax是否为 1,表示两个字符串相等。jne error:如果不相等,跳转到error标签处,表示用户名错误。
; cmp 密码
lea rsi, [Password]
lea rdi, [password]
call compare_strings
cmp rax, 1
jne error
- 比较用户输入的密码和管理员密码。
lea rsi, [Password]:将管理员密码Password的地址加载到寄存器rsi中。lea rdi, [password]:将用户输入的密码password的地址加载到寄存器rdi中。call compare_strings:调用compare_strings函数比较两个字符串。cmp rax, 1:比较返回值rax是否为 1,表示两个字符串相等。jne error:如果不相等,跳转到error标签处,表示密码错误。
lea rcx, [string2]
call printf
jmp end
- 如果用户名和密码都正确,输出 "正确" 字符串。
lea rcx, [string2]:将字符串 "正确" 的地址加载到寄存器rcx中,作为printf的参数。call printf:调用printf函数输出字符串。jmp end:跳转到end标签处,结束程序。
compare_strings:
push rbp
mov rbp, rsp
sub rsp, 16
compare_strings函数开始。push rbp:将基址指针rbp压入栈,用于保存当前栈帧的基地址。mov rbp, rsp:将栈指针rsp的值赋给rbp,建立新的栈帧。sub rsp, 16:为局部变量和临时数据在栈上分配 16 个字节的空间。
xor rax, rax
xor rcx, rcx
loop_compare:
mov dl, [rsi + rcx]
mov al, [rdi + rcx]
cmp dl, al
jne not_l
cmp dl, 0
je l
inc rcx
jmp loop_compare
- 比较两个字符串的循环部分。
xor rax, rax:将寄存器rax清零。xor rcx, rcx:将寄存器rcx清零,用于作为字符串索引。loop_compare:循环标签。mov dl, [rsi + rcx]:将第一个字符串(由rsi指向)的当前字符加载到寄存器dl中。mov al, [rdi + rcx]:将第二个字符串(由rdi指向)的当前字符加载到寄存器al中。cmp dl, al:比较两个字符是否相等。jne not_l:如果不相等,跳转到not_l标签处。cmp dl, 0:比较当前字符是否为字符串结束符0。je l:如果是字符串结束符,表示两个字符串相等,跳转到l标签处。inc rcx:增加字符串索引rcx。jmp loop_compare:跳转到循环开始处,继续比较下一个字符。
not_l:
mov rax, 0
leave
ret
- 如果两个字符串不相等,设置返回值为 0,并返回。
mov rax, 0:将返回值设置为 0。leave:恢复栈帧,相当于mov rsp, rbp; pop rbp。ret:从函数返回。
l:
mov rax, 1
leave
ret
- 如果两个字符串相等,设置返回值为 1,并返回。
mov rax, 1:将返回值设置为 1。leave:恢复栈帧,相当于mov rsp, rbp; pop rbp。ret:从函数返回。
end:
; 暂停 system
lea rcx, [Command]
call system
- 暂停程序执行。
lea rcx, [Command]:将字符串 "pause" 的地址加载到寄存器rcx中,作为system的参数。call system:调用system函数执行系统命令,这里是暂停程序。
mov eax, 0
add rsp, 190
pop rbp
ret
- 结束程序。
mov eax, 0:将返回值设置为 0。add rsp, 190:恢复栈指针,释放之前分配的空间。pop rbp:恢复基址指针rbp。ret:从函数返回。
error:
lea rcx, [string1]
call printf
jmp end
- 如果用户名或密码错误,输出 "错误" 字符串,并跳转到
end标签处结束程序。lea rcx, [string1]:将字符串 "错误" 的地址加载到寄存器rcx中,作为printf的参数。call printf:调用printf函数输出字符串。jmp end:跳转到end标签处,结束程序。
三、文件操作
global main
extern fopen
extern fwrite
extern fclose
extern printf
SECTION .data
write_mode db "w", 0
file_name db "123.txt", 0
error_msg db "Error opening file", 0
hello_msg db "hello", 0
file_mode db "w",0
content db "Hello, this is a test content for file writing.",0
format db "File written successfully.",0
error db "Error opening file for writing."
SECTION .text
main:
push rbp
mov rbp, rsp
sub rsp, 48 ; 对应 sub rsp, 30h
; 设置 "w" 模式
mov rdx, write_mode
; 设置文件名 "123.txt"
mov rcx, file_name
call fopen
; 将返回的文件指针存储到相对于 rbp - 8 的位置
mov qword [rbp - 8], rax
cmp qword [rbp - 8], 0
jnz file_open_success
; 文件打开失败
mov rcx, error_msg
call printf
mov eax, 1
jmp end_program
file_open_success:
mov rax, [rbp - 8]
mov r9, rax ; Stream
mov r8d, 5 ; ElementCount
mov edx, 1 ; ElementSize
mov rcx, hello_msg ; "hello"
call fwrite
mov rax, [rbp - 8]
mov rcx, rax ; Stream
call fclose
mov eax, 0
end_program:
add rsp, 48 ; 对应 add rsp, 30h
pop rbp
ret
以下是对这段 NASM 汇编代码的逐行解析:
global main
extern fopen
extern fwrite
extern fclose
extern printf
- 声明全局符号
main,表示这是程序的入口点。同时声明外部函数fopen、fwrite、fclose和printf,表示程序将调用这些外部库函数。
SECTION.data
write_mode db "w", 0
file_name db "123.txt", 0
error_msg db "Error opening file", 0
hello_msg db "hello", 0
file_mode db "w",0
content db "Hello, this is a test content for file writing.",0
format db "File written successfully.",0
error db "Error opening file for writing."
.data段用于存储初始化的数据。write_mode存储字符串 "w" 和字符串结束符0,表示以写入模式打开文件。file_name存储字符串 "123.txt" 和字符串结束符0,表示要打开的文件名。error_msg存储字符串 "Error opening file" 和字符串结束符0,用于在文件打开失败时输出错误信息。hello_msg存储字符串 "hello" 和字符串结束符0,将写入文件的内容。file_mode存储字符串 "w" 和字符串结束符0,与write_mode重复,可能是冗余的。content存储一段较长的字符串,作为另一个可能写入文件的内容。format存储字符串 "File written successfully." 和字符串结束符0,用于在文件写入成功时输出提示信息。error存储字符串 "Error opening file for writing." 和字符串结束符0,与error_msg类似,可能是冗余的。
SECTION.text
main:
push rbp
mov rbp, rsp
sub rsp, 48 ; 对应 sub rsp, 30h
main函数开始。push rbp:将基址指针rbp压入栈,用于保存当前栈帧的基地址。mov rbp, rsp:将栈指针rsp的值赋给rbp,建立新的栈帧。sub rsp, 48:为局部变量和临时数据在栈上分配 48 个字节的空间。
; 设置 "w" 模式
mov rdx, write_mode
; 设置文件名 "123.txt"
mov rcx, file_name
call fopen
- 尝试以写入模式打开文件 "123.txt"。
mov rdx, write_mode:将写入模式字符串的地址加载到寄存器rdx中,作为fopen的模式参数。mov rcx, file_name:将文件名字符串的地址加载到寄存器rcx中,作为fopen的文件名参数。call fopen:调用fopen函数打开文件,并返回文件指针。
; 将返回的文件指针存储到相对于 rbp - 8 的位置
mov qword [rbp - 8], rax
- 将
fopen返回的文件指针存储在栈上相对于rbp - 8的位置,以便后续使用。
cmp qword [rbp - 8], 0
jnz file_open_success
- 检查文件是否成功打开。如果文件指针不为零,表示文件打开成功,跳转到
file_open_success标签处继续执行;如果文件指针为零,表示文件打开失败。
; 文件打开失败
mov rcx, error_msg
call printf
mov eax, 1
jmp end_program
- 如果文件打开失败,输出错误信息,设置返回值为 1,并跳转到
end_program标签处准备结束程序。mov rcx, error_msg:将错误信息字符串的地址加载到寄存器rcx中,作为printf的参数。call printf:调用printf函数输出错误信息。mov eax, 1:将返回值设置为 1。jmp end_program:跳转到end_program标签处。
file_open_success:
mov rax, [rbp - 8]
mov r9, rax ; Stream
mov r8d, 5 ; ElementCount
mov edx, 1 ; ElementSize
mov rcx, hello_msg ; "hello"
call fwrite
- 如果文件打开成功,将文件指针加载到寄存器中,设置写入参数,然后调用
fwrite函数向文件写入内容。mov rax, [rbp - 8]:将存储在栈上的文件指针加载到寄存器rax中。mov r9, rax:将文件指针复制到寄存器r9,作为fwrite的第一个参数(文件流指针)。mov r8d, 5:设置写入的元素个数为 5,这里可能是错误的,因为hello_msg的长度并不是 5。mov edx, 1:设置每个元素的大小为 1 字节。mov rcx, hello_msg:将要写入的内容(字符串 "hello")的地址加载到寄存器rcx中。call fwrite:调用fwrite函数向文件写入内容。
mov rax, [rbp - 8]
mov rcx, rax ; Stream
call fclose
- 关闭文件。
mov rax, [rbp - 8]:将存储在栈上的文件指针加载到寄存器rax中。mov rcx, rax:将文件指针复制到寄存器rcx,作为fclose的参数(文件流指针)。call fclose:调用fclose函数关闭文件。
mov eax, 0
- 设置返回值为 0,表示程序正常结束。
end_program:
add rsp, 48 ; 对应 add rsp, 30h
pop rbp
ret
end_program标签处,恢复栈指针,恢复基址指针,并从函数返回。add rsp, 48:恢复栈指针,释放之前分配的空间。pop rbp:恢复基址指针rbp。ret:从函数返回。
1524

被折叠的 条评论
为什么被折叠?



