How to Append to a File Using X86-64 Linux System Calls

How to append to a file using x86-64 Linux system calls?

O_APPEND by itself would imply O_RDONLY (because it's 0), not O_WRONLY or O_RDWR. Opening read-only for append isn't usually useful. As in C, you need to OR together multiple flags to get a file descriptor you can use to write at the end of the file, even if its length is changed by something other than than your writes.


For appending to a file use the O_APPEND = 0x400 flag as int flags parameter in rsi. So you were close: it's a value of hexadecimal 400 and not decimal 400 as you attempted.

Here is a list of the raw int flags values in hexadecimal:

O_ACCMODE                        = 0x3
O_APPEND = 0x400
O_ASYNC = 0x2000
O_CLOEXEC = 0x80000
O_CREAT = 0x40
O_DIRECT = 0x4000
O_DIRECTORY = 0x10000
O_DSYNC = 0x1000
O_EXCL = 0x80
O_FSYNC = 0x101000
O_LARGEFILE = 0x0
O_NDELAY = 0x800
O_NOATIME = 0x40000
O_NOCTTY = 0x100
O_NOFOLLOW = 0x20000
O_NONBLOCK = 0x800
O_RDONLY = 0x0
O_RDWR = 0x2
O_RSYNC = 0x101000
O_SYNC = 0x101000
O_TRUNC = 0x200
O_WRONLY = 0x1

So this should work:

mov   rax, 2
lea rdi, [rel path]
mov rsi, 0x441 ; O_CREAT| O_WRONLY | O_APPEND
mov edx, 0q666 ; octal permissions in case O_CREAT has to create it
syscall
mov r8, rax ; save the file descriptor

I added the three values to create the file if it doesn't exist or open it in write_only mode if it does exist. You could define them as assemble-time equ constants so you could actually write mov esi, O_CREAT| O_WRONLY | O_APPEND in your source.

Also, make sure to supply a value for the 3rd arg (permissions) any time you include O_CREAT. Otherwise the file could get created with random garbage for its permissions, including possibly setuid + exec. Normally you pass octal 666 (NASM 0q666) and let the user's umask knock off write permission for other and optionally group.

If you don't actually want open to create it if it doesn't already exist, omit O_CREAT. Then it will return -ENOENT.



Also is there a way to not specify the length of the string in rdx, but instead have the string be terminated by a /0?

In short: No.

Create a strlen function and pass the result in rdx. Kernel system calls take data to be read/written on file descriptors as buffer + length, only pathnames as C implicit-length strings.

Nasm x86_64: Why can't I write and read from the same file?

Since the file are read sequentially, after a call to sys_read in append mode the cursor is moved to the end of the file, so if you try to read from that position you won't read anything.

To solve this problem you have to reposition the cursor at the beginning of the file.

To do that you can use the lseek system call.

    mov     rax, 8        ;system call Id for sys_lseek
mov rdi, [rsp] ;file descriptor
mov rsi, 0 ;offset value, so number of characters to move the cursor
mov rdx, 0 ;It indicates the initial position from which move the cursor, in this case the value 0 indicates that the initial position is the beginning of the file
syscall

After a call to this system call the cursor position will be at the beginning of the file and you will be able to read from it.

Concatenate one .data and one .bss string together in x86_64 assembly

You don't need the terminating NUL of a string since you don't use functions that check for this character.

Delete the colon in section .text:. Then you can debug the program with GDB.

I suggest to append secondText directly to the entered name. For this you have to reserve more space for the variable name. SYS_READ does not notice this, so the space remains blank after the call to SYS_READ.

section .data
promptText: db "What is your name: "
promptTextLength: equ $ - promptText
secondText: db " is your name?", 10
secondTextLength: equ $ - secondText

section .bss
name resb 15 + secondTextLength ; Space for input and appended string

section .text
global _start

_start:

; Print prompt message
mov rax, 1 ; SYS_WRITE - http://man7.org/linux/man-pages/man2/write.2.html
mov rdi, 1 ; fd = STDOUT
mov rsi, promptText ; *buf
mov rdx, promptTextLength ; Count of bytes to write
syscall ; Call Linux

; Get name
mov rax, 0 ; SYS_READ - http://man7.org/linux/man-pages/man2/read.2.html
mov rdi, 0 ; fd = STDIN
mov rsi, name ; *buf
mov rdx, 15 ; Max count of bytes to read
syscall ; Call Linux - return EAX = number of bytes read
dec rax ; number of bytes read without ENTER

; Append secondText
mov rsi, secondText ; *source
mov rdi, name ; *dest
add rdi, rax ; Set pointer one byte behind the real name
mov rcx, secondTextLength ; Count of bytes to copy
lea rbx, [rax + rcx] ; Save the total length of the string
rep movsb ; Copy RCX bytes from [RSI] to [RDI]

; Print name (input + second message)
mov rax, 1 ; SYS_WRITE - http://man7.org/linux/man-pages/man2/write.2.html
mov rdi, 1 ; fd = STDOUT
mov rsi, name ; *buf
mov rdx, rbx ; Count of bytes to write (RBX was saved above)
syscall ; Call Linux

; Exit (0)
mov rax, 60 ; SYS_EXIT
mov rdi, 0 ; Exitcode
syscall ; Call Linux / no return

Using Assembly Language on Mac to Append a file

Below is the corrected assembly language code version (AT&T style) for appending a file on mac os x (MacOS).

Compile your 'myfile.s' assembly file in Mac Terminal using the 'as' and 'ld' commands:

as -m32 -o myfile.o myfile.s

ld -e _main -o myfile myfile.o

.data
filename:
.asciz "cpuid.txt"
output:
.asciz "The processor Vendor ID is 'xxxxxxxxxxxx'\n"

.bss
.lcomm filehandle, 4

.text
.globl _main
_main:
movl $0, %eax

# Get the CPUID and place the CPUID values (stored in ebx, edx and ecx) accordingly within,
# the correct address space, after the 'output' address.
cpuid
movl $output, %edi
movl %ebx, 28(%edi)
movl %edx, 32(%edi)
movl %ecx, 36(%edi)

# OPEN/CREATE A FILE:
movl $5, %eax
movl $filename, %ebx
movl $0x09, %ecx # Access mode values loaded into ECX
# on Linux, APPEND TEXT FILE using a $02002(octal) according to "Professional Assembly Language" (PAL) textbook
# however, on Mac OS X (MacOS), APPEND mode is 0x0008, according to usr/include/sys/fcntl.h
# ... so for write (0x01) and append (0x08) access (O_WRONLY | O_APPEND), the value becomes 0x09
# (for read/write (0x02) and append (0x08) access (O_RDWR|O_APPEND), the value would be 0x0a)

movl $0644, %edx # file permission values loaded into EDX

# For MacOS, we need to put all of this on the stack (in reverse order),
# and, add an additional 4-bytes of space on the stack,
# prior to the system call (with 'int')
pushl %edx
pushl %ecx
pushl %ebx
subl $4, %esp
int $0x80 # ...make the system call
addl $16, %esp # clear the stack

test %eax, %eax # check the error code returned (stored in EAX) after attempting to open/create the file
js badfile # if the value was negative (i.e., an error occurred, then jump)
movl %eax, filehandle # otherwise, move the error code to the 'filehandle'

# WRITE TO FILE:
movl $4, %eax
movl filehandle, %ebx
movl $output, %ecx
movl $42, %edx

# once again, for MacOS, put all of this on the stack,
# and, include an additional 4-bytes of space on the stack,
# (stack grows downward, thus the 'subl' instruction) prior to the 'int' system call
pushl %edx
pushl %ecx
pushl %ebx
subl $4, %esp
int $0x80
addl $16, %esp # and, again, clear the stack

test %eax, %eax
js badfile

# CLOSE THE FILE:
movl $6, %eax
movl filehandle, %ebx

# okay, move it onto the stack again (only one parameter on stack for closing this time)
pushl %ebx
subl $4, %esp
int $0x80

addl $8, %esp

badfile:
subl $9, %esp
movl %eax, %ebx
movl $1, %eax
int $0x80

Adding a new system call in Linux kernel 3.3

I think in kernel 3.3 its shifted here

http://lxr.free-electrons.com/source/arch/x86/syscalls/

How to write system calls on debian/ubuntu

This is just example how to write a simple kernel system call.
Consider the following C function system_strcpy() that simply copies one string into another: similar to what strcpy() does.

#include<stdio.h>

long system_strcpy(char* dest, const char* src)
{
int i=0;
while(src[i]!=0)
dest[i]=src[i++];

dest[i]=0;
return i;
}

Before writing, get a kernel source tar and untar it to get a linux-x.x.x directory.

File 1: linux-x.x.x/test/system_strcpy.c
Create a directory within the linux-x.x.x, named test and save this code as file system_strcpy.c in it.

#include<linux/linkage.h>
#include<linux/kernel.h>
asmlinkage long system_strcpy(char*dest, const char* src)
{
int i=0;
while(src[i]!=0)
dest[i]=src[i++];

dest[i]=0;
return i;
}

File 2: linux-x.x.x/test/Makefile
Create a Makefile within the same test directory you created above and put this line in it:

obj-y := system_strcpy.o

File 3: linux-x.x.x/arch/x86/kernel/syscall_table_32.S
Now, you have to add your system call to the system call table.
Append to the file the following line:

.long system_strcpy

NOTE: For Kernel 3.3 and higher versions.

*Refer:linux-3.3.xx/arch/x86/syscalls/syscall_64.tbl*

And in there, now add at the end of the following series of lines:

310 64 process_vm_readv sys_process_vm_readv

311 64 process_vm_writev sys_process_vm_writev

312 64 kcmp sys_kcmp

313 64 system_strcpy system_strcpy

The format for the 3.3 version is in:
number abi name entry point

File 4: linux-x.x.x/arch/x86/include/asm/unistd_32.h

NOTE: This section is redundant for 3.3 and higher kernel versions

In this file, the names of all the system calls will be associated with a unique number. After the last system call-number pair, add a line

#define __NR_system_strcpy 338

(if 337 was the number associated with the last system call in the system call-number pair).

Then replace NR_syscalls value, stating total number of system calls with (the existing number incremented by 1) i.e. in this case the NR_syscalls should've been 338 and the new value is 339.

#define NR_syscalls 339

File 5: linux-x.x.x/include/linux/syscalls.h

Append to the file the prototype of our function.

asmlinkage long system_strcpy(char *dest,char *src);

just before the #endif line in the file.

File 6: Makefile at the root of source directory.

Open Makefile and find the line where core-y is defined and add the directory test to the end of that line.

core-y += kernel/ mm/ fs/ test/

Now compile the kernel. Issue:
make bzImage -j4

Install the kernel by executing the following command as root(or with root permissions):
make install

Reboot the system.

To use the recently created system call use:

syscall(338,dest,src); (or syscall(313,dest,src); for kernel 3.3+) instead of the regular strcpy library function.

#include "unistd.h"
#include "sys/syscall.h"
int main()
{
char *dest=NULL,*src="Hello";
dest=(char*)malloc(strlen(src)+1);
syscall(338,dest,src);//syscall(313,dest,src); for kernel 3.3+
printf("%s \n %s\n",src,dest);
return 0;
}

Instead of numbers like 313,etc in syscall, you can also directly use __NR_system_strcpy

This is a generic example. You will need to do a little experimentation to see what works for your specific kernel version.

How do you allocate memory on the heap without using libc in linux

Well, a great opportunity to use strace and use the debugger. From man 2 syscall:

   Arch/ABI      arg1  arg2  arg3  arg4  arg5  arg6  arg7  Notes
──────────────────────────────────────────────────────────────
x86-64 rdi rsi rdx r10 r8 r9 -

gdb a.out:

(gdb) b syscalls.s:5
Breakpoint 1 at 0x1050: file syscalls.s, line 5.
(gdb) r
Starting program: /dev/shm/.1000.home.tmp.dir/a.out

Breakpoint 1, mmap () at syscalls.s:5
5 syscall
(gdb) info registers
rax 0x9 9
rbx 0x0 0
rcx 0x22 34 # here it is
rdx 0x3 3
rsi 0x1000 4096
rdi 0x0 0
rbp 0x7fffffffd968 0x7fffffffd968
rsp 0x7fffffffd950 0x7fffffffd950
r8 0xffffffffffffffff -1
r9 0x0 0
r10 0x555555554000 93824992231424 # WRONG!
r11 0x206 518
r12 0x555555555000 93824992235520
r13 0x7fffffffd970 140737488345456
r14 0x0 0
r15 0x0 0
rip 0x555555555050 0x555555555050 <mmap+7>
eflags 0x202 [ IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0

We see the value in r10 is some garbage, while 0x22 is in rcx. Consult https://uclibc.org/docs/psABI-x86_64.pdf .

You have to do https://github.com/numactl/numactl/blob/master/syscall.c#L160 some rotating. Just mov %rcx, %r10 is enough.

Overall, use https://github.com/lattera/glibc/blob/master/sysdeps/unix/sysv/linux/x86_64/syscall.S#L29 .

Is it possible to intercept stat calls on files in a Linux file system? (from userspace)

It is possible with the technique is known as function interposition.

It works for applications that you start or control the start-up environment to be able to set LD_PRELOAD environment variable.



Related Topics



Leave a reply



Submit