I found the list of syscalls for x86-64 mode (with arguments):
http://filippo.io/linux-syscall-table/
but where can I get detailed description of this syscalls?
For example below, which flags can be used for 'open' syscall except 0102o (rw, create), in other cases:
read only, write only, etc.
SECTION .data
message: db 'Hello, world!',0x0a
length: equ $-message
fname db "result"
fd dq 0
SECTION .text
global _start
_start:
mov rax, 2 ; 'open' syscall
mov rdi, fname ; file name
mov rsi, 0102o ; read and write mode, create if not
mov rdx, 0666o ; permissions set
syscall
mov [fd], rax
mov rax, 1 ; 'write' syscall
mov rdi, [fd] ; file descriptor
mov rsi, message ; message address
mov rdx, length ; message string length
syscall
mov rax, 3 ; 'close' syscall
mov rdi, [fd] ; file descriptor
syscall
mov rax, 60
mov rdi, 0
syscall
Based on source (may be)
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/open.c
how to understand it, which (list of all for open) flags can be used?
The documentation for the syscalls is in section 2 of the man pages and/or in the comments in the source code.
The man page begins with:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
The argument flags must include one of the following access modes: O_RDONLY, O_WRONLY, or O_RDWR. These request opening the file read-only, write-only, or read/write, respectively.
In addition, zero or more file creation flags and file status flags can be bitwise-or'd in flags. The file creation flags are O_CREAT, O_EXCL, O_NOCTTY, and O_TRUNC.
The values for these are trivially looked up in the system header files.
Related
I have written simple hello_world program in nasm.
segment .text ; code section
global _start ; must be declared for linker
_start: ; tell linker entry point
mov edx, len ; message len (DX is for data register)
mov ecx, msg ; message to write (CX is for count register)
mov ebx, 1 ; (stdout) file descriptor
mov eax, 1 ; system call number for write system call
int 0x80 ; call kernel
mov eax, 1 ; system call number for exit system call
int 0x80 ; call kernel
section .data
msg db "Hello, World", 0xa
len equ $ - msg
so when i am compiling this program with elf64 flag
$nasm -f elf64 hello_world.nasm
and then linking with ld
$ld hello_world.o
finally when i ran a.out it not write anything to stdout.
when i opened file unistd_64.h to show which system call is calling for no 1.
// /usr/include/asm/unistd_64.h
#ifndef _ASM_X86_UNISTD_64_H
#define _ASM_X86_UNISTD_64_H 1
#define __NR_read 0
#define __NR_write 1
#define __NR_open 2
#define __NR_close 3
#define __NR_stat 4
As you can see for write system call number is 1. this program will work if i put 4 instead of 1 but 4 is specified in unistd_32.h and i also compiled with elf64 flag so why it is not working for 64 bit?
for your reference unistd_32.h
#ifndef _ASM_X86_UNISTD_32_H
#define _ASM_X86_UNISTD_32_H 1
#define __NR_restart_syscall 0
#define __NR_exit 1
#define __NR_fork 2
#define __NR_read 3
#define __NR_write 4
You are making a mistake in which you need to use the sysv-64-abi which states that functions parameters to be passed to rdi, rsi, rdx, rcx. In addition exit syscall number is decimal 60 not 1. also you mistyped section .text. Any you need to use syscall and not old slower int soft interrupt instruction to trap into kernel.
section .text ; code section
global _start ; must be declared for linker
_start: ; tell linker entry point
mov rdx, len ; message len (DX is for data register)
mov rsi, msg ; message to write (CX is for count register)
mov rdi, 1 ; (stdout) file descriptor
mov rax, 1 ; system call number for write system call
syscall ; call kernel
mov rax, 60 ; system call number for exit system call
syscall ; call kernel
section .data
msg db "Hello, World", 0xa
len equ $ - msg
I recently make input using Linux assembly (x86_64) i got this code and i try it eventually the code are doesn't work like what i expected, i should wait a keystroke but it auto input from no where, i suspect termios flags... code are in below :
;Get current settings
Mov EAX, 16 ; SYS_ioctl
Mov EDI, 0 ; STDIN_FILENO
Mov ESI, 0x5401 ; TCGETS
Mov RDX, termios
Int 80h
And dword [c_cflag], 0xFD ; Clear ICANON to disable canonical mode
; Write termios structure back
Mov EAX, 16 ; SYS_ioctl
Mov EDI, 0 ; STDIN_FILENO
Mov ESI, 0x5402 ; TCSETS
Mov RDX, termios
Int 80h
Mov EAX,0 ;sys_read kernel call
Mov EBX,0 ;stdin trap (standart input)
Mov ECX,Nada ;Masukkan offset/jumlah byte yang akan di baca
Mov EDX,1 ;Jumlah byte yang dibaca
Int 80h ;Call Kernel
for the termios struct :
SECTION .bss ;deklarasi untuk variable yang belum terdefinisi
Enter: Resb 1 ;Pesan 1 byte untuk Enter
Nada: Resb 1
termios:
c_iflag Resd 1 ; input mode flags
c_oflag Resd 1 ; output mode flags
c_cflag Resd 1 ; control mode flags
c_lflag Resd 1 ; local mode flags
c_line Resb 1 ; line discipline
c_cc Resb 64 ; control characters
for the output :
nasm -f elf64 -g -F stabs key.asm
ld -o KeyPress key.o
./KeyPress
Untuk memulai tekan tombol enter:
Tekan tombol untuk memainkan satu not: (1,2,3,4,5,6,7,8)
//this part are the error occur,i have to check if user inputed right value
if not it will jump to error label and printed below message//
Error note not found please contact the app developer !!
reference : Linux Getch(), My Github Repo
PS: For the newest code i already push on my repository i use ubuntu 20.04 and Intel i7 (64-bit), thanks for the help
... i got this code and i try it eventually the code are doesn't work like what i expected ...
Mov ESI, 0x5401
Mov RDX, termios
Int 80h
This won't work:
Int 80h is the 32-bit system call used in 32-bit programs. The first three arguments are passed in EBX, ECX and EDX, and definitely not in ESI.
And the values of EAX required for Int 80h differ from the method used in 64-bit programs: read() would be EAX=3, not EAX=0.
Int 80h seems to work in 64-bit programs, too, however, passing 64-bit values wont work; so you cannot use Int 80h for system calls that take addresses (in the example: the address of termios) as argument.
Either you assemble and link your code as 32-bit program, use int 80h, pass the arguments in EBX, ECX and EDX and use the values in EAX required for 32-bit programs (for example: EAX=3 for read()):
mov eax, 54 ; sys_ioctl when using "int 80h"
mov ebx, 0 ; stdin
mov ecx, 0x5402 ; TCSETS
mov edx, termios
int 80h
Or you build a 64-bit program and use the syscall instruction to call system calls (see this question):
mov eax, 0 ; sys_read when using "syscall"
; note that this instruction will actually set RAX to 0
mov edi, 0 ; set RDI to stdin (implicitly sets rdi)
mov rsi, Nada ; Address of the buffer (see below)
; we explicitly have to use "rsi" here!
mov edx, 1 ; number of bytes
syscall
mov ecx, Nada
I don't use "nasm" but another assembler; so maybe I am wrong. But as far as I know the instruction above would be interpreted by "nasm" as:
Read the value stored in the RAM at the address Nada and write that value to the ecx register.
However, you want the address of Nada to be written to the ecx register.
As far as I know, this instruction would be written as: mov ecx, offset Nada in "masm".
If this is true, the corresponding line in my example above must be: mov rsi, offset Nada.
And dword [c_cflag], 0xFD ; Clear ICANON to disable canonical mode
This line contains two errors:
ICANON is located in C_LFLAG, not in C_CFLAG.
And this instruction would be identical to the C/C++ instruction: c_cflag &= ~0xFFFFFF02, but you want to do: c_cflag &= ~2.
To clear bit 1 only, you have two possibilities:
And byte [c_lflag], 0xFD
; OR:
And dword [c_lflag], 0xFFFFFFFD
I want to make Linux just take 1 keystroke from keyboard using sys_read, but sys_read just wait until i pressed enter. How to read 1 keystroke ? this is my code:
Mov EAX,3
Mov EBX,0
Mov ECX,Nada
Mov EDX,1
Int 80h
Cmp ECX,49
Je Do_C
Jmp Error
I already tried using BIOS interrupt but it's failed (Segmentation fault), I want capture number 1 to 8 input from keyboard.
Syscalls in 64-bit linux
The tables from man syscall provide a good overview here:
arch/ABI instruction syscall # retval Notes
──────────────────────────────────────────────────────────────────
i386 int $0x80 eax eax
x86_64 syscall rax rax See below
arch/ABI arg1 arg2 arg3 arg4 arg5 arg6 arg7 Notes
──────────────────────────────────────────────────────────────────
i386 ebx ecx edx esi edi ebp -
x86_64 rdi rsi rdx r10 r8 r9 -
I have omitted the lines that are not relevant here. In 32-bit mode, the parameters were transferred in ebx, ecx, etc and the syscall number is in eax. In 64-bit mode it is a little different: All registers are now 64-bit wide and therefore have a different name. The syscall number is still in eax, which now becomes rax. But the parameters are now passed in rdi, rsi, etc. In addition, the instruction syscall is used here instead of int 0x80 to trigger a syscall.
The order of the parameters can also be read in the man pages, here man 2 ioctl and man 2 read:
int ioctl(int fd, unsigned long request, ...);
ssize_t read(int fd, void *buf, size_t count);
So here the value of int fd is in rdi, the second parameter in rsi etc.
How to get rid of waiting for a newline
Firstly create a termios structure in memory (in .bss section):
termios:
c_iflag resd 1 ; input mode flags
c_oflag resd 1 ; output mode flags
c_cflag resd 1 ; control mode flags
c_lflag resd 1 ; local mode flags
c_line resb 1 ; line discipline
c_cc resb 19 ; control characters
Then get the current terminal settings and disable canonical mode:
; Get current settings
mov eax, 16 ; syscall number: SYS_ioctl
mov edi, 0 ; fd: STDIN_FILENO
mov esi, 0x5401 ; request: TCGETS
mov rdx, termios ; request data
syscall
; Modify flags
and byte [c_lflag], 0FDh ; Clear ICANON to disable canonical mode
; Write termios structure back
mov eax, 16 ; syscall number: SYS_ioctl
mov edi, 0 ; fd: STDIN_FILENO
mov esi, 0x5402 ; request: TCSETS
mov rdx, termios ; request data
syscall
Now you can use sys_read to read in the keystroke:
mov eax, 0 ; syscall number: SYS_read
mov edi, 0 ; int fd: STDIN_FILENO
mov rsi, buf ; void* buf
mov rdx, len ; size_t count
syscall
Afterwards check the return value in rax: It contains the number of characters read.
(Or a -errno code on error, e.g. if you closed stdin by running ./a.out <&- in bash. Use strace to print a decoded trace of the system calls your program makes, so you don't need to actually write error handling in toy experiments.)
References:
What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?
Why does the sys_read system call end when it detects a new line?
How do i read single character input from keyboard using nasm (assembly) under ubuntu?
Using the raw keyboard mode under Linux (external site with example in 32-bit assembly)
I'm using inotify to listen to events on a directory IN_MOVED_FROM/IN_MOVED_TO pair. Here is the simple assembly program:
SYS_exit equ 0x3C
SYS_inotify_init equ 253
SYS_read equ 0x00
MEMPAGE_SIZE equ 4096
SYS_inotify_add_watch equ 254
IN_MOVED_FROM equ 0x40
IN_MOVED_TO equ 0x80
section .text
global _start
_start:
mov rax, SYS_inotify_init
syscall
mov r13, rax ;storing inotify instance
mov rax, SYS_inotify_add_watch
mov rdi, r13
mov rsi, watcher_dir
mov rdx, IN_MOVED_TO
syscall
mov rax, SYS_inotify_add_watch
mov rdi, r13
mov rsi, watcher_dir
mov rdx, IN_MOVED_FROM
syscall
mov rax, SYS_read
mov rdi, r13
mov rsi, buf
mov rdx, MEMPAGE_SIZE
syscall <------ I set gdb breakpoint here
mov eax, SYS_exit
mov rdi, 0x00
syscall
section .bss
buf resb MEMPAGE_SIZE ;reserving a page
section .data
watcher_dir: db '/home/me/asm/inotify/data', 0
Ok I run the program in gdb with breakpoint set to reading from inotify descriptor and examine the
struct inotify_event {
int wd; /* Watch descriptor */
uint32_t mask; /* Mask describing event */
uint32_t cookie; /* Unique cookie associating related
events (for rename(2)) */
uint32_t len; /* Size of name field */
char name[]; /* Optional null-terminated name */
};
And when I move a file as follows
mv data/test data/test.moved
I got only one event IN_MOVED_FROM
(gdb) x/1xw $rsi + 4
0x600138: 0x00000040
The lenght is 0x10:
(gdb) x/1xw $rsi + 12
0x600140: 0x00000010
and
(gdb) x/1xw $rsi + 32
0x600154: 0x00000000
But I expected IN_MOVED_TO event in the buf (get lost?). When I swap the order of the inotify_add_watch in the code the first again get lost. Why?
This is not an assembly problem, you just used the system call wrong. You forgot to specify IN_MASK_ADD. This is what man inotify says:
IN_MASK_ADD
If a watch instance already exists for the filesystem
object corresponding to pathname, add (OR) the events in
mask to the watch mask (instead of replacing the mask).
Admittedly it's counter-intuitive that inotify_add_watch replaces by default and only adds if you ask for it explicitly.
My problem is related with Assembler and Shellcoding.
I started off by writing my first shellcode and it worked out pretty well so far. I then made an assembly script of the following C code:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
int fd = open("test.txt", O_CREAT | O_WRONLY);
write(fd, "Hello World!", 6);
return 0;
}
The assembly code for that piece looks like this:
global _start
_start:
xor eax, eax ; null eax reg
push 0x7478742e ; push "test.txt" on stack
push 0x74736574
mov ebx, esp ; first Argument
mov cl, 0x41 ; Flags O_CREAT | O_WRONLY
mov al, 0x5 ; sys_open
int 0x80
push 0x736b6330 ; "shellcodingr0cks"
push 0x72676e69
push 0x646f636c
push 0x6c656853
mov ebx, eax ; file identifier
mov ecx, esp ; string on the stack
mov dl, 0x10 ; 0x10 is the size of the string
mov al, 0x4 ; sys_write
int 0x80
xor eax, eax ; exit proc
inc eax
int 0x80
The Program works pretty well and I've got the expected output but there is one problem and I don't know why this is occurring.
The filename of the file I'm writing to should be test.txt but it is writing to test.txt^A. I don't know where the ^A is coming from, nor do I know how to fix it.
Does anyone know what is wrong, and how I can fix it?