Assembly Tutorial – Writing to a File

We’re going to cover one more quick file based example. To do this we will write a utility that reads from the command line and writes to a file. We will also try a couple of different file modes.

First lets look at the code:

.section .data
filename:
.string "output\.txt"

.section .bss
.lcomm buffer_data, 500

.section .text

.globl _start
_start:

movq $0, %rax
movq $0, %rdi
movq $buffer_data, %rsi
movq $500, %rdx
syscall

movq $2, %rax
movq $filename, %rdi
movq $0x41, %rsi
movq $0666, %rdx
syscall

movq %rax, %rdi
movq $1, %rax
movq $buffer_data, %rsi
movq $500, %rdx
syscall

movq $60, %rax
movq $0, %rdi
syscall

This should all be pretty familiar by now. First we define a string named “filename” with the value “output.txt” and we define a buffer named “buffer_data” of size 500 bytes.

The first few lines of instructions:

movq $0, %rax
movq $0, %rdi
movq $buffer_data, %rsi
movq $500, %rdx
syscall

read from stdin into our buffer. As usual, we set rax to 0 to indicate we are reading, rdi to 0 to as that is the file descriptor of stdin, rsi to the label of the memory buffer we wish to read to and rdx to the number of bytes we want to read. We then have another interrupt that creates the file we wish to write to:

movq $2, %rax
movq $filename, %rdi
movq $0x41, %rsi
movq $0666, %rdx
syscall

We set rax to 2 as this is the linux open file system call number and the filename goes in rdi. We set rdx to 0666 to indicate that every user will have read, write and execute permissions with this file. We set rsi to the mode we would like to use when opening this file. There are quite a few different flags you can use when opening files. They include

  • Create 0x40
  • Append 0x400
  • Truncate 0x200
  • Read Only 0x0
  • Write Only 0x1
  • Read and Write 0x2

You can combine these flags with bitwise or |. Note however, that you can’t combine read only and write only in this way. Also, notice that we prefaced the numeric value here with 0x to indicate it is a hexadecimal value. We didn’t bother using this preface previously as we were using the read only flag ‘0’ which is the same in hex and decimal.

Let’s look at some examples. In our above program, we wanted create a new file, and we only write to it, so we use the create and write only flags, 0x40 and 0x1 respectively. When we take the bitwise or of these flags we get

0x40 | 0x1 = 0x41

so we set rsi to 0x41.

When we assemble, link and run this file, the command line should give us an opportunity to enter text, which then gets written to a file named “output.txt”. If a file with that name already exists, the first line of that file will be overwritten.

Suppose that, when a file named “output.txt” already exists, we would rather completely overwrite it rather than just overwriting the first line. We do this by setting rsi to 0x241 when we perform the open file system call. 0x241 is the bitwise or of the create flag 0x40, the write only flag 0x1 and the truncate flag 0x200.

Now, suppose that, instead we wanted to append to the end of an existing file rather than overwriting it. To do this, when opening the file, we set the rsi register to 0x441, that is the open flag 0x40, the write flag 0x1 and the append flag 0x400. Now we can repeatedly run our binary and append text to the end of the file.

Leave a Reply

Your email address will not be published. Required fields are marked *