phantom_wizard
phantom_wizard

Reputation: 3188

NASM: Two subsequent file writes not working

Trying to run this code so I could create bmp file - I write headline, then I want to write content to file - everything works separately but not together. I'm using hexedit for checking file if it matters.

If I run the code with headline writing part it works. If I run the code with content writing part it works. When I run both of them it doesn't.

Any ideas?

Here's the code:

section     .text
global      _start                              

_start:                                         

;#######################################################################
;### main ##############################################################
;#######################################################################

    ; open file
    mov     eax,8                               ;system call number - open/create file
    mov     ebx,msg                             ;file name
    mov     ecx,111111111b                      ;file mode
    int     0x80                                ;call kernel

    ; save file descriptor to r8d
    mov     r8d, eax

    ; write headline to file
    mov     eax, 4                              ;write 54 bytes to file
    mov     ebx, r8d                            ;load file desc
    mov     ecx, bmpheadline                    ;load adress of memory to write
    mov     edx, 54                             ;load number of bytes
    int     0x80                                ;call kernel

    ; write content to file
    mov     eax, 4                              ;number of syscall - write
    mov     ebx, r8d                            ;load file desc
    ;add     ebx, 54                             ;add 54 bytes to location of file location
    mov     ecx, empty_space                    ;load adress of buffer
    mov     edx, 40054                          ;load number of bytes
    int     0x80                                ;call kernel

    ; close file
    mov     eax, 6                              ;load syscall number - close
    mov     ebx, r8d                            ;load file desc
    int     0x80                                ;call kernel

    ; exit program
    mov     eax,1                               ;syscall number - exit     
    int     0x80                                ;call kernel

section     .data

    msg     db  'filename.bmp',0x00                 ;name of out file, 0x00 = end of string                        

    bmpheadline db  0x42,0x4D,0xB6,0xDA,0x01,0x00,0x00,0x00,0x00,0x00,0x7A,0x00,0x00,0x00,0x6C,0x00,0x00,0x00,0xC9,0x00,0x00,0x00,0xC9,0x00,0x00,0x00,0x01,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x3C,0xDA,0x01,0x00,0x13,0x0B,0x00,0x00,0x13,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x42,0x47,0x52,0x73,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00

section .bss

    empty_space: resb 40054

Upvotes: 3

Views: 364

Answers (3)

halfer
halfer

Reputation: 20335

(Posted solution on behalf of the OP).

Here is the source code of solution, 64 bit version:

section     .text
global      _start                              ;must be declared for linker (ld)

_start:                                         ;tell linker entry point

;#######################################################################
;### This program creates empty bmp file - 64 bit version ##############
;#######################################################################
;### main ##############################################################
;#######################################################################

    ; open file
    mov     rax,85                               ;system call number - open/create file
    mov     rdi,msg                             ;file name
                                                ;flags
    mov     rsi,111111111b                      ;mode
    syscall                                     ;call kernel

    ; save file descriptor
    mov     r8, rax

    ; write headline to file
    mov     rax, 1                              ;write to file
    mov     rdi, r8                             ;load file desc
    mov     rsi, bmpheadline                    ;load adress of memory to write
    mov     rdx, 54                             ;load number of bytes
    syscall                                     ;call kernel

    ; write content to file
    mov     rax, 1                              ;write to file
    mov     rdi, r8                             ;load file desc
    mov     rsi, empty_space                    ;load adress of memory to write
    mov     rdx, 40000                          ;load number of bytes
    syscall                                     ;call kernel

    ; close file
    mov     rax, 3                              ;load syscall number - close
    mov     rdi, r8                             ;load file desc
    syscall                                     ;call kernel

    ; exit program
    mov     rax,60                               ;system call number (sys_exit)
    syscall                                     ;call kernel

section     .data

    msg     db  'filename.bmp',0x00                 ;name of out file, 0x00 = end of string
    len     equ $ - msg                             ;length of our dear string

    bmpheadline db  0x42,0x4D,0xB6,0xDA,0x01,0x00,0x00,0x00,0x00,0x00,0x7A,0x00,0x00,0x00,0x6C,0x00,0x00,0x00,0xC9,0x00,0x00,0x00,0xC9,0x00,0x00,0x00,0x01,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x3C,0xDA,0x01,0x00,0x13,0x0B,0x00,0x00,0x13,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x42,0x47,0x52,0x73,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00

section .bss

    empty_space: resb 40000

Makefile:

all: a.out

a.out: main.o
        ld main.o

main.o: main64.asm
        nasm -f elf64 main64.asm -o main.o

Upvotes: 0

Michael Petch
Michael Petch

Reputation: 47653

There are 2 significant problems with your code. R8D (R8) is not preserved across int 0x80. Secondly, the add ebx, 54 in your original question is incorrect. You don't need to change the file descriptor.


SYSCALL preferred for 64-bit code

int 0x80 is an IA32 compatibility feature in the Linux kernel. This feature is generally turned on in most 64-bit Linux kernels but it can be turned off. You can't use 64-bit pointers with int 0x80. This prevents using stack based addresses as parameters to int 0x80. For these reasons it is preferred that you use SYSCALL for 64-bit programs rather than int 0x80.

More on using SYSCALL in Linux can be found in Ryan Chapman's Blog . Note that the system call numbers used with SYSCALL are different from int 0x80. The registers used to pass parameters are different, and the only registers not preserved across a SYSCALL are RCX, R11, and RAX (RAX being the return value). The system calling convention is thoroughly described in the current 64-bit Linux System V ABI. In particular:

  1. User-level applications use as integer registers for passing the sequence %rdi, %rsi, %rdx, %rcx, %r8 and %r9. The kernel interface uses %rdi, %rsi, %rdx, %r10, %r8 and %r9.
  2. A system-call is done via the syscall instruction. The kernel destroys registers %rcx and %r11.
  3. The number of the syscall has to be passed in register %rax.
  4. System-calls are limited to six arguments, no argument is passed directly on the stack.
  5. Returning from the syscall, register %rax contains the result of the system-call. A value in the range between -4095 and -1 indicates an error, it is -errno.
  6. Only values of class INTEGER or class MEMORY are passed to the kernel

If you want your 64-bit code to work with INT 0x80

INT 0x80 has some quirks in 64-bit code. It adheres to the 32-bit calling convention of preserving RBX, RCX, RDX, RSI, RDI, and RBP. For the other 64-bit registers the 64-bit C calling convention applies. From the ABI:

A.2.1 Calling Conventions

... applications that like to call system calls should use the functions from the C library. The interface between the C library and the Linux kernel is the same as for the user-level applications

See Figure 3.4: Register Usage in the 64-bit Linux ABI linked to above. R12, R13, R14, and R15 will also be preserved.

This means that RAX, R8, R9, R10, and R11 will not be preserved. Change your code from using R8D to one of the registers that are saved. R12D for example.


Why does your code fail?

Since R8D is not preserved across int 0x80 it is being potentially overwritten by the SYS_WRITE system calls. The first write works, the second one doesn't because R8D was likely trashed by the first SYS_WRITE, and R8D likely became an invalid file descriptor. Using one of the registers that will be preserved should solve this issue. If you run out of registers you can always allocate space on the stack for temporary storage.

Upvotes: 2

David Hoelzer
David Hoelzer

Reputation: 16379

You add 54 to the file descriptor without explanation; I have absolutely no clue why you are doing that.

I suspect that you misunderstand file descriptors and believe that you need to add the total amount of data written so far to the descriptor. This is not so. The descriptor does not change from the time you open/create to the time that you close the file handle. It's a really good idea to verify that your comments are synced with your code. When you are writing detailed comments, lines with no comments become immediately suspect (the add instruction, for instance.)

You appear to have some issues from the very beginning. For example, your comments say "open file" and "sys_write" but your code doesn't match. What your code currently does is attempt to call sys_creat. What you are calling the file descriptor is actually the permissions mode. ebx should contain the address of the string representing the path... The comments seem to indicate it should be stdout, but it's clearly not. :)

You also don't state whether this is for 64 bit or 32 bit Linux. Your code seems to mix the two, using r8d and using int 0x80.

Upvotes: 2

Related Questions