Llamadas al sistema personalizadas en Linux/Ubuntu 16.04
Por todos es conocido que el sistema operativo nos ofrece una serie de funciones para poder solicitarle algún tipo de servicio. Dichas funciones son conocidas como llamadas al sistema y vienen ya predefinidas e incluidas en el kernel del SO para que nuestras aplicaciones puedan hacer uso de los servicios que ofrece el sistema operativo. Pero, ¿que pasa si queremos crear alguna llamada propia para que dicho sistema operativo ofrezca un servicio propio? A continuación, veremos como añadir una nueva llamada al kernel del sistema operativo con la funcionalidad que nosotros mismos deseemos.
Antes de empezar
La siguiente investigación forma parte de un cuaderno de prácticas opcional de la asignatura Diseño de Sistemas Operativos que estoy cursando actualmente. El guión original pretendía añadir una llamada al sistema operativo Ubuntu en su distribución 14.04 y ha sido adaptado por mi para la siguiente versión del SO, Ubuntu 16.04 LTS.
Advertencia: Vamos a trabajar directamente sobre el Kernel del Sistema Operativo, eso significa que si algo sale mal nos podemos cargar todo. Es recomendable usar una máquina virtual por si las moscas :P
Finalmente, el equipo sobre el que estoy trabajando cuenta con las siguientes características:
- Sistema Operativo: Ubuntu 16.04 LTS
- Versión del kernel: 4.4.0–21-generic
Configuración del entorno
En primer lugar, vamos a necesitar asegurarnos de que tenemos algunos de los elementos necesarios para la compilación del kernel de linux, la cual haremos posteriormente instalados en el sistema. Con los siguientes comandos podemos descargarlos si no estuvieran ya instalados.
sudo apt-get install fakeroot build-essential crash
sudo apt-get install kexec-tools makedumpfile kernel-wedge
sudo apt-get install git-core libncurses5 libncurses5-dev
sudo apt-get install libelf-dev binutils-dev
El siguiente paso es instalar y descomprimir las fuentes del kernel de Linux, si no se encuentran ya instaladas en el sistema.
sudo apt-get install linux-source kernel-package
//Si no estaban instaladas, procedemos a descomprimirlas.
cd /usr/src/
sudo tar xjvf /usr/src/linux-source-<LA_VERSION_DESCARGADA>.tar.bz2
Por último, para poder trabajar dentro del kernel realizamos un enlace simbólico (en caso de que no esté ya realizado) y comenzaremos usando la configuración actual del kernel de linux como configuración inicial.
sudo ln -s linux-source-<LA_VERSION_DESCARGADA> linuxcd /usr/src/linux
sudo cp -vi /boot/config-<LA_VERSION_DESCARGADA> .configCreación de la nueva llamadaUna vez realizada la configuración estamos listos para añadir nuestra llamada al sistema. En este ejemplo la llamada al sistema que vamos a añadir se encargará de copiar dos strings (a.k.a "strcpy"). Recibirá dos parámetros, en primer lugar el string (char *) fuente donde se encuentra la información y el string (char *) destino donde se va a copiar.He elegido dicha función por simplicidad pero obviamente se puede añadir cualquier función que creáis de utilidad.1) Crearemos un nuevo directorio donde se almacenará el código fuente de nuestra llamada al sistema. A mayores, añadiremos un subdirectorio que simule el espacio de usuario donde corren nuestras aplicaciones que usen dicha llamada para poder testarla.sudo mkdir /usr/src/linux/mms
sudo mkdir /usr/src/linux/mms/userspace2) Buscaremos el fichero que contiene el listado de todas las llamadas al sistema ya existentes junto a su número identificativo y el directorio donde se encuentra su código. En este fichero, debemos añadir una nueva línea para obtener un identificador para nuestra llamada.//Accedemos a la siguiente ruta
cd usr/src/linux/arch/x86/entry/syscalls//Y debemos modificar el siguiente archivo. (Podemos usar el editor vim)
sudo vim syscall_64.tlbComo ya he comentado dicho archivo contiene un listado de las llamadas al sistema existentes. Debemos buscar la última y añadir a continuación nuestra llamada al sistema junto a su nombre, nombre de la función de tratamiento y número que la identificará (será el primero libre de dicho listado, en mi caso el número 326)
Una vez realizado el cambio en el fichero syscall_64.tlb, debemos modificar el Makefile del kernel para que cuando intente compilar dicha llamada, sepa encontrar la ruta del código fuente de la misma. Para ello volvemos al directorio linux y abrimos para editar el archivo Makefile.cd usr/src/linux//Volvemos a editar con vim. (Cualquier otra alternativa es válida)
sudo vim MakefileDentro del Makefile debemos buscar la siguiente línea (en mi caso, la línea 892) y añadir el directorio donde vamos a albergar el código fuente de la llamada al sistema (en mi caso, es el creado anteriormente y llamado mms).
3) Añadidas las referencias de la nueva llamada al sistema a los ficheros correspondientes, el siguiente paso es crearnos el código fuente de la misma y el makefile para compilarla en código objeto. Ambos ficheros deben residir dentro del directorio que hemos identificado previamente en el makefile del kernel, en mi caso /mms.El archivo mms-strcpy.c contendrá el código fuente de la llamada al sistema:#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <asm/uaccess.h>// En caso de crear una llamada con mas o menos parametros, se debe utilizar otra macro.
// SYSCALL_DEFINEX donde 'X' representa el numero de parametros de la llamada al sistema.
SYSCALL_DEFINE2(mms_strcpy, char *, dest, char *, src)
{
int i=0;
char ch;do {
get_user(ch, src+i);
put_user(ch, dest+i);
i++;
} while (ch != 0);printk ("++ mms_strcpy: done\n");return 1;
}Por su parte, el makefile que compilará dicha llamada cuenta con el siguiente código:obj-y := mms-strcpy.o4) ¡Ya tenemos nuestra llamada al sistema lista para ser añadida al kernel de Linux! Pero antes de eso vamos a crear un pequeño programa que simule a un programa de usuario que la vaya a utilizar para poder probarla. Para ello, dentro del directorio que habíamos creado previamente mms/userspace vamos a añadir dos nuevos ficheros. Por un lado prueba.c y por otro lado un makefile para compilar prueba.c.El archivo prueba.c será un programa de usuario que haga uso de la nueva llamada al sistema incluida. Será codificado de la siguiente forma:#include <stdio.h>
#include <unistd.h>
#include <syscall.h>int main (int argc, char *argv[]){char *src="testing the system call";
char dest[40];
int ret;//IMPORTANTE: La llamada al sistema se identifica por su numero en el fichero
// syscall_64.tlb, en mi caso el 326, en vuestro caso puede cambiar.
ret = syscall(326, dest, src);
printf("copied string: %s\n", dest) ;
printf("code: %d\n", ret) ;return 1;
}El makefile que compilará el programa de usuario es:all:
gcc -Wall -o prueba prueba.c
clean:
rm -fr prueba5) Ya estamos listos para probarla. !Recompilemos el kernel¡Compilación del KernelA la hora de recompilar el kernel vamos a necesitar mucha paciencia y los siguientes comandos. Aviso a navegantes, este proceso puede tardar bastante, en mi caso, ha tardado alrededor de dos horas en recompilar el kernel entero.1) Recompilamos el kernel de nuevo//Dentro del directorio de linux creado
cd /usr/src/linuxsudo fakeroot make-kpkg --initrd \
--append-to-version=-infodso \
kernel-image kernel-headers2) Instalamos las nuevas cabeceras//Dentro del directorio src
cd /usr/srcsudo dpkg -i linux-image-*.deb
sudo dpkg -i linux-headers-*.deb3) Reiniciamos el sistemasudo reboot4) !Probar la nueva llamada al sistema usando el programa que habíamos preparado para ello!//Dentro del directorio que simula espacio de usuario
cd /usr/src/linux/mms/userspace///Compilamos el programa y lo ejecutamos
make
./pruebaConclusionesComo hemos podido ver es bastante sencillo añadir una llamada al sistema personalizada al kernel de Linux. El motivo de este desarrollo no es más que poner en práctica la teoría estudiada en la asignatura de diseño de sistemas operativos y aplicarla a la ultima distribución de Ubuntu que hay actualmente en el mercado: la 16.04.Finalmente, me gustaría añadir algunas referencias que me han servido para escribir este artículo.Referencias
- Sotomayor Fernández, R., del Rio Astorga, D., Calderón Mateos, A. and Fernández Muñoz, J. (2016). Añadir nuevas llamadas al sistema en Linux/Ubuntu 14.04 LTS. Universidad Carlos III de Madrid.
- Franksthinktank.com. (2016). How to: Add system call to Ubuntu. [online] Available at: http://www.franksthinktank.com/howto/addsyscall/ [Accessed 22 May 2016].