如何在x86_64汇编中逐字节读取含零字节的二进制文件并计算数值?
x86_64汇编实现:逐字节读取二进制文件并计算退出值
嘿,我完全get到你的需求了——你想用x86_64汇编逐字节读取二进制文件,把每个字节转成0-255的无符号整数,最后算出一个结果(比如你举的C0 01 A2得到356)作为程序退出值,而且要避开字符串处理函数遇到零字节就罢工的坑对吧?
之前你看到的cat示例用块读取+字符串输出,确实会被零字节截断,所以咱们得换个思路:直接用系统调用操作文件,完全不碰C标准库的字符串函数。下面我给你拆解实现逻辑,再附上完整代码。
核心思路
- 直接用Linux系统调用(
open、read、close)处理文件,彻底规避字符串函数的零字节问题 - 每次只读取1个字节(
read系统调用可以指定读取长度为1),确保每个字节都能被单独处理 - 把读取到的单字节通过零扩展转换成0-255的无符号整数(避免符号位干扰)
- 按你的需求计算最终结果(比如你例子里的356,我会在代码里留好可修改的逻辑钩子)
- 最后把计算结果传给
exit系统调用作为退出码(注意:shell里看$?会取结果的低8位,但程序本身是传递完整数值的)
完整NASM语法代码
section .data ; 替换成你要读取的二进制文件名 filename db "test.bin", 0 err_msg db "Error accessing file", 0x0a err_len equ $ - err_msg section .bss current_byte resb 1 ; 存每次读取的单个字节 section .text global _start _start: ; 第一步:打开文件(sys_open) mov rax, 2 ; sys_open的系统调用号 mov rdi, filename ; 文件名地址 mov rsi, 0 ; 打开模式:只读 mov rdx, 0644o ; 权限(仅创建文件时有用,这里忽略) syscall cmp rax, 0 ; 检查是否打开失败(rax为负数表示错误) jl print_error ; 失败就跳去打印错误 mov rbx, rax ; 把文件描述符存到rbx里备用 ; 初始化计算相关的寄存器 mov rcx, 0 ; rcx存最终计算结果 mov r8, 0 ; r8用来计数读取的字节数 read_loop: ; 第二步:逐字节读取(sys_read) mov rax, 3 ; sys_read的系统调用号 mov rdi, rbx ; 文件描述符 mov rsi, current_byte mov rdx, 1 ; 读取长度:1字节 syscall cmp rax, 0 ; 检查读取结果:0=文件读完,负数=读取错误 je cleanup_exit ; 读完了就去收尾退出 jl print_error ; 读错了就打印错误 ; 把读取到的字节转成0-255的无符号整数 ; movzx是零扩展,把8位字节转成64位无符号数,完美避免符号问题 movzx rdx, byte [current_byte] ; -------------------------- ; 这里是你的计算逻辑,按需修改! ; 示例:对应你说的C0 01 A2得到356,我假设是取第2个字节*256 + 第3个字节 ; (01*256 + 100 = 356,如果你实际是其他逻辑,直接改这里就行) inc r8 cmp r8, 1 je read_loop ; 跳过第一个字节 cmp r8, 2 je save_second_byte ; 保存第二个字节 cmp r8, 3 je compute_final ; 计算最终结果 ; -------------------------- jmp read_loop save_second_byte: mov r10, rdx ; 把第二个字节存到r10 jmp read_loop compute_final: mov rax, r10 imul rax, 256 ; 第二个字节左移8位(等价于*256) add rax, rdx ; 加上第三个字节 mov rcx, rax ; 把结果存到rcx jmp cleanup_exit ; 去收尾退出 cleanup_exit: ; 第三步:关闭文件(sys_close) mov rax, 3 mov rdi, rbx syscall ; 退出程序,把计算结果作为退出码 mov rax, 60 ; sys_exit的系统调用号 mov rdi, rcx syscall print_error: ; 打印错误信息到stderr(sys_write) mov rax, 1 mov rdi, 2 ; stderr的文件描述符是2 mov rsi, err_msg mov rdx, err_len syscall ; 错误退出,退出码设为1 mov rax, 60 mov rdi, 1 syscall
关键细节说明
- 系统调用的优势:用
sys_open、sys_read这些底层调用,完全不依赖C标准库,所以零字节不会被当成字符串结束符,能完整读取所有二进制内容。 - 零扩展转换:
movzx指令是关键——它把8位的字节数据零扩展成64位整数,这样不管字节是0x00还是0xFF,都能正确转换成0或者255的无符号值,不会出现符号位错误。 - 计算逻辑可定制:代码里用注释框出来的部分就是你可以自由修改的计算逻辑,比如改成累加所有字节、按顺序拼接成大整数,或者只取特定位置的字节计算,完全按需调整。
- 错误处理:代码里处理了文件打开失败、读取失败的情况,会打印错误信息再退出,方便调试。
编译运行方法
用NASM编译成64位可执行文件:
nasm -f elf64 file_reader.asm -o file_reader.o ld file_reader.o -o file_reader
运行程序:
./file_reader
查看退出码(注意:超过255的话会取模256):
echo $?
如果需要输出完整的计算结果(而不是只用退出码),可以加一段代码把rcx里的数值转成字符串,再用sys_write打印出来——不过如果只是要退出值的话,当前代码就够用了。
内容的提问来源于stack exchange,提问作者Jytug
相关产品推荐
相关产品推荐

