创建/写入文件x86 Linux

时间:2017-08-20 19:54:31

标签: linux assembly x86

我一直在努力教自己一些32位x86(NASM)。我试图让用户输入文件名,打开/创建文件,然后获取用户的消息并将该消息写入文件。我在GDB中完成了它并且所有系统调用都正确返回。程序运行后,文件似乎创建不正确,没有任何内容写入。我看到其他一些类似的问题,但我的代码似乎与他们的代码差不多,所以我似乎无法弄清楚到底发生了什么。

这是我的菜鸟代码:

global _start
section .data
  fmsg:  db "Enter Filename: ", 0
  .len:  equ $ - fmsg
  umsg:  db "Enter message: ", 0
  .len:  equ $ - umsg
  buff:  times 50 db 0  ;array for user string
  .blen: equ $ - buff
  fname: times 50 db 0 ;array for filename
  .flen: equ $ - fname

  ;modes
  O_RDONLY: db 0        ;read-only
  O_WRONLY: db 1        ;wirte-only
  O_RDWR:   db 2        ;read and write

  ;flags
  O_CREAT:  dw 100o     ;create file if file doesnt exists
  O_TRUNC:  dw 1000o    ;truncate file
  O_APPEND: dw 2000o    ;append to file


section .bss
  fd:   resd 1          ;file descriptor
  bret: resd 1          ;read buffer return value
  fret: resd 1          ;read filename return value
  tmp:  resd 1          ;temp 4 byte variable

section .text
_start:

fprompt:               ;Print prompt
  mov eax, 0x4         ;syscall 4 - write()
  mov ebx, 0x1         ;file desc 1 - stdout
  mov ecx, fmsg        ;print message
  mov edx, fmsg.len    ;length of message
  int 80h              ;syscall interupt

filein:
  mov eax, 0x3          ;syscall 3 - read()
  mov ebx, 0x0          ;file desc 0 - stdin
  mov ecx, fname        ;dst buffer
  mov edx, fname.flen   ;length of buffer
  int 80h               ;syscall interupt
  mov [fret], eax       ;save return value to file return variable
  cmp eax, edx          ;read max bytes or more?
  jb  fileopen          ;jmp is bytes read < max
  mov bl, [ecx+eax-1]   ;grab last byte @ last index before '\0'
  cmp bl, 10            ;does it = '\n' ?
  je  clean1
  inc DWORD [fret]      ;len++

clean1:               ;loop to clear excess input, if any
  mov eax, 0x3        ;syscall 3 - read()
  mov ebx, 0x0        ;file desc 0 - stdin
  mov ecx, tmp        ;temp buffer
  mov edx, 0x1        ;read only 1 byte
  int 80h             ;;syscall interupt
  test eax, eax       ;EOF?
  jz  fileopen        ;Yes, jump to pback
  mov al, [tmp]       ;put character into lower 8 bits of EAX
  cmp al, 10          ;is it = to lf ?
  jne clean1          ;no, jump to begining of loop

fileopen:
  mov eax, 0x05
  mov ebx, fname      ;filename
  or  ecx, O_CREAT    ;if it doesn't exist create the file
  or  ecx, O_TRUNC    ;truncate
  mov edx, O_WRONLY   ;write only
  int 80h             ;syscall interupt
  mov [fd], eax       ;save file descripor

prompt2:
  mov eax, 0x4         ;syscall 4 - write()
  mov ebx, 0x1         ;file desc 1 - stdout
  mov ecx, umsg        ;print message
  mov edx, umsg.len    ;length of message
  int 80h

userin:
  mov eax, 0x3          ;syscall 3 - read()
  mov ebx, 0x0          ;file desc 0 - stdin
  mov ecx, buff         ;dst buffer
  mov edx, buff.blen    ;length of buffer
  int 80h               ;syscall interupt
  mov [bret], eax       ;save return value to buff return variable
  cmp eax, edx          ;read max bytes or more?
  jb  writetofile       ;jmp is bytes read < max
  mov bl, [ecx+eax-1]   ;grab last byte @ last index before '\0'
  cmp bl, 10            ;does it = '\n' ?
  je  clean2
  inc DWORD [bret]      ;len++

clean2:               ;loop to clear excess input, if any
  mov eax, 0x3        ;syscall 3 - read()
  mov ebx, 0x0        ;file desc 0 - stdin
  mov ecx, tmp        ;temp buffer
  mov edx, 0x1        ;read only 1 byte
  int 80h             ;syscall
  test eax, eax       ;EOF?
  jz  writetofile     ;Yes, jump to pback
  mov al, [tmp]       ;put character into lower 8 bits of EAX
  cmp al, 10          ;is it = to lf ?
  jne clean2          ;no, jump to begining of loop


writetofile:
  mov eax, 0x4         ;syscall 4 - write()
  mov ebx, [fd]        ;file desc 1 - stdout
  mov ecx, buff        ;print message
  mov edx, [bret]      ;length of message
  int 80h              ;syscall interupt

closefile:
  mov eax, 0x6      ;syscall 6 - close()
  mov ebx, [fd]     ;file desc
  int 80h           ;syscall interupt

exit:               ;return 0
  mov eax, 1        ;syscall 1 - exit()
  mov ebx, 0        ;return val
  int 80h           ;syscall interupt

以下是运行后获得的示例: Output

文件“test.txt?”显示并显示为可执行文件,即使我只设置文件的读/写。即使我试图打开它,也没有任何东西。有什么想法吗?另外正如我所提到的,我是新人并自学,所以如果你有任何关于改进其他方面的好方法,请告诉我! :)

2 个答案:

答案 0 :(得分:1)

我们在以下三行代码中有多个错误(或一个大错误):

or  ecx, O_CREAT    ;if it doesn't exist create the file
or  ecx, O_TRUNC    ;truncate
mov edx, O_WRONLY   ;write only

问题:

寄存器ecxedx在这些行之后有什么值?

您使用or寄存器执行了两次ecx操作,但显然此时尚未初始化!

这意味着您可以确定代表O_CREATO_TRUNC的位(无论这些值是什么意思 - 见下文)都已设置,但您不知道其他位具有哪些值。< / p>

O_WRONLY位应设置为ecx,而不是edxedx应该包含所需的文件模式。

不幸的是,有两种不同类型的汇编程序 - 我不知道NASM的类型是什么:

  • 一种类型的汇编程序会将第一条指令解释为:or ecx, [O_CREAT]
  • 另一种类型会将其解释为:or ecx, address_of(O_CREAT)

在第一种情况下,指令mov edx, O_WRONLY将从O_WRONLY字节开始读取四个字节到edx寄存器,因此edx将具有值0x400201 (O_CREAT *为0x10000 + O_RDWR *为0x100 + O_WRONLY)。

在第二种情况下,edx将包含O_WRONLY的地址,而不是值。

在任何情况下,该值都是错误的。

答案 1 :(得分:0)

将模式常量存储在内存中效率非常低,即使你做得对(你不是这样)。

您可以通过运行strace ./writefile跟踪系统调用的实际内容。

使用O_RDONLY: db 0存储一个静态数据字节(由于某种原因,在读/写部分而不是.rodata)。相反,您应该使用equ

定义汇编程序常量
  O_RDONLY equ 0
  O_WRONLY equ 1
  O_RDWR   equ 2

  O_CREAT  equ 100o     ;create file if file doesnt exists
  O_TRUNC  equ 1000o    ;truncate file
  O_APPEND equ 2000o    ;append to file

然后你可以写

mov  ecx, O_CREAT | O_TRUNC | O_WRONLY
mov  edx, 0777o                        ; mode is the permission bits if open() creates the file

请参阅the open(2) man page了解其args的工作原理。

汇编程序将在汇编时为您执行OR,而不是让CPU从内存中执行2次加载。

你实际写的是什么

  or  ecx, O_CREAT    ;if it doesn't exist create the file
  or  ecx, O_TRUNC    ;truncate

汇总到两条or ecx, imm32条指令,其中两个dw位置的地址或ecx的原始值。如果你写了

  movzx  ecx, word [O_CREAT]
  or      cx, [O_TRUNC]
  or      cx, [O_WRONLY]

你的代码会有效,但那真的很傻。 (并且在仅写入低16位后,某些CPU会在某些内容读取满ecx时导致部分寄存器停止。)

如果你写了or ecx, [O_TRUNC],它会做32位加载,所以你有效地做ecx |= (2000o << 16) | 1000o。即,存储在O_APPEND的字将被“或”到ECX的高16位,在那里它可能具有不同的含义。

同样,mov edx, O_WRONLY汇编到mov edx, imm32,其中地址是直接的。这就是为什么你最终为文件模式(包括粘滞位设置)的奇怪垃圾。

使用调试程序