You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

32位x86汇编文件写入异常:年龄未存储+格式错乱求助

32位x86汇编写入文件的问题排查与修复

问题描述

在Kali Linux环境下测试32位x86汇编代码,编译命令为as --32 myapp.s -o myapp.o、ld -m elf_i386 myapp.o -o myapp。代码意图将3条包含姓名、地址、long类型年龄的记录写入test.dat文件,但运行后出现两个问题:

  • 定义为long类型的年龄字段未被正确存储;
  • 输出内容格式错乱,出现“-MarMarilyn”“DerrDerrick”这类异常拼接内容,预期每条记录的姓名、地址、年龄依次换行显示,实际不符合预期。

原汇编代码如下:

.section .data
.equ RECORD_FIRSTNAME, 0
.equ RECORD_LASTNAME, 40
.equ RECORD_ADDRESS, 80
.equ RECORD_AGE, 320
.equ RECORD_SIZE, 324
.equ SYS_EXIT, 1
.equ SYS_READ, 3
.equ SYS_WRITE, 4
.equ SYS_OPEN, 5
.equ SYS_CLOSE, 6
.equ SYS_BRK, 45
.equ LINUX_SYSCALL, 0x80
.equ STDIN, 0
.equ STDOUT, 1
.equ STDERR, 2
.equ END_OF_FILE, 0
.equ ST_WRITE_BUFFER, 8
.equ ST_FILEDES, 12

record1:
.ascii "Fredrick\0"
.rept 31 #Padding to 40 bytes
.byte 0
.endr
.ascii "Bartlett\0"
.rept 31 #Padding to 40 bytes
.byte 0
.endr
.ascii "4242 S Prairie\nTulsa, OK 55555\0"
.rept 209 #Padding to 240 bytes
.byte 0
.endr
.long 45
record2:
.ascii "Marilyn\0"
.rept 32 #Padding to 40 bytes
.byte 0
.endr
.ascii "Taylor\0"
.rept 33 #Padding to 40 bytes
.byte 0
.endr
.ascii "2224 S Johannan St\nChicago, IL 12345\0"
.rept 203 #Padding to 240 bytes
.byte 0
.endr
.long 29
record3:
.ascii "Derrick\0"
.rept 32 #Padding to 40 bytes
.byte 0
.endr
.ascii "McIntire\0"
.rept 31 #Padding to 40 bytes
.byte 0
.endr
.ascii "500 W Oakland\nSan Diego, CA 54321\0"
.rept 206 #Padding to 240 bytes
.byte 0
.endr
.long 36
file_name:
.ascii "test.dat\0"
.equ ST_FILE_DESCRIPTOR, -4

.section .text
.globl _start
_start:
#Copy the stack pointer to %ebp
movl %esp, %ebp
#Allocate space to hold the file descriptor
subl $4, %esp
#Open the file
movl $SYS_OPEN, %eax
movl $file_name, %ebx
movl $0101, %ecx #This says to create if it
#doesn’t exist, and open for
#writing
movl $0666, %edx
int $LINUX_SYSCALL
#Store the file descriptor away
movl %eax, ST_FILE_DESCRIPTOR(%ebp)
#Write the first record
pushl ST_FILE_DESCRIPTOR(%ebp)
pushl $record1
call write_record
addl $8, %esp
#Write the second record
pushl ST_FILE_DESCRIPTOR(%ebp)
pushl $record2
call write_record
addl $8, %esp
#Write the third record
pushl ST_FILE_DESCRIPTOR(%ebp)
pushl $record3
call write_record
addl $8, %esp
#Close the file descriptor
movl $SYS_CLOSE, %eax
movl ST_FILE_DESCRIPTOR(%ebp), %ebx
int $LINUX_SYSCALL
#Exit the program
movl $SYS_EXIT, %eax
movl $0, %ebx
int $LINUX_SYSCALL


.globl write_record
.type write_record, @function
write_record:
pushl %ebp
movl %esp, %ebp
pushl %ebx
movl $SYS_WRITE, %eax
movl ST_FILEDES(%ebp), %ebx
movl ST_WRITE_BUFFER(%ebp), %ecx
movl $RECORD_SIZE, %edx
int $LINUX_SYSCALL
#NOTE - %eax has the return value, which we will
# give back to our calling program
popl %ebx
movl %ebp, %esp
popl %ebp
ret

问题根源

1. 年龄字段存储错误

你定义的每条记录总长度是324字节(40+40+240+4),但各个字段的填充字节数计算错误,导致实际字段偏移和定义的RECORD_AGE(320)不匹配。比如:

  • record1的firstname是"Fredrick\0"(8字节),你只填充了31个0,总共39字节,少1字节;
  • lastname是"Bartlett\0"(8字节),同样填充31个0,又少1字节;
    这就导致后面的address和age字段整体偏移2字节,最终年龄的long值没有写到正确的位置,存储自然出错。

2. 内容拼接错乱

因为每条记录的实际长度超过了定义的RECORD_SIZE(324),当write_record调用SYS_WRITE写入324字节时,会把下一条记录的开头部分也写入当前记录的末尾,比如record1的末尾会带上record2的firstname开头,就出现了“-MarMarilyn”这类拼接错误。

修复方案

步骤1:修正各字段的填充字节数

计算每个字符串的实际长度(包含末尾的\0),用目标字段长度减去该值得到需要填充的0的数量:

  • record1:
    • firstname:"Fredrick\0"共8字节,需填充40-8=32个0(把.rept 31改成.rept 32);
    • lastname:"Bartlett\0"共8字节,需填充40-8=32个0(把.rept 31改成.rept 32);
    • address:"4242 S Prairie\nTulsa, OK 55555\0"共32字节,需填充240-32=208个0(把.rept 209改成.rept 208);
  • record2:
    • firstname:"Marilyn\0"共7字节,需填充40-7=33个0(把.rept 32改成.rept 33);
    • lastname:"Taylor\0"共6字节,需填充40-6=34个0(把.rept 33改成.rept 34);
    • address:"2224 S Johannan St\nChicago, IL 12345\0"共38字节,需填充240-38=202个0(把.rept 203改成.rept 202);
  • record3:
    • firstname:"Derrick\0"共7字节,需填充40-7=33个0(把.rept 32改成.rept 33);
    • address:"500 W Oakland\nSan Diego, CA 54321\0"共35字节,需填充240-35=205个0(把.rept 206改成.rept 205);

步骤2:验证记录总长度

修正后每条记录的长度正好是40+40+240+4=324字节,和RECORD_SIZE一致,写入时就不会越界到下一条记录。

可选:年龄字段可读性优化

当前代码写入的是long类型的二进制值,文本编辑器查看会显示乱码。如果需要文件中显示可读的数字,需要把年龄数值转换成ASCII字符串再写入(比如把.long 45改成.ascii "45\0"并填充到4字节),但如果是存储二进制数据,原写法没问题,用xxd test.dat就能看到正确的十六进制值。

修正后的完整代码

.section .data
.equ RECORD_FIRSTNAME, 0
.equ RECORD_LASTNAME, 40
.equ RECORD_ADDRESS, 80
.equ RECORD_AGE, 320
.equ RECORD_SIZE, 324
.equ SYS_EXIT, 1
.equ SYS_READ, 3
.equ SYS_WRITE, 4
.equ SYS_OPEN, 5
.equ SYS_CLOSE, 6
.equ SYS_BRK, 45
.equ LINUX_SYSCALL, 0x80
.equ STDIN, 0
.equ STDOUT, 1
.equ STDERR, 2
.equ END_OF_FILE, 0
.equ ST_WRITE_BUFFER, 8
.equ ST_FILEDES, 12

record1:
.ascii "Fredrick\0"
.rept 32 #Padding to 40 bytes (8+32=40)
.byte 0
.endr
.ascii "Bartlett\0"
.rept 32 #Padding to 40 bytes (8+32=40)
.byte 0
.endr
.ascii "4242 S Prairie\nTulsa, OK 55555\0"
.rept 208 #Padding to 240 bytes (32+208=240)
.byte 0
.endr
.long 45
record2:
.ascii "Marilyn\0"
.rept 33 #Padding to 40 bytes (7+33=40)
.byte 0
.endr
.ascii "Taylor\0"
.rept 34 #Padding to 40 bytes (6+34=40)
.byte 0
.endr
.ascii "2224 S Johannan St\nChicago, IL 12345\0"
.rept 202 #Padding to 240 bytes (38+202=240)
.byte 0
.endr
.long 29
record3:
.ascii "Derrick\0"
.rept 33 #Padding to 40 bytes (7+33=40)
.byte 0
.endr
.ascii "McIntire\0"
.rept 31 #Padding to 40 bytes (9+31=40)
.byte 0
.endr
.ascii "500 W Oakland\nSan Diego, CA 54321\0"
.rept 205 #Padding to 240 bytes (35+205=240)
.byte 0
.endr
.long 36
file_name:
.ascii "test.dat\0"
.equ ST_FILE_DESCRIPTOR, -4

.section .text
.globl _start
_start:
#Copy the stack pointer to %ebp
movl %esp, %ebp
#Allocate space to hold the file descriptor
subl $4, %esp
#Open the file
movl $SYS_OPEN, %eax
movl $file_name, %ebx
movl $0101, %ecx #Create if not exist, open for writing
movl $0666, %edx
int $LINUX_SYSCALL
#Store the file descriptor away
movl %eax, ST_FILE_DESCRIPTOR(%ebp)
#Write the first record
pushl ST_FILE_DESCRIPTOR(%ebp)
pushl $record1
call write_record
addl $8, %esp
#Write the second record
pushl ST_FILE_DESCRIPTOR(%ebp)
pushl $record2
call write_record
addl $8, %esp
#Write the third record
pushl ST_FILE_DESCRIPTOR(%ebp)
pushl $record3
call write_record
addl $8, %esp
#Close the file descriptor
movl $SYS_CLOSE, %eax
movl ST_FILE_DESCRIPTOR(%ebp), %ebx
int $LINUX_SYSCALL
#Exit the program
movl $SYS_EXIT, %eax
movl $0, %ebx
int $LINUX_SYSCALL


.globl write_record
.type write_record, @function
write_record:
pushl %ebp
movl %esp, %ebp
pushl %ebx
movl $SYS_WRITE, %eax
movl ST_FILEDES(%ebp), %ebx
movl ST_WRITE_BUFFER(%ebp), %ecx
movl $RECORD_SIZE, %edx
int $LINUX_SYSCALL
#Return the write result to caller
popl %ebx
movl %ebp, %esp
popl %ebp
ret

内容的提问来源于stack exchange,提问作者user25018149

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 08:19:48