[===============================================================] | | | R34LL33T P4P3R | | SYSCALLS | | | | F31t0 P3l0 m415 l33t d4 4r3a | | ~ Merlin | | | [===============================================================]
[ Syscall ]
Afinal, o que é uma syscall? Falando de forma (bem) resumida, uma syscall é a interface de comunicação entre o usuario e o kernel.
Quando um processo é iniciado, o kernel cria uma range de funções que o programa pode usar, usando da API de syscalls.
É chamada de alguns por entrypoint do usuário para o kernel, por conta de quando uma syscall for executada, ela muda de user-mode para kernel-mode.
As syscalls possuem um número, que corresponde a ela na arquitetura que esteja usando, esse valor da syscall é armazenado no AX/EAX/RAX (16 bits, 32 bits e 64 bits), que é chamado de accumulator (não vou me aprofundar nos registradores nesse artigo em específico), e também possuem argumentos, que são armazenados nos outros registradores (bx, cx, dx, si, di e bp). Vamos usar a syscall mkdir no x86, de exemplo! Ela possui 2 argumentos, então caso queiramos usa-la, teremos que definir os dois argumentos, que no caso ficariam armazenados dentro do EBX e no ECX, esses dois argumentos são o pathname e o mode, onde o pathname é o nome da pasta, e o mode é a permissão do diretorio.
[ GLIBC Wrappers ]
Bom, a GLIBC Tem uma coisa chamada System call wrappers, que são funções que imitam as syscalls do Linux. Vamos usar de novo um exemplo, dessa vez uma syscall-wrapper. Vamos usar o wrapper do write!, ela possui 3 argumentos, FD, o buf e o count! o FD, é o file descriptor que queremos usar, temos como opção: 0, 1 e 2 (STDIN, STDOUT e STDERR), Como só queremos escrever algo na tela, iremos usar o FD 0, o buf, é básicamente o texto que queremos, vamos escrever Hello World, nesse caso, e por último, vem o count, que é quantos caracteres tem o buf, que são 11 caracteres. Tendo isso em mente, podemos fazer o seguinte:
#include <unistd.h> int main() { write(0, "Hello World", 11); }
Pronto, temos o código pronto, agora vamos rodar e compilar ele!
[merlin@7K SyscallsLinux]$ ls -l main.c -rw-r--r-- 1 merlin merlin 67 mai 14 01:02 main.c [merlin@7K SyscallsLinux]$ cc main.c -o main [merlin@7K SyscallsLinux]$ ./main Hello World [merlin@7K SyscallsLinux]$
Catapimbas!!! Funcionou, mas vamos para algo mais interessante, vamos ver se ele realmente usa a syscall write.
[merlin@7K SyscallsLinux]$ strace ./main [...] write(0, "Hello World", 11Hello World) = 11 exit_group(0) = ?
É, agora temos certeza! Ele realmente usou a syscall write, POREM, isso não acontece todas as vezes. Vamos usar uma syscall-wrapper como exemplo denovo, nesse caso vai ser a exit.
[merlin@7K SyscallsLinux]$ cat main.c #includeint main() { _exit(1); write(0, "Hello World", 11); // Não vai ser executado } [merlin@7K SyscallsLinux]$ cc main.c -o main [merlin@7K SyscallsLinux]$ ./main
nosso write, realmente não foi executado, então o exit funcionou!!! Agora, vamos ver se ele realmente virou a syscall exit do Linux.
[merlin@7K SyscallsLinux]$ strace ./main [...] exit_group(1) = ? +++ exited with 1 +++
Oloco, bixo. Ele não executou a syscall exit, e sim a syscall exit_group, porém, isso não é incomum. Nem sempre o wrapper vai executar a mesma syscall de seu nome!
.''. (~~~~) || __||__ /______\ | |' _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |'|o| - - - - - - - - - - - - - - - - - - - - - - - - -|| | |'| | || | |'| | . ' . || | |'| | . ' ' . || | |'| | . ' .-'"'-. ' . || | |'| | . ' ," ". ' . || |r |'| | . ' /: x0x0x :\ ' . || |s |'| | . ' ; . x0x0x ; ' . || |t |'| | ' . \: ..x0x0x :/ . ' || | |'| | ' . `. . . ,/ . ' || | |'| | ' . `-.,,.-' . ' || | |'| | ' . . ' || | |'| | ' . . ' || | |'| | ' || | |'|o|-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_|| | |' | |' | |' united by brazil - 100% BRAZUKAAAAAAAAAAAA | |' '~~'
Fontes:syscall.sh