it-swarm-es.tech

Kernel de Linux: buen tutorial para principiantes

Estoy interesado en modificar los componentes internos del núcleo, aplicar parches, manejar controladores de dispositivos y módulos, para mi propia diversión personal.

¿Existe un recurso integral para hackear kernel, destinado a programadores experimentados?

52
Adam Matan
**TODO** +editPic: Linux Kernel Developer -> (Ring Layer 0)
         +addSection: Kernel Virtualization Engine

KERN_WARN_CODING_STYLE: Do not Loop unless you absolutely have to.

Libros recomendados para Sin inicializar void *i

"Los hombres no entienden los libros hasta que tienen una cierta cantidad de vida, o en cualquier caso, ningún hombre entiende un libro profundo, hasta que ha visto y vivido al menos parte de su contenido". –¡Ezra Libra

Un viaje de mil ¡millas de código debe comenzar con un solo paso. Si está confundido acerca de con cuál de los siguientes libros comenzar, no se preocupe, elija uno de su elección. No todos aquellos que divagan están perdidos. Como ¡todas las carreteras finalmente se conectan a la autopista, explorará cosas nuevas en su viaje del núcleo a medida que las páginas progresen sin encontrar ningún callejón sin salida, y finalmente se conectará a la code-set. Lea con alerta mental y recuerde: ¡El código no es literatura.

Lo que queda no es una cosa o una emoción o una imagen o una imagen mental o un recuerdo o incluso una idea. Es una funcion. Un proceso de algún tipo. Un aspecto de la vida que podría describirse como una función de algo "más grande". Y por lo tanto, parece que no está realmente "separado" de esa otra cosa. Al igual que la función de un cuchillo, cortar algo, no está, de hecho, separado del cuchillo en sí. La función puede o no estar en uso en este momento, pero NUNCA está potencialmente separada.

Algoritmo desrandomizado Solovay Strassen para prueba de primalidad :

Solovay Strassen Derandomized Algorithm for Primality Test

Leer para no contradecir y confutar; ni creer y dar por sentado; ni encontrar conversación y discurso; pero para pesar y considerar. Algunos libros deben probarse, otros deben tragarse, y algunos pocos deben ser masticados y digeridos: es decir, algunos libros deben leerse solo en partes, otros deben leerse, pero no con curiosidad, y algunos pocos deben leerse por completo , y con diligencia y atención.

static void tasklet_hi_action(struct softirq_action *a)
{
        struct tasklet_struct *list;

        local_irq_disable();
        list = __this_cpu_read(tasklet_hi_vec.head);
        __this_cpu_write(tasklet_hi_vec.head, NULL);
        __this_cpu_write(tasklet_hi_vec.tail, this_cpu_ptr(&tasklet_hi_vec.head));
        local_irq_enable();

        while (list) {
                struct tasklet_struct *t = list;

                list = list->next;

                if (tasklet_trylock(t)) {
                        if (!atomic_read(&t->count)) {
                                if (!test_and_clear_bit(TASKLET_STATE_SCHED,
                                                        &t->state))
                                        BUG();
                                t->func(t->data);
                                tasklet_unlock(t);
                                continue;
                        }
                        tasklet_unlock(t);
                }

                local_irq_disable();
                t->next = NULL;
                *__this_cpu_read(tasklet_hi_vec.tail) = t;
                __this_cpu_write(tasklet_hi_vec.tail, &(t->next));
                __raise_softirq_irqoff(HI_SOFTIRQ);
                local_irq_enable();
        }
}

Core Linux (5 -> 1 -> 3 -> 2 -> 7 -> 4 -> 6)

“La naturaleza no tiene kernel ni Shell; ella es todo a la vez "- Johann Wolfgang von Goethe

El lector debe estar bien versado con conceptos del sistema operativo ; una comprensión justa de los procesos de larga ejecución y sus diferencias con procesos con breves ráfagas de ejecución; tolerancia a fallas mientras se cumplen restricciones de tiempo real blando y duro. Mientras lee, es importante entender y n/ack las elecciones de diseño realizadas por la fuente del kernel de Linux en los subsistemas centrales.

Los hilos [y] las señales [son] un rastro de miseria, desesperación, horror y locura dependiente de la plataforma (~ Anthony Baxte). Dicho esto, debe ser un experto en autoevaluación de C, antes de sumergirse en el núcleo. También debe tener una buena experiencia con Listas vinculadas, pilas, colas, árboles rojos negros, funciones hash, etc.

volatile int i;
int main(void)
{
    int c;
    for (i=0; i<3; i++) {
        c = i&&&i;
        printf("%d\n", c);    /* find c */
    }
    return 0;
}

La belleza y el arte de la fuente del kernel de Linux reside en la ofuscación deliberada del código que se usa a lo largo. Esto a menudo es necesario para transmitir el significado computacional que involucra dos o más operaciones de una manera limpia y elegante. Esto es especialmente cierto cuando se escribe código para arquitectura de múltiples núcleos.

Conferencias de video en sistemas en tiempo real , Programación de tareas , Compresión de memoria , Barreras de memoria , SMP

#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
  1. Desarrollo del kernel de Linux - Robert Love
  2. Comprensión del kernel de Linux - Daniel P. Bovet, Marco Cesati
  3. El arte del diseño KerneL de Linux - Yang Lixiang
  4. Arquitectura profesional del kernel de Linux - Wolfgang Mauerer
  5. Diseño del sistema operativo UNIX - Maurice J. Bach
  6. Comprensión del Administrador de memoria virtual de Linux - Mel Gorman
  7. Linux Kernel Internals - ¡Tigran Aivazian
  8. Manual de Linux incorporado - Christopher Hallinan

Controladores de dispositivos Linux (1 -> 2 -> 4 -> 3 -> 8 -> ...)

"La música no te lleva a lo largo. Tienes que llevarlo estrictamente por tu capacidad de concentrarte realmente en ese pequeño núcleo de emoción o historia". - Debbie Harry

Su tarea es básicamente establecer una interfaz de comunicación de alta velocidad entre el dispositivo de hardware y el núcleo del software. Debe leer la hoja de datos/manual de referencia de hardware para comprender el comportamiento del dispositivo y sus estados de control y datos y canales físicos proporcionados. El conocimiento de ensamblaje para su arquitectura particular y un conocimiento justo de los lenguajes de descripción de hardware VLSI como VHDL o Verilog lo ayudarán a largo plazo.

[~ # ~] q [~ # ~] : Pero, ¿por qué tengo que leer las especificaciones de hardware?

[~ # ~] a [~ # ~] : Porque, "Hay un abismo de carbono y silicio que el software no puede salvar" - Rahul Sonnad

Sin embargo, lo anterior no plantea un problema para ¡Algoritmos computacionales ( Código del controlador - procesamiento de la mitad inferior), ya que se puede simular completamente en un niversal Turing Machine . Si el resultado calculado es verdadero en dominio matemático , es seguro que también es cierto en dominio físico .

Conferencias de video en controladores de dispositivos Linux (Lec. 17 y 18), Anatomía de un controlador KMS incorporado , Control de pin y actualización GPIO , Marco de reloj común , Escribir un controlador real de Linux - Greg KH

static irqreturn_t phy_interrupt(int irq, void *phy_dat)
{
         struct phy_device *phydev = phy_dat;

         if (PHY_HALTED == phydev->state)
                 return IRQ_NONE;                /* It can't be ours.  */

         /* The MDIO bus is not allowed to be written in interrupt
          * context, so we need to disable the irq here.  A work
          * queue will write the PHY to disable and clear the
          * interrupt, and then reenable the irq line.
          */
         disable_irq_nosync(irq);
         atomic_inc(&phydev->irq_disable);

         queue_work(system_power_efficient_wq, &phydev->phy_queue);

         return IRQ_HANDLED;
}
  1. Controladores de dispositivo Linux - Jonathan Corbet, Alessandro Rubini y Greg Kroah-Hartman
  2. Controladores de dispositivos esenciales de Linux - Sreekrishnan Venkateswaran
  3. Escribiendo controladores de dispositivos Linux - Jerry Cooperstein
  4. La guía de programación del módulo del kernel de Linux - Peter Jay Salzman, Michael Burian, Ori Pomerantz
  5. Linux PCMCIA Programmer's Guide - David Hinds
  6. Guía de programación de Linux SCSI - ¡Heiko Eibfeldt
  7. Guía de programación en serie para sistemas operativos POSIX - Michael R. Sweet
  8. Controladores de gráficos de Linux: una introducción - Stéphane Marchesin
  9. Guía de programación para controladores de dispositivos USB de Linux - Detlef Fliegl
  10. El modelo de dispositivo del kernel de Linux - Patrick Mochel

Redes de kernel (1 -> 2 -> 3 -> ...)

"Llámalo clan, llámalo red, llámalo tribu, llámalo familia: como sea que lo llames, seas quien seas, necesitas uno". - Jane Howard

Comprender un recorrido de paquetes en el kernel es clave para comprender las redes del kernel. Comprenderlo es imprescindible si queremos entender Netfilter o IPSec internos, y más. Las dos estructuras más importantes de la capa de red del kernel de Linux son: struct sk_buff y struct net_device

static inline int sk_hashed(const struct sock *sk)
{
        return !sk_unhashed(sk);
} 
  1. Comprender los aspectos internos de la red Linux - Christian Benvenuti
  2. Redes de kernel de Linux: implementación y teoría - Rami Rosen
  3. Programación de red UNIX - W. Richard Stevens
  4. La guía definitiva para la programación de redes Linux - Keir Davis, John W. Turner, Nathan Yocom
  5. La pila TCP/IP de Linux: Redes para sistemas integrados - Thomas F. Herbert
  6. Programación de sockets de Linux por ejemplo - Warren W. Gay
  7. Linux Cómo enrutar y controlar el tráfico - Bert Hubert

Depuración del núcleo (1 -> 4 -> 9 -> ...)

A menos que al comunicarse con él se diga exactamente lo que se quiere decir, es probable que surjan problemas. ~ Alan Turing, sobre las computadoras

Brian W. Kernighan, en el documento Unix para principiantes (1979) dijo: "La herramienta de depuración más eficaz sigue siendo un pensamiento cuidadoso, junto con declaraciones impresas juiciosamente colocadas". Saber qué recopilar le ayudará a obtener los datos correctos rápidamente para un diagnóstico rápido. El gran científico informático Edsger Dijkstra dijo una vez que las pruebas pueden demostrar la presencia de errores pero no su ausencia. Las buenas prácticas de investigación deben equilibrar la necesidad de resolver problemas rápidamente, la necesidad de desarrollar sus habilidades y el uso efectivo de expertos en la materia.

Hay momentos en que tocas fondo, nada parece funcionar y te quedas sin todas tus opciones. Es entonces cuando comienza la verdadera depuración. Un error puede proporcionar el descanso que necesita para desconectarse de una fijación en la solución ineficaz.

Conferencias de video sobre Kernel Debug and Profiling , Core Dump Analysis , Depuración multinúcleo con GDB , Control de condiciones de carrera multinúcleo , Depuración electrónica

/* Buggy Code -- Stack frame problem
 * If you require information, do not free memory containing the information
 */
char *initialize() {
  char string[80];
  char* ptr = string;
  return ptr;
}

int main() {
  char *myval = initialize();
  do_something_with(myval);
}
/*  “When debugging, novices insert corrective code; experts remove defective code.”
 *     – Richard Pattis
#if DEBUG
 printk("The above can be considered as Development and Review in Industrial Practises");
#endif
 */
  1. Depuración de Linux y ajuste del rendimiento - Steve Best
  2. Técnicas de depuración de aplicaciones de Linux - Aurelian Melinte
  3. Depuración con GDB: El GNU Depurador de nivel de fuente - Roland H. Pesch
  4. Depuración de Linux incorporado - Christopher Hallinan
  5. El arte de la depuración con GDB, DDD y Eclipse - ¡Norman S. Matloff
  6. Por qué fallan los programas: una guía para la depuración sistemática - Andreas Zeller
  7. Software Exorcism: A Handbook for Debugging and Optimizing Legacy Code - Bill Blunden
  8. Depuración: Encontrando los problemas de hardware y software más difíciles de alcanzar - David J. Agans
  9. Depuración por pensamiento: un enfoque multidisciplinario - Robert Charles Metzger
  10. Encuentra el error: un libro de programas incorrectos - Adam Barr

Sistemas de archivos (1 -> 2 -> 6 -> ...)

"Quería tener memoria virtual, al menos ya que está acoplada con sistemas de archivos". - Ken Thompson

En un sistema UNIX, todo es un archivo; Si algo no es un archivo, es un proceso, a excepción de las tuberías y sockets con nombre. En un sistema de archivos, un archivo está representado por un inode, un tipo de número de serie que contiene información sobre los datos reales que componen el archivo. El sistema de archivos virtual de Linux VFS almacena en caché la información en la memoria de cada sistema de archivos a medida que se monta y se usa. Se debe tener mucho cuidado para actualizar el sistema de archivos correctamente ya que los datos dentro de estos cachés se modifican a medida que se crean, escriben y eliminan archivos y directorios. El más importante de estos cachés es el Buffer Cache, que está integrado en la forma en que los sistemas de archivos individuales acceden a sus dispositivos de almacenamiento de bloques subyacentes.

Conferencias de video en sistemas de almacenamiento , Sistema de archivos compatible con Flash

long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{
        struct open_flags op;
        int fd = build_open_flags(flags, mode, &op);
        struct filename *tmp;

        if (fd)
                return fd;

        tmp = getname(filename);
        if (IS_ERR(tmp))
                return PTR_ERR(tmp);

        fd = get_unused_fd_flags(flags);
        if (fd >= 0) {
                struct file *f = do_filp_open(dfd, tmp, &op);
                if (IS_ERR(f)) {
                        put_unused_fd(fd);
                        fd = PTR_ERR(f);
                } else {
                        fsnotify_open(f);
                        fd_install(fd, f);
                }
        }
        putname(tmp);
        return fd;
}

SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
{
        if (force_o_largefile())
                flags |= O_LARGEFILE;

        return do_sys_open(AT_FDCWD, filename, flags, mode);
}
  1. Sistemas de archivos Linux - ¡Moshe Bar
  2. Linux Filesystems - William Von Hagen
  3. Sistemas de archivos UNIX: evolución, diseño e implementación - Steve D. Pate
  4. Diseño práctico del sistema de archivos - Dominic Giampaolo
  5. Análisis forense del sistema de archivos - Brian Carrier
  6. Jerarquía del sistema de archivos de Linux - Binh Nguyen
  7. BTRFS: El sistema de archivos del árbol B de Linux - Ohad Rodeh
  8. StegFS: un sistema de archivos esteganográfico para Linux - Andrew D. McDonald, Markus G. Kuhn

Seguridad (1 -> 2 -> 8 -> 4 -> 3 -> ...)

"UNIX no fue diseñado para evitar que sus usuarios hagan cosas estúpidas, ya que eso también les impediría hacer cosas inteligentes". - Doug Gwyn

Ninguna técnica funciona si no se usa. La ética cambia con la tecnología.

" F × S = k" el ​​producto de libertad y seguridad es una constante. - Leyes de Niven

La criptografía forma la base de la confianza en línea. Hackear es explotar los controles de seguridad, ya sea en un elemento técnico, físico o humano. Proteger el kernel de otros programas en ejecución es un primer paso hacia un sistema seguro y estable, pero esto obviamente no es suficiente: también debe existir cierto grado de protección entre diferentes aplicaciones de usuario. Los exploits pueden apuntar a servicios locales o remotos.

"No puedes hackear tu destino, fuerza bruta ... necesitas una puerta trasera, un canal lateral hacia la Vida." ― Clyde Dsouza

Las computadoras no resuelven problemas, ejecutan soluciones. Detrás de cada código algorítmico no determinista , hay una mente determinada . --/var/log/dmesg

Conferencias de video sobre criptografía y seguridad de red , espacios de nombres para seguridad , Protección contra Ataques remotos , Linux embebido seguro

env x='() { :;}; echo vulnerable' bash -c "echo this is a test for Shellsock"
  1. Hacking: El arte de la explotación - Jon Erickson
  2. The Rootkit Arsenal: Escape and Evasion in the Dark Corners of the System - Bill Blunden
  3. Hacking expuesto: secretos de seguridad de red - Stuart McClure, Joel Scambray, George Kurtz
  4. na guía para la explotación del núcleo: atacando el núcleo - ¡Enrico Perla, Massimiliano Oldani
  5. The Art of Memory Forensics - Michael Hale Ligh, Andrew Case, Jamie Levy, AAron Walters
  6. Ingeniería inversa práctica - Bruce Dang, Alexandre Gazet, Elias Bachaalany
  7. Análisis práctico de malware - Michael Sikorski, Andrew Honig
  8. Máxima seguridad de Linux: una guía de piratas informáticos para proteger su servidor Linux - ¡Anónimo
  9. Seguridad de Linux - Craig Hunt
  10. Seguridad del mundo real de Linux - Bob Toxen

Fuente del núcleo (0.11 -> 2.4 -> 2.6 -> 3.18)

"Al igual que el vino, el dominio de la programación del núcleo madura con el tiempo. Pero, a diferencia del vino, se vuelve más dulce en el proceso". --Lawrence Mucheka

Puede que no pienses que los programadores son artistas, pero la programación es una profesión extremadamente creativa. Es creatividad basada en la lógica. La educación en ciencias de la computación no puede convertir a nadie en un programador experto, del mismo modo que estudiar pinceles y pigmentos no puede hacer que alguien sea un pintor experto. Como ya sabes, hay una diferencia entre conocer el camino y recorrerlo; Es de suma importancia arremangarse y ensuciarse las manos con el código fuente del núcleo. Finalmente, con su kernel así ganado ¡conocimiento, donde quiera que vaya, tendrá ¡brillo.

Los codificadores inmaduros imitan; codificadores maduros roban; los codificadores malos desfiguran lo que toman, y los codificadores buenos lo convierten en algo mejor, o al menos en algo diferente. El buen codificador suelda su robo en una sensación única, completamente diferente de la que se desgarró.

Conferencias de video en Recetas de Kernel

linux-0.11
├── boot
│   ├── bootsect.s      head.s      setup.s
├── fs
│   ├── bitmap.c    block_dev.c buffer.c        char_dev.c  exec.c
│   ├── fcntl.c     file_dev.c  file_table.c    inode.c     ioctl.c
│   ├── namei.c     open.c      pipe.c          read_write.c
│   ├── stat.c      super.c     truncate.c
├── include
│   ├── a.out.h     const.h     ctype.h     errno.h     fcntl.h
│   ├── signal.h    stdarg.h    stddef.h    string.h    termios.h
│   ├── time.h      unistd.h    utime.h
│   ├── asm
│   │   ├── io.h    memory.h    segment.h   system.h
│   ├── linux
│   │   ├── config.h    fdreg.h fs.h    hdreg.h     head.h
│   │   ├── kernel.h    mm.h    sched.h sys.h       tty.h
│   ├── sys
│   │   ├── stat.h      times.h types.h utsname.h   wait.h
├── init
│   └── main.c
├── kernel
│   ├── asm.s       exit.c      fork.c      mktime.c    panic.c
│   ├── printk.c    sched.c     signal.c    sys.c       system_calls.s
│   ├── traps.c     vsprintf.c
│   ├── blk_drv
│   │   ├── blk.h   floppy.c    hd.c    ll_rw_blk.c     ramdisk.c
│   ├── chr_drv
│   │   ├── console.c   keyboard.S  rs_io.s
│   │   ├── serial.c    tty_io.c    tty_ioctl.c
│   ├── math
│   │   ├── math_emulate.c
├── lib
│   ├── close.c  ctype.c  dup.c     errno.c  execve.c  _exit.c
│   ├── malloc.c open.c   setsid.c  string.c wait.c    write.c
├── Makefile
├── mm
│   ├── memory.c page.s
└── tools
    └── build.c
  1. Comienzo para principiantes con fuente Linux 0.11 (menos de 20,000 líneas de código fuente). Después de 20 años de desarrollo, en comparación con Linux 0.11, Linux se ha vuelto muy grande, complejo y difícil de aprender. Pero el concepto de diseño y la estructura principal no tienen cambios fundamentales. Aprender Linux 0.11 todavía tiene una importancia práctica importante.
  2. Lectura obligatoria para los piratas informáticos del kernel => Linux_source_dir/Documentation/*
  3. Debe estar suscrito y activo en al menos una lista de correo del núcleo. Comience con novatos del kernel .
  4. No necesita leer el código fuente completo. Una vez que esté familiarizado con las API del núcleo y su uso, comience directamente con el código fuente del subsistema que le interesa. También puede comenzar escribiendo sus propios módulos plug-n-play para experimentar con el núcleo.
  5. Los escritores de controladores de dispositivos se beneficiarían al tener su propio hardware dedicado. Comience con Raspberry Pi .
33
manav m-n

Linux Kernel Newbies es un gran recurso.

12
Hemant

Le sugiero que lea " Kernel de Linux en una cáscara de nuez ", de Greg Kroah-Hartman y " Comprensión del kernel de Linux ", de Robert Love. Debe leer :)

5
wzzrd

Controladores de dispositivo Linux es otro buen recurso. Te daría otra forma de entrar en el funcionamiento interno. Del prefacio:

Este es, en la superficie, un libro sobre la escritura de controladores de dispositivos para el sistema Linux. Esa es una meta digna, por supuesto; No es probable que el flujo de nuevos productos de hardware disminuya en el corto plazo, y alguien tendrá que hacer que todos esos nuevos dispositivos funcionen con Linux. Pero este libro también trata sobre cómo funciona el kernel de Linux y cómo adaptar su funcionamiento a sus necesidades o intereses. Linux es un sistema abierto; Con este libro, esperamos que sea más abierto y accesible para una comunidad más grande de desarrolladores.

4
Larry Smithmier

Vea The Proyecto de documentación de Linux . En particular, la "Guía del módulo del kernel de Linux".

2
wazoox

Linux Kernel 2.4 Internals es otro recurso en línea para mirar. Parece adoptar un enfoque bastante "básico", comenzando con el arranque. Aquí el TOC:

  1. Arranque
    • 1.1 Construyendo la Imagen del Kernel de Linux
    • 1.2 Arranque: descripción general
    • 1.3 Arranque: BIOS POST
    • 1.4 Arranque: sector de arranque y configuración
    • 1.5 Usando LILO como gestor de arranque
    • 1.6 Inicialización de alto nivel
    • 1.7 Arranque SMP en x86
    • 1.8 Liberación de datos y código de inicialización
    • 1.9 Procesando la línea de comando del núcleo
  2. Gestión de procesos e interrupciones
    • 2.1 Estructura de tareas y tabla de procesos
    • 2.2 Creación y terminación de tareas y subprocesos del núcleo
    • 2.3 Programador de Linux
    • 2.4 Implementación de la lista enlazada de Linux
    • 2.5 Colas de espera
    • 2.6 Temporizadores de kernel
    • 2.7 Mitades inferiores
    • 2.8 Colas de tareas
    • 2.9 Tasklets
    • 2.10 Softirqs
    • 2.11 ¿Cómo se implementan las llamadas al sistema en la arquitectura i386?
    • 2.12 Operaciones atómicas
    • 2.13 Spinlocks, Spinlocks de lectura-escritura y Big-Reader Spinlocks
    • 2.14 Semáforos y semáforos de lectura/escritura
    • 2.15 Soporte de kernel para cargar módulos
  3. Sistema de archivos virtual (VFS)
    • 3.1 Inche Caches e interacción con Dcache
    • 3.2 Registro/desregistro del sistema de archivos
    • 3.3 Gestión del descriptor de archivos
    • 3.4 Gestión de estructura de archivos
    • 3.5 Superblock y gestión de punto de montaje
    • 3.6 Ejemplo de sistema de archivos virtual: pipefs
    • 3.7 Ejemplo de sistema de archivos de disco: BFS
    • 3.8 Dominios de ejecución y formatos binarios
  4. Caché de página de Linux
  5. Mecanismos de la CIP
    • 5.1 Semáforos
    • 5.2 Colas de mensajes
    • 5.3 Memoria compartida
    • 5.4 Linux IPC Primitivas

Y, para hacerlo aún más dulce, hay una nueva tercera edición de Linux Kernel Development de Robert Love y Slashdot tiene una revisión

2
Larry Smithmier

Comience con Linux Kernel Primer por Claudia Salzberg et al. Buena para empezar para principiantes. El libro de Robert Love definitivamente no es el libro con el que los principiantes deberían comenzar. El último libro está por encima del nivel intermedio.

1
PaulDaviesC